Найти в Дзене
Под капотом ПО

DLL в ядре Windows? Пишем драйвер для перехвата syscalls

Оглавление

Привет! Ты когда-нибудь задумывался, как заглянуть в святая святых Windows - перехватить системные вызовы? Это как вскрыть сейф с динамитом: одно неверное движение - и BSOD! Но если ты готов к вызову, я покажу, как сделать это чисто и профессионально. Погнали?

🔥 Зачем это нужно?

Перехват syscalls - это суперсила для:

  • Анализа малвари (антивирусы обожают это),
  • Мониторинга процессов (кто что вызывает?),
  • Хотфиксов уязвимостей (патчим на лету!).

Но предупреждаю:

⚠️ Пишем для образовательных целей. Ошибки в драйверах = мгновенный крах системы. Тестируем в виртуалке (VMWare/VirtualBox) с отключенным цифровым подписыванием драйверов.

📚 Теория за 60 секунд

  1. Syscall - это переход из user-mode в kernel-mode через syscall/sysenter.
  2. SSDT (System Service Descriptor Table) - таблица в ядре, где хранятся указатели на обработчики системных вызовов.
  3. Перехват: подменяем адрес функции в SSDT на свой обработчик.
  4. DLL в ядре? Миф! Драйверы - это .sys, но мы можем инжектить код в kernel-space.

💻 Пишем драйвер: пошагово

Шаг 1: Скелет драйвера

#include <ntddk.h>
// Наш перехватчик для NtOpenProcess
NTSTATUS HookedNtOpenProcess(
_Out_ PHANDLE ProcessHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_opt_ PCLIENT_ID ClientId
) {
KdPrint(("NtOpenProcess вызван! PID: %d\n", ClientId->UniqueProcess));
// Вызываем оригинальную функцию (позже покажем, как её получить)
return OriginalNtOpenProcess(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId);
}
// Точка входа драйвера
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
UNREFERENCED_PARAMETER(RegistryPath);
DriverObject->DriverUnload = DriverUnload;
KdPrint(("Драйвер загружен!\n"));
return STATUS_SUCCESS;
}
// Выгрузка (обязательно!)
VOID DriverUnload(PDRIVER_OBJECT DriverObject) {
KdPrint(("Драйвер выгружен\n"));
}

Шаг 2: Ищем SSDT

Для x64 Windows используем KiServiceTable (не экспортируется, но адрес можно вычислить).

// Получаем адрес SSDT
ULONG_PTR FindSsdtAddress() {
PUCHAR code = (PUCHAR)__readmsr(0xC0000082); // IA32_LSTAR (syscall handler)
// Ищем сигнатуру mov eax, [KiServiceTable]
for (int i = 0; i < 512; i++) {
if (code[i] == 0x8B && code[i+1] == 0x04 && code[i+2] == 0xC5) {
return *(ULONG_PTR*)(code + i + 3);
}
}
return 0;
}

Шаг 3: Меняем адрес в SSDT

// Отключаем защиту страниц (CR0 WP-бит)
void DisableWriteProtection() {
__writecr0(__readcr0() & ~0x10000);
}
// Включаем обратно
void EnableWriteProtection() {
__writecr0(__readcr0() | 0x10000);
}
// Подменяем функцию
void HookSsdtFunction(ULONG_PTR ssdt, UINT32 index, PVOID newHandler) {
ULONG_PTR* table = (ULONG_PTR*)ssdt;
DisableWriteProtection();
OriginalNtOpenProcess = (NtOpenProcessFunc)table[index]; // Сохраняем оригинал
table[index] = (ULONG_PTR)newHandler; // Вставляем перехватчик
EnableWriteProtection();
}

Шаг 4: Инициализация в DriverEntry

// Объявляем оригинальную функцию
typedef NTSTATUS (*NtOpenProcessFunc)(...);
NtOpenProcessFunc OriginalNtOpenProcess = NULL;
NTSTATUS DriverEntry(...) {
ULONG_PTR ssdt = FindSsdtAddress();
if (!ssdt) return STATUS_FAILED_DRIVER_ENTRY;
const UINT32 NtOpenProcessIndex = 0x26; // Индекс для NtOpenProcess (Windows 10)
HookSsdtFunction(ssdt, NtOpenProcessIndex, HookedNtOpenProcess);
...
}

⚡ Фишки для избежания BSOD

  1. Проверка индексов: Убедись, что индекс syscall актуален для твоей версии Windows (смотри в WinDBG).
  2. Семафоры: Отключи прерывания на время модификации SSDT (cli/sti).
  3. Подпись драйвера: Используй Test Mode или DSEFIX для временного отключения проверки подписи.

Итог

Мы создали драйвер, который:

  • Перехватывает NtOpenProcess,
  • Логирует вызовы без падения системы,
  • Корректно выгружается.
Попробуй добавить фильтрацию по PID или скрытие процессов!

Финальное предупреждение:

🔥 Этот код - трамплин в мир ядра. Одно неверное смещение - и виртуалка взорвётся синим экраном. Бэкапься, тестируй, читай Windows Internals.

Удачи, и пусть отладчик будет с тобой!