Ча­ще все­го при ана­лизе поведе­ния подоз­ритель­ной прог­раммы наб­люда­ют за поль­зователь­ским режимом, а код в ядре оста­ется вне поля зре­ния ана­лити­ка. Отчасти это пра­виль­но, пос­коль­ку боль­ше все­го вре­донос­ной деятель­нос­ти ведет­ся имен­но в поль­зователь­ском прос­транс­тве. Тем не менее вре­донос­ный код в ядре может нанес­ти боль­ше ущер­ба, и его слож­нее обна­ружить. Одна­ко это воз­можно.

С появ­лени­ем KPP (Kernel Patch Protection), или сок­ращен­но PatchGuard, в Windows ста­ло слож­нее модифи­циро­вать ядро без пос­ледс­твий. Если рань­ше, нап­ример, таб­лицу сис­темных вызовов с целью филь­тра­ции сис­темных вызовов перех­ватыва­ли даже такие легитим­ные прог­раммы, как анти­виру­сы, то с появ­лени­ем KPP это ста­ло не так прос­то. Тем не менее для рут­китов PatchGuard не пред­став­ляет осо­бой угро­зы, пос­коль­ку тех­ники его обхо­да мож­но най­ти в откры­том дос­тупе, они раз­вива­ются и акту­аль­ны по сей день. В этой статье будет рас­смат­ривать­ся мно­жес­тво тех­ник модифи­кации ядра, вклю­чая те, что обна­ружи­вает PatchGuard, на при­мере нового пла­гина для DRAKVUF — rootkitmon.

Мы уже нес­коль­ко лет раз­рабаты­ваем песоч­ницу для рис­кори­енти­рован­ной защиты, исполь­зующую фрей­мворк DRAKVUF. DRAKVUF — безаген­тная песоч­ница, осно­ван­ная на биб­лиоте­ке LIBVMI и гипер­визоре XEN. Все воз­можнос­ти DRAKVUF реали­зова­ны в пла­гинах. Каж­дый из двад­цати с лиш­ним пла­гинов выпол­няет опре­делен­ную работу: для обна­руже­ния дос­тупа к фай­лам есть пла­гин filetracer, для трас­сиров­ки сис­темных вызовов — пла­гин syscalls. Rootkitmon — новый пла­гин, поз­воля­ющий отсле­живать вре­донос­ную активность в ядре средс­тва­ми DRAKVUF.

Су­щес­тву­ет мно­жес­тво опре­деле­ний понятия «рут­кит», мы же будем опи­рать­ся на сле­дующее: «компь­ютер­ная прог­рамма, которая исполь­зует недоку­мен­тирован­ные и (или) зап­рещен­ные тех­ники для манипу­ляции ядром опе­раци­онной сис­темы в сво­их целях».

 

Техники

В «джен­тель­мен­ский набор» пла­гина вхо­дят воз­можнос­ти детек­тировать сле­дующие типы перех­ватов:

  • мо­дифи­кация кода заг­ружен­ных драй­веров в памяти (inline-перех­ваты);
  • мо­дифи­кация таб­лиц сис­темных вызовов, таб­лиц пре­рыва­ний и таб­лиц дес­крип­торов;
  • мо­дифи­кация регис­тра MSR LSTAR;
  • мо­дифи­кация ука­зате­лей на фун­кции DRIVER_OBJECT, стек DEVICE_OBJECT;
  • сок­рытие про­цес­са из спис­ка EPROCESS;
  • ре­гис­тра­ция раз­личных сис­темных фун­кций обратно­го вызова.

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

 

Inline-перехваты

Рас­смот­рим самый рас­простра­нен­ный тип перех­ватов и, воз­можно, самый лег­ко обна­ружи­ваемый — inline-перех­ваты. Inline-перех­ваты очень популяр­ны, и даже Microsoft пре­дос­тавля­ет воз­можность перех­ватить API-биб­лиоте­ки, добав­ляя перед фун­кци­ями двух­бай­товый про­лог вро­де mov edi, edi для быс­тро­го редак­тирова­ния фун­кци­ональ­нос­ти уже заг­ружен­ных и работа­ющих ком­понен­тов. Конеч­но, такие перех­ваты воз­можны толь­ко в поль­зователь­ском режиме, а в ядре кара­ются синим экра­ном с кодом ошиб­ки 0x109, если PatchGuard не вык­лючен.

Inline-перех­ваты обыч­но сос­тоят из трех час­тей:

  • под­мена пер­вых нес­коль­ких инс­трук­ций целевой фун­кции для перенап­равле­ния потока выпол­нения на код сво­его при­ложе­ния;
  • об­работ­ка целевой фун­кции: изме­нение парамет­ров, филь­тра­ция, логиро­вание;
  • вы­пол­нение под­менен­ных инс­трук­ций и воз­врат на ори­гиналь­ную фун­кцию.

Рас­смот­рим прос­той при­мер вызова CreateFileW из биб­лиоте­ки kernel32.dll. Прой­дя все биб­лиоте­ки, в ито­ге код ока­жет­ся в ядер­ной фун­кции nt!NtCreateFile. Если бы рут­кит уста­новил перех­ват на эту фун­кцию, он бы мог выг­лядеть сле­дующим обра­зом.

Схема системного вызова
Схе­ма сис­темно­го вызова

Пос­коль­ку код находит­ся в стра­ницах с пра­вами толь­ко на чте­ние и выпол­нение, для записи в такие стра­ницы необ­ходимо либо выделить новую вир­туаль­ную стра­ницу с пра­вами на запись и спро­еци­ровать ее на физичес­кую стра­ницу, где находит­ся код, либо отклю­чить бит Write Protect в спе­циаль­ном регис­тре управле­ния CR0, что поз­волит выпол­нять запись в стра­ницы в обход их прав для текуще­го ядра.

Структура регистра управления CR0
Струк­тура регис­тра управле­ния CR0
Описание значения флага Write Protect
Опи­сание зна­чения фла­га Write Protect

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

PsLoadedModules — двус­вязный спи­сок струк­тур _KLDR_DATA_TABLE_ENTRY, опи­сыва­ющих заг­ружен­ный драй­вер: его базовый адрес, раз­мер, имя, харак­терис­тики и про­чее.

Двусвязный список загруженных модулей
Двус­вязный спи­сок заг­ружен­ных модулей
Описание структуры _KLDR_DATA_TABLE_ENTRY
Опи­сание струк­туры _KLDR_DATA_TABLE_ENTRY

Для перечис­ления заг­ружен­ных модулей ядра с помощью DRAKVUF API мы реали­зова­ли метод drakvuf_enumerate_drivers. В пла­гине rootkitmon спи­сок PsLoadedModules про­ходит­ся два раза: в начале ини­циали­зации пла­гина — для под­сче­та кон­троль­ных сумм сек­ций драй­веров — и в кон­це — для срав­нения зна­чений.

Перечисление и подсчет контрольных сумм секций драйвера
Пе­речис­ление и под­счет кон­троль­ных сумм сек­ций драй­вера

В слу­чае рас­хожде­ния в логе появит­ся стро­ка:

"\Device\HarddiskVolume2\Windows\System32\explorer.exe":KiDeliverApc SessionID:0 PID:1968 PPID:476 Reason:"Driver section modification" Driver:"ntoskrnl.exe"

Для отра­бот­ки в кон­це ана­лиза мы добав­ляем перех­ват час­то вызыва­емой ядер­ной фун­кции KiDeliverApc. Пос­коль­ку мы заранее не зна­ем, какой поток вызовет эту фун­кцию и в каком кон­тек­сте про­цес­са этот поток находил­ся, имя про­цес­са, его PID и PPID, которые DRAKVUF авто­мати­чес­ки сох­раня­ет в жур­нале, не име­ют отно­шения к детек­ту, в то вре­мя как поле Reason и все поля, сле­дующие за ним, име­ют. В дан­ном слу­чае поле Reason озна­чает обна­ружен­ную вре­донос­ную тех­нику, а поле Driver — наз­вание модифи­циро­ван­ного драй­вера.

 

Перехват таблицы системных вызовов

SSDT (System Service Descriptor Table) — мас­сив ука­зате­лей на обра­бот­чики сис­темных вызовов в 32-бит­ных сис­темах или спи­сок сме­щений отно­ситель­но базово­го адре­са таб­лицы на 64-бит­ных ОС. Как говори­лось ранее, до появ­ления PatchGuard эта таб­лица активно исполь­зовалась легитим­ными прог­рамма­ми, а так­же рут­китами для филь­тра­ции и монито­рин­га сис­темных вызовов. Переза­пись одно­го ука­зате­ля в такой таб­лице рав­носиль­на перех­вату всех вызовов опре­делен­ного обра­бот­чика, что нам­ного про­ще inline-перех­ватов, рас­смот­ренных до это­го.

На дан­ный момент в ОС Windows сущес­тву­ет два таких мас­сива под сим­волами nt!KiServiceTable и win32k!W32pServiceTable для сис­темных вызовов к модулю ntoskrnl.exe и гра­фичес­кой под­систе­ме win32k.sys соот­ветс­твен­но. Количес­тво эле­мен­тов в этих мас­сивах сох­ранено в перемен­ных nt!KiServiceLimit и win32k!W32pServiceLimit.

Значения структур nt!KiServiceTable и win32k!W32pServiceTable
Зна­чения струк­тур nt!KiServiceTable и win32k!W32pServiceTable

Ука­затель на SSDT находит­ся в спе­циаль­ной струк­туре KSYSTEM_SERVICE_TABLE, или SST, под име­нем ServiceTableBase:

Опи­сание струк­туры KSYSTEM_SERVICE_TABLE
typedef struct _KSYSTEM_SERVICE_TABLE
{
PULONG ServiceTableBase;
PULONG ServiceCounterTableBase;
ULONG NumberOfService;
ULONG ParamTableBase;
} KSYSTEM_SERVICE_TABLE, *PKSYSTEM_SERVICE_TABLE;

В то же вре­мя мас­сив этих струк­тур лежит в таб­лицах nt!KeServiceDescriptorTable и nt!KeServiceDescriptorTableShadow. Если в пер­вой хра­нит­ся толь­ко SST для обра­бот­чиков сис­темных вызовов NT, то во вто­рой добав­ляет­ся SST-таб­лица гра­фичес­кой под­систе­мы win32k.

Значения таблиц nt!KeServiceDescriptorTable и nt!KeServiceDescriptorTableShadow
Зна­чения таб­лиц nt!KeServiceDescriptorTable и nt!KeServiceDescriptorTableShadow

Мож­но заметить, что пер­вая SST-струк­тура в обе­их таб­лицах оди­нако­ва. При сис­темном вызове ОС Windows исполь­зует ниж­ние 12 бит регис­тра eax как индекс в одной из SSDT-таб­лиц, а биты 12–13 ука­зыва­ют, какую SDT-струк­туру выб­рать: nt!KeServiceDescriptorTable или nt!KeServiceDescriptorTableShadow.

В пер­вой час­ти статьи уже шла речь о том, что на 64-бит­ных сис­темах SSDT содер­жит мас­сив сме­щений, а вир­туаль­ный адрес обра­бот­чика сис­темно­го вызова вычис­ляет­ся по фор­муле

RoutineAddress = ServiceTableBase + (ServiceTableBase[index] >> 4)

Сдвиг на 4 необ­ходим, пос­коль­ку пер­вые 4 бита содер­жат количес­тво парамет­ров, переда­ваемых через стек.

Пример подсчета адреса обработчика системного вызова
При­мер под­сче­та адре­са обра­бот­чика сис­темно­го вызова

Ес­ли взгля­нуть на при­мер с вызовом CreateFileW, учи­тывая выше­изло­жен­ное, он будет выг­лядеть сле­дующим обра­зом.

Схема системного вызова
Схе­ма сис­темно­го вызова

Мо­нито­ринг модифи­кации SSDT-таб­лиц зак­люча­ется в выс­тавле­нии ловушек на запись в физичес­кие стра­ницы памяти дан­ных таб­лиц и реали­зован в пла­гине ssdtmon.

Пе­рех­ват nt!KiServiceTable уже был реали­зован в этом пла­гине, и мы лишь добави­ли под­дер­жку таб­лицы win32k!W32pServiceTable. Так­же в пла­гине вре­донос­ной счи­тает­ся запись, совер­шенная на 8 байт ниже начала таб­лицы. Это свя­зано с тем, что с помощью 16-бит­ных XMM-инс­трук­ций мож­но перепи­сать пер­вые 12 байт таб­лицы, записы­вая на 4 бай­та ниже ее начала. Тем самым мож­но обой­ти три­виаль­ную про­вер­ку гра­ниц. Сто­ит заметить, что в то вре­мя, как таб­лица nt!KiServiceTable находит­ся в сек­ции дан­ных модуля ntoskrnl.exe и дос­тупна из все­го ядер­ного прос­транс­тва, win32k!W32pServiceTable при­над­лежит модулю win32k.sys и для дос­тупа к дан­ному драй­веру нуж­но находить­ся в кон­тек­сте какого‑либо гра­фичес­кого про­цес­са, нап­ример explorer.exe.

Продолжение доступно только участникам

Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».

Присоединяйся к сообществу «Xakep.ru»!

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

Подписаться
Уведомить о
0 комментариев
Межтекстовые Отзывы
Посмотреть все комментарии