获取C++函数的"size"(长度)?

Get the "size" (length) of a C++ function?

本文关键字:长度 size 函数 获取 C++      更新时间:2023-10-16

可能重复:
如何获得以字节为单位的函数长度?

我正在制作一个Hooking程序,该程序将用于将一个方法插入内存的指定部分。

我需要得到一个局部C++函数的长度,我已经使用了一个强制转换来获得一个函数的位置,但我该如何获得长度?

int GetFuncLen()
{
  int i = 0;
  while((DWORD*)Function+i<max)
  {
    if((DWORD*)Function+i==0xC3)
    {
      return i;
    }
    i++;
  }
}

工作?

您的代码似乎是特定于操作系统、编译器和机器体系结构的。

(我对Windows一无所知)

如果未定义max,则可能是错误的。

它是特定于操作系统的(可能仅限于Windows),因为DWORD不是标准的C++类型。您可以使用intptr_t(来自<cstdint>标头)。

您的代码是特定于编译器的,因为您假设每个编译的函数都有一个定义良好的唯一结束,并且不与其他一些函数共享任何代码。(一些编译器能够进行这样的优化,例如,使用跳转指令使两个函数共享一个公共的尾声或代码块)。

您的代码是特定于机器的,因为您假设最后一条指令是RET编码的0xC3,而这是特定于x86&x86-64(无法在Alpha或ARM上工作,据传Windows已经或将要在其上移植)。此外,该字节可能出现在其他指令或内联常量中(正如Mat所评论的)。

我不确定二元函数在哪里结束的概念是否有明确的含义。但如果是这样,我希望链接器可能知道它。在一些系统上,例如在具有ELF可执行文件的Linux上,编译器和链接器会产生每个函数的大小。

也许你最好在给定地址附近找到这个符号。我不知道Windows是否有这样的功能(在Linux上,<dlfcn.h>中的dladdrGNU函数可能很有用)。也许你的操作系统提供了一个等效的?

编号。有几个原因。

1) 如果0xC3位于预期新指令的位置,则它只是一条"ret"指令。很容易有其他指令在其操作数中包含0xc3字节,并且您只能获得部分代码。

2) 在任何给定的函数中都可以有多个"ret"指令,这取决于编译器及其设置。同样,你只能得到函数的一部分。

3) 函数经常使用像"switch"语句这样的结构,这些结构使用位于ret指令之后的"跳转表"。同样,你只能得到函数的一部分。

而且你想做的事情无论如何都不太可能奏效。

最大的问题是,各种汇编指令通常会使用偏移量而不是绝对地址来引用内存的特定区域。因此,尽管极小的函数可能会工作,但任何调用其他函数的函数都可能失败。

假设您正试图将这些函数加载到外部进程中,并且您正试图在Windows上执行此操作,则更好的解决方案是使用DLL注入将DLL加载到目标进程中。

如果您真的需要注入内存,那么您将需要针对特定平台的汇编语言解析器来更新相关指令的所有内存地址,这是一项非常复杂的任务。或者,你可以用汇编语言编写函数,并确保除了引用自己代码的部分之外,你没有对任何事情使用相对偏移,这有点容易,但对你能做的事情更具限制性http://msdn.microsoft.com/en-us/library/s20kdbse(v=VS.71).aspx).

我认为,如果你定义一个部分,在其中声明一个变量,在其中定义你的函数,然后在其中定义另一个变量——那么这两个变量的地址将覆盖你的函数。

更好的方法是将两个变量和函数放在单独的部分中,然后使用部分合并来控制它们在结果代码中的显示顺序(请参阅如何在Visual Studio项目中引用用户定义的段的开头?)

正如其他人所指出的,你可能无法用它做任何有用的事情,而且它根本不可移植。

唯一可靠的方法是使用函数长度的虚数编译代码(但不运行它),对其进行反汇编,并手动计算长度,然后取该数字并将其替换为虚数,然后重新编译程序。

当我需要这样做的时候,我只是猜测函数应该有多大。只要你的猜测不太小(也不太大),你就应该没有问题。

您可以使用

  • objdump

以获取具有外部链接的对象的大小。否则,您可以获取编译器的汇编输出(例如gcc-S)并手动进行汇编,您将有机会查看长度字段的名称:

        .file   "test.cpp"
        .text
        .globl  main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        movl    %edi, -4(%rbp)
        movq    %rsi, -16(%rbp)
        movl    $0, %eax
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE0:
        .size   main, .-main
        .ident  "GCC: (Ubuntu 4.6.0-3~ppa1) 4.6.1 20110409 (prerelease)"
        .section        .note.GNU-stack,"",@progbits

参见.size main, .-main评估:它计算函数大小