本帖最后由 z-one 于 2022-3-9 09:29 编辑
异常发生后,KiUserExceptionDispatcher是R3最先被调用的函数,优先于VEH和SEH
Hook KiUserExceptionDispatcher可以让我们的异常处理在R3优先级最高
Hook代码
[C] 纯文本查看 复制代码 bool BeginHook(IN PVOID HookAddr, IN PVOID MyFun,IN ULONG HookLen, OUT PVOID* RepairPos)
{
BYTE HookCode[5] = { 0xE9,0,0,0,0 };
BYTE ReturnCode[5] = { 0xE9,0,0,0 };
ULONG PatchSize = Disasm(HookAddr, HookLen);
*RepairPos = VirtualAlloc(NULL, PatchSize + 5, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (!(*RepairPos)) return FALSE;
*(PULONG)((ULONG)HookCode + 1) = (ULONG)MyFun -((ULONG)HookAddr + 5);//从HookAddr,jmp到MyFun
*(PULONG)((ULONG)ReturnCode + 1) = (ULONG)HookAddr - ((ULONG)*RepairPos + 5);//从RepairPos+PatchSize,jmp到HookAddr+PatchSize
DWORD oldProtect;
VirtualProtect(HookAddr, 1, PAGE_READWRITE, &oldProtect);
//若Hook处有偏移类型的转移指令,Repair会出错
RtlCopyMemory(*RepairPos, HookAddr, PatchSize);//把Hook位置的PatchSize字节数据复制到RepairPos
RtlCopyMemory((PVOID)((ULONG)*RepairPos+PatchSize), ReturnCode, 5);//在RepairPos+PatchSize后面加上返回代码
RtlCopyMemory(HookAddr, HookCode, 5);//在Hook位置写入Hook代码
VirtualProtect(HookAddr, 1, oldProtect, &oldProtect);
return TRUE;
}
代码片段,为调用PatchFun做预备措施,并正确返回
[C] 纯文本查看 复制代码
void PatchFun(EXCEPTION_INFO Info)
{
printf("-------已经进入KiUserExceptionDispatcher Patch Fun\n");
printf("-------异常位置:%X\n", Info.pContext->Eip);
printf("-------异常Code:%X\n", Info.pRecord->ExceptionCode);
printf("-------\n");
}
__declspec(naked) void Preliminary()
{
__asm
{
mov ecx, [esp + 4];
mov ebx, [esp];
push ecx;
push ebx;
call PatchFun;
//__cdecl不对esp加8,给RtlDispatchException用
//HookLen>5时,修复位置处有偏移类型的转移指令,跳转过去会出错,所以直接调用RtlDispatchException
mov eax, KiUserExceptionDispatcher;
add eax, 0x21;
jmp eax;//jmp位置:call RtlDispatchException
//mov eax, repairPos;//HookLen=5时,修复位置处无偏移类型的转移指令,跳转过去不会出错
//jmp eax;
}
}
需要注意的是若HookLen>7,在Hook位置出现了jz short loc_4B2F5097,这种短偏移跳转很难修复,所以很难用统一的代码解决,要具体情况具体分析
一开始看网上资料只说了跳转到修复代码处会出错,但没有解释为什么会出错。
通过反汇编引擎很容易发现问题,试验的HookLen=10
通过IDA分析KiUserExceptionDispatcher,可以发现就算不修补原来的代码,也不会影响异常派发,所以直接跳回到原函数
最后附上源码
KiUserExceptionDispatcher.7z
(339.57 KB, 下载次数: 22)
|