我的目标是钩住OutputDebugStringA,以便我可以读取传递给它的所有消息。我不是要挂钩任何其他进程,而是尝试当前的进程,只是为了学习如何挂钩。
我不是在使用Detours,而是希望不使用Detours,因为我想了解钩子(Hook)如何在更深层次上起作用。
我正在使用此代码进行挂钩:
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;
}
我的函数被调用,如下所示:
但是,它在那里中断,我得到一个异常(exception):
这将我引到
fflush.c
文件:我检查了
0x77020c02
(从异常中)有什么,然后看到了:从上一张图片中,我想这可能是还原上下文和/或刷新的问题,但是...老实说,我不知道为什么会发生这种情况,我之前已经钩上了这样的函数(非Windows),我没有问题。
注意:我没有在真实代码中使用这种钩子(Hook),我只是想在没有MS Detours这样的外部帮助的情况下进行操作。
我有点新,所以任何解释将不胜感激。 :)
最佳答案
我解决了
首先,感谢Hans Passant,他帮助我解决了第一次车祸。
解
似乎钩住存根根本不是一个好主意。我没有钩住kernel32.dll
的存根,而是钩住了KernelBase.dll
的实函数。只需在IDA中打开DLL,搜索OutputDebugStringA
,您将找到该函数:
如果看一下,您会看到.text:7D8634A4
将lpOutputString
存储在ecx
中,这是我们需要的。由于只有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);
}
知道了! :)
关于c++ - Hook OutputDebugStringA引发异常,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/24573304/