Skip to content
KitploitKITPLOIT
ИнструментыЭксплойтыБлог
Log in
Отправить
ИнструментыЭксплойтыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

ЛентыКонтактыКонфиденциальность© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
BYOVD-DriverKiller — Драйвер: реверс и эксплуатация | Kitploit
Инструменты/GitHubGitHub/alex3o/byovd-driverkiller
ЭксплуатацияОбратная инженерияПост-эксплуатацияАнализ Бинарных ФайловОбучение и ОбразованиеRed Teaming
GitHubalex3o/byovd-driverkiller

BYOVD-DriverKiller

Драйвер: реверс и эксплуатация

Репозиторий
8314111 год назадПроверено Kitploit

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

BYOVD-DriverKiller

⚠️ Предупреждение: Этот проект носит строго образовательный и демонстрационный характер. Он не предназначен для использования в вредоносных целях. Цель — изучить методологию reverse engineering и этапы эксплуатации Windows-драйвера.


Здесь я объясняю подход, который я использовал для решения упражнения, предложенного d1rk(SaadAhla) https://github.com/SaadAhla, а именно reverse engineering и эксплуатацию легитимного подписанного драйвера, отсутствующего в блок-листах (HVCI, LOLBIN...). Доступна программа на C, позволяющая завершить любой активный процесс в системе через этот драйвер режима ядра (Kernel-mode Driver); ниже я подробно описываю, как она работает.

POC-BYOD

📃 Использование: DriverKiller.exe <nom_processus.exe> [-d]

Опция -d: позволяет удалить службу и драйвер из системы после эксплуатации.

На целевой машине должен быть включён режим тестовой подписи (testsigning mode), так как сертификат драйвера истёк.


Часть 1 — Reverse engineering:

В упражнении предоставляется файл .sys, названный по его SHA-256 хешу. Первый шаг — открыть этот файл в IDA.
IDA доступна бесплатно. Достаточно зайти на сайт Hex-Rays, чтобы сгенерировать лицензию и скачать программу.

Начнём с просмотра IAT (Import Address Table) драйвера и поищем вызов нужного API: ZwTerminateProcess.

screen1-git

Двойным щелчком по ZwTerminateProcess IDA перенаправляет нас к скомпилированному коду этой функции. Выбрав запись и открыв перекрёстные ссылки (cross-references), мы получаем список функций драйвера, которые её вызывают.

screen2-git

Мы видим, что именно функция sub_12EF4, по смещению 1CE, использует ZwTerminateProcess. После двойного щелчка IDA показывает её скомпилированный код.

screen11-git

Декомпилированный код показывает вызовы ZwOpenProcess (открывает дескриптор целевого процесса) и ZwTerminateProcess (завершает процесс через этот дескриптор).

Согласно документации ZwOpenProcess (https://learn.microsoft.com/fr-fr/windows-hardware/drivers/ddi/ntddk/nf-ntddk-zwopenprocess), параметр ClientID — это указатель, задающий PID целевого процесса.

В строке выше ClientId.UniqueProcess инициализируется переменной v22. Она определена чуть выше:

v22 = (void )((_QWORD *)i + 10);

Чтобы понять это присваивание, нужно определить переменную i и поле +10.

Выше в этой функции виден вызов ZwQuerySystemInformation с параметром SYSTEM_PROCESS_INFORMATION. Также становится понятно, что i — итератор по записям этой структуры вместе с переменной v6.

Согласно документации ZwQuerySystemInformation (https://learn.microsoft.com/en-us/windows/win32/sysinfo/zwquerysysteminformation), эта функция возвращает массив с одной записью на каждый активный процесс в системе.

Структура SYSTEM_PROCESS_INFORMATION описана здесь: https://learn.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntquerysysteminformation

typedef struct _SYSTEM_PROCESS_INFORMATION {
    ULONG NextEntryOffset;
    ULONG NumberOfThreads;
    BYTE Reserved1[48];
    UNICODE_STRING ImageName;
    KPRIORITY BasePriority;
    HANDLE UniqueProcessId;
    PVOID Reserved2;
    ULONG HandleCount;
    ULONG SessionId;
    PVOID Reserved3;
    SIZE_T PeakVirtualSize;
    SIZE_T VirtualSize;
    ULONG Reserved4;
    SIZE_T PeakWorkingSetSize;
    SIZE_T WorkingSetSize;
    PVOID Reserved5;
    SIZE_T QuotaPagedPoolUsage;
    PVOID Reserved6;
    SIZE_T QuotaNonPagedPoolUsage;
    SIZE_T PagefileUsage;
    SIZE_T PeakPagefileUsage;
    SIZE_T PrivatePageCount;
    LARGE_INTEGER Reserved7[6];
} SYSTEM_PROCESS_INFORMATION;

Напоминание: размеры некоторых типов в Windows x64

  • ULONG = 4 байта
  • USHORT = 2 байта
  • HANDLE = 8 байт
  • PWSTR = 8 байт
  • KPRIORITY (typedef от LONG) = 4 байта
  • UNICODE_STRING = 16 байт, потому что вот её структура:
  typedef struct _UNICODE_STRING {
    USHORT Length;        -> 2      
    USHORT MaximumLength; -> + 2 = 4
    PWSTR  Buffer;        -> + 8 = 12 (12 n'est pas un multiple de 8 donc padding de 4 ajouté en amont de Buffer) = 16
} UNICODE_STRING;

Расчёт смещения UniqueProcessId:

    ULONG NextEntryOffset;        -> 4
    ULONG NumberOfThreads;        -> + 4 = 8
    BYTE Reserved1[48];           -> + 48 = 56
    UNICODE_STRING ImageName;     -> + 16 = 72
    KPRIORITY BasePriority;       -> + 4 = 76 (76 n'est pas un multiple de 8 donc padding de 4 ajouté) = 80
    HANDLE UniqueProcessId;       -> + 8 = 88

Член UniqueProcessId находится по смещению 0x50 (80 в десятичной системе).

Глядя на присваивание переменной v22, видно, что i приводится к указателю QWORD (8 байт):

v22 = (void )((_QWORD *)i + 10);
Таким образом, v22 соответствует адресу i + 10 * 8 = 80 байт. Эта переменная действительно содержит PID, полученный из структуры SYSTEM_PROCESS_INFORMATION.

Чтобы узнать, какой PID будет передан в ZwTerminateProcess, нужно проанализировать условие, окружающее это присваивание.

screen4-git

Видно, что сначала извлекается имя образа процесса:

v9 = (wchar_t )((_QWORD *)i + 8);
Так как v9 = адрес i + 8 × 8 = 64 байта. Это соответствует полю Buffer члена ImageName, поскольку этот член находится по смещению 56 + 2 (USHORT) + 2 (USHORT) + 4 (padding) = 64.

Судя по операциям и циклам ниже, можно предположить, что выполняется сравнение имени процесса, переданного аргументом (a2), с активными процессами системы v9/String.

sub_1C078(String, v9, (int)v13);
v17 = strupr(a2);
v18 = strupr(String);

Значит, параметр a2 должен содержать имя процесса, который нужно завершить через ZwTerminateProcess. Заметим, что a2 — параметр функции sub_12EF4. Чтобы продвинуться дальше, нужно изучить ссылки на эту функцию (я переименовал её в ZwTerminateProcessCaller для лучшей читаемости).

screen5-git

Видно, что ZwTerminateProcessCaller вызывается функцией sub_13624 по смещению 61A.

screen6-git

Прежде чем анализировать этот декомпилированный код, я найду ссылки на функцию sub_13624 (переименованную в ZwTerminateProcessCallerCaller), чтобы убедиться, что этот код действительно используется после вызова API DeviceIoControl из UserMode.

Скачать инструмент