Объекты операционной системы. Часть 4: Отладчик уровня ядра WinDbg
Объекты операционной системы. Часть 1: Общие сведения Объекты операционной системы. Часть 2: Объект ядра Объекты операционной системы. Часть 3: Уровень целостности В этой части я решил показать самые интересные, на мой взгляд, возможности ядерного отладчика WinDbg. Но сначала немного слов о том, как его установить и как им пользоваться. В этом блоге я буду показывать работу отладчика WinDbg сразу для двух операционных: Windows 7 (x64) и Windows 10/11 (x64). Почему две ОС? По двум причинам: 1. Windows 7 всё ещё остаётся актуальной; 2. Работа ядер этих ОС имеет большие различия, которые несомненно вызывают интерес, и которые мы увидим на примерах. Итак, начнём с Windows 7. Здесь всё просто, идём на MSDN, качаем Windows 11 WDK (прямая ссылка на online установщик) и устанавливаем по умолчанию в папку Program Files (x86). После установки отладчик WinDbg будет располагаться по пути: Program Files (x86)\Windows Kits\10\Debuggers\x64 Попробуйте запустить windbg.exe, если вылезет вот такая ошибка то скачайте эту библиотеку api-ms-win-core-sysinfo-l1-2-0.rar и положите в туже папку, где находится windbg.exe, ещё раз проверьте запускается ли отладчик. Теперь, чтобы воспользоваться именно KernelMode отладкой необходимо выполнить три шага: 1. В BIOS отключите опцию SecureBoot (должно быть Disabled), если такой опции в BIOS нет, то переходите ко второму шагу. 2. Запустите командную строку (cmd.exe) от имени администратора и введите следующую команду bcdedit -debug on , должна быть надпись об успешном выполнении команды.3. Перезагрузите компьютер. Запустите windbg.exe от имени администратора. В меню нажмите File -> Kernel Degug... -> выберите Local и нажмите OK Дождитесь окончания загрузки символов (в командной строке должна появиться надпись lkd> , что означает, что отладчик готов к принятию команд).Теперь установка и настройка для Windows 10/11. В этих ОС можно выбрать какой отладчик устанавливать: - тот же, что и описанный в Windows 7 из пакета Windows 11 WDK; - либо из магазина Microsoft Store WinDbg Preview, который также можно найти и в самом магазине. Лично я предпочитаю WinDbg Preview, у него неплохой интерактивный GUI, да и сам интерфейс куда более приятный и современный. Настройка аналогична трем шагам из Windows 7: выключить SecureBoot, включить debug on и перезагрузить компьютер. Из Пуска запустите WinDbg Preview от имени администратора. В меню нажмите File -> Attach to kernel -> выберите Local и нажмите OK Дождитесь окончания загрузки символов (в командной строке должна появиться надпись lkd> , что означает, что отладчик готов к принятию команд).Так как отладчик сейчас работает в режиме ядра ОС, то эксперименты будем ставить над самым главным процессом - ядром операционной системы System. Итак, первой командой, которой мы воспользуемся, это !process Она выводит базовую информацию о процессе: сессию в которой он работает, идентификатор, родительский процесс, количество открытых дескрипторов этим процессом. Давайте введем команду !process 0 0 System Отлично! Теперь у нас есть значение объекта процесса System PROCESS fffffa80048d8040 Для того, чтобы понять что находится в этом объекте, необходимо получить заголовок этого объекта (ObjectHeader). В отладчике WinDbg для этого предусмотрена команда !object Соответственно введём эту команду и значение того объекта, у которого требуется получить заголовок !object fffffa80048d8040 Теперь у нас есть значение заголовка объекта процесса System ObjectHeader: fffffa80048d8010 (new version) Зная это значение, можно посмотреть что содержится в структуре _OBJECT_HEADER Воспользуемся следующей командой, которой передадим значение заголовка объекта dt nt!_object_header fffffa80048d8010 Самыми интересными полями этой структуры являются TypeIndex и SecurityDescriptor. Начнём с SecurityDescriptor : 0xfffff8a0`0000464b. Это указатель на структуру, содержащую уже знакомые нам ACL (SACL и DACL), а также их ACE, SID группы и владельца и их флаги. Введем специальную команду, чтобы отобразить содержимое структуры, находящейся по значению SecurityDescriptor: !sd 0xfffff8a0`0000464b & -10 Заметьте, что KernelExplorer, кроме вывода этой информации, также выводит более детализированную информацию, в частности по структуре ACTRL_ALIST. Данная структура является недокументированной, и собирать её содержимое достаточно сложно, видимо в Microsoft не стали заморачиваться, а ведь там есть очень ценная и уникальная информация по правам доступа каждого ACE (ACTRL_ACCESS_ENTRY) Теперь давайте разберемся, что такое TypeIndex : 0x7. TypeIndex это индекс указателя на структуру _OBJECT_TYPE в таблице ObTypeIndexTable. Давайте посмотрим что это за таблица ObTypeIndexTable, находящаяся в ядре ОС. Для этого введём команду dps nt!ObTypeIndexTable Так как эксперименты проводятся на 64 битной ОС, соответственно значения являются 8-ми байтовыми. Обратите внимание: второе значение является ошибочным 00000000BAD0B0B0. Что это такое? Это заплатка (patchGuard) от эксплойта вида В Windows 7 существовал способ, позволяющий через shellcode получить контроль на объектами ядра в таблице ObTypeIndexTable. После patchGuard все обращения ко второму значению приводили к ошибке вида BAD ObjectHeader. В Windows 8.1 исправили этот баг непосредственно в коде ядра, тем самым представив метаданные следующим образом Но давайте вернёмся к TypeIndex и его значению 0x7. Начав отсчитывать от первого нулевого значения (нулевой индекс) до седьмого индекса (восьмого значения) в таблице ObTypeIndexTable, мы попадаем на значение fffffa80048769b0. Пока запомним его. Для того, чтобы посмотреть содержимое структуры _OBJECT_TYPE воспользуемся специальной командой, в которой происходит арифметическое действие над таблицей с учетом индекса 7 и размерности значения, находящегося по этому индексу (8 байт) dt nt!_OBJECT_TYPE poi(nt!ObTypeIndexTable + (7 * 8)) Первым делом посмотрите на поле Name : _UNICODE_STRING "Process". Вернитесь в скриншоту, где мы использовали команду !object - поле Type: (fffffa80048769b0) Process: данный объект ядра является Process. А теперь посмотрите на поле TypeList : _LIST_ENTRY (0xfffffa80`048769b0). Точно такое же значение, которое мы запомнили чуть раньше. Поздравляю, мы получили валидный индекс процесса System (ядра ОС) в таблице ObTypeIndexTable. Теперь вернёмся к Windows 10/11 и попробуем определить индекс в таблице ObTypeIndexTable. Вот что мы получим в итоге TypeIndex в Windows 10/11 зашифрован. Кроме того, в Windows 10 21H2 диапазон индексов лежит в промежутке [0...68] Кликните здесь для просмотра всего текста
Код:
TypeName | TypeIndex ---------------------------------|---------- (null) | 0 | 1 Type | 2 Directory | 3 SymbolicLink | 4 Token | 5 Job | 6 Process | 7 Thread | 8 Partition | 9 UserApcReserve | 10 IoCompletionReserve | 11 ActivityReference | 12 PsSiloContextPaged | 13 PsSiloContextNonPaged | 14 DebugObject | 15 Event | 16 Mutant | 17 Callback | 18 Semaphore | 19 Timer | 20 IRTimer | 21 Profile | 22 KeyedEvent | 23 WindowStation | 24 Desktop | 25 Composition | 26 RawInputManager | 27 CoreMessaging | 28 ActivationObject | 29 TpWorkerFactory | 30 Adapter | 31 Controller | 32 Device | 33 Driver | 34 IoCompletion | 35 WaitCompletionPacket | 36 File | 37 TmTm | 38 TmTx | 39 TmRm | 40 TmEn | 41 Section | 42 Session | 43 Key | 44 RegistryTransaction | 45 ALPC Port | 46 EnergyTracker | 47 PowerRequest | 48 WmiGuid | 49 EtwRegistration | 50 EtwSessionDemuxEntry | 51 EtwConsumer | 52 CoverageSampler | 53 DmaAdapter | 54 PcwObject | 55 FilterConnectionPort | 56 FilterCommunicationPort | 57 NdisCmState | 58 DxgkSharedResource | 59 DxgkSharedKeyedMutexObject | 60 DxgkSharedSyncObject | 61 DxgkSharedSwapChainObject | 62 DxgkDisplayManagerObject | 63 DxgkCurrentDxgThreadObject | 64 // Only in Windows 10 DxgkSharedProtectedSessionObject | 65 DxgkSharedBundleObject | 66 DxgkCompositionObject | 67 VRegConfigurationContext | 68 а 0x4E это 78, что уже означает выход за допустимый диапазон. Кстати в Windows 11 21H2 диапазон индексов был расширен Кликните здесь для просмотра всего текста
Код:
TypeName | TypeIndex ---------------------------------|---------- (null) | 0 | 1 Type | 2 Directory | 3 SymbolicLink | 4 Token | 5 Job | 6 Process | 7 Thread | 8 Partition | 9 UserApcReserve | 10 IoCompletionReserve | 11 ActivityReference | 12 ProcessStateChange | 13 // Only in Windows 11 ThreadStateChange | 14 // Only in Windows 11 PsSiloContextPaged | 15 PsSiloContextNonPaged | 16 DebugObject | 17 Event | 18 Mutant | 19 Callback | 20 Semaphore | 21 Timer | 22 IRTimer | 23 Profile | 24 KeyedEvent | 25 WindowStation | 26 Desktop | 27 Composition | 28 RawInputManager | 29 CoreMessaging | 30 ActivationObject | 31 TpWorkerFactory | 32 Adapter | 33 Controller | 34 Device | 35 Driver | 36 IoCompletion | 37 WaitCompletionPacket | 38 File | 39 IoRing | 40 // Only in Windows 11 TmTm | 41 TmTx | 42 TmRm | 43 TmEn | 44 Section | 45 Session | 46 Key | 47 RegistryTransaction | 48 ALPC Port | 49 EnergyTracker | 50 PowerRequest | 51 WmiGuid | 52 EtwRegistration | 53 EtwSessionDemuxEntry | 54 EtwConsumer | 55 CoverageSampler | 56 DmaAdapter | 57 PcwObject | 58 FilterConnectionPort | 59 FilterCommunicationPort | 60 NdisCmState | 61 DxgkSharedResource | 62 DxgkSharedKeyedMutexObject | 63 DxgkSharedSyncObject | 64 DxgkSharedSwapChainObject | 65 DxgkDisplayManagerObject | 66 DxgkSharedProtectedSessionObject | 67 DxgkSharedBundleObject | 68 DxgkCompositionObject | 69 VRegConfigurationContext | 70 Касаемо новых объектов ядра в Windows 11, могу порекомендовать почитать вот этот материал ProcessStateChange и ThreadStateChange - Thread and Process State Change IoRing - I/O Rings – When One I/O Operation is Not Enough А это таблица индексов объектов ядра Windows 7 Кликните здесь для просмотра всего текста
Код:
TypeName | TypeIndex ---------------------------------|---------- (null) | 0 BAD0B0B0 | 1 Type | 2 Directory | 3 SymbolicLink | 4 Token | 5 Job | 6 Process | 7 Thread | 8 UserApcReserve | 9 IoCompletionReserve | 10 DebugObject | 11 Event | 12 EventPair | 13 // Only in Windows 7 Mutant | 14 Callback | 15 Semaphore | 16 Timer | 17 Profile | 18 KeyedEvent | 19 WindowStation | 20 Desktop | 21 TpWorkerFactory | 22 Adapter | 23 Controller | 24 Device | 25 Driver | 26 IoCompletion | 27 File | 28 TmTm | 29 TmTx | 30 TmRm | 31 TmEn | 32 Section | 33 Session | 34 Key | 35 ALPC Port | 36 PowerRequest | 37 WmiGuid | 38 EtwRegistration | 39 EtwConsumer | 40 FilterConnectionPort | 41 FilterCommunicationPort | 42 PcwObject | 43 В Microsoft посчитали, что такой лёгкий доступ к таблице индексов ObTypeIndexTable недопустим и постарались запутать исследователей. Но их защита простояла недолго, достаточно быстро был найден способ получения валидного индекса, довольно нетрадиционным способом. Во-первых, исследователи ядра начали искать фрагменты кода, которые имеют что-то общее со структурой _OBJECT_TYPE Код:
lkd> x nt!ob*type* fffff806`2bc21f8c nt!ObpCreateDefaultObjectTypeSD (void) fffff806`2bc218d0 nt!ObCreateObjectTypeEx (void) fffff806`2bd82214 nt!ObEnumerateObjectsByType (ObEnumerateObjectsByType) fffff806`2c024ef0 nt!ObpTypeObjectType = <no type information> fffff806`2c024ee0 nt!ObpDirectoryObjectType = <no type information> fffff806`2c107470 nt!ObTypeIndexTable = <no type information> fffff806`2bb23b28 nt!ObQueryTypeInfo (ObQueryTypeInfo) fffff806`2bc21ef8 nt!ObpInitObjectTypeSD (ObpInitObjectTypeSD) fffff806`2be36038 nt!ObpObjectTypesPathString = <no type information> fffff806`2bd82a60 nt!ObpDestroyTypeArray (ObpDestroyTypeArray) fffff806`2bd8295c nt!ObpCreateTypeArray (ObpCreateTypeArray) fffff806`2bf54aa0 nt!ObpTypeMapping = <no type information> fffff806`2be36028 nt!ObpObjectTypesNameString = <no type information> fffff806`2baa3cc0 nt!ObGetObjectType (ObGetObjectType) fffff806`2c024e20 nt!ObpTypeDirectoryObject = <no type information> fffff806`2bc218b0 nt!ObCreateObjectType (ObCreateObjectType) fffff806`2c024ee8 nt!ObpSymbolicLinkObjectType = <no type information> fffff806`2c024620 nt!ObpObjectTypes = <no type information> fffff806`2bd82d3c nt!ObQueryTypeName (ObQueryTypeName) Затем было решено посмотреть на её мнемонику ассемблерного кода Код:
lkd> uf nt!ObGetObjectType nt!ObGetObjectType: fffff806`2baa3cc0 488d41d0 lea rax,[rcx-30h] fffff806`2baa3cc4 0fb649e8 movzx ecx,byte ptr [rcx-18h] fffff806`2baa3cc8 48c1e808 shr rax,8 fffff806`2baa3ccc 0fb6c0 movzx eax,al fffff806`2baa3ccf 4833c1 xor rax,rcx fffff806`2baa3cd2 0fb60d2f2f6600 movzx ecx,byte ptr [nt!ObHeaderCookie (fffff806`2c106c08)] fffff806`2baa3cd9 4833c1 xor rax,rcx fffff806`2baa3cdc 488d0d8d376600 lea rcx,[nt!ObTypeIndexTable (fffff806`2c107470)] fffff806`2baa3ce3 488b04c1 mov rax,qword ptr [rcx+rax*8] fffff806`2baa3ce7 c3 ret 1. Код вычисляет адрес структуры _OBJECT_HEADER путем вычитания 0x30 из адреса памяти объекта, переданного в качестве аргумента. 0x30 это смещение от начала объекта до его заголовка в 64 битных ОС. В 32 битных ОС это смещение равняется 0x18. 2. Код выполняет операцию XOR для значения TypeIndex со вторым младшим байтом адреса структуры _OBJECT_HEADER, вычисленного на предыдущем шаге. 3. Затем код выполняет операцию XOR для результата из второго шага с байтом по адресу ObHeaderCookie. 4. Наконец результат операций XOR используется как индекс в ObTypeIndexTable и возвращает указатель на структуру _OBJECT_TYPE по этому индексу. Теперь более простым языком, чтобы было понятно. У нас есть объект PROCESS ffffc4052a8df040 процесса System, определенный после выполнения команды !process 0 0 System От его значения мы отнимаем смещение до начала заголовка объекта: ffffc4052a8df040 - 0x30 = ffffc4052a8df010 Отсчёт байт начинается справа налево. Таким образом второй младший байт это 0xf0 (ffffc4052a8d f0 10). Затем, необходимо выполнить команду db nt!ObHeaderCookie l1 чтобы определить байт в cookie, он равен 0xb9.TypeIndex в зашифрованном виде равен 0x4e. Теперь у нас есть все необходимые данные, они же ключи для расшифровки закодированного индекса методом XOR. Введём следующую команду: ? f0 ^ 4e ^ b9 В итоге получаем расшифрованный TypeIndex: 0x7 для процесса System как и в случае с Windows 7. Выведем таблицу ObTypeIndexTable командой dps nt!ObTypeIndexTable (как видите BAD ObjectHeader в Windows 10/11 уже нет).Остаётся лишь только проверить имя Name : _UNICODE_STRING "Process" и адреса в TypeList : _LIST_ENTRY (0xffffc405`2a8bf380), командой dt nt!_OBJECT_TYPE poi(nt!ObTypeIndexTable + (7 * 8)) Ну чтож, на этом я хотел бы поставить точку в изучении объектов операционной системы Windows. Конечно же это только верхушка айсберга, и внутреннее устройство ОС Windows куда сложнее того, что я показал здесь. Но главное, мы научились вскрывать то, что Microsoft зашифровала, причём на уровне ядра. |
Всего комментариев 0
Комментарии