首先需要明白什么是硬件断点,硬件断点其实是通过一个调试寄存器来实现的,这个调试寄存器是CPU上的东西,就是前面截图的这个东西,叫做Debug Registers,在intel手册卷3 17章第二节里面)。
DR0-DR3为设置断点的地址,DR4和DR5为保留内容。DR6为调试异常产生后显示的一些信息,DR7保存了断点是否启用、断点类型和长度等信息。
重点在DR0-DR3和DR7这四个里面。
DR0-DR3中存放的是断点的地址。
然后DR7根据各各字段的不同意义不同:
字段 | 意义 |
---|---|
L0-L3 | 对于DR0-DR3存放的地址断点是否有效,并且是一个局部断点 |
G0-G3 | 对应DR0-DR3存放的地址是否有效,但是是一个全局断点(在Windows中用不了。) |
LEN0-LEN3 | 对应DR0-DR3断点的长度。00表示1字节,01表示二字节,11表示四字节 |
RW0-RW3 | 对应DR0-DR3断点的类型。00表示执行断点,01表示写入断点,11表示读写断点。 |
获取硬点断点信息:
这里我们要使用到一个API:
BOOL GetThreadContext(
HANDLE hThread,
LPCONTEXT lpContext
);
用这个API来获取当前线程的环境,肯定有人要问为什么是线程,不是进程,不是啥啥啥的。因为在操作系统中,线程才是真正执行代码流程的东西,进程只是一个分配资源的概念,而每个代码执行的时候,自己的寄存器或多或少都会受到影响,简单来说就是线程才能实实在在执行代码,如果用进程的话每个线程都会受到影响,而且进程也没有这个GetProcessContext得到进行环境寄存器的API。
参数:
参数 | 意义 |
---|---|
hThread | 线程句柄 |
lpContext | 指向CONTEXT结构体的指针,需要注意的是context是一个输出参数就是通过这个API会修改CONTEXT结构体里的值,但是需要设置CONTEXT里的ContextFlags值来限制要获得什么内容,如果不设置则不能获得内容。 |
所以如果我们要获取Debug Register的内容,这里需要设置为CONTEXT_DEBUG_REGISTERS了。
简单实现硬件断点反调试:
最后查看通过OD打下硬件断点的情况,和直接运行的情况:
采用异常实现硬件断点反调试:
如果说直接用前面那种来实现反调试就太辣鸡了,很容易就被识破,但是如果我们添加一个异常呢,这样就会好很多,因为异常要通过一些寄存器,一些不是上面那种直接调用API的办法,会麻烦一点。
采用异常过滤器来处理:
关于异常过滤器可以查看直接的博客:
这里我直接上代码了:
这里eip+3的原因是错误代码的硬编码是三个字节,跳过就好了。
但是这个用调试器就不行了,会一直在异常这里,不知道为什么。
修正:
官方文档上写了这个异常过滤器的函数的描述,如果这个程序正在被调试,那么异常会直接传递给调试器,所以当我们进行调试的时候这个异常过滤器是没有作用的。
采用SEH来处理:
EXCEPTION_DISPOSITION mySEH(struct _EXCEPTION_RECORD* ExceptionRecord,PVOID EstablisherFrame,PCONTEXT pcontext,PVOID DispatcherContext)
{
if (pcontext->Dr1 != 0 || pcontext->Dr2 != 0 || pcontext->Dr3 != 0 || pcontext->Dr0 != 0)
{
printf("SEH验证异常,程序正在被调试,即将退出程序\n");
ExitProcess(0);
}
else
{
printf("SEH验证正常\n");
}
pcontext->Eip += 3;
return ExceptionContinueExecution;
}
void TestSeh()
{
printf("test SEH\n");
__asm
{
push mySEH
mov eax,fs:[0]
push eax
mov fs:[0],esp
mov eax, 0
mov[eax], 1
}
cout << "SEH: 跳过了异常代码" << endl;
cout << "SEH: 程序正常结束" << endl;
}
然后通过调试就OK了,当调试器没有选择捕获你这个异常的时候,异常就是交给程序的VEH,SEH,异常过滤器来处理的。
采用VEH来处理:
LONG WINAPI MyPvectoredExceptionHandler(_EXCEPTION_POINTERS* ExceptionInfo)
{
if (ExceptionInfo->ContextRecord->Dr1 != 0 || ExceptionInfo->ContextRecord->Dr2 != 0 || ExceptionInfo->ContextRecord->Dr3 != 0 || ExceptionInfo->ContextRecord->Dr0 != 0)
{
printf("VEH 验证异常,程序正在被调试,即将退出程序\n");
ExitProcess(0);
}
else
{
printf("硬件断点的VEH验证正常,无硬件断点\n");
}
ExceptionInfo->ContextRecord->Eip += 3;
return EXCEPTION_CONTINUE_EXECUTION;
}
void TestVeh()
{
printf("test VEH\n");
AddVectoredExceptionHandler(1, MyPvectoredExceptionHandler);
__asm
{
mov eax, 0
mov[eax], 1
}
cout << "VEH: 跳过了异常代码" << endl;
cout << "VEH: 程序正常结束" << endl;
}
小结