假设有一个A.DLL带有已知入口点的DLL DoStuff,我在某种程度上用我自己的DLL挂钩,fakeA.dll这样系统就会调用我的DoStuff.如何编写这样的函数,以便它可以在A.DLL不知道函数参数的情况下调用钩子DLL()的相同入口点?即我的功能fakeA.DLL看起来像
LONG DoStuff(
// don't know what to put here
)
{
FARPROC pfnHooked;
HINSTANCE hHooked;
LONG lRet;
// get hooked library and desired function
hHooked = LoadLibrary("A.DLL");
pfnHooked = GetProcAddress(hHooked, "DoStuff");
// how do I call the desired function without knowing the parameters?
lRet = pfnHooked( ??? );
return lRet;
}
Run Code Online (Sandbox Code Playgroud)
我目前的想法是,参数在堆栈上,所以我猜我必须有一个足够大的堆栈变量(struct例如一个大屁股)来捕获任何参数,然后将它传递给pfnHooked?即
// actual arg stack limit is >1MB but we'll assume 1024 bytes is sufficient
typedef struct { char unknownData[1024]; } ARBITARY_ARG;
ARBITARY_ARG DoStuff(ARBITARY_ARG args){
ARBITARY_ARG aRet;
...
aRet = pfnHooked(args);
return aRet;
}
Run Code Online (Sandbox Code Playgroud)
这会有用吗?如果是这样,有更好的方法吗?
更新:经过一些基本的(和非结论性的)测试后传递任意块作为参数DOES工作(这并不奇怪,因为程序将只读取它需要的堆栈).但是,收集返回值更难,因为它太大会导致访问冲突.将任意返回大小设置为8个字节(或者对于x86可能为4个)可能是大多数情况下的解决方案(包括void返回),但这仍然是猜测.如果我有一些方法可以知道DLL的返回类型(不一定是在运行时),那将是宏伟的.
小智 5
这应该是一个注释,但是元回答是肯定的,你可以在不知道调用约定和参数的情况下在x64/x86平台上挂钩函数.它可以纯粹用C语言完成吗?不,它还需要对各种调用约定和汇编编程有很多理解.挂钩框架将在Assembly中写入一些位.
大多数挂钩框架通过创建一个trampoline来实现这一点,该trampoline将执行流程从被调用函数的前导码重定向到存根代码,该存根代码通常独立于它所挂钩的功能.在用户模式下,您可以保证堆栈始终存在,因此您可以在同一堆栈上推送自己的本地变量,只要您可以弹出它们并将堆栈恢复到其原始状态.
您不需要将现有参数复制到自己的堆栈变量中.您可以只检查堆栈,在尝试任何操作之前,必须先阅读一些关于调用约定以及如何在不同体系结构上构建堆栈以进行各种类型的调用.