R0 CREW

Malware Analysis Tutorial 23: Tracing Kernel Data Using Data Breakpoints (Перевод: Prosper-H)

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

Цели урока:

  1. Использовать WinDbg для отладки на уровне ядра.
  2. Применить аппаратные брэйкпойнты на данные, чтобы проследить за их содержимым и проанализировать код, который к ним обращается.
  3. Понять, как руткиты устанавливают и скрывают модуль драйвера

1. Введение

Этот урок продолжает анализ из Урока 20. Здесь мы покажем, как Max++ выполняет другие действия касаемые заражения драйвера, и то, как он устанавливает и скрывает инфицированный драйвер. Также мы научимся использовать аппаратные брэйкпойнты, устанавливаемые на данные, чтобы отследить действия, которые выполняются над данными и структурами ядра.

Начнем наш анализ с адреса « _+1BF2 ».

2. Подготовка

Мы будем использовать настройки Раздела 2 из Урока 20. Ниже мы напомним только несколько из наиболее важных шагов, которые требуются для настройки рабочей среды:

(1) Вам нужен отдельный образ виртуальной машины, с именем «Win_Notes», чтобы иметь возможность комментировать код. На этой виртуальной машине, мы не будет запускать исследуемую вредоносную программу, а будем просто использовать ее для записи всех ваших наблюдений с помощью .udd файла. Чтобы сделать это, необходимо изменить поток исполнения IMM таким образом, чтобы избежать сбоя (crash) при работе с .sys файлами. За подробностями обращайтесь к Разделу 2 из Урока 20. Перейдите на адрес 0x100037AF, чтобы начать анализ.

(2) Второй образ виртуальной машины «Win_DEBUG» должен быть запущен в DEBUG режиме. При этом, на HostXP должен быть запущен WinDbg, который, с помощью COM-порта, присоединиться к «Win_DEBUG» – таким образом, мы сможем отлаживать данную ОС в режиме ядра.

(3) В WinDbg установите брэйкпойнт «bu _+37af», чтобы перехватить функцию driver entry.

3. Брэйкпойнты на данные и слежение за именем файла

Сейчас мы продолжим анализ после Урока 21. Начнем с адреса « _+37AF ». На Рис. 1 показано несколько первых инструкций. Как вы можете видеть, первая часть кода работает со строками.

Рис. 1. Копирование и манипулирование строками

По адресу 0x100037BF, код копирует строку «??\C2CAD…\snifer67» в область памяти на которую указывает EDI. Анализ данных в WinDbg приводит к следующему результату. Очевидно, что значение в EDI является начальным адресом строки, расположенной по адресу 0xFAFAF9F8 (на который в данный момент ссылается ESP+34).

kd> db [B][U]fafaf9f8[/U][/B]
fafaf9f8  5c 00 3f 00 3f 00 5c 00-43 00 32 00 43 00 41 00  [B]\.?.?.\.C.2.C.A.[/B]
fafafa08  44 00 39 00 37 00 32 00-23 00 34 00 30 00 37 00  [B]D.9.7.2.#.4.0.7.[/B]
fafafa18  39 00 23 00 34 00 66 00-64 00 33 00 23 00 41 00  [B]9.#.4.f.d.3.#.A.[/B]
fafafa28  36 00 38 00 44 00 23 00-41 00 44 00 33 00 34 00  [B]6.8.D.#.A.D.3.4.[/B]
fafafa38  43 00 43 00 31 00 32 00-31 00 30 00 37 00 34 00  [B]C.C.1.2.1.0.7.4.[/B]
fafafa48  5c 00 4c 00 5c 00 53 00-6e 00 69 00 66 00 65 00  [B]\.L.\.S.n.i.f.e.[/B]
fafafa58  72 00 36 00 37 00 00 00-14 fb 57 80 00 f3 c4 e1  [B]r.6.7[/B].....W.....
fafafa68  00 52 2e 81 00 20 2f 81-00 10 00 00 d8 fa 57 80  .R... /.......W.

Аналогичным образом вы можете определить вторую строку, генерируемую функцией swpringf по адресу 0x100037DB (Рис. 1), «\systemroot\system32\drivers\rasppoe» (это имя случайно выбранного драйвера).

Проблема, которая стоит перед нами, заключается в том, что если мы посмотрим в «Win_Notes», то не сможем определить место, где эти две строки используются! Чтобы разрешить эту проблему, нам нужно использовать брэйкпонты на данные (в WinDbg).

Давайте, в качестве примера, возьмем вторую строку. Проанализировав входящий параметр swprintf (Рис. 1, вторая выделенная область), мы знаем, что вторая строка «\systemroot\system32\drivers\rasppoe» размещена по адресу 0xFAFB7A78. Имея эти данные, мы можем установить брэйкпойнт, на чтение данных, используя следующую команду: «ba r4 fafb7a78» (она означает, следить за любыми попытками чтения 4-х байт, начиная с адреса fafb7a78).

kd> [B]db fafb7a78 [/B]
fafb7a78  5c 00 73 00 79 00 73 00-74 00 65 00 6d 00 72 00  \.s.y.s.t.e.m.r.
fafb7a88  6f 00 6f 00 74 00 5c 00-73 00 79 00 73 00 74 00  o.o.t.\.s.y.s.t.
fafb7a98  65 00 6d 00 33 00 32 00-5c 00 64 00 72 00 69 00  e.m.3.2.\.d.r.i.
fafb7aa8  76 00 65 00 72 00 73 00-5c 00 6b 00 62 00 64 00  v.e.r.s.\.k.b.d.
fafb7ab8  63 00 6c 00 61 00 73 00-73 00 2e 00 73 00 79 00  c.l.a.s.s...s.y.
fafb7ac8  73 00 00 00 77 7a 56 80-10 0d 00 e1 c4 06 00 00  s...wzV.........
fafb7ad8  a8 7b fb fa 10 0d 00 e1-01 00 00 00 c4 06 00 00  .{..............
fafb7ae8  00 00 00 00 20 0d 00 e1-88 2d 00 e1 f9 ba 13 81  .... ....-......

kd> ba r4 fafb7a78

Теперь, запустите программу. Мы попадем на адрес « _+0x1b » в RtlInitUnicodeString, в этом месте, если вы выполните команду Kp (чтобы отобразить содержимое стэка), то вы вероятно не сможете получить правильную последовательность фрэймов в стэке (как показано ниже).

kd> g
Sun Mar 25 20:26:39.359 2012 (UTC - 4:00): Breakpoint 1 hit
nt!RtlInitUnicodeString+0x1b:
804d92c2 66f2af          repne scas word ptr es:[edi]

kd> Kp
ChildEBP RetAddr  
fafb7970 faeaefea nt!RtlInitUnicodeString+0x1b
[B][U]WARNING: Stack unwind information not available. Following frames may be wrong.[/U][/B]
fafb79b4 faeaf808 _+0x2fea
fafb7c7c 805a399d _+0x3808
fafb7d4c 805a3c73 nt!IopLoadDriver+0x66d
fafb7d74 804e426b nt!IopLoadUnloadDriver+0x45
fafb7dac 8057aeff nt!ExpWorkerThread+0x100
fafb7ddc 804f88ea nt!PspSystemThreadStartup+0x34
00000000 00000000 nt!KiThreadStartup+0x16

В этом случае, нам нужно выйти из RtlInitUnicodeString. Для этой цели существует команда Step Out (Shift+F11), однако, она здесь не работает, потому что Max++ не следует соглашениям о вызовах языка C. Поэтому, мы должны очень терпеливо жать на F10. Где-то, примерно, после 10 шагов (по F10), мы достигнем адреса « _+1a32 », как показано ниже!

kd> p
nt!RtlInitUnicodeString+0x38:
804d92df 5f              pop     edi
kd> p
nt!RtlInitUnicodeString+0x39:
804d92e0 c20800          ret     8
kd> p
_+0x1a32:
faeada32 33c0            xor     eax,eax

Адрес «_+1a32 » является частью функции Max++, которая ответственна за создание экземпляра объекта _OBJECT_ATTRIBUTES (где «\systemroot\system32\drivers\rasppoe» подается в качестве ObjectName).

Рис. 2. Функция, которая вызывает RtlInitUnicodeString

Трассируя от адреса « _+1a32 », мы можем обнаружить, что поток выполнения программы переходит на адрес « _+23e9 » (который читает содержимое драйвера и помещает его в набор заблокированных виртуальных страниц)

Задача 1. Закончите выше упомянутый анализ и предоставьте детальный отчет о том, как используется строка «\systemroot\system32\drivers\raspppoe».

4. Виртуальные страницы

Продолжим анализ. По адресу « _+3803 », Max++ вызывает другую функцию, размещенную по адресу « _+23C8 » (которая читает содержимое файла и помещает прочитанное в виртуальные страницы). Здесь есть несколько интересных технических деталей. На Рис. 3. показано тело этой функции. Обратите внимание на первую выделенную область, она создает экземпляр объекта _OBJECT_ATTRIBUTES, который, как уже было обсуждено ране в Разделе 3, содержит имя файла «\systemroot\system32\drivers\raspppoe», Затем Max++ открывает файл и запрашивает о нем стандартную файловую информацию. Когда все операции были успешно выполнены, он приступает к созданию виртуальных страниц.

Рис. 3. Первая часть функции _+23C8

Перейдем ко второй части функции « _+23C8 » (Рис. 4). В реализации драйвера, во многих случаях, необходимо заблокировать физические страницы памяти для виртуальных адресов (так, чтобы ваши данные, которые хранятся в оперативной памяти, не были сброшены на диск, в файл подкачки ОС). Значение этой части кода довольно таки ясно: вначале, код запрашивает виртуальные страницы (см. первую выделенную зону), затем дескриптор виртуальных страниц сохраняется в структуре, называемой _MDL (хранится по адресу 8121c970). После успешного выполнения этих действий, код просит систему выделить физические страницы (см. MmMapLockedPageSpecifyCache). Затем Max++ читает инфицированный драйвер в эти страницы (начиная с адреса 0xf7649000). Если вы сдампите данные, начиная с адреса 0xf7649000, то обнаружите, что в действительности это двоичный исполняемый файл (т.е., см. на magic-последовательность 4D5A из DOS заголовка)

kd> dd f7649000
f7649000  0090[B][U]5a4d[/U][/B] 00000003 00000004 0000ffff
f7649010  000000b8 00000000 00000040 00000000

Рис. 4. Вторая часть функции _+23C8

Сейчас мы подходим к интересной части (см. последнюю выделенную область, Рис. 4). Как только содержимое инфицированного драйвера считано, Max++ сразу же освобождает (released) физические страницы (для виртуального адреса 0xf7649000). Это совершенно не логично! Max++, не хочет использовать эти данные позже? Ваша задача состоит в том, чтобы разобраться с этим вопросом.

Задача 3. Чтобы проследить за данными – установите два брэйкпойнта на данные. Один на _MDL (т.е. в нашем случае это адрес 0x8121c970), а другой на начальный адрес двоичных данных инфицированного драйвера (т.е. в нашем случае это адрес 0xf7649000). Попытайтесь выяснить, действительно ли используются эти страницы или нет. Таким образом, вы должны ответить на вопрос: почему Max++ освобождает страницы на Рис. 4?

Задача 4. Проанализируйте функцию « _+22C3 ».

5. Снова инфицирование драйвера и использование виртуальных страниц

По адресу «_+3889 » Max++ вызывает функцию 2D9F. Перейдем к анализу этой функции (Рис. 5). Она используется для инфицирования файла драйвера (имя файла передается в качестве первого параметра, в ее стэковом фрейме). Сначала функция создает в файле section object, затем выполняет memcopy, чтобы скопировать содержимое из MDL в файл, и записывает (flushes) содержимое обратно в файл.

Рис. 5. Инфицирование драйвера fips.sys

Задача 5. Используйте установку брэйкпойнтов на данные, чтобы проанализировать, откуда вредоносное содержимое файла (where is the malicious file content from)?

6. Окончательная установка вредоносного драйвера диска

В Уроке 22 мы показали, как вредоносный драйвер диска используется для имитации файловых запросов направленных устройству «??\C2CAD…», используя для этого файл «12345678.sav». Далее, мы покажем, как путем копирования атрибутов из реального драйвера диска, настраивается вредоносный драйвер.

Рис. 6. Подключение и копирование из Driver Object

Первая часть (как показано в первой выделенной области, Рис. 6), корректирует поле DriverSection инфицированного объекта. На самом деле это основные операции, которые осуществляются над связанным списком, цель которых удалить инфицированный драйвер из списка модулей. Обратите внимание на то, что типом поля DriverSection (смещение 0x14) является _LDR_DATA_TABLE_ENTRY. Вы можете использовать WinDbg, чтобы убедиться в этом.

Далее во второй выделенной области (Рис. 6), Max++ пытается скопировать все атрибуты из оригинального объекта «\Driver\Disk» в инфицированный драйвер (в нашем случае, в комментариях это .serial, имя может измениться во время сессии). В инфицированном драйвере, есть только один неизменный атрибут: это major function « _+2bDE »! Дойдя до этой точки, Max++ успешно установил инфицированный драйвер диска и скрыл его из списка загружаемых модулей.

© Translated by Prosper-H from r0 Crew