VEH(Vectored Exception Handling)hook是一种古老但常用的技术,可以在程序发生异常时进行无痕hook。与传统的断点方式不同,VEH hook不会修改原始代码,因此不会破坏代码完整性。
首先,我们来以调用MessageBoxA函数为例,演示如何使用VEH hook。
第一步:获取MessageBoxA函数的地址。我们可以使用LoadLibraryA函数加载user32.dll库,然后使用GetProcAddress函数获得MessageBoxA函数的地址。
HMODULE hmodule = LoadLibraryA("user32.dll");
DWORD breakpoint = (DWORD)GetProcAddress(hmodule, "MessageBoxA");
if (breakpoint == 0) {
MessageBoxA(NULL, "error", "warning", MB_OK);
}
第二步:设置寄存器。我们需要设置上下文(Context)结构体,并将ContextFlags成员指定为CONTEXT_DEBUG_REGISTERS,表示要使用调试寄存器。然后,将Dr0寄存器设置为MessageBoxA函数的地址,Dr7寄存器设置为1,表示在Dr0寄存器指定的地址上设置断点。
CONTEXT context = { 0 };
context.ContextFlags = CONTEXT_DEBUG_REGISTERS;
context.Dr0 = breakpoint;
context.Dr7 = 1;
SetThreadContext(GetCurrentThread(), &context);
第三步:设置回调函数。回调函数需要有固定的格式,并且注册到VEH异常处理链中。在回调函数中,我们可以根据ExceptionInfo结构体中的信息判断是否是我们关注的异常,并执行相应的操作。在这个例子中,如果发生中断的地址与MessageBoxA函数的地址相同,则说明MessageBoxA函数被调用了,我们可以在这里加入自己的代码来实现hook的效果。最后,通过修改ContextRecord结构体中的Eip成员,可以跳过中断指令,从而执行MessageBoxA函数后续的代码。
LONG WINAPI MyExceptionHandler(_EXCEPTION_POINTERS* ExceptionInfo) {
if ((DWORD)ExceptionInfo->ExceptionRecord->ExceptionAddress == breakpoint) {
printf("EXCEPTION\n");
ExceptionInfo->ContextRecord->Eip += 2;
}
return EXCEPTION_CONTINUE_EXECUTION;
}
最后,通过调用AddVectoredExceptionHandler函数将我们的回调函数添加到VEH异常处理链中。随后,调用MessageBoxA函数即可触发异常。
AddVectoredExceptionHandler(1, MyExceptionHandler);
MessageBoxA(NULL, "aaa", "sss", MB_OK);
需要注意的是,MessageBoxA函数的前两个字节是mov edi, edi指令,对应的机器码是FF 8B。在设置Dr7寄存器时,将最低位设置为1,表示设置一个字节的断点长度。这样我们就可以在任何地址上设置断点。在我们的代码中,断点被设置在了MessageBoxA函数的起始地址上,通过修改Eip(即程序计数器)跳过断点位置,保证了代码的正确执行。
希望以上解答能够帮助到您!如果还有其他问题,请随时提问。
以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。 |