跳转至

编写调用钩子

无论如何,新被调用者 someNewFunction 必须遵循__cdecl 约定并有三个参数。下面是新被调用者的示例骨架:

DWORD __cdecl someNewFunction(DWORD arg1, DWORD 
arg2, DWORD arg3)
{
}

在 Visual Studio 中,C++ 程序默认使用 __cdecl 约定,所以严格来说你可以从函数定义中省略它;但我发现明确写出更好,让你养成具体化的习惯。还要记住,如果调用者期望返回值,你的函数返回类型也应该匹配。这个示例假设返回类型总是 DWORD 或更小。由于这个大小范围内的返回类型都会在 EAX 上传递,后面的示例也会使用 DWORD 返回类型。

大多数情况下,钩子以调用原始函数并把返回值传回调用者结束。以下是如何把所有这些组合在一起:

typedef DWORD (__cdecl _origFunc)(DWORD arg1, 
DWORD arg2, DWORD arg3);
_origFunc* originalFunction =
    (_origFunc*)hookCall(0xDEADBEEF, 
(DWORD)&someNewFunction);
arg2, DWORD arg3)
{
    return originalFunction(arg1, arg2, arg3);
}

这个示例用 typedef 声明一个表示原始函数原型的类型,并用这个类型创建一个指向原始函数的指针。然后 someNewFunction() 用这个指针以原始参数调用原始函数,并把返回值传回调用者。

现在,someNewFunction() 所做的只是返回原始函数。但从这里开始,你可以在 someNewFunction() 调用内部做任何想做的事情。你可以修改传给原始函数的参数,或拦截并存储有趣的参数供以后使用。如果你知道调用者不期望返回值(或者你知道如何伪造返回值),你甚至可以完全忘记原始函数,在新被调用者内部完全替换、复制或改进它的功能。一旦你完善了这项技能,就可以把自己的原生 C 或 C++ 代码添加到游戏中任何你想要的地方。