понедельник, 31 января 2011 г.

Почему в Windows не может быть двух карт ввода-вывода одновременно

Карта ввода-вывода находится в TSS и описывает всё адресное пространство портов ввода/вывода, которое составляет 65536 портов, т.е. для описания всего адресного пространства понадобится 8Кб. 

TSS представлен дескриптором в GDT, с пределом равным 000020AB (8363 байта)

=============================================
Sel.    Base            Limit     DPL  P   G    Description
=============================================
0028  F7717D70  000020AB   0    P   1b   32-Bit TSS (Busy)
=============================================

#define INT_DIRECTION_MAP_SIZE   32
typedef UCHAR KINT_DIRECTION_MAP[INT_DIRECTION_MAP_SIZE];

#define IOPM_COUNT        1         // Number of i/o access maps that exist
#define IOPM_SIZE            8192    // Size of map callers can set.
#define PIOPM_SIZE          8196    // Size of structure we must allocate to hold it.

typedef struct _KiIoAccessMap
{
    KINT_DIRECTION_MAP DirectionMap;
    UCHAR IoMap[PIOPM_SIZE];
} KIIO_ACCESS_MAP;

В TSS лежит сама карта и смещение:

typedef struct _KTSS
{
...
   USHORT IoMapBase; // Смещение карты ввода/вывода от начала TSS.
   KIIO_ACCESS_MAP IoMaps[IOPM_COUNT];
...

Установить карту ввода-вывода для процесса можно с помощью ф-ции Ke386SetIoAccessMap, которая скопирует в TSS->IoMaps.IoMap пользовательскую карту и установит IoMapBase для процесса на нее.

Связь процесса с картой ввода-вывода:

typedef struct _KPROCESS
{
...
    USHORT IopmOffset; <== определено смещение на нее
    UCHAR Iopl;
...
} KPROCESS, *PKPROCESS, *PRKPROCESS;

При создании процесса вызывается ф-ция KeInitializeProcess:

VOID KeInitializeProcess( __out PRKPROCESS Process, __in KPRIORITY BasePriority, __in KAFFINITY Affinity, __in ULONG_PTR DirectoryTableBase[2], __in BOOLEAN Enable )
{
...
    //
    // Initialize IopmBase and Iopl flag for this process (i386 only)
    //

    Process->IopmOffset = KiComputeIopmOffset(IO_ACCESS_MAP_NONE); <== IoMapBase будет указывать за пределы сегмента TSS
...
}

Для всех процессов(если для них не установлена карта ввода-вывода):

+0x000 Pcb             : _KPROCESS
...
+0x030 IopmOffset  : 0x20ac <== смещение за пределы сегмента
+0x032 Iopl             : 0 ''

А если учесть, что TSS представлен дескриптором в GDT, с пределом равным 000020AB (8363 байта), то получается, что любое обращение процесса к TSS будет генерить #GF, если не установлено 2 бита в eflags.

Итог:

IOPM для всех процессов может быть только один, причем по умолчанию смещение на карту находится на пределами сегмента TSS, однако ф-ция Ke386IoSetAccessProcess к примеру для конкретного процесса и для общей TSS устанавливает IopmOffset на общую карту доступа портов ввода-вывода.

А само смещение обновляется при переключении процессов(аттач/детач):

cPublicProc _KiSwapProcess  ,2
...
;
;   Change IOPM
;
    mov     ax,[edx]+PrIopmOffset
    mov     [ecx]+TssIoMapBase,ax
...

То есть в Windows просто не возможно иметь две и более разных карт ввода-вывода в TSS, места там хватает только для одной( limit == 20ABh ).

воскресенье, 30 января 2011 г.

Shim Engine и режим совместимости

Shim Engine это технология для обеспечения совместимости, реализованная в различных DLL'ках, а также через некоторые колбеки и хаки в PE Loader'е, который находится в ntdll.dll.

Shim Engine в основном реализован в shimeng.dll и apphelp.dll — которые суть есть Application Compatibility Interface,

Shim также содержит различные записи в реестре для своего конфигурирования, а также system database files(*.sdb).

Основы описаны у Ионеску, а в данной заметке будет немного описана реализация и возможности шимов для режима совместимости.

Итак, для исполняемых файлов в их свойствах, в разделе совместимость, можно указать, с какими ОС должно быть совместимо данное приложение.

Конфигурация хранится в ветке HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers.
Для режима совместимости с вин95, например, ключ будет выглядеть так: "C:\\Test\\TEST.EXE"="WIN95".

Далее при запуске ехе в режиме совместимости будет запущен shim engine. Последовательность вызовов выглядит примерно следующим образом:

KiUserApcDispatcher => LdrInitializeThunk => LdrpInitialize => LdrpInitializeProcess => LdrpLoadShimEngine.

LdrpLoadShimEngine грузит shimeng.dll (LdrpLoadShimEngine), получает адреса ф-ций из нее (LdrpGetShimEngineInterface), загружает базы шимов (SdbInitDatabase), распаковывает их (SdbUnpackAppCompatData) и применяет.

Данные шима процесс может получить через Peb->pShimData.

В режиме совместимости грузяться дополнительные длл: Aclayers.dll и AcGenral.dll, в которых находятся реализации шимов оформленные в виде классов, список шимов для Aclayers.dll:

"ChangeAuthenticationLevel"
"CorrectBitmapHeader"
"CorrectCreateEventName"
"CorrectFilePaths"
"CorrectSoundDeviceId"
"DirectPlayEnumOrder"
"DuplicateHandleFix"
"EmulateBitmapStride"
"EmulateCDFS"
"EmulateClipboardDIBFormat"
"EmulateCreateFileMapping"
"EmulateCreateProcess"
"EmulateDeleteObject"
"EmulateDirectDrawSync"
"EmulateDrawText"
"EmulateEnvironmentBlock"
"EmulateFindHandles"
"EmulateGetCommandLine"
"EmulateGetDeviceCaps"
"EmulateGetDiskFreeSpace"
"EmulateGetProfileString"
"EmulateJoystick"
"EmulateHeap"
"EmulateMissingEXE"
"EmulatePlaySound"
"EmulatePrinter"
"EmulateSlowCPU"
"EmulateTextColor"
"EmulateToolHelp32"
"EmulateUSER"
"EmulateVerQueryValue"
"EmulateWriteFile"
"EnableRestarts"
"FeedbackReport"
"FileVersionInfoLie"
"Force640x480"
"Force8BitColor"
"ForceAnsiGetDisplayNameOf"
"ForceCDStop"
"ForceCoInitialize"
"ForceDXSetupSuccess"
"ForceKeepFocus"
"ForceMessageBoxFocus"
"ForceShellLinkResolveNoUI"
"HandleAPIExceptions"
"HandleRegExpandSzRegistryKeys"
"HandleWvsprintfExceptions"
"HideDisplayModes"
"IgnoreException"
"IgnoreLoadLibrary"
"IgnoreOleUninitialize"
"IgnoreScheduler"
"MapMemoryB0000"
"ProfilesEnvStrings"
"ProfilesGetFolderPath"
"ProfilesRegQueryValueEx"
"Shrinker"
"SingleProcAffinity"
"SyncSystemAndSystem32"
"VirtualRegistry"
"WinXPVersionLie"
"WinXPSP1VersionLie"
"WinXPSP2VersionLie"
"Win2000VersionLie"
"Win2000SP1VersionLie"
"Win2000SP2VersionLie"
"Win2000SP3VersionLie"
"Win95VersionLie"
"Win98VersionLie"
"WinExecRaceConditionFix"
"WinNT4SP5VersionLie"
"Win2kPropagateLayer"


Каждый шим реализован ввиде класса, с префиксом "NS_"(например class NS_IgnoreLoadLibrary) и предназначен для изменения стандартного поведения системы ( как раз для обеспечения той самой обратной совместимости ).

Данная технология позволяет очень многое, от простых вещей вроде добавления флагов в PEB, до виртуализации реестра и полного изменения поведения Heap Manager'a.

К примеру шим, виртуализирующий реестр перехватывает практически все ф-ции работы с реестром в "ADVAPI32.DLL":

"RegConnectRegistryA";
"RegConnectRegistryW";
"RegOpenKeyExA";
"RegOpenKeyExW";
"RegQueryValueExA";
"RegQueryValueExW";
"RegCloseKey";
"RegOpenKeyA";
"RegOpenKeyW";
"RegQueryValueA";
"RegQueryValueW";     
"RegCreateKeyA";     
"RegCreateKeyW";     
"RegCreateKeyExA";     
"RegCreateKeyExW";     
"RegEnumValueA";     
"RegEnumValueW";     
"RegEnumKeyA";     
"RegEnumKeyW";     
"RegEnumKeyExA";     
"RegEnumKeyExW";
"RegQueryInfoKeyA";     
"RegQueryInfoKeyW";     
"RegSetValueExA";     
"RegSetValueExW";     
"RegDeleteKeyA";     
"RegDeleteKeyW";


Подменяются они соответствующими ф-циями из класса данного шима( NS_VirtualRegistry::APIHook_RegOpenKeyExA и т.д. ).
Или например шим Shrinker фиксит проблемы приложений с Shrinker resource compression library, подменяя ф-ции "LdrAccessResource" в "NTDLL.DLL" и "ExitProcess" в "KERNEL32.DLL" на свои.

Таким образом, shim engine предоставляет легальный путь изменения поведения приложений на уровне Win API.

пятница, 28 января 2011 г.

MmIsAddressValid

В некоторых задачах, таких как, скажем, поиск неэкспортируемых символов, полезно юзать ассерты проверяющие найденные адреса, пример:

ASSERT( MmIsAddressValid(currentAddress) == TRUE );

И если найдется что-то совсем не то, что искалось, то в отладчике по команде .cxr произойдет остановка на ассерте в сорцах, плюс будут видны все локальные переменные.

Но тут есть один нюанс. При использовании верифаера и опции emulate low resources, он помечает некоторые PTE как инвалидные, бит Valid == 0 (Transition PTE в моем случае).


kd> !pte 0xfffff800`0292fe04

    VA fffff8000292fe04

PXE at FFFFF6FB7DBEDF80 PPE at FFFFF6FB7DBF0000 PDE at FFFFF6FB7E0000A0 PTE at FFFFF6FC00014978
contains 0000000000199063 contains 0000000000198063 contains 00000000001DA063 contains 062000000292F860
pfn 199 ---DA--KWEV pfn 198 ---DA--KWEV pfn 1da ---DA--KWEV not valid

    Transition: 292f

Protect: 3 - ExecuteRead


А ф-ция MmIsAddressValid делает проверку:

PointerPte = MiGetPteAddress(VirtualAddress);

if (PointerPte->u.Hard.Valid == 0)
    return FALSE;

Из-за этого ассерт сфейлится.
В релизе ессно таких проблем нет, но в debug'е пришлось убрать подобные ассерты.