INTRO (для исследователей NT) ============================= Мы всего лишь момент во времени Отблеск в глазах, Мечты о слепоте, Образы умирающего рассудка... (c) by Anathema 00.Компоненты системы 01.Карта памяти ОС Windows NT 02.Windows NT и FLAT модель 03.Блок информации о потоке (THREAD INFORMATION BLOCK) 04.Область информации процессора (PROCESSOR CONTROL REGION) 00.Компоненты системы ===================== Практически все компоненты ОС Windows NT представляют собой либо модули DLL, либо .EXE файлы в формате PE, экспортирующие и импортирующие функции. Основными компонентами ОС Windows NT являются: *Ntoskrnl.exe Ядро системы. В нем сосредоточены функции исполнительной системы, которые используют другие компоненты. Ядро содержит: менеджер объектов, управление памятью, создание потоков и процессов, управление потоками и процессами, LPC, управление безопасностью, обработка исключений, файловая система, ввод-вывод, VDM и т.д. Обычно отображается по адресу 80100000h. *Hal.dll Hardware Abstraction Layer - аппаратно зависимый модуль. Обеспечивает изоляцию аппаратно-зависимой части ОС и ее переносимость. Модуль, в основном, реализует очень низкоуровневые функции: программирование контроллера прерываний, аппаратный ввод-вывод и т.д. Обычно отображается по адресу 80010000h. *Ntdll.dll Реализует некоторые функции Win32 API. Обеспечивает интерфейс между пользовательским режимом и режимом ядра. Отображается в пользовательском пространстве. Другими словами, в основном экспортирует заглушки - вызовы системных функций. *Kernel32.dll Реализует некоторые функции, аналогичные функциям ядра Win9x. Большая часть функций - это заглушки в ntdll.dll. *Csrss.exe Процесс-сервер подсистемы. Отдельный процесс, а потому защищен от других процессов (находится в другом адресном пространстве). Обращения к серверу происходят с помощью LPC. *Win32k.sys Драйвер. Служит для уменьшения накладных расходов по вызову сервера Csrss. В нем реализованы функции GDI и USER. Вместо LPC используются системные вызовы - это заметно ускорило работу графики в Windows NT 4.0 и 2K. 01.Карта памяти ОС Windows NT ============================= В ОС Windows NT обычно старшие два гигабайта 32-разрядного линейного адресного пространства зарезервированы для системы. Таким образом, пространство адресов 80000000 - ffffffff разделяется системными компонентами: драйверами, системными таблицами, системными структурами и др. Точную карту памяти ОС получить невозможно, однако можно выделить области по их функциональному назначению и примерному расположению. *80000000-9FFFFFFF Код системы. Здесь располагаются код и данные Hal и ntoskrnl, а так же драйверы (boot драйверы, драйверы загружаемые ntosldr.) Структуры GDT, IDT и TSS также располагаются в этой области. *C0000000-C0FFFFFF Область системных таблиц. Этот регион линейного адресного пространства отображает таблицы страниц процесса, каталог страниц и другие, относящиеся к процессам структуры. Эта область не является глобальной, как другие области пространства системы, и для каждого процесса отображается на разные физические адреса, хранящие структуры текущего процесса. *E1000000-E57FFFFF Страничный пул. Эта область может вытесняться на диск. В этой области создаются большинство объектов ОС. На самом деле в этой области находится несколько пулов. *FB000000-FFDFEFFF Не вытесняемая страничная область (Non Paged Poll). Данные этой области никогда не вытесняются на диск. Данные этой области нужны системе всегда. Например, тут располагаются информационные блоки процессов и потоков. (Thread environment block, Process Environment block) *FFDFF000-FFFFFFFF PCR - Processor Control Region (Область процессора) Для каждого процессора в этой области находится структура PCR. В структуре хранится информация о состоянии системы. Например, информация о IRQL, текущем потоке, IDT и т.д. Младшие два гигабайта линейного адресного пространства (00000000-0FFFFFFFF) - адресное пространство пользовательского процесса. (Свое для каждого процесса). Адресное пространство Win32 приложения выглядит обычно следующим образом. *00000000-0000FFFF Защищенный диапазон. При доступе к этой области происходит исключение. Используется для обнаружения NULL указателей. *00xx0000 Обычно, адрес загруженного образа приложения. *70000000-78000000 Обычно сюда отображаются библиотеки подсистемы Win32. *7FFB0000-7FFD3FFF Кодовые страницы. *7FFDE000-7FFDEFFF Thread Environment Block пользовательского режима. *7FFDF000-7FFDFFFF Process Environment Block пользовательского режима. *7FFE0000-7FFE0FFF Область очереди сообщений *7FFFF000-7FFFFFFF Защищенная область. 02.Windows NT и FLAT модель =========================== В процессорах Intel, начиная с i286, реализован четырехуровневый механизм защиты. Соответственно, имеется четыре уровня привилегий. Код или данные могут обладать каким-либо уровнем привилегии. Предполагалось, что прикладные программы, системные программы, ядро и т.д. будут работать на своих уровнях, и не будут иметь возможность произвольного доступа к более привилегированному коду или данным. На практике, ни одна ОС для Intel процессоров не использует все четыре уровня (по крайней мере, мне такие ОС не известны). ОС Windows NT не является исключением, используются два уровня (кольца) привилегий. Уровень 0 (наиболее привилегированный уровень - уровень супервизора) на котором работает ядро. И уровень 3 (наименее привилегированный) - пользовательский уровень. Процессоры Intel поддерживают достаточно мощный механизм сегментации памяти, который совместно с уровнями привилегий позволяют реализовать защиту вплоть до уровня сегментов (например, каждый логический сегмент программы, может быть представлен в какой-либо таблице дескрипторов). Однако, в ОС Windows NT реализована FLAT модель. Это означает, что использование селекторов сведено к минимуму. Глобальная таблица дескрипторов GDT (Global Descriptor Table) процессора, работающего под управлением ОС Windows NT, содержит следующие дескрипторы (лог-файл от SoftIce'a): Sel. Type Base Limit DPL Attributes GDTbase=80036000 Limit=03FF 0008 Code32 00000000 FFFFFFFF 0 P RE 0010 Data32 00000000 FFFFFFFF 0 P RW 001B Code32 00000000 FFFFFFFF 3 P RE 0023 Data32 00000000 FFFFFFFF 3 P RW 0028 TSS32 8024D000 000020AB 0 P B 0030 Data32 FFDFF000 00001FFF 0 P RW 003B Data32 7FFD9000 00000FFF 3 P RW 0043 Data16 00000400 0000FFFF 3 P RW 0048 LDT E1190000 000001FF 0 P 0050 TSS32 80149F60 00000068 0 P 0058 TSS32 80149FC8 00000068 0 P 0060 Data16 00022940 0000FFFF 0 P RW 0068 Data16 000B8000 00003FFF 0 P RW 0070 Data16 FFFF7000 000003FF 0 P RW 0078 Code16 80400000 0000FFFF 0 P RE 0080 Data16 80400000 0000FFFF 0 P RW 0088 Data16 00000000 00000000 0 P RW 0090 Reserved 00000000 00000000 0 NP ... 00E0 Reserved 00008003 00006100 0 NP 00E8 Data16 00000000 0000FFFF 0 P RW 00F0 Code16 80117DB0 0000028D 0 P EO 00F8 Data16 00000000 0000FFFF 0 P RW 0100 Reserved 00008003 00006108 0 NP ... 03F8 Reserved 00000000 00000000 0 NP Первые четыре селектора адресуют все линейное адресное пространство. Причем у первых двух селекторов уровни привилегий дескриптора DPL (Descriptor Privilege Level ) равны 0, а у следующих двух - 3. Селекторы 8 и 10 используются при работе пользовательских приложений. Сами приложения не заботятся о содержимом сегментных регистров в Flat модели, во время работы на кольце 3 регистры CS,DS,SS все время хранят значения 8,10,10 соответственно. Таким образом системный код следит за значениями сегментных регистров. Селекторы 1b и 23 используются для адресации во время работы ядра (драйвера устройств, системный код ). Описание информации адресуемой с помощью селекторов 30 и 3b представлено далее . Селекторы указывают на Kernel Process Region и Thread Information Block соответственно. Когда код выполняется на кольце 0, регистр FS содержит селектор 30, если это кольцо 3 - то в регистр FS загружено значение 3b. Индекс селектора 30 всегда указывает на дескриптор с базой FFDFF000. База, относящаяся к селектору 3b, зависит от выполняющегося пользовательского потока. Селектор 48 определяет локальную таблицу селекторов LDT (Local Descriptor Table). LDT используется только в Virtual DOS machine (VDM) приложениях. Когда активен такой процесс, в регистр LDTR процессора загружается соответствующий указатель. Иначе в LDTR загружен 0. LDT, в основном, используется при выполнении приложений ОС Windows 3.x. Такие приложения выполняются в подсистеме WOW (Windows On Windows), выполняющейся внутри процесса VDM. LDT процесса VDM используется подобно тому, как она используется в Win3.x. В таблице GDT имеется всего два селектора типа TSS. Это объясняется тем, что ОС Windows NT, работающая на Intel процессоре, не использует механизм переключения задач с помощью шлюзов задач. IDT содержит следующие дескрипторы. (лог-файл от SoftIce'a) Int Type Sel:Offset Attributes Symbol/Owner IDTbase=F8500FC8 Limit=07FF 0000 IntG32 0008:8013EC54 DPL=0 P _KiTrap00 ... 0007 IntG32 0008:8013F968 DPL=0 P _KiTrap07 0008 TaskG 0050:00001338 DPL=0 P 0009 IntG32 0008:8013FCA8 DPL=0 P _KiTrap09 ... 0012 IntG32 0008:80141148 DPL=0 P _KiTrap0F ... 001F IntG32 0008:80141148 DPL=0 P _KiTrap0F 0020 Reserved 0008:00000000 DPL=0 NP ... 0029 Reserved 0008:00000000 DPL=0 NP 002A IntG32 0008:8013E1A6 DPL=3 P _KiGetTickCount 002B IntG32 0008:8013E290 DPL=3 P _KiCallbackReturn 002C IntG32 0008:8013E3A0 DPL=3 P _KiSetLowWaitHighThread 002D IntG32 0008:8013EF5C DPL=3 P _KiDebugService 002E IntG32 0008:8013DD20 DPL=3 P _KiSystemService 002F IntG32 0008:80141148 DPL=0 P _KiTrap0F 0030 IntG32 0008:80014FFC DPL=0 P _HalpClockInterrupt 0031 IntG32 0008:807E4224 DPL=0 P 0032 IntG32 0008:8013D464 DPL=0 P _KiUnexpectedInterrupt2 0033 IntG32 0008:80708864 DPL=0 P 0034 IntG32 0008:807CEDC4 DPL=0 P 0035 IntG32 0008:807E3464 DPL=0 P 0036 IntG32 0008:8013D48C DPL=0 P _KiUnexpectedInterrupt6 0037 IntG32 0008:8013D496 DPL=0 P _KiUnexpectedInterrupt7 0038 IntG32 0008:80010A58 DPL=0 P _HalpProfileInterrupt 0039 IntG32 0008:8013D4AA DPL=0 P _KiUnexpectedInterrupt9 ... 00FF IntG32 0008:8013DC66 DPL=0 P _KiUnexpectedInterrupt207 Основной тип шлюзов в таблице - шлюз прерывания. Переключение задач используется только в критических случаях, чтобы гарантировать, по крайней мере, правильное аварийное завершение. Например, при двойной ошибке (исключение 8). Прерывание 2e - это системный вызов. Прерывания от 30 до 3f соответствуют irq0 - irq15. Итак, имеется всего два селектора TSS. Один из них (50) используется при двойной ошибке. Оставшийся, занесен в регистр TR и разделяется всеми процессами ОС Windows NT. TSS кроме обязательной части размером в 104 байта содержит битовую карту ввода/вывода. Во время выполнения Win32 приложений указатель в TSS на битовую карту содержит неверное значение, и таким образом любая работа с портами приводит к исключению. Во время работы VDM - битовая карта используется для выборочного запрета доступа к портам. Рассмотрим, как организована защита в Flat модели. При рассмотрении этого вопроса, надо обратить внимание на следующие требования - защищены должны быть: ядро от пользовательских процессов, процессы друг от друга, код и данные подсистемы от пользовательских процессов. Линейное адресное пространство ОС Windows NT можно разделить на пользовательское пространство (обычно 0-7fffffff) и пространство системы или ядра (обычно 80000000-ffffffff). При переключении контекстов, пространство ядра почти одинаково для всех процессов. В ОС Windows NT используется страничная защита памяти. Таким образом, изоляция ядра от пользовательских процессов реализуется за счет установки бита U/S в ноль в указателях на кадры страниц для страниц адресного пространства ядра. Это делает невозможным любой несанкционированный доступ кода кольца 3 к данным/коду кольца 0. Страничная адресация также делает адресные пространства процессов изолированными друг от друга. ОС Windows NT резервирует 4 мегабайта для страничных таблиц в районе c0300000 для отображения всего 4гб физического адресного пространства. В каталоге страниц 1024 входов на таблицы кадров. Каждая таблица кадров должна представлять 4Мб (4Гб/1024). Тогда 1024*4096 (размер таблицы кадра) = 4Мб. Базовый адрес каталога страниц, при переключении контекстов изменяется, и указывает на каталог страниц, который и будет использоваться для преобразования линейных адресов в физические во время выполнения данного потока. (В Intel базовый физический адрес загружается в регистр CR3). В результате, в различных контекстах одинаковые линейные адреса могут отображаться на разные физические адреса , в пространстве (00000000-7fffffff), что изолирует пользовательские адресные пространства друг от друга. Пространство ядра практически одинаково во всех контекстах, и защищено от кода пользователя очищенными битами U/S в указателях на кадры страниц. Следует обратить внимание , что указатели пользовательского режима корректны только в режиме ядра. Действительно ,дескрипторы, используемые при адресации на кольце 0 и 3, имеют одинаковые базы и границы. Это может служить причиной потенциальных ошибок в ядре или драйвере ядра. При возникновении ошибки могут быть разрушены код и данные в адресном пространстве текущего пользовательского процесса. Например , при неправильной проверке указателей пользовательского процесса при вызове сервисов ядра можно разрушить код и данных ядра. Подсистемы ОС предоставляют свои сервисы через экспортируемые вызовы модулей DLL. Имеется в виду, что при обращении пользовательского процесса к подсистеме, например вызовам WIn32 API функциям, в начале всегда происходит обращение к коду одного из модулей DLL. Затем, код модуля DLL может выполнять системные вызовы или LPC. Так как модуль DLL отображается в пользовательское пространство, т.е. разделяет контекст процесса, использующего библиотечные функции, необходимо защитить код и данные самого модуля DLL от возможного непосредственного доступа. На самом деле, при такой организации данные защитить нельзя. Вся защита организована на уровне бит R/W и U/S в указателях на кадры страниц. Код модуля DLL и часть данных обычно разрешены только на чтение. Пользовательский процесс может несанкционированно (минуя вызовы) писать в данные, разрешенные на запись, если, конечно известен их примерный линейный адрес. Однако, для каждого процесса создается своя копия данных если это необходимо (отрабатывает механизм copy-on-write). Убедиться в этом, например, можно, следующим образом :по анализу заголовка PE файла kernel32.dll можно определить виртуальный адрес секции данных, затем написать программу записывающую "неправильные" данные в эту область и затем, считывающую испорченные данные. При запуске нового процесса данные будут "восстановлены". Итак, если процесс и нарушит нормальную работу подсистемы, то только в своем контексте. Кроме того, предполагается, что все критические данные ( данные, при прямом доступе к которым можно повредить работу подсистемы в целом или глобальные данные) находятся в отдельном адресном пространстве процесса - сервера подсистемы. Работа с этими данными происходит посредством обращений к серверу из модуля DLL подсистемы (с помощью LPC). Отметим, что эта операция вносит ощутимые накладные расходы , поэтому в ОС Windows NT 4.0 предпринят ряд попыток, чтобы их уменьшить. Наиболее важно то, что теперь функции GDI и USER реализованы в ядре (вернее в драйвере Win32k.sys). На самом деле, напрашивается вывод, что в безопасности подсистемы многое зависит от ее продуманности. Страничные механизмы защиты сами по себе тут мало что дают. 03.Блок информации о потоке (THREAD INFORMATION BLOCK) ====================================================== Во время выполнения потока в пользовательском режиме, регистр FS содержит значение селектора 3b. Этот селектор адресует структуру данных, которая в файле NTDDK.H носит название THRAD ENVIRONMENT BLOCK. Для каждого потока существует своя структура, и при переключении контекстов, база дескриптора изменяется, чтобы указывать на эту структуру для текущего потока. В NTDDK.H описана очень малая часть этой структуры, как _NT_TIB (Thread Information Block). На самом деле, _NT_TIB это совместимая часть структур NT_TEB и NT_TIB. NT_TEB - структура доступная пользовательскому потоку. NT_TIB доступен из режима ядра по селектору 30. Приведу структуру TEB, с некоторыми полями, не описанными в NTDDK.H (для ОС Windows NT 4.0): typedef struct _EXCEPTION_REGISTRATION_RECORD { struct _EXCEPTION_REGISTRATION_RECORD * pNext; FARPROC pfnHandler; } EXCEPTION_REGISTRATION_RECORD, *PEXCEPTION_REGISTRATION_RECORD; typedef struct _NT_TIB { struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList; // 00h Head of exception // record list PVOID StackBase; // 04h PVOID StackLimit; // 08h PVOID SubSystemTib; // 0Ch union { // 10h PVOID FiberData; // for TIB ULONG Version; // for TEB }; PVOID ArbitraryUserPointer; // 14h Available // for application use struct _NT_TIB *Self; // 18h Linear address // of TEB structure } NT_TIB; typedef NT_TIB *PNT_TIB; typedef struct _TEB { // Size: 0xF88 /*000*/ NT_TIB NtTib; /*01C*/ VOID *EnvironmentPointer; /*020*/ CLIENT_ID ClientId; // PROCESS id, THREAD id /*028*/ HANDLE ActiveRpcHandle; /*02C*/ VOID *ThreadLocalStoragePointer; /*030*/ PEB *ProcessEnvironmentBlock; // PEB /*034*/ ULONG LastErrorValue; /*038*/ ULONG CountOfOwnedCriticalSections; /*03C*/ ULONG CsrClientThread; /*040*/ ULONG Win32ThreadInfo; /*044*/ UCHAR Win32ClientInfo[0x7C]; /*0C0*/ ULONG WOW32Reserved; /*0C4*/ ULONG CurrentLocale; /*0C8*/ ULONG FpSoftwareStatusRegister; /*0CC*/ UCHAR SystemReserved1[0xD8]; // ExitStack ??? /*1A4*/ ULONG Spare1; /*1A8*/ ULONG ExceptionCode; /*1AC*/ UCHAR SpareBytes1[0x28]; /*1D4*/ UCHAR SystemReserved2[0x28]; /*1FC*/ UCHAR GdiTebBatch[0x4E0]; /*6DC*/ ULONG gdiRgn; /*6E0*/ ULONG gdiPen; /*6E4*/ ULONG gdiBrush; /*6E8*/ CLIENT_ID RealClientId; /*6F0*/ ULONG GdiCachedProcessHandle; /*6F4*/ ULONG GdiClientPID; /*6F8*/ ULONG GdiClientTID; /*6FC*/ ULONG GdiThreadLocalInfo; /*700*/ UCHAR UserReserved[0x14]; /*714*/ UCHAR glDispatchTable[0x460]; /*B74*/ UCHAR glReserved1[0x68]; /*BDC*/ ULONG glReserved2; /*BE0*/ ULONG glSectionInfo; /*BE4*/ ULONG glSection; /*BE8*/ ULONG glTable; /*BEC*/ ULONG glCurrentRC; /*BF0*/ ULONG glContext; /*BF4*/ ULONG LastStatusValue; /*BF8*/ LARGE_INTEGER StaticUnicodeString; /*C00*/ UCHAR StaticUnicodeBuffer[0x20C]; /*E0C*/ ULONG DeallocationStack; /*E10*/ UCHAR TlsSlots[0x100]; /*F10*/ LARGE_INTEGER TlsLinks; /*F18*/ ULONG Vdm; /*F1C*/ ULONG ReservedForNtRpc; /*F20*/ LARGE_INTEGER DbgSsReserved; /*F28*/ ULONG HardErrorsAreDisabled; /*F2C*/ UCHAR Instrumentation[0x40]; /*F6C*/ ULONG WinSockData; /*F70*/ ULONG GdiBatchCount; /*F74*/ ULONG Spare2; /*F78*/ ULONG Spare3; /*F7C*/ ULONG Spare4; /*F80*/ ULONG ReservedForOle; /*F84*/ ULONG WaitingOnLoaderLock; } TEB, *PTEB; В ОС Windows 95, по смещению 0x30 в TIB находится указатель на базу данных процесса, владеющего потоком. В ОС Windows NT 4.0 по этому же смещению также содержится указатель на структуру, которая достаточно часто используется в kernel32.dll. К сожалению, на данный момент, формат этой структуры мне не известен, за исключением нескольких полей. Кроме того, похоже, что структура PEB изменилась (незначительно) в Win 2K. typedef struct _PROCESS_PARAMETERS { /*000*/ ULONG AllocationSize; /*004*/ ULONG ActualSize; /*008*/ ULONG Flags;//PPFLAG_xxx /*00c*/ ULONG Unknown1; /*010*/ ULONG Unknown2; /*014*/ ULONG Unknown3; /*018*/ HANDLE InputHandle; /*01c*/ HANDLE OutputHandle; /*020*/ HANDLE ErrorHandle; /*024*/ UNICODE_STRING CurrentDirectory; /*028*/ HANDLE CurrentDir; /*02c*/ UNICODE_STRING SearchPaths; /*030*/ UNICODE_STRING ApplicationName; /*034*/ UNICODE_STRING CommandLine; /*038*/ PVOID EnvironmentBlock; /*03c*/ ULONG Unknown[9]; UNICODE_STRING Unknown4; UNICODE_STRING Unknown5; UNICODE_STRING Unknown6; UNICODE_STRING Unknown7; } PROCESS_PARAMETERS, *PPROCESS_PARAMETERS; typedef struct _PEB { // Size: 0x1D8 /*000*/ UCHAR InheritedAddressSpace; /*001*/ UCHAR ReadImageFileExecOptions; /*002*/ UCHAR BeingDebugged; /*003*/ UCHAR SpareBool; // Allocation size /*004*/ HANDLE Mutant; /*008*/ HINSTANCE ImageBaseAddress; // Instance /*00C*/ VOID *DllList; /*010*/ PPROCESS_PARAMETERS *ProcessParameters; /*014*/ ULONG SubSystemData; /*018*/ HANDLE DefaultHeap; /*01C*/ KSPIN_LOCK FastPebLock; /*020*/ ULONG FastPebLockRoutine; /*024*/ ULONG FastPebUnlockRoutine; /*028*/ ULONG EnvironmentUpdateCount; /*02C*/ ULONG KernelCallbackTable; /*030*/ LARGE_INTEGER SystemReserved; /*038*/ ULONG FreeList; /*03C*/ ULONG TlsExpansionCounter; /*040*/ ULONG TlsBitmap; /*044*/ LARGE_INTEGER TlsBitmapBits; /*04C*/ ULONG ReadOnlySharedMemoryBase; /*050*/ ULONG ReadOnlySharedMemoryHeap; /*054*/ ULONG ReadOnlyStaticServerData; /*058*/ ULONG AnsiCodePageData; /*05C*/ ULONG OemCodePageData; /*060*/ ULONG UnicodeCaseTableData; /*064*/ ULONG NumberOfProcessors; /*068*/ LARGE_INTEGER NtGlobalFlag; // Address of a local copy /*070*/ LARGE_INTEGER CriticalSectionTimeout; /*078*/ ULONG HeapSegmentReserve; /*07C*/ ULONG HeapSegmentCommit; /*080*/ ULONG HeapDeCommitTotalFreeThreshold; /*084*/ ULONG HeapDeCommitFreeBlockThreshold; /*088*/ ULONG NumberOfHeaps; /*08C*/ ULONG MaximumNumberOfHeaps; /*090*/ ULONG ProcessHeaps; /*094*/ ULONG GdiSharedHandleTable; /*098*/ ULONG ProcessStarterHelper; /*09C*/ ULONG GdiDCAttributeList; /*0A0*/ KSPIN_LOCK LoaderLock; /*0A4*/ ULONG OSMajorVersion; /*0A8*/ ULONG OSMinorVersion; /*0AC*/ USHORT OSBuildNumber; /*0AE*/ USHORT OSCSDVersion; /*0B0*/ ULONG OSPlatformId; /*0B4*/ ULONG ImageSubsystem; /*0B8*/ ULONG ImageSubsystemMajorVersion; /*0BC*/ ULONG ImageSubsystemMinorVersion; /*0C0*/ ULONG ImageProcessAffinityMask; /*0C4*/ ULONG GdiHandleBuffer[0x22]; /*14C*/ ULONG PostProcessInitRoutine; /*150*/ ULONG TlsExpansionBitmap; /*154*/ UCHAR TlsExpansionBitmapBits[0x80]; /*1D4*/ ULONG SessionId; } PEB, *PPEB; В начале TEB находится структура NT_TIB (совместимая часть TEB и TIB). Большинство названий этой структуры понятны, наибольший интерес представляет указатель на цепочку обработчиков исключений peExcept. (Fs:[0]) Ссылка на это поле происходит очень часто. Если посмотреть практически в любое приложение Win32, можно увидеть примерно следующие строки кода: .01B45480: 64A100000000 mov eax,fs:[000000000] .01B45486: 55 push ebp .01B45487: 8BEC mov ebp,esp .01B45489: 6AFF push 0FF .01B4548B: 68F868B401 push 001B468F8 .01B45490: 687256B401 push 001B45672 .01B45495: 50 push eax .01B45496: 64892500000000 mov fs:[000000000],esp .01B4549D: 83EC78 sub esp,078 Представленный код сгенерирован компилятором. Происходит построение структуры из _EXCEPTION_REGISTRATION_RECORD в стеке. Эта структура в стеке используется в механизме так называемой "structured exception handling" - структурной обработки исключений. Следует отметить , механизм структурной обработки исключений в ОС Windows NT. Однако этот механизм очень хорошо известен и используется, в частности , при написании компиляторов. В MSDN можно найти очень подробную статью Мэтта Пьетрика под названием "A Crash Course on the Depths of Win32 Structured Exception Handling", в которой изложен этот механизм . Механизм структурной обработки исключений использован при анализе исходных текстов модуля kernel32.dll, полученных после дизассемблирования. Указатель в FS:[0] - указатель на голову списка из структур _EXCEPTION_REGISTRATION_RECORD. Соответственно, каждая структура списка содержит указатель на следующую структуру в списке pNext и указатель на callback функцию pfnHandler. Не трудно догадаться, что это пользовательские обработчики исключений. Прототип функции выглядит следующим образом: EXCEPTION_DISPOSITION __cdecl _except_handler( struct _EXCEPTION_RECORD *ExceptionRecord, void * EstablisherFrame, struct _CONTEXT *ContextRecord, void * DispatcherContext ); Рассмотрим параметры функции. Первый параметр - указатель на следующую структуру. typedef struct _EXCEPTION_RECORD { DWORD ExceptionCode; DWORD ExceptionFlags; struct _EXCEPTION_RECORD *ExceptionRecord; PVOID ExceptionAddress; DWORD NumberParameters; DWORD ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; } EXCEPTION_RECORD; ExceptionCode - это код исключения ОС Windows NT. Исключения описаны в файле NTSTATUS.H как STATUX_xxxxxx: ExceptionAddres - адрес, где произошло исключение. Третий параметр - указатель на структуру CONTEXT. typedef struct _CONTEXT { DWORD ContextFlags; DWORD Dr0; DWORD Dr1; DWORD Dr2; :. DWORD Esp; DWORD SegSs; } CONTEXT; Структура описана в файле WINNT.H. Ее назначение понятно и из сокращенного варианта. Функция возвращает один из элементов перечисления: typedef enum _EXCEPTION_DISPOSITION { ExceptionContinueExecution, ExceptionContinueSearch, ExceptionNestedException, ExceptionCollidedUnwind } EXCEPTION_DISPOSITION; Значение ExceptionFlags определяется следующими битами-признаками: #define EH_NONCONTINUABLE 1 #define EH_UNWINDING 2 #define EH_EXIT_UNWIND 4 #define EH_STACK_INVALID 8 #define EH_NESTED_CALL 0x10 При возникновении исключения, управление передается в соответствующий обработчик в ntoskrnl.exe. Например, если попытаться выполнить следующий код: mov eax,80100000h mov dword ptr [eax],0 Произойдет исключение 0e, управление перейдет к обработчику KiTrap0E с кодом ошибки 07 в стеке. (Попытка записи из пользовательского режима. Страница находится в памяти, потому что линейный адрес 80100000h - это начальный адрес загруженного ядра.) Затем, управление передается в ntdll.dll где анализируется TEB потока и вызываются по очереди все обработчики в цепочке до тех пор, пока какой-либо из обработчиков не примет на себя функции по обработке ситуации (возвратит код не равный ExceptionContinueSearch). После этого, повторно вызывается вся цепочка до найденной функции, но уже с другим кодом ExceptionCode (STATUS_UNWIND) и установленным битом EH_UNWINDINGS в ExceptionFlags. Это знак для обработчиков, произвести действия по очистке стека и другие, необходимые действия. Если ни один обработчик не принимает обслуживание, очередь дойдет до последнего обработчика, установленного подсистемой Win32. Как и где устанавливается этот обработчик, будет сказано в дальнейшем, при рассмотрении функции CreateProcessW. Полное представление, о механизме обработки исключений можно получить из MSDN, так как в дизассемблированных исходных текстах используется расширенный механизм (механизм более высокого уровня) обработки исключений, такой - как в Visual C и структура, которая строится на стеке, расширена по сравнению с EXCEPTION_REGISTRATION_RECORD. 04.Область информации процессора (PROCESSOR CONTROL REGION) =========================================================== Когда поток выполняется в режиме ядра, в регистр FS загружен селектор 30, адресующий структуру PCR (база 0xFFDFF000, граница 0x00001FFF). В NTDDK.H описана далеко не вся структура, только ее небольшая часть. Для представления о составе информации в PCR приведена структура для i386 (для Windows NT 4.0). typedef struct _KPCR { // Size: 0xB10 /*000*/ NT_TIB NtTib; /*01C*/ struct _KPCR* SelfPcr; /*020*/ struct _KPRCB* Prcb; // Current PCB /*024*/ KIRQL Irql; // Current IRQL /*028*/ ULONG IRR; /*02C*/ ULONG IrrActive; /*030*/ ULONG IDR; /*034*/ ULONG Reserved2; /*038*/ struct _KIDTENTRY* ULONG IDT; /*03C*/ struct _KGDTENTRY* GDT; /*040*/ struct _KTSS* TSS; /*044*/ USHORT MajorVersion; // 1 /*046*/ USHORT MinorVersion; // 1 /*048*/ KAFFINITY SetMember; /*04C*/ ULONG StallScaleFactor; /*050*/ UCHAR DebugActive; /*051*/ UCHAR Number; // End of official portion of KPCR /*052*/ BOOLEAN VdmAlert; /*053*/ UCHAR Reserved; /*054*/ UCHAR KernelReserved[0x90-0x54]; /*090*/ ULONG SecondLevelCacheSize; /*094*/ UCHAR HalReserved[0xD4-0x94]; /*0D4*/ ULONG InterruptMode; /*0D8*/ ULONG Spare1; /*0DC*/ UCHAR KernelReserved2[0x120-0xDC]; // Note that current thread is at offset 0x124 (pointer to KTHREAD) // This essentially means that the 1st PRCB must match the active CPU /*120*/ UCHAR PrcbData[0xB10-0x120]; // PCBs for all CPUs supported } KPCR, *PKPCR; PCR содержит указатель на PCRB (Processor Control Region), но на практике, PCRB находится за структурой PCR по смещению 0x120, и все обращения к полям PCRB происходят относительно начала PCR. В WINNT.H описана также часть структуры PCRB. Приведу всю структуру (для ОС Windows NT 4.0): // 0x120 from KPCR typedef struct _KPRCB { /*000*/ USHORT MinorVersion; /*002*/ USHORT MajorVersion; /*004*/ struct _KTHREAD *CurrentThread; /*008*/ struct _KTHREAD *NextThread; /*00c*/ struct _KTHREAD *IdleThread; /*010*/ CCHAR Number; /*011*/ CCHAR Reserved; /*012*/ USHORT BuildType; /*014*/ KAFFINITY SetMember; /*015*/ struct _RESTART_BLOCK *RestartBlock; /*018*/ CCHAR CpuType; /*019*/ CCHAR CpuID; /*01A*/ CCHAR CpuStep; /*01C*/ KPROCESSOR_STATE ProcessorState; /*13C*/ CCHAR KernelReserved[0x40]; /*17C*/ CCHAR HalReserved[0x40]; /*1BC*/ ULONG NpxThread; /*1C0*/ ULONG InterruptCount; /*1C4*/ ULONG KernelTime; /*1C8*/ ULONG UserTime; /*1CC*/ ULONG DpcTime; /*1D0*/ ULONG InterruptTime; /*1D4*/ ULONG ApcBypassCount; /*1D8*/ ULONG DpcBypassCount; /*1DC*/ ULONG AdjustDpcThreshold; /*1E0*/ UCHAR Spare2[0x14]; /*1F4*/ ULONG64 ThreadStartCount; /*1FC*/ SINGLE_LIST_ENTRY FsRtlFreeSharedLockList; /*200*/ SINGLE_LIST_ENTRY FsRtlFreeExclusiveLockList; /*204*/ ULONG CcFastReadNoWait; /*208*/ ULONG CcFastReadWait; /*20C*/ ULONG CcFastReadNotPossible; /*210*/ ULONG CcCopyReadNoWait; /*214*/ ULONG CcCopyReadWait; /*218*/ ULONG CcCopyReadNoWaitMiss; /*21C*/ ULONG KeAlignmentFixupCount; /*220*/ ULONG KeContextSwitches; /*224*/ ULONG KeDcacheFlushCount; /*228*/ ULONG KeExceptionDispatchCount; /*22C*/ ULONG KeFirstLevelTbFills; /*230*/ ULONG KeFloatingEmulationCount; /*234*/ ULONG KeIcacheFlushCount; /*238*/ ULONG KeSecondLevelTbFills; /*23C*/ ULONG KeSystemCalls; /*240*/ SINGLE_LIST_ENTRY FsRtlFreeWaitingLockList; /*244*/ SINGLE_LIST_ENTRY FsRtlFreeLockTreeNodeList /*248*/ CCHAR ReservedCounter[0x18]; /*260*/ PVOID SmallIrpFreeEntry; /*264*/ PVOID LargeIrpFreeEntry; /*268*/ PVOID MdlFreeEntry /*26C*/ PVOID CreateInfoFreeEntry; /*270*/ PVOID NameBufferFreeEntry /*274*/ PVOID SharedCacheMapEntry /*278*/ CCHAR CachePad0[8]; /*280*/ CCHAR ReservedPad[0x200]; /*480*/ CCHAR CurrentPacket[0xc]; /*48C*/ ULONG TargetSet; /*490*/ PVOID WorkerRoutine; /*494*/ ULONG IpiFrozen; /*498*/ CCHAR CachePad1[0x8]; /*4A0*/ ULONG RequestSummary; /*4A4*/ ULONG SignalDone; /*4A8*/ ULONG ReverseStall; /*4AC*/ ULONG IpiFrame; /*4B0*/ CCHAR CachePad2[0x10]; /*4C0*/ ULONG DpcInterruptRequested; /*4C4*/ CCHAR CachePad3 [0xc]; /*4D0*/ ULONG MaximumDpcQueueDepth; /*4D4*/ ULONG MinimumDpcRate; /*4D8*/ CCHAR CachePad4[0x8]; /*4E0*/ LIST_ENTRY DpcListHead; /*4E8*/ ULONG DpcQueueDepth; /*4EC*/ ULONG DpcRoutineActive; /*4F0*/ ULONG DpcCount; /*4F4*/ ULONG DpcLastCount; /*4F8*/ ULONG DpcRequestRate; /*4FC*/ CCHAR KernelReserved2[0x2c]; /*528*/ ULONG DpcLock; /*52C*/ CCHAR SkipTick; /*52D*/ CCHAR VendorString[0xf]; /*53C*/ ULONG MHz; /*540*/ ULONG64 FeatureBits; /*548*/ ULONG64 UpdateSignature; /*550*/ ULONG QuantumEnd; } KPRCB, *PKPRCB, *RESTRICTED_POINTER PRKPRCB; Наиболее используемая часть PCRB - это указатель на текущий поток (структура KTHREAD). Обычно, код ядра получает этот указатель как MOV REG, FS:[124h]. --------------------------------------------------------------------------- (c)Gloomy aka Peter Kosyh, Melancholy Coding'2001 http://gloomy.cjb.net mailto:gl00my@mail.ru