借壳上市:利用 CE 官方驱动 (DBK64) 加载未签名驱动原理分析
在折腾内核驱动加载的时候,不想花几百刀去买 EV 证书,又不想每次都开测试模式(TestSigning)。这年头,BYOVD (Bring Your Own Vulnerable Driver) 几乎是标准操作了。大家最熟悉的一般是 kdmapper 利用那个著名的 Intel 网卡驱动漏洞。
但其实 Cheat Engine (CE) 自带的那个 dbk64.sys 也是个狠角色。它本身是签了名的合法驱动,但为了给 CE 提供强大的内存修改功能,它暴露出来的 IOCTL 接口简直就是给黑客留的后门。
今天就来扒一扒 CECheater 这个项目,看看它是怎么“借”用 DBK 驱动,把我们自己的未签名驱动(Payload)强行塞进内核并跑起来的。
核心原理:它是怎么“偷渡”的?
简单来说,Windows 给了 dbk64.sys 一张合法的“门禁卡”(数字签名)。CECheater 不需要自己进门,而是把 Payload 递给已经进门的 DBK,让 DBK 在里面帮我们办事。
整个流程可以拆解为这几步:
- 加载工具人:先把正版
dbk64.sys加载起来。 - 申请地皮:利用 DBK 的接口在内核里申请一块内存(RWX 权限)。
- **手动映射 (Manual Map)**:把我们的未签名驱动文件(.sys),像拼图一样手动在用户层解析好,填到刚才申请的内核内存里。
- 执行入口:再利用 DBK 的执行接口,跳转到我们驱动的
DriverEntry。
下面结合源码细节通过去分析。
1. 搞定“工具人” (DBK 驱动加载)
dbk64.sys 比较矫情,它不像普通驱动直接 SC create 就能跑,它启动时会检查注册表里的一堆配置。
在 DBKControl.cpp 里可以看到,加载前必须手动伪造这些注册表项:
1 | // 伪造服务注册表项 |
通过 DBK_AllocNonPagedMem 函数,我们在内核非分页池(NonPagedPool)中拿到了一块带有执行权限的内存地址 pKernelImage。这就是我们 Payload 的新家。
3. 手动映射 (The Hard Part)
这一步是整个项目的核心。因为我们不是通过系统加载器(OS Loader)加载驱动,所以 PE 文件里的所有地址都是错的,导入表也是空的。我们必须手动干系统加载器的活。
代码主要在 MemLoadDriver.cpp 的 DBK_LoadMyDriver 函数中。
修复重定位 (Fix Relocation)
驱动编译时的基址(ImageBase)和我们在内核申请到的地址肯定不一样。FixRelocation 函数会遍历 PE 的重定位表,计算出偏移量 delta,然后把代码里所有硬编码的绝对地址都修正过来。
1 | // 计算偏移 |
修复导入表 (Fix Imports)
我们的驱动可能会调用 ntoskrnl.exe 里的 DbgPrint 或者 PsCreateSystemThread。系统加载时会自动填好这些函数地址,但现在必须手动填。
FixImports 函数通过 GetDriverAddress(获取内核模块基址)和 GetDriverExportFuncByName(解析导出表)来找到这些函数的真实内核地址,并填入我们驱动的 IAT 表中。
这一步做完,把处理好的 PE 镜像通过 DBK_WriteProcessMem 写入刚才申请的内核内存中,驱动就“就位”了。
4. 激活 (Execution)
最后一步是让 CPU 跳过去执行我们的 DriverEntry。
这里有个很有意思的设计。MemLoadDriver.cpp 里准备了两段 Shellcode:
- **
shellcode_CallIoCreateDriver**:这段 Shellcode 会模拟调用IoCreateDriver。这会让我们的未签名驱动在内核对象管理器里注册一个合法的DriverObject。这对于需要处理 IOCTL 的复杂驱动来说是必须的。 - **
shellcode_JmpDriverEntry**:简单粗暴,直接jmp到入口点。适合那种跑完就退的测试驱动。
最后通过 DBK_ExecuteCode 发送 IOCTL,DBK 驱动会在内核态执行这段 Shellcode,从而拉起我们的 Payload。
1 | // 构造 Shellcode 并让 DBK 执行 |
实战踩坑记录 (重点!)
原理听起来简单,但实操起来全是坑。如果直接写个驱动扔进去,99% 会蓝屏或者没反应。以下是调试时遇到的几个关键点:
1. 必须禁用缓冲区安全检查 (/GS-)
这是最容易被忽视的。VS 默认开启 /GS,编译器会在函数入口插入 __security_init_cookie。
但是!手动映射的驱动没人帮你初始化这个 Cookie。驱动一跑,Cookie 检查失败,直接自爆。
解决: 在 CMakeLists.txt 或 VS 属性里,务必加上 /GS-。
2. 入口点必须是 DriverEntry
如果你用 KMDF 框架,入口点其实是 FxDriverEntry;如果你开了 /GS,入口点是 GsDriverEntry。这俩都需要环境支持,手动映射搞不定。
解决: 强制链接器选项 /ENTRY:DriverEntry,把那些初始化壳子全扒掉。
3. “僵尸驱动” 问题
如果你用 IoCreateDriver 方式加载,驱动会注册一个设备名(如 \Device\MyDriver)。如果你调试时驱动崩了或者没卸载干净,第二次加载时 IoCreateDriver 会因为名字冲突直接返回失败,导致你以为代码没执行,其实是入口都没进去。
解决: 调试阶段改个驱动名再加载,或者确保卸载干净。
4. 虚假的 Win11 兼容性
虽然项目声称支持 Win11,但如果开了 **HVCI (内核隔离)**,DBK 申请 RWX 内存的操作可能会失败,或者执行时被拦截。要在 Win11 上顺畅玩耍,最好先关掉内核隔离,或者在编译时加上 /guard:cf- 禁用控制流保护。
总结
CECheater 本质上就是把 Cheat Engine 当作了一个通过合法签名的“加载器”。相比于利用漏洞(Exploit)的 kdmapper,这种方式利用的是 Feature,相对稳定一些,但反作弊(EAC/BE)对 dbk64.sys 的监控也更严。
对于驱动开发学习者来说,研究这个项目的代码(特别是 PE 解析和 Shellcode 构造部分)比单纯跑通一个 Demo 要有价值得多。
本文代码参考自 CECheater 项目源码及调试日志。仅供安全研究,请勿用于非法用途。


