文章总结: 本文深度论证CEVEHDebugger使用DR7硬件调试寄存器而非PAGEGUARD。通过对比粒度、数量、异常码等维度,结合CE的4断点限制、异常码0x80000004、开源代码操作DR寄存器及API调用特征等证据,证明其核心是DR0-DR3和DR7。PAGEGUARD存在竞态窗口、误触发风暴等缺陷,仅适用于单线程或粗粒度监控。建议在需要精确多线程调试时使用硬件断点。 综合评分: 100 文章分类: 逆向分析,漏洞分析,安全工具,实战经验,渗透测试
深度理解 VEH-CheatEngine vs VEH-PAGE_GUARD
KsaNL KsaNL
看雪学苑
2026年7月18日 17:59 上海
在小说阅读器读本章
去阅读
深度论证 CE VEH Debugger 的真正实现机制
本文将从原理到源码、从实验到实战,彻底讲清楚两种方案的本质差异,并论证 CE VEH Debugger 的断点核心是 DR0-DR3 + DR7 硬件调试寄存器。
选手 A:PAGE_GUARD
// 给内存页加上哨兵属性
DWORD old;
VirtualProtect(addr, size, PAGE_READWRITE | PAGE_GUARD, &old);
// 任何线程碰一下这个页 → 触发异常
// ExceptionCode: STATUS_GUARD_PAGE_VIOLATION (0x80000001)
// 然后 GUARD 自动移除,需要手动恢复
特征:页级粒度,一次性触发,自动消失。
选手 B:DR7 硬件断点
// 把目标地址写入调试寄存器
ctx.Dr0 = (DWORD_PTR)targetAddr;
ctx.Dr7 = 0x000D0001; // 启用 DR0,写入监控,4字节
// CPU 每条指令自动比对
// 精确命中 → 触发 #DB 异常
// ExceptionCode: STATUS_SINGLE_STEP (0x80000004)
特征:字节级精度,CPU 硬件执行,持久有效。
正面对比
#
| 维度 | PAGE_GUARD | DR7 硬件断点 |
| — | — | — |
| 粒度 | 4KB 整页 | 1 / 2 / 4 / 8 字节 |
| 数量 | 无限 | 最多 4 个 |
| 触发异常 | 0x80000001 | 0x80000004 |
| 多线程 | 竞态灾难 | 安全(每线程独立 DR) |
| 性能开销 | 高(同页误触发) | 零(CPU 硬件比对) |
| 触发后状态 | GUARD 自动移除 | 断点持续存在 |
| 监控类型 | 读 / 写 | 执行 / 写 / 读写 |
| 检测难度 | VirtualQuery 一查便知 | 需要 GetThreadContext |
#
证据链:CE 用的是 DR7
证据一:4 断点限制
#
CE → Memory View → Debug → 设置第 5 个断点:
"You can only have 4 breakpoints at atime when using the VEH debugger"
4 个。不多不少。正好是 DR0、DR1、DR2、DR3。
PAGE_GUARD 没有这个限制——你想给多少页加 GUARD 都行。
证据二:支持的断点类型
CE VEH 断点选项:
☐ Break on Write → DR7 R/W = 01
☐ Break on Access → DR7 R/W = 11
☐ Break on Execute → DR7 R/W = 00
Size: 1 / 2 / 4 → DR7 LEN = 00/01/11
与 DR7 位域完全对应:
DR7 结构(per breakpoint):
┌────────┬────────┬─────────┐
│ Enable │ R/W │ LEN │
│ L0=1 │ 00=Exe │ 00=1byte│
│ │ 01=Wri │ 01=2byte│
│ │ 11=R/W │ 11=4byte│
└────────┴────────┴─────────┘
PAGE_GUARD 做不到:
- 无 Execute 断点
- 无 字节级粒度选择
- 无 法区分”只写”和”读写”
证据三:实际触发的异常码
在 VEH Handler 内拦截,CE 断点命中时:
ExceptionCode = 0x80000004 (STATUS_SINGLE_STEP)
DR6 = 0x00000001 (DR0 triggered)
如果是 PAGE_GUARD:
ExceptionCode = 0x80000001 (STATUS_GUARD_PAGE_VIOLATION)
DR6 = 0x00000000 (无关)
实测是 0x80000004。铁证如山。
证据四:CE 开源代码
// CEDebugger.pas - 设置 VEH 断点
procedure TDebuggerThread.SetBreakpoint(address: PtrUInt;
bptype: TBreakpointType;
bpsize: Integer);
var
ctx: CONTEXT;
begin
ctx.ContextFlags := CONTEXT_DEBUG_REGISTERS;
GetThreadContext(hThread, ctx);
case FindFreeDR() of
0: ctx.Dr0 := address;
1: ctx.Dr1 := address;
2: ctx.Dr2 := address;
3: ctx.Dr3 := address;
end;
// 配置 DR7
ctx.Dr7 := EncodeDR7(drIndex, bptype, bpsize);
SetThreadContext(hThread, ctx);
end;
VEH Handler:
function VEHHandler(ExceptionInfo: PEXCEPTION_POINTERS): LONG;
begin
if ExceptionInfo.ExceptionRecord.ExceptionCode = STATUS_SINGLE_STEP then
begin
// 检查 DR6,确定哪个断点命中
dr6 := ExceptionInfo.ContextRecord.Dr6;
if (dr6 and 1) <> 0 then ProcessBreakpoint(0);
if (dr6 and 2) <> 0 then ProcessBreakpoint(1);
if (dr6 and 4) <> 0 then ProcessBreakpoint(2);
if (dr6 and 8) <> 0 then ProcessBreakpoint(3);
// 临时禁用,设置 TF 单步跳过
DisableDR(drIndex);
ExceptionInfo.ContextRecord.EFlags :=
ExceptionInfo.ContextRecord.EFlags or $100;
Result := EXCEPTION_CONTINUE_EXECUTION;
end;
end;
源码直接操作 DR 寄存器。没有任何 VirtualProtect、PAGE_GUARD 的影子。
证据五:API 调用特征
用 API Monitor 监控 CE 设置 VEH 断点时的系统调用:
✓ 观察到:
NtSetContextThread(handle, &ctx) // CONTEXT_DEBUG_REGISTERS
NtGetContextThread(handle, &ctx)
✗ 没有观察到:
NtProtectVirtualMemory(..., PAGE_GUARD, ...)
VirtualProtectEx(..., PAGE_GUARD, ...)
证据六:多线程稳定性
CE VEH Debugger 在以下场景稳定工作:
- 60+ 线程的 3A 游戏
- 多线程疯狂读写同一结构体
- 不漏检、不崩溃
如果是 PAGE_GUARD:
线程1触发 → GUARD移除 → 窗口期 → 线程2/3/4/5访问 → 全部漏掉
→ 恢复GUARD → 又被线程6触发 → 异常风暴
多线程游戏 + PAGE_GUARD = 直接GG。
#
PAGE_GUARD 的致命缺陷详解
缺陷一:竞态窗口
时间线:
─────────────────────────────────────────────────────
T1: 访问 0x1000 → GUARD触发 → 异常进入Handler
T1: Handler 中设置 TF,准备单步跳过
┌──── 此时 GUARD 已被 CPU 自动移除 ────┐
T2: │ 访问 0x1004(同页)→ 正常通过 │ ← 漏了!
T3: │ 访问 0x1008(同页)→ 正常通过 │ ← 漏了!
T4: │ 访问 0x100C(同页)→ 正常通过 │ ← 漏了!
└────────────────────────────────────────┘
T1: 单步完成 → 恢复 GUARD
─────────────────────────────────────────────────────
缺陷二:误触发风暴
// 你想监控的地址
int* target = (int*)0x12345678;
// 但同一页(0x12345000 ~ 0x12345FFF)上还有:
struct Player {
float x, y, z; // 0x12345000 - 被渲染线程每帧读取
int health; // 0x1234500C
char name[32]; // 0x12345010
// ...
int ammo; // 0x12345678 ← 你真正关心的
};
// PAGE_GUARD 结果:
// 渲染线程每帧触发 60 次(读 x,y,z)
// 网络线程每秒触发 20 次(同步 name)
// 你关心的 ammo 写入:可能 1 次/秒
// 信噪比:1 : 4800
缺陷三:无法做执行断点
PAGE_GUARD 只能检测数据访问(读/写)
无法设置"当 EIP 执行到这个地址时中断"
而 DR7 R/W=00 就是执行断点,CPU 硬件原生支持
#
PAGE_GUARD 真正的用武之地
也别把它一棒子打死。它有自己的定位:
┌─────────────────────────────────────────────┐
│ PAGE_GUARD 适用场景 │
├─────────────────────────────────────────────┤
│ ✅ 单线程漏洞分析 │
│ ✅ 堆栈溢出检测(自查) │
│ ✅ 需要 >4 个监控点时的妥协方案 │
│ ✅ 粗粒度监控:"这片内存有没有被碰过" │
│ ✅ Windows 栈扩展机制(系统自用) │
│ ✅ 简易 Guard Page Pool(内存池哨兵) │
├─────────────────────────────────────────────┤
│ ❌ 多线程精确监控 │
│ ❌ 生产级调试器 │
│ ❌ 游戏逆向(多线程 + 高频访问) │
│ ❌ 需要执行断点 │
└─────────────────────────────────────────────┘
一句话:PAGE_GUARD 是单线程穷人的无限断点,自己查查自己没问题。
0x06 VEH + DR7 的完整工作流
┌─────────────────────────────────────────────────────┐
│ CE VEH Debugger 架构 │
├─────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ 注入 ┌────────────────────┐ │
│ │ CE GUI │ ─────────→ │ 目标进程 │ │
│ └──────────┘ │ │ │
│ │ ┌──────────────┐ │ │
│ │ │ VEH Handler │ │ │
│ │ │ (最高优先级) │ │ │
│ │ └──────┬───────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ STATUS_SINGLE_STEP│ │
│ │ → 检查 DR6 │ │
│ │ → 记录 RIP/操作 │ │
│ │ → 禁用 DR, 设 TF │ │
│ │ → 单步后恢复 DR │ │
│ │ │ │
│ 设置断点流程: │ │ │
│ ┌─────────────────────────────────────────┐ │ │
│ │ 1. 枚举所有线程 │ │ │
│ │ 2. SuspendThread │ │ │
│ │ 3. GetThreadContext(CONTEXT_DEBUG_REGS) │ │ │
│ │ 4. 写入 DR0~DR3 + 配置 DR7 │ │ │
│ │ 5. SetThreadContext │ │ │
│ │ 6. ResumeThread │ │ │
│ │ 7. Hook 线程创建,新线程自动设 DR │ │ │
│ └─────────────────────────────────────────┘ │ │
│ │
└─────────────────────────────────────────────────────┘
#
动手验证
实验:自己实现一个迷你 VEH + DR7 断点:
#include <windows.h>
#include <stdio.h>
volatile DWORD g_target = 0xDEAD;
volatile int g_step = 0;
LONG CALLBACK VEH(EXCEPTION_POINTERS* ep) {
PCONTEXT ctx = ep->ContextRecord;
if (ep->ExceptionRecord->ExceptionCode == STATUS_SINGLE_STEP) {
if (g_step == 0) {
// 断点命中
printf("[!] BP HIT | RIP: 0x%p | DR6: 0x%llX\n",
(void*)ctx->Rip, ctx->Dr6);
printf(" Target value: 0x%X\n", g_target);
// 临时禁用 DR0, 设 TF 单步跳过
ctx->Dr7 &= ~(DWORD_PTR)0x1;
ctx->EFlags |= 0x100;
g_step = 1;
} else {
// TF 单步完成,恢复断点
ctx->Dr7 |= 0x1;
g_step = 0;
}
ctx->Dr6 = 0; // 清除 DR6
return EXCEPTION_CONTINUE_EXECUTION;
}
return EXCEPTION_CONTINUE_SEARCH;
}
voidSetHWBP_Write4(void* addr) {
CONTEXT ctx = {0};
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
GetThreadContext(GetCurrentThread(), &ctx);
ctx.Dr0 = (DWORD_PTR)addr;
// DR7: L0=1, R/W0=01(Write), LEN0=11(4bytes)
// Bits: LEN0[19:18]=11, R/W0[17:16]=01, L0[0]=1
ctx.Dr7 = (3 << 18) | (1 << 16) | 1;
SetThreadContext(GetCurrentThread(), &ctx);
}
intmain() {
AddVectoredExceptionHandler(1, VEH);
printf("[*] Setting hardware write BP on g_target @ %p\n", &g_target);
SetHWBP_Write4((void*)&g_target);
printf("[*] Writing to g_target...\n");
g_target = 0xBEEF; // 触发断点
printf("[*] Writing again...\n");
g_target = 0xCAFE; // 再次触发
printf("[*] Final value: 0x%X\n", g_target);
return 0;
}
输出:
[*] Setting hardware write BP on g_target @ 0x00007FF7A1B04000
[*] Writing to g_target...
[!]BP HIT | RIP:0x00007FF7A1B01122 | DR6:0x1
Target value:0xBEEF
[*] Writing again...
[!]BP HIT | RIP:0x00007FF7A1B01135 | DR6:0x1
Target value:0xCAFE
[*] Final value:0xCAFE
这就是 CE VEH Debugger 的核心原理。20 行代码复现。
#
总结
#
┌──────────────────────────────────────────────────┐
│ │
│ CE VEHDebugger = VEH + 硬件断点 (DR0-DR3) │
│ │
│ PAGE_GUARD:单线程场景,自查泄露足以 │
│ DR7 硬件断点:精确、高效、多线程安全 │
│ │
│ VEH 是异常的接收框架 │
│ DR 寄存器才是断点的触发源 │
│ │
└──────────────────────────────────────────────────┘
#
看雪ID:KsaNL
https://bbs.kanxue.com/user-home-969146.htm
*本文为看雪论坛优秀文章,由 KsaNL 原创,转载请注明来自看雪社区
第十届安全开发者峰会【议题征集】-欢迎投稿
往期推荐
Android ARM64 内核态内存访问与隐蔽交互机制逆向分析
电动牙刷小程序”越权控制”逆向
一道简单但不简约的堆题——CISCN2026初赛堆题robo分析
第二届软件系统安全赛决赛 StudentManagement 详解
APP整体加固原理
球分享
球点赞
球在看
点击阅读原文查看更多
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:看雪学苑 KsaNL KsaNL《深度理解 VEH-CheatEngine vs VEH-PAGE_GUARD》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。








评论