挂钩OutputDebugStringA引发exception

我的目标是钩OutputDebugStringA,所以我可以阅读任何消息传递给它。 我不是想要挂钩任何其他的过程,而是现在的过程,只是为了学习如何挂钩。

我没有使用Detours,我宁愿不使用它们,因为我想了解如何在更深层次上进行挂钩。


我正在使用这个代码钩:

void MakeJMP(BYTE *pAddress, DWORD dwJumpTo, DWORD dwLen); void myCallback(LPCSTR s); DWORD dwAddr = (DWORD) GetProcAddress(GetModuleHandleA("kernel32.dll"), "OutputDebugStringA"); // I cast it to byte and later to DWORD so it adds 5 DWORD dwRetAddr = ((BYTE)dwAddr) + 5; // +5 because I will override 5 bytes void __declspec(naked) My_OutputDebugStringA(){ __asm{ mov edi, edi push ebp mov ebp, esp // ^those 5 bytes are overriden from the original OutputDebugStringA stub from kernel32.dll, so I restore them here pushad; pushfd; // to prevent stack messing push [ebp+8]; // OutputDebugStringA takes a LPCSTR parameter which is ebp + 8 (KernelBase.dll) call myCallback; // call my callback to print the string popfd; popad; jmp [dwRetAddr]; // jump back to next instruction so execution continues } } int _tmain(int argc, _TCHAR* argv[]){ printf("OutputDebugStringA address is: %8X\nPress any key to hook...", dwAddr); system("pause > nul"); MakeJMP((BYTE*) dwAddr, (DWORD) My_OutputDebugStringA, 5); puts("Hooked. Press any key to call it...\n"); system("pause > nul"); printf("Calling OutputDebugStringA (%8X) with \"hi\"\n", dwAddr); OutputDebugStringA("hi"); //puts("Called\n"); //system("pause"); return 0; } void myCallback(LPCSTR s){ printf("\n===Inside hook!===\nParam address is %8X", &s); } void MakeJMP(BYTE *pAddress, DWORD dwJumpTo, DWORD dwLen){ DWORD dwOldProtect, dwBkup, dwRelAddr; VirtualProtect(pAddress, dwLen, PAGE_EXECUTE_READWRITE, &dwOldProtect); dwRelAddr = (DWORD)(dwJumpTo - (DWORD)pAddress) - 5; *pAddress = 0xE9; *((DWORD *)(pAddress + 0x1)) = dwRelAddr; for (DWORD x = 0x5; x < dwLen; x++) *(pAddress + x) = 0x90; VirtualProtect(pAddress, dwLen, dwOldProtect, &dwBkup); return; } 

我的函数被调用,像这样: 控制台输出

然而,它打破了,我得到一个例外: 例外

它把我带到文件fflush.cfflush.c

我检查0x77020c02有什么(从例外),我看到这个: 拆卸

从最后一张图片,我想这可能是一个问题,恢复上下文和/或冲洗,但…老实说,我不知道为什么会发生,我已经挂钩function(非Windows)之前和我没有问题。


注意 :我没有在实际的代码中使用这种钩子,我只是试图没有像MS Detours这样的外部帮助。

我有点新,所以任何解释将非常感激。 🙂

我解决了它

首先,感谢Hans Passant帮助我解决了第一次事故。


似乎挂钩存根本不是一个好主意。 我挂钩了coreelBase.dll的真正功能,而不是挂钩kernel32.dll的存根。 只需在IDA中打开DLL,搜索OutputDebugStringA ,就可以找到这个函数: OutputDebugStringA

如果你看看它,你会看到.text:7D8634A4 lpOutputStringecx存储了lpOutputString ,这是我们需要的。 由于它只有3个字节,所以不能被JMP编辑,所以我挂接了下一个mov (OutputDebugStringA + 0x12),结果如下所示:

 DWORD _temp; // here we'll store our address void __declspec(naked) My_OutputDebugStringA(){ __asm{ mov _temp, ecx; // store ecx, or lpOutputString pushad; // preserve stack pushfd; call myCallback; // call our function popfd; popad; // pop to restore context mov dword ptr ss:[ebp-0234h], ecx; // restore overwritten function jmp [dwRetAddr]; // go back to caller + original instruction size // ^here the mov instruction was 6 bytes, so it'd be hookAddr+6 } } 

那么, 这是抓住 。 你可能会检查一下内存查看器中的地址是否正确,并且确实包含正确的字符串, 但是如果你使用*(char*) _temp来读取它,你将会崩溃。 原因是该函数需要一个LPCSTR而不是一个char* ,如下所示:

 void OutputDebugStringA(LPCSTR lpOutputString); 

所以最后,你写你的回调:

 void myCallback(){ printf("%s", (LPCSTR) _temp); } 

你知道了! 🙂