R0 CREW

Malware Analysis Tutorial 7: Exploring Kernel Data Structure (Перевод: Prosper-H)

Перейти к содержанию

Цели урока:

  1. Научиться исследовать структуры ядра с помощью WinDbg.
  2. Разобраться с важными структурами ядра для поддержания актуальной информации о процессах и потоках.
  3. Усвоить разницу между аппаратными и программными брэйкпойнтами и научиться эффективно их использовать во время отладки.
  4. Продолжить практиковать навыки реврес инженеринга для лучшего понимания ассемблерного кода.

1. Введение

Этот урок покажет вам, как исследовать структуры ядра с помощью WinDbg. Это очень будет очень полезно нам для понимания методов инфицирования используемых в Max++. Мы рассмотрим некоторые интересные структуры, такие как TIB (Thread Information Block), PEB (Process Information Block) и the loaded modules/dlls of a process. Так же рассмотрим взаимодействие Max++ с некоторыми важными DLL-библиотеками ядра.

1.1 Подготовка

Если вы не установили WinDbg на вашей основной машине, то, пожалуйста, следуйте инструкциям из Урока 1; вначале установите платформу VirtualBox (с небольшой локальной сетью, состоящей из одного Linux шлюза и одного экземпляра Windows, зараженного руткитом Max++). Затем следуйте Уроку 4, чтобы установить WinDbg на «Host XP» и настройте pipe-порт «Guest XP», для отладки. Ниже приведены шаги, предназначенные для запуска виртуальной машины и WinDbg.

  1. Вначале запустите «Guest XP» в VirtualBox. Загрузитесь в режиме «Debugged». (Следуйте Уроку 4, чтобы узнать, как включить опцию загрузки «Debugged»).
  2. На «Host XP» запустите окно командной строки, перейдите в директорию «С:\Program Files\Debugging Tools for Windows(x86)» и введите следующую команду:

windbg -b -k com:pipe,port=\.\pipe\com_11

  1. После чего вы должны увидеть в окне WinDbg следующее «Breakpoint on INT 3». Это означает, что WinDbg, в данный момент, остановился на программном брэйкпойнте (INT 3). Нажмите «g» (означает «go»), чтобы дать системе продолжить свое выполнение. В случае необходимости, нажмите «g» во второй раз.
  2. Иногда вы можете обнаружить, что ваша «Guest XP» замораживается. В этом случае, просто нажмите «g» в окне WinDbg.
  3. Запустите Immunity Debugger в «Guest XP» и загрузите Max++ (см. Урок 1, где говорится, откуда можно получить двоичный файл Max++).
  4. В панели кода IMM, нажмите «right click => Go to => Expression => [B]0x401018[/B]» и затем установите на данном адресе «HARDWARE BREAKPOINT» (нажмите «right click» и выберите «Breakpoint => Hardware, on Execution»). Это то место, где мы остановились в Уроке 6. Как вы видите, прямо сейчас, по адресу 0x401018 расположена инструкция «DEC DWORD [EAX+20]». Позже, когда мы остановимся на этом адресе, эта инструкция будет перезаписана, из-за самораспаковывающейся особенности Max++, см. детали в Уроке 6. Затем нажмите F9 (continue), чтобы выполнить код программы и остановиться на адресе 0x00401018.

1.1.1 Почему был использован аппаратный брэйкпойнт?

Обратите внимание, что на Шаге 6 вы должны использовать аппаратный брэйкпойнт. Но почему не программный? Подумайте о том, как реализован программный брэйкпойнт. Когда вы устанавливаете программный брэйкпойнт в отладчике, то он в действительности изменяет первый байт инструкции на «INT 3». Когда выполнение достигает инструкции «INT 3», ядро операционной системы вызывает отладчик для обработки прерывания (которое затем останавливает и подсвечивает (highlights) указанное место в окне отладчика, а когда вы восстановите исполнение или отмените брейкпойнт, отладчик запишет оригинальный опкод обратно).

Напомним, что вредоносная программ производит самораспаковку кода в нутрии себя (см. Урок 6). Это приводит к перезаписи «INT 3», поэтому вы никогда не сможете остановиться в нужном месте 0x401018! Вот почему мы использовали аппаратный брэйкпойнт. При установке аппаратного брэйкпойнта, его адрес записывается в один из четырех HW-регистров, которым снабжены процессоры Intel. Процессор проверяет регистры во время выполнения каждой инструкции и останавливает исполнение при обнаружении совпадения. Единственным недостатком является то, что вы можете установить только 4 аппаратных брэйкпойнта.

1.2 Объект анализа

Мы проанализируем около 20 инструкций, начиная с адреса 0x00401018 по 0x0040105B. Ассемблерный код представлен на рис. 1.

Рис. 1. Кусок кода для анализа (0x401018 - 0x40105B)

2. Регистр FS, структуры TIB и PEB

Как показано на рис. 1, инструкция MOV EAX, DWORD FS:[18] (0x00401018) осуществляет важный прием. Она читает данные из памяти, указатель на которую расположен в FS:[18], в EAX. Тут FS, как SS и DS, является одним из сегментных регистров. The FS:[18] is an address specified using the displacement addressing mode. Адрес вычисляется как [значение хранящееся в FS] + 0x18.

Всякий раз, когда вы видите некоторый код, обращающийся к регистру [B]FS[/B], следует обратить на него особое внимание! FS указывает на очень важную структуру ядра Windows, связанную с текущим процессом/потоком. Обратившись за деталями к [1], вы увидите, что FS:[18] хранит адрес структуры TIB (Thread Information Block) – также известной как TEB.

Затем инструкция MOV EAX, [EAX+30] (0x40101E) берет данные, расположенные по адресу EAX+0x30. Что это значит? Так как сейчас EAX хранит адрес TIB, то эта инструкция берет некоторые данные из поля, которое расположено по смещению 0x30 от начала TIB.

Чтобы двигаться дальше, нам нужно выяснить внутреннюю структуру TIB. Существует два способа сделать это: (1) обратиться к MSDN или (2) воспользоваться ядерным отладчиком WinDbg. Для наиболее известных структур данных, таких как TIB, люди уже сделали, за вас, вычисление смещений. Например, прочитав [1], вы бы знали, что по смещению 0x30 хранится адрес PEB (Process Information Block). Но в большинстве случаев, для структур ядра, вам нужно будет вручную вычислять смещения (т.е. выяснить размер всех предыдущих атрибутов в структуре и подвести их итог).

Самый удобным способом будет использование WinDbg. Давайте вернемся обратно к нашему WinDbg, запущенному на «Host XP», и выполним следующую комбинацию: [B]«Ctrl+Break»[/B]. Это действие прервет выполнение «Guest XP» и вернет управление в WinDbg. После это введите следующую команду:

[B]dt nt!_TEB[/B]

Что говорит, отобразить (display) тип (type) данных из структуры «_TEB», расположенной в NT-модуле. Если вам нужна информация о NT-модуле, то вы можете ввести команду:

[B]lm[/B]

Которая отобразит загруженные модули, и вы увидите, что «NT» является именем модуля для «ntoskrnl.dll».

На самом деле, WinDbg очень мощный отладчик, если добавить «-r n» к команде «dt», то типы данных отобразятся рекурсивно, т.е. когда поле структуры, само по себе, является сложным типом – отобразится его содержимое.

Из вывода команды «dt», вы можете сразу сделать вывод, что 0x30 из TEB является адресом PEB.

3. Список загруженных модулей

Перейдем к двум следующим инструкциям. Используя метод, описанный в Разделе 2, можно определить, что инструкция MOV ECX, [EAX+C] (0x401021) загружает в EXC указатель на LDR (Loaded Module List). Информацию о структуре PEB можно найти в MSDN [2], однако, вы обнаружите, что, на самом деле, WinDbg может предоставить более детальную информацию, включая много недокументированных атрибутов.

Сейчас нам нужно посмотреть на структуру LDR (_LIST_ENTRY). Выполнив команду «dt nt!_PEB_LDR_DATA» в WinDbg, получим следующий дамп (dump):

kd> dt _PEB_LDR_DATA
nt!_PEB_LDR_DATA
   +0x000 Length           : Uint4B
   +0x004 Initialized      : UChar
   +0x008 SsHandle         : Ptr32 Void
   +0x00c InLoadOrderModuleList : _LIST_ENTRY
   +0x014 InMemoryOrderModuleList : _LIST_ENTRY
   +0x01c InInitializationOrderModuleList : _LIST_ENTRY
   +0x024 EntryInProgress  : Ptr32 Void
kd> dt _LIST_ENTRY
nt!_LIST_ENTRY
   +0x000 Flink            : Ptr32 _LIST_ENTRY
   +0x004 Blink            : Ptr32 _LIST_ENTRY

Обратите внимание, что EAX сейчас содержит адрес, взятый по смещению 0xC, из структуры _PEB_LDR_DATA, началом которого является структура _LIST_ENTRY, которая содержит два компьютерных слова (каждое слово имеет длину 4 байта). Первые четыре байта – это указатель Flink, который указывает на следующую структуру _LIST_ENTRY, а следующие четыре байта – это указатель Blink, который указывает на предыдущую структуру _LIST_ENTRY. Так что – это структура двунаправленного связанного списка. Больше деталей о структуре PEB_LDR_DATA можно найти в MSDN [4].

Давайте перейдем к инструкции MOV EAX, DWORD [ECX] (00401029). По сути, она перемещает содержимое FLink в EAX. После этой операции, согласно [4], EAX содержит адрес _LDR_DATA_TABLE_ENTRY для следующего модуля. Однако, это НЕ ТАК! На самом деле EAX сейчас содержит адрес из смещения 0x8 из структуры _LDR_DATA_TABLE_ENTRY (т.е. адрес из поля «InMemoryOrderLinks»). [B](ПРИМ. ПЕР. на самом деле, автор урока ошибся, EAX содержит адрес из смещения 0x10, а не 0x8, т.е. адрес из поля «InInitializationOrederModuleList.Flink», а не «InMemoryOrderLinks»)[/B]

Тут мы начинаем подходить к интересной части. Посмотрите на инструкцию MOV EDX, DWORD [EAX+20] (0x0040102D), что она означает? Вначале, давайте рассмотрим структуру LDR_DATA_TABLE_ENTRY.

kd> dt _LDR_DATA_TABLE_ENTRY -r2
nt!_LDR_DATA_TABLE_ENTRY
   +0x000 InLoadOrderLinks : _LIST_ENTRY
      +0x000 Flink            : Ptr32 _LIST_ENTRY
         +0x000 Flink            : Ptr32 _LIST_ENTRY
         +0x004 Blink            : Ptr32 _LIST_ENTRY
      +0x004 Blink            : Ptr32 _LIST_ENTRY
         +0x000 Flink            : Ptr32 _LIST_ENTRY
         +0x004 Blink            : Ptr32 _LIST_ENTRY
   +0x008 InMemoryOrderLinks : _LIST_ENTRY
      +0x000 Flink            : Ptr32 _LIST_ENTRY
         +0x000 Flink            : Ptr32 _LIST_ENTRY
         +0x004 Blink            : Ptr32 _LIST_ENTRY
      +0x004 Blink            : Ptr32 _LIST_ENTRY
         +0x000 Flink            : Ptr32 _LIST_ENTRY
         +0x004 Blink            : Ptr32 _LIST_ENTRY
   +0x010 InInitializationOrderLinks : _LIST_ENTRY
      +0x000 Flink            : Ptr32 _LIST_ENTRY
         +0x000 Flink            : Ptr32 _LIST_ENTRY
         +0x004 Blink            : Ptr32 _LIST_ENTRY
      +0x004 Blink            : Ptr32 _LIST_ENTRY
         +0x000 Flink            : Ptr32 _LIST_ENTRY
         +0x004 Blink            : Ptr32 _LIST_ENTRY
   +0x018 DllBase          : Ptr32 Void
   +0x01c EntryPoint       : Ptr32 Void
   +0x020 SizeOfImage      : Uint4B
   +0x024 FullDllName      : _UNICODE_STRING
      +0x000 Length           : Uint2B
      +0x002 MaximumLength    : Uint2B
      [B][U]+0x004 Buffer           : Ptr32 Uint2B[/U][/B]
   +0x02c BaseDllName      : _UNICODE_STRING
      +0x000 Length           : Uint2B
      +0x002 MaximumLength    : Uint2B
      +0x004 Buffer           : Ptr32 Uint2B
   +0x034 Flags            : Uint4B
   +0x038 LoadCount        : Uint2B
   +0x03a TlsIndex         : Uint2B
   +0x03c HashLinks        : _LIST_ENTRY
      +0x000 Flink            : Ptr32 _LIST_ENTRY
         +0x000 Flink            : Ptr32 _LIST_ENTRY
         +0x004 Blink            : Ptr32 _LIST_ENTRY
      +0x004 Blink            : Ptr32 _LIST_ENTRY
         +0x000 Flink            : Ptr32 _LIST_ENTRY
         +0x004 Blink            : Ptr32 _LIST_ENTRY
   +0x03c SectionPointer   : Ptr32 Void
   +0x040 CheckSum         : Uint4B
   +0x044 TimeDateStamp    : Uint4B
   +0x044 LoadedImports    : Ptr32 Void
   +0x048 EntryPointActivationContext : Ptr32 Void
   +0x04c PatchInformation : Ptr32 Void

Мы знаем, что инструкция MOV EDX, DWORD [EAX+20] загружает содержимое из памяти, на которое указывает адрес EAX+0x20. Но, на что указывает EAX? Он указывает на смещение 0x8 (прим. пер. на 0x10) из _LDR_DATA_TABLE_ENTRY. Таким образом, EAX+0x20 указывает на смещение 0x28 (прим. пер. на 0x30) (см. выше, на подчеркнутую область данных, в дампе структуры), которое является полем «Buffer» из FullDllName (прим. пер. BaseDllName).

В Windows, структура _UNICODE_STRING является попыткой Microsoft справиться с потребностями в многоязычности для того, чтобы предоставить локализованную версию своей операционной системы в разных частях мира. Структура состоит из двух частей: (1) длина строки и (2) необработанные данные строки. Таким образом, поле «Buffer» составляет «полное» (прим. пер. базовое) имя DLL-библиотеки в формате UNICODE!

Это значит, что код по адресу 0x0040102D начинает работать с именем DLL-библиотеки! Чтобы проверить наше предположение, посмотрите на регистр EDX в Immunity Debugger (Рис. 2). Вы увидите, что первым именем модуля, на которое мы смотрим является «ntdll.dll».

Рис. 2. EDX указывает на имя DLL-библиотеки

4. Вопросы дня

Давайте попытаемся составить полную картинку происходящего с кодом, который начинается с адреса 0x00401018 по 0x00401054. Вы могли заметить, что на самом деле тут мы имеем двух уровневый цикл.

Внешний цикл начинается с 0x40102E по 0x401054, по сути это цикл «do-while». Внутренний цикл с 0x401036 по 0x401046. Наши вопросы на сегодня:

  1. Что делает внутренний цикл с адреса 0x401036 по 0x401046?
  2. Что делает внешний цикл?

Подсказка: код, который сегодня обсуждали, пытается найти модуль и сделать некоторые плохие вещи to that module (эти вредоносные операции начинаются с адреса 0x40105C). Используйте Immunity Debugger, чтобы найти их. Мы покажем эти вредоносные операции в следующем уроке.

© Translated by Prosper-H from r0 Crew

Ссылки

[1] Wiki, “Windows Thread Information Block”
[2] Microsoft, “PEB Structure”
[3] Microsoft, “PEB_LDR_DATA structure”