挂钩具有可变参数列表的函数

逆向工程 艾达
2021-06-29 04:30:06

我正在使用 Detours 3.0 连接到我从 Ida Pro 获得的函数。问题是我遇到了一个我似乎无法挂钩的函数,这是由于无效的争论或它使用可变争论列表的事实。

int (*MakePacketBuffer)(const char * buf, int len, const char * splitstr, ...);
#define MakePacketBufferProto int (*)(const char *, int, const char *, ...)

这就是我想象的参数,在 Ida Pro 中它实际上是这样显示的:

int sub_66EEF0(int a1, int a2, const char *a3, ...)

在 Ida 中如何调用该函数:

sub_66EEF0(buf, 2048, "%c%c%c%c%s%c%c", v15, 250, v20, v21, v22, v23, v25);
2个回答

您需要知道编译器是如何生成函数的。对于使用 MSVC 编译的 32 位 Windows 应用程序,由于所有参数都在堆栈上传递,因此您的生活变得更加轻松。

在进入 x86 Windows 上的例程时设置堆栈帧的方式:

----------------
 Return Address 
----------------
 Argument #0 
----------------
 Argument #1
----------------
 Argument #2 
----------------
  ....

对于 vararg 例程,情况仍然如此。这是一个快速示例,向您展示如何获取附加值。如果您正在拦截诸如此类的printf内容,您将遇到的问题是您必须解析格式字符串以了解已传递给您的参数数量。

#include <stdio.h>
#include <windows.h>
#include <intrin.h>

#define print(x) printf("%10s: %x\n", #x, x)

void fn(int a, int b, ...) {
    void ** Stack = (void**) _AddressOfReturnAddress();

    print(Stack);    // Address of SP upon function entry
    print(Stack[0]); // Return address
    print(Stack[1]); // a
    print(Stack[2]); // b
    print(Stack[3]); // vararg 1
};

void main() {
    fn(1,2,3,4,5,6);
}

这打印出来:

   Stack: 101feac
Stack[0]: ce10a4
Stack[1]: 1
Stack[2]: 2
Stack[3]: 3

没有必要知道参数的数量。

实际上,参数的数量受常量限制(否则程序将需要无限内存:)。

因此,您可以声明一个带有 16 个 args 的钩子函数,打印 args,并使用相同的 16 个 args 调用原始函数。如果代码中断,则将 args 的数量加倍。

typedef unsigned long DW;
#define DW_MANYARGS  DW arg1,DW arg2,DW arg3,DW arg4,DW arg5,DW arg6,DW arg7,DW arg8
#define MANYARGS     arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8

DW hookFunc(DW_MANYARGS) {
    // we do not expect a quadruple-word result here
    DW res = originalFunc(MANYARGS);
    return res;
}

注意事项:

不要忘记返回原始函数返回的内容。

请记住,有两个隐藏的参数:

0) 返回值的地址,如果返回值不适合int的大小;

1)this指针