我正在编写一个简单的Dll注入和挂钩程序,当我为CreateFileA手动声明函数挂钩时,一切都很好:
HANDLE WINAPI Hook_CreateFileA(
LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile
)
{
...
return CreateFileA(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
}
但是现在我需要编写一个函数来处理来自kernel32.dll的任何函数。这意味着除了名称和地址之外,我对功能一无所知。
我对调用约定有一点了解 - 函数参数按直接顺序推送(__stdcall),然后调用该函数。我试着编写一个如下所示的 __ declspec(裸)函数:
PVOID __declspec(naked) HookAnyFunction()
{
/* do something */
__asm {
mov ebx, [esp]
add esp, 4
call pfnFuncAddr
sub esp, 4
mov[esp], ebx
ret
}
}
pfnFuncAddr - 是原始函数的地址。但它使用注入的Dll崩溃了一个应用程序。我猜我的代码会破坏堆栈或其他东西。我究竟做错了什么?希望我的解释是有道理的。
答案 0 :(得分:3)
ebx
不易变,你不能只是写它,你必须保存/恢复原始值。
编写通用挂钩函数会很困难,因为32位Windows ABI有3个调用约定; stdcall(callee清理堆栈),cdecl(调用者清理堆栈)和fastcall(寄存器中的前两个参数,但在公共API中没有使用太多)。
如果您只想记录函数调用,您可能会执行以下操作:
push esp
push pfnFuncAddr
call mylogger ; assumed to be stdcall in this case, it can also change the jump
jmp eax
和FARPROC __stdcall mylogger(FARPROC function, SIZE_T stackaddress) { ...; return function; }
(请记住esp
已更改,因此如果要记录堆栈的内容,则必须将堆栈参数调整为8 + 4个字节。)如果您关心快速调用也必须推送寄存器,但通用记录器无法知道第一个参数是在堆栈上还是在寄存器中。