【问题标题】:Hypothetical - about making a header for an *existing* static/dynamic library假设的 - 关于为*现有*静态/动态库制作标题
【发布时间】:2013-06-30 05:17:20
【问题描述】:

我想了解有关 unix/linux 的更多信息,这个问题突然出现在我的脑海中 - 假设我创建了一个静态/动态库(.a 或 .so)并且丢失了 c/c++ 源代码和头文件。默认的 nm 输出为我提供了符号的名称,但我需要知道返回类型和参数计数/类型才能制作标题。是否有可能以某种方式获取这些额外信息来对给定库的标头进行逆向工程?

【问题讨论】:

    标签: c++ c linux unix


    【解决方案1】:

    您标记了 C 和 C++,但两者之间的答案略有不同。

    对于 C++,类的方法名称在符号名称中嵌入了类型信息。您只需要弄清楚编译该库的编译器是如何修改名称的。

    对于 C,没有真正干净的方法可以做到这一点。您可以拆开程序集并分析读取了哪些寄存器和堆栈区域,而无需编写以确定函数需要多少参数。这需要了解编译库的任何编译器使用的调用约定。

    同样,您可以查看每个参数在程序集中的使用方式。如果你看到它被用于加载指令,它很可能是某种指针,而如果你看到它被用于算术,它可能是某种整数。

    对于返回类型,您可以检查在返回指令之前是否将任何看似有意义的内容放入返回寄存器中。同样,这需要了解您平台的调用约定。

    这是一个我将如何在 ARM 汇编中做事的示例。

    我知道ARM中的参数在寄存器r0到r3中传递,返回值存储在寄存器r0中。考虑到这一点,我们可以开始逆向工程。让我们看一下两个函数的程序集,并尝试找出函数原型是什么。

    00000000 <func1>:
       0:   e3510000    cmp r1, #0
       4:   0a000007    beq 28 <func1+0x28>
       8:   e0801001    add r1, r0, r1
       c:   e1a03000    mov r3, r0
      10:   e3a00000    mov r0, #0
      14:   e4d32001    ldrb    r2, [r3], #1
      18:   e1530001    cmp r3, r1
      1c:   e0800002    add r0, r0, r2
      20:   1afffffb    bne 14 <func1+0x14>
      24:   e12fff1e    bx  lr
      28:   e1a00001    mov r0, r1
      2c:   e12fff1e    bx  lr
    

    如果我们看一下这里,r0 和 r1 在写入任何内容之前都会被读取。我们还可以看到 r2 和 r3 在被读取之前被写入。因此我们可以推断func1 最多有两个参数。

    我们还意识到 r0 被移动到 r3 然后用作ldrb 的地址,这是一条从内存中加载一个字节的指令。因此,我们推断第一个参数是一个指针。因为指令只加载一个字节,我们也可以判断它可能是指向某种单字节数据类型的指针。

    r1 中的第二个参数似乎从未使用过,除非在比较和添加指令中使用,因此它可能是一个整数。

    在每个bx lr(返回调用者指令)之前,都会在 r0 中放置一些东西,因此我们推断该函数返回某种值。

    如果把这个函数呈现给我,我猜函数原型应该是这样的:

    int func1(unsigned char *, int);
    

    原文:

    unsigned int func1(void *, unsigned int);
    

    这是另一个函数

    00000030 <func2>:
      30:   e0822001    add r2, r2, r1
      34:   e5c02000    strb    r2, [r0]
      38:   e12fff1e    bx  lr
    

    这个很简单。

    我们看到 r0、r1 和 r2 都是在写入之前读取的,因此我们可以猜测该函数采用三个参数。 r0 用作strb 指令(存储字节)的地址,因此它可能是一个指针。同样,它只存储一个字节,因此它可能是一个指向字节大小的数据类型的指针。

    另外两个只用在加法指令中,所以可能是整数。

    在 r0 末尾似乎没有放入任何内容,因此该函数要么返回第一个参数,要么不返回值。

    我猜原型将是以下之一

    void func2(unsigned char *, int, int);
    unsigned char *func2(unsigned char *, int, int);
    

    原文:

    void func2(char *, char, char);
    

    【讨论】:

    • 哦,是的,没错,我记得很久以前在某处读到过有关信息被嵌入符号名称或使用 c++ 的东西,但当时它让我大吃一惊。我实际上使用的是 c++ 而不是 c,但是当我发现这一点时正在阅读一本 c 书籍。我现在用 g++ 重新编译了我的测试代码并制作了一个 .a 文件,我可以看到这些符号以 i 为后缀,它们采用整数,这就是它的意思。 _Z4 前缀可能是(4 字节)int 返回值?不管怎样,谢谢!非常有趣的东西...学习汇编程序在我的待办事项清单上,但我还没有。不过,我现在更有动力!
    • 我还要举个例子
    【解决方案2】:

    请记住,调用者/被调用者的约定因不同的处理器指令集而异,并且您已经意识到在一起使用 c 和 c++ 库时会发生名称修改,您可以尝试以下方式:

     gdb <executable>
     ....
     disas <function name>
     ....
     Here you can make a wild guess about the type of return value and parameters using the bit size of those values written on stack making use of assembly code.
    

    【讨论】:

      猜你喜欢
      • 2017-10-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-10
      • 2014-07-25
      • 2018-09-11
      • 2014-02-24
      • 1970-01-01
      相关资源
      最近更新 更多