软件断点
- 在目标进程指定写入0xCC(int 3),当目标进程执行这个位置,会触发异常,发给调试器
bool SetBreakPoint(HANDLE hProcess, LPVOID Address)
{
BREAKPOINT bp;
DWORD dwSize;
DWORD oldProtect;
// 1.修改目标分页属性
VirtualProtectEx(hProcess, Address, 1, PAGE_EXECUTE_READWRITE,
&oldProtect);
//2 .读取目标进程中的1个字节
ReadProcessMemory(hProcess, Address, &bp.bp_olddata, 1, &dwSize);
//3 .将0xcc 写入内存地址
WriteProcessMemory(hProcess, Address, "\xCC", 1, &dwSize);
//4. 恢复目标分页属性
VirtualProtectEx(hProcess, Address, 1, oldProtect, &oldProtect);
//5. 保存断点信息
bp.bp_enable = TRUE;
bp.bp_address = Address;
// 6. 保存到断点列表
bp_list.push_back(bp);
return true;
}
单步断点
- 设置ELFAGS->TF位,CPU每执行一条汇编指令都会检查TF,如果出现TF,会在下一个位置断下,同时CPU会自动将TF置为0
void SetSingleBreakPoint(HANDLE hThread)
{
// 线程上下文
CONTEXT context = { CONTEXT_FULL };
// 获取线程上下文
GetThreadContext(hThread, &context);
((PEFLAGS)&context.EFlags)->TF = 1;
// 设置线程上下文
SetThreadContext(hThread, &context);
}
硬件断点
- CPU提供了Dr0-Dr7八个调试寄存器,其中Dr0-Dr3用于设置断点位置,DR6用于检查哪一个调试寄存器触发的,DR7用于配置DR0-DR3启用,(对于读写,长度,类型),执行长度与类型都必须是0
Dr0~3用于设置硬件断点,即在调试器中经常使用的bpm断点,由于只有4个断点寄存器,所以最多只能设置4
个bpm断点。Dr7是一些控制位,用于控制断点的方式,Dr6用于显示是哪些引起断点的原因,如果是Dr0~3
或单步(EFLAGS的TF)或由于GD置位时访问调试寄存器引起1号调试陷阱的话,则相应设置对应的位。下面对
Dr6和Dr7的对应位做一些详细介绍:
调试控制寄存器Dr7:
位0 L0和位1 G0:用于控制Dr0是全局断点还是局部断点,如果G0置位则是全局断点,L0置位则是局部
断点。 L0有效,G0无效,G1 L1~G3 L3用于控制D1~Dr3,其功能同上。
LEN0:占两个位,开始于位15,用于控制Dr0的断点长度,可能取值:
00 1字节
01 2字节
10 保留
11 4字节
RWE0:从第17位开始,占两个位,控制Dr0的断点是读、写还是执行断点或是I/O端口断点:
00 只执行
01 写入数据断点
10 I/O端口断点(只用于pentium+,需设置CR4的DE位)
11 读或写数据断点
RWE1~3,LEN1~3分别用于控制Dr1~3的断点方式,含义如上。
调试状态寄存器Dr6:
DR6 前两个字节,用于检查DR0-DR1的哪一个硬件断点产生的:
00 DR0异常由于DR0产生的。
01 DR1异常由于DR1产生的。
10 DR2异常由于DR2产生的。
11 DR3异常由于DR3产生的。
内存断点
- 根据windows分页内存属性来设置内存断点。比如代码段可以设置不可执行,当指令执行到这个位置就会触发访问异常。
- 内存读写断点,如果设置读写断点需要查看 Exception.ExceptionInformation[1] 里的内容来确定这个异常是我们产生的
// 通过这个函数来设置内存断点
VirtualProtectEx(hProcess, Address, 1, PAGE_EXECUTE_READWRITE, &oldProtect)
评论区