Содержание статьи
С появлением 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.
. Пройдя все библиотеки, в итоге код окажется в ядерной функции nt!
. Если бы руткит установил перехват на эту функцию, он бы мог выглядеть следующим образом.
![Схема системного вызова Схема системного вызова](https://static.xakep.ru/images/a3a40bf47e56509b90045a971f96a1f0/23145/image1.png)
Поскольку код находится в страницах с правами только на чтение и выполнение, для записи в такие страницы необходимо либо выделить новую виртуальную страницу с правами на запись и спроецировать ее на физическую страницу, где находится код, либо отключить бит Write Protect в специальном регистре управления CR0
, что позволит выполнять запись в страницы в обход их прав для текущего ядра.
![Структура регистра управления CR0 Структура регистра управления CR0](https://static.xakep.ru/images/20766f016909b0fcecbcca9af0a21d32/23235/image2.png)
![Описание значения флага Write Protect Описание значения флага Write Protect](https://static.xakep.ru/images/a3a40bf47e56509b90045a971f96a1f0/23147/image3.png)
Обнаружение таких перехватов сводится к подсчету контрольной суммы секций драйвера в момент начала анализа и пересчету, сверке контрольной суммы в конце. В отличие от PatchGuard, который защищает только небольшой список системных драйверов, мы можем проверять абсолютно все загруженные драйверы из списка PsLoadedModules
.
PsLoadedModules
— двусвязный список структур _KLDR_DATA_TABLE_ENTRY
, описывающих загруженный драйвер: его базовый адрес, размер, имя, характеристики и прочее.
![Двусвязный список загруженных модулей Двусвязный список загруженных модулей](https://static.xakep.ru/images/a3a40bf47e56509b90045a971f96a1f0/23148/image4.png)
![Описание структуры _KLDR_DATA_TABLE_ENTRY Описание структуры _KLDR_DATA_TABLE_ENTRY](https://static.xakep.ru/images/a3a40bf47e56509b90045a971f96a1f0/23149/image5.png)
Для перечисления загруженных модулей ядра с помощью DRAKVUF API мы реализовали метод drakvuf_enumerate_drivers
. В плагине rootkitmon список PsLoadedModules
проходится два раза: в начале инициализации плагина — для подсчета контрольных сумм секций драйверов — и в конце — для сравнения значений.
![Перечисление и подсчет контрольных сумм секций драйвера Перечисление и подсчет контрольных сумм секций драйвера](https://static.xakep.ru/images/a3a40bf47e56509b90045a971f96a1f0/23150/image6.png)
В случае расхождения в логе появится строка:
"\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!
и win32k!
для системных вызовов к модулю ntoskrnl.
и графической подсистеме win32k.
соответственно. Количество элементов в этих массивах сохранено в переменных nt!
и win32k!
.
![Значения структур nt!KiServiceTable и win32k!W32pServiceTable Значения структур nt!KiServiceTable и win32k!W32pServiceTable](https://static.xakep.ru/images/a3a40bf47e56509b90045a971f96a1f0/23151/image7.png)
Указатель на 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!
и nt!
. Если в первой хранится только SST для обработчиков системных вызовов NT, то во второй добавляется SST-таблица графической подсистемы win32k.
![Значения таблиц nt!KeServiceDescriptorTable и nt!KeServiceDescriptorTableShadow Значения таблиц nt!KeServiceDescriptorTable и nt!KeServiceDescriptorTableShadow](https://static.xakep.ru/images/a3a40bf47e56509b90045a971f96a1f0/23152/image8.png)
Можно заметить, что первая SST-структура в обеих таблицах одинакова. При системном вызове ОС Windows использует нижние 12 бит регистра eax
как индекс в одной из SSDT-таблиц, а биты 12–13 указывают, какую SDT-структуру выбрать: nt!
или nt!
.
В первой части статьи уже шла речь о том, что на 64-битных системах SSDT содержит массив смещений, а виртуальный адрес обработчика системного вызова вычисляется по формуле
RoutineAddress = ServiceTableBase + (ServiceTableBase[index] >> 4)
Сдвиг на 4 необходим, поскольку первые 4 бита содержат количество параметров, передаваемых через стек.
![Пример подсчета адреса обработчика системного вызова Пример подсчета адреса обработчика системного вызова](https://static.xakep.ru/images/a3a40bf47e56509b90045a971f96a1f0/23153/image9.png)
Если взглянуть на пример с вызовом CreateFileW
, учитывая вышеизложенное, он будет выглядеть следующим образом.
![Схема системного вызова Схема системного вызова](https://static.xakep.ru/images/a3a40bf47e56509b90045a971f96a1f0/23154/image10.png)
Мониторинг модификации SSDT-таблиц заключается в выставлении ловушек на запись в физические страницы памяти данных таблиц и реализован в плагине ssdtmon.
Перехват nt!
уже был реализован в этом плагине, и мы лишь добавили поддержку таблицы win32k!
. Также в плагине вредоносной считается запись, совершенная на 8 байт ниже начала таблицы. Это связано с тем, что с помощью 16-битных XMM-инструкций можно переписать первые 12 байт таблицы, записывая на 4 байта ниже ее начала. Тем самым можно обойти тривиальную проверку границ. Стоит заметить, что в то время, как таблица nt!
находится в секции данных модуля ntoskrnl.
и доступна из всего ядерного пространства, win32k!
принадлежит модулю win32k.
и для доступа к данному драйверу нужно находиться в контексте какого‑либо графического процесса, например explorer.
.
Продолжение доступно только участникам
Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
Вариант 2. Открой один материал
Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.
Я уже участник «Xakep.ru»