【问题标题】:Get the "size" (length) of a C++ function? [duplicate]获取 C++ 函数的“大小”(长度)? [复制]
【发布时间】:2012-02-01 01:50:04
【问题描述】:

可能重复:
How to get the length of a function in bytes?

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

我需要获取本地 C++ 函数的长度,我使用强制类型转换来获取函数的位置,但是如何获取长度?

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

工作?

【问题讨论】:

  • 没有便携的方法可以做到这一点。 (不,你的版本并不总是有效。)我猜你是在 Windows 上的 DWORD 东西。也许存在 Windows 特定的东西,但请在这种情况下指定你的编译器。
  • 您是否正在尝试获取函数的大小,以便将其复制并注入远程进程?如果是这样,那就行不通了。
  • 您考虑过这些:(a)(b)(c) 吗?
  • 这比听起来要难得多。有一次我尝试了一种叫做 LDE64(或类似的东西)的东西,它应该通过遵循代码的路径、跟随分支等来计算函数的长度,以确定函数返回的最远点,但它有很多问题,几乎从来没有奏效,尤其是在优化的情况下。
  • 你能描述一下为什么任何函数都不适合在它已经在的位置吗?这是一个不寻常的问题。

标签: c++


【解决方案1】:

没有。有几个原因。

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

2) 任何给定函数中都可以有多个“ret”指令,具体取决于编译器及其设置。同样,您只能获得部分功能。

3) 函数经常使用类似“switch”语句的结构,这些结构使用位于 ret 指令之后的“跳转表”。同样,您只能获得部分功能。

而且您尝试做的事情无论如何都不太可能奏效。

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

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

如果您确实需要注入内存,那么您将需要针对您的特定平台的汇编语言解析器来更新相关指令的所有内存地址,这是一项非常复杂的任务。或者你可以用汇编语言编写你的函数,并确保除了引用你自己的代码部分之外,你没有使用相对偏移量,这更容易一些,但你可以做的事情更多。

【讨论】:

  • 我发现这与我的问题无关。
  • @iDomo 回答的第一个字就回答了您的问题。搜索 0xC3 将不起作用。如果您真的不想将代码注入另一个进程 - 这可能会使您尝试做的事情毫无意义 - 那么您可以忽略第三段。其余的仍然适用,无论您是否尝试在同一过程中移动它。如果您选择假设其他情况,您很快就会发现。
  • 我对您的具体问题的答案进行了详细说明。
  • 吹毛求疵:它不是一个汇编语言解析器,而是一个机器语言解析器(这对于x86来说是相当困难的,因为你不知道指令从哪里开始;0xc3byte 可能是 RET 指令,但也可能在其他指令中)。
【解决方案2】:

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

(我对Windows一无所知)

如果没有定义max 可能是错误的。

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

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

您的代码是特定于机器的,因为您假设最后一条指令是RET,编码为0xC3,这是特定于 x86 和 x86-64(不适用于 Alpha 或 ARM,Windows 是传闻已被或将被移植)。此外,该字节可能出现在其他指令或内联常量中(正如 Mat 所评论的那样)。

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

也许您最好在给定地址附近找到符号。我不知道 Windows 是否有这样的功能(在 Linux 上,&lt;dlfcn.h&gt;dladdr GNU 功能可能很有用)。或许您的操作系统提供了等价物?

【讨论】:

  • 0xC3 技巧还假设 0xC3 只出现在函数末尾,这是一个非常弱的假设。
  • Windows CE 在 ARM 和 MIPS 上运行良好,而且我看到 Windows 8 也可以在 ARM 设备上运行。
【解决方案3】:

您可以强制将您的函数单独放在一个部分中(参见例如http://msdn.microsoft.com/en-us/library/s20kdbse(v=VS.71).aspx)。

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

更好的办法是将两个变量和函数放在单独的部分中,然后使用部分合并来控制它们在结果代码中出现的顺序(参见How to refer to the start-of a user-defined segment in a Visual Studio-project?

正如其他人指出的那样,您可能无法对此做任何有用的事情,而且它根本不便携。

【讨论】:

    【解决方案4】:

    你可以使用

    • 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 评估:它计算函数大小

    【讨论】:

    • 那个答案只与Linux有关,而原始发​​帖人使用的是Windows(对他来说很不幸)。
    • @BasileStarynkevitch:我们不需要那种偏见。让“他”来决定什么是幸运的或不幸运的。 Windows 和 Linux 是操作系统。不是宗教。
    • 至少在 Linux 上他会有dladdr。但我仍然不明白什么是“挂钩程序”。
    【解决方案5】:

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

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

    【讨论】:

      猜你喜欢
      • 2023-04-06
      • 2016-05-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-20
      • 1970-01-01
      相关资源
      最近更新 更多