R0 CREW

Malware Analysis Tutorial 20: Kernel Debugging - Intercepting Driver Loading (Перевод: ximera)

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

Цели урока:

  1. Научится использовать WinDBG для отладки на уровне ядра.
  2. Пропатчить отладчик для защиты от злонамеренных действий вируса.
  3. Понять Driver Entry
  4. Перехватить загрузку драйвера

1. Введение

В этом уроке будет рассмотрен процесс реверсинга более сложной части руткита. До этого момента, все вредоносные действия, производимые руткитом Max++, исполнялись на прикладном уровне (Ring 3), где можно было использовать IMM. В случае, когда Max++ внедряет свой код в случайно выбранный драйвер, и загружает его при помощи функции ZwLoadDriver(), анализ усложняется — IMM не способен анализировать код на уровне ring-0, поэтому мы будем использовать WinDBG в качестве отладчика уровня ядра. К сожалению, WinDBG не поддерживает удобного средства для создания заметок. Для этого мы сделаем копию виртуальной машины. Одну мы будем использовать для создания заметок (используя IMM), а вторую машину мы будем использовать для отладки на уровне ядра (эту машину мы будем контролировать с помощью WinDBG, с хостовой машины через COM порт).

В этом уроке, мы сосредоточимся на тонкой настройке среды для эксперементов (VBox с остальными плюшками прим. пер.) и приступим к анализу функции driver entry. Наша задача состоит в том, чтобы перехватить загрузку драйвера зараженного Max++.

Далее в этом уроке под Win_Notes - мы будем подразумевать машину с IMM, а под Win_Debug — машину с отлаживаемым ядром. Начнем мы введение с настройки среды для эксперементов, а затем перейдем к процессу отладки на уровне ядра.

Давайте предположим, что имя нашего зараженного драйвера rasppoe_2.sys. Обратите внимание, что в этом уроке мы используем IMM версии 1.8.3. Инструкции, описанные во второй части этого урока, применимы только для этой версии.

2. Конфигурация среды для эксперементов

Конфигурация для данного урока замысловата. Она содержит следующие шаги:

  1. Получение зараженного драйвера.
  2. Создание копии виртуальной машины
  3. Запуск машины Win_Notes
  4. Запуск машины Win_Debug

2.1 Поиск зараженного драйвера

Наш первый шаг это поиск зараженного драйвера. Вы можете последовать простым инструкциям из Части 2 Главы 19 данного руководства, и сохранить драйвер. Получив драйвер, отправьте самому себе эти файлы посредством email’а (можно использовать Dropbox, Gdrive, etc. Прим. пер.), и восстановите снимок чистой системы (потому что сейчас она заражена, и Max++ удалил сам себя с жесткого диска). Потом скачайте сохраненые файлы (email, Dropbox, Gdrive etc. Прим. пер.). Теперь мы можем сделать копию нашей виртуальной машины.

2.2 Создание копии виртуальной машины

Щелкните ПКМ по вашей виртуальной машине в VBox и выберите пункт CLONE, затем переименуйте новую машину в Win_Notes и выберите «полное копирование машины и снимков». Мы получили две копии виртуальных машин с ОС WinXP. Будем использовать Win_Notes только для ведения заметок.

2.3 Запуск окружения виртуальной машины Win_Notes

Целью использования окружения машины Win_Notes является ведение заметок. Так как вирус вызывает падение IMM по определенным причинам, мы должны немного поправить логику IMM для успешного создания заметок. Следуйте следующим шагам для настройки окружения:

(1) Запустите IMM
(2) В первом окне IMM, откройте IMM.exe из «C:\Program Files\Immunity Debugger\IMM.exe»
(3) Установите бряк на 0x004E6095. Это уязвимая часть IMM. IMM изредка падает получая неверно отформатированные данные. Сейчас мы сделаем здесь небольшое пояснение. Ни в коем случае мы не будем пытаться реверсить IMM, мы просто исправим некоторые ошибки и сделаем небольшое исправление в текущей версии отладчика.

По адресу 0x004E6095 (как показано на рис.1), IMM пытается добавить ‘\0’ в конец строки. Регистр ESI содержит указатель на начало строки, а EDI содержит ее длину. Если в ESI и EDI содержится NULL, что происходит в инструкции, она вызывает ошибку сегментации (это когда происходит попытка получения доступа по адресу 0xFFFFFFFF, что и вызывает ошибку сегментации). Для решения проблемы, мы должны пропустить данную инструкцию когда в ESI и EDI значения неверны.

Рис 1. Сброс EIP для пропуска одной инструкции

Следуйте приведенным ниже инструкциям:
Загрузите rapppoe_2.sys во втором экземпляре IMM (тот который уже пропатчен прим. пер.), и давайте запустим его. Ранее мы установили бряк на 0x004e6095 в первом экземпляре IMM. Когда вы увидите что пара ESI/EDI равна 0, запустите командную строку Python (как показано на Рис.1, вторая кнопка на панели инструментов IMM), и введите команду для исправления регистра EIP.

  imm.setReg(«EIP», 0x004e6095a)

Но если ESI/EDI не содержат 0, то не следует выполнять описаное выше, просто нажмите F9 и продолжите выполнени. Возможно вам придется выполнить описанные выше действия несколько раз пока *.sys файл не будет загружен. Обратите внимание, в процессе загрузки, вы получите несколько предупреждений о том что некоторые модули вне диапазона адрессов, просто нажмите OK и продолжайте далее.

(4) Теперь во втором IMM, перейдите по View->Executable Modules, и дважды щелкните по rapppoe_2.sys, и вы сможете перепрыгнуть на адрес начала модуля. Модуль должен начинаться по адресу 0x10001000(PUSH ESI). Рис. 2 показывает окно анализатора, с загруженными комментариями(Студенты университета «Hofstra» могут получить .udd файл прямо из офиса автора).

Рис. 2. Дамп файла дравйвера

(5) Нам нужен дополнительный шаг для того чтобы завершить настроку: перейдите View->Modules во втором IMM, и запишите адрес начала модуля. Не существенно какой именно драйвер Max++ выбрал для заражения, входная точка(смещение) всегда будет 0x372B (как показано на Рис.3).

Рис 3. Входная точка модуля raspppoe_2.sys

(6) Теперь щелкнем ПКМ по окну CPU->Go to (0x1000372B), и вы должны оказаться во входной точке драйвера. Win_Notes готова и вы можете использовать ее для ведения заметок (которые будут располагаться внутри кода). На Рис.4 показана первая часть зараженного драйвера.

Рис. 4. Начальная часть зараженного драйвера

2.4 Трассировка в Win_Debug

Сейчас мы покажем как использовать WinDBG на хосте (хостовой машине) для выполнения трассировки модуля зараженного драйвера. Наша цель остановить систему на функции driver entry драйвера raspppoe_2.sys. Заметьте, что данная функция может быть оттрассирована только при помощи WinDBG, потому что IMM это отладчик уровня ring-3. Наша цель остановиться на входной функции драйвера. Это достигается следующими шагами.

(1) Запустите ВМ WinXP_DEBUG в режиме ОТЛАДКИ. После этого на вашей хостовой машине, запустите командную строку и перейдите в каталог «C:\Program Files\Debugging Tools for Windows(x86)» (где установлен WinDBG). Наберите в командной строке «windbg -b -k com:pipe.port=\.\pipe\com_12» (проверьте номер com порта в настройках вашей виртуальной машины).

(2) Теперь в вашем окне WinDBG наберите «bu _+372b», это установит бряк по смещению 0x372b в модуле по имени «». Вы удивитесь откуда взялось имя «». Позже мы покажем вам, как мы нашли это имя после вызова функции zwLoadDriver(), которая вызывается Max++, модуль с именем «_» добавлен Max++.

(3) Настала пора дважды нажать «g» для запуска системы. В дальнейшем, мы запустим Max++ в контролируемом режиме, а пока мы загрузим драйвер.

(4) Запустите IMM в данной ВМ WinXP, очистите все бряки и аппаратные бряки в IMM(найти их можно в View->Breakpoints и View->Hardware Breakpoints).

(5) Перейдите по адресу 0x4012DC и установите аппаратный бряк по данному адресу.(Почему не обычный бряк? Потому что эта секция само-распаковывающаяся и перезаписываемая, соответственно обычные бряки будут потеряны). Обратите особое внимание что вы переходите по адресу 0x4012DC один раз, здесь нажмите ПКМ для установки аппаратного бряка(на данном этапе это совершенно бредовый и бесполезный код).

(6) Нажмите F9 несколько раз и продолжайте выполнение пока не достигнете 0x4012DC. Вы можете столкнуться с несколькими бряками до 0x4012DC. Вы можете получить предупреждения, это вызваны трюками инструкции int 2d.(рассмотренными в главах 3, 4 и 5). Просто игнорируйте их и продолжайте (используя F9) пока не достигнете 0x4012DC.

На рис.5 изображен код который вы должны увидеть. Как вы видите, он находится прямо перед вызовом RtlAddVectoredException, где установлен аппаратный бряк для прерывания вызова LdrLoadDll.(Подробнее см. Главу 11)

Рис. 5. Код по адресу 0x4012DC

(7) Теперь пролистайте вниз на 2 страницы и установите АППАРАТНЫЙ БРЯК по адресу 0x401417. Перед вызовом LdrLoadDll(«lz32.dll»), где Max++ заканчивает загрузку lz32.dll. Затем нажмите SHIFT+F9 несколько раз, пока вы не достигнете 0x401417 (вы попадете дважды на 0x7C90D500, это где то внутри ntdll.zwMapViewSection которая была вызвана LdrLoadDll).

Рис. 6. Код по адресу 0x401407

(8) Теперь мы установим бряк по адресу 0x3C1B3E. Как показано на рис.7, перейдите по адресу 0x3C1B3E и установите ОБЫЧНЫЙ БРЯК. Нажмите SHIFT+F9 для выполнения до 0x3C1B3E. (Вы должны увидеть предупреждение о выходе из диапазона сегмента кода, просто игнорируйте его).

Рис7. Код перед началом zwLoadDriver

Рис. 7. Показывает код который вы должны увидеть по адресу 0x3C1B3E. Перед началом zwLoadDriver.

*** Сейчас мы будем перехватывать событие загрузки драйвера. В WinDbg. Нажмите «Ctrl+Break» для остановки выполнения программы. Перейдите в Debug->Event Filters, выберите LoadModule event и нажмите (добавить) «add». Затем нажмите «g» для продолжения. ***

Теперь вернемся в WinXP_Debug, дважды нажмите F8 и выполните функцию zwLoadDriver, вы увидите как WinXP_Debug приостановилась, потому что модуль перехвата WinDbg перехватил событие первым. Если вы дадите WinDbg комманду «lm», вы увидите новый загруженный модуль «». Снова нажмите «g» в WinDbg, вы должны попасть на бряк [U]bu+372b[/U].

Как показано на рис.8, инструкция на которой мы остановились находится по адресу fae3772b (это смещение 372b относительно базового адреса модуля «_»).

Рис. 8. WinDbg дамп

Нажмите Alt+7(View->Disassembly), вы можете увидеть текущую инструкцию (как показано на рис.9). Если вы сравните с рис.1, вы можете увидите что мы действительно находимся во входной функции драйвера raspppoe_2.sys.

Рис. 9. Дизассемблированый дамп

3. Функция Driver Entry

Сейчас мы приступим к анализу функции driver entry используя WinDbg. Согласно этой ссылке http://msdn.microsoft.com/en-us/library/windows/hardware/ff544113(v=vs.85).aspx[1], функция driver entry в ОС Windows должна соответствовать следующему прототипу:

DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry(
	__in struct _DRIVER_OBJECT [B][U]*DriverObject[/U][/B],
	__in PUNICODE_STRING [B][U]RegistryPath[/U][/B]
)

Первый параметр это объект содержащий в себе информацию о соответствующем объекте драйвера., а второй параметр это строка в формате unicode содержащая путь к соответствующему ключу реестра. Первая цель нашего анализа, выяснить значения этих двух параметров.

Взгляните на первую пару инструкций в функции (рис.9 от fae3772b до fae37738), они создают стек (например для того чтобы сохранять предыдущее значение EBP, для исправления значения ESP, чтобы создать стек и сохранить туда значения других значимых регистров).

Перейдите в WinDbg, и нажмите F10 (один шаг), для выполнения до 0xFAE37738 (см. Рис.9), согласно соглашению о передаче параметров в стиле C-функций, первый параметр (_DRIVER_OBJECT) находится в EBP+8, а второй параметр (PUNICODE_STRING) в EBP+C. Сейчас эти две переменные просто указатели. Давайте сначала получим значения этих двух указателей набрав «bb EBP», ниже мы получим дамп памяти. Заметим что первая колонка это адрес строки, и каждая строка содержит в себе 4 компьютерных слова (WORD, 32-бита каждое), например, fafabc7c (первое слово, в первой строке) это адрес для fafbd4c (первая строка), а fafabc8c (первое слово, второй строки) это адрес для 00000000. Здесь значение EBP это fafabc7c.

kd> dd EBP
fafabc7c  fafabd4c 805a399d [B][U]811254d8 81184000[/U][/B]
fafabc8c  00000000 f7a1dcf4 00000000 00000018
fafabc9c  00000000 fafabcc0 00000010 00000000
fafabcac  00000000 811254d8 fafabd70 811462f0
fafabcbc  81184000 00200020 81128f48 e101b000
fafabccc  811254d8 81143280 00000020 8000030c
fafabcdc  fae34000 00000308 00120010 e14fe090
fafabcec  0000007a 00060004 e1bcc5f8 00000000

Очевидно что, значение первого параметра _DRIVER_OBJECT это 811254d8 (входной адрес объекта), а второй _UNICODE_STRING начинается по адресу 81184000.

Теперь мы отобразим эти значения:

kd> [B][U]dt _DRIVER_OBJECT 811254d8[/U][/B]
nt!_DRIVER_OBJECT
   +0x000 Type             : 0n4
   +0x002 Size             : 0n168
   +0x004 DeviceObject     : (null) 
   +0x008 Flags            : 2
   +0x00c DriverStart      : 0xfae34000 Void
   +0x010 DriverSize       : 0x8000
   +0x014 DriverSection    : 0x81143280 Void
   +0x018 DriverExtension  : 0x81125580 _DRIVER_EXTENSION
   +0x01c DriverName       : _UNICODE_STRING [B][U]"\Driver\.NDProxy"[/U][/B]
   +0x024 HardwareDatabase : 0x8068fa90 _UNICODE_STRING "\REGISTRY\MACHINE\HARDWARE\DESCRIPTION\SYSTEM"
   +0x028 FastIoDispatch   : (null) 
   +0x02c DriverInit       : 0xfae3772b     long  +0
   +0x030 DriverStartIo    : (null) 
   +0x034 DriverUnload     : (null) 
   +0x038 MajorFunction    : [28] 0x804fa87e     long  nt!IopInvalidDeviceRequest+0
kd> dt _UNICODE_STRING 81184000

[B][U]nt!_UNICODE_STRING[/U][/B]
 "\REGISTRY\MACHINE\SYSTEM\ControlSet001\Services\.NDProxy"
   +0x000 Length           : 0x70
   +0x002 MaximumLength    : 0x70
   +0x004 Buffer           : 0x81184008  [B][U]"\REGISTRY\MACHINE\SYSTEM\ControlSet001\Services\.NDProxy"[/U][/B]

Как вы можете заметить, это соответствует роли raspppoe.sys (который предназначен для удаленного подключения к сети).

Задание дня: Проанализируйте вызов по адресу _+3743, его параметры и то что там выполняется?

© Translated by ximera from r0 Crew

Ссылки

[1] Microsoft, «DriverEntry Routine»

Хочу сказать Спасибо людям которые поправляли это перевод и помогали мне:
Limit-No-Exist
root

Offtop

Та я там пару строк поправил, так что не за что =)

PS: Я думал, что “Limit-No-Exist” уже давно где-то потерялась, а оказывается нет)))

Offtop

На самом деле потерялась, но пару месяцев назад столкнулись в аське. И она мне скинула свой профиль на яндексе. Имя оказалось запоминающимся, вот недавно нашел ее в соц.сетях.=)
Она то и помогла мне привести его в более менее художественный вид.