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

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.

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

// КОММЕНТАРИИ (0)

// ОСТАВИТЬ КОММЕНТАРИЙ

// Ваш email не будет опубликован. Обязательные поля помечены *