是否有可能以编程方式在共享库中获取函数的签名?

时间:2011-07-30 05:37:56

标签: c shared-libraries

标题很清楚,我们可以通过dl_open等加载一个库。

但我如何才能获得其中的功能签名?

6 个答案:

答案 0 :(得分:7)

这个答案一般无法回答。从技术上讲,如果您使用详尽的调试信息编译可执行文件(代码可能仍然是优化的发行版本),那么可执行文件将包含额外的部分,提供二进制文件的某种反射性。在* nix系统上(您引用dl_open),这是通过DWARF二进制文件的额外部分中的ELF调试数据实现的。类似于MacOS X上的 Mach Universal Binaries

然而,Windows PE使用完全不同的格式,所以不幸的是DWARF不是truley cross plattform(实际上在我的3D引擎的早期开发阶段我为Windows实现了ELF / DWARF加载器,因此我可以使用通用格式引擎各种模块,所以可以做一些认真的努力。)

如果您不想实现自己的加载器或调试信息访问器,那么您可以通过一些额外的符号(通过一些标准命名方案)嵌入反射信息,这些符号引用函数名称表,映射到他们的签名。在C源文件的情况下编写解析器以从源文件本身提取信息是相当简单的。众所周知,C ++ OTOH很难正确解析,你需要一些完全成熟的编译器才能正确解析它。为此,开发了GCCXML,技术上是一个GCC,它以XML格式而不是对象二进制形式发出AST。然后,发出的XML更容易解析。

从提取的信息中创建一个带有某种链表/数组/等的源文件。描述每个功能的结构。如果不直接导出每个函数的符号,而是使用函数指针初始化反射结构中的某个字段,则会得到一个非常漂亮且干净的带注释的导出方案。从技术上讲,你也可以将这些信息放在二进制文件的一个特殊部分,但是把它放在只读数据部分也能完成这项工作。


但是,如果给你一个第三方二进制文件 - 说最坏情况它是从C源编译的,没有调试信息和所有没有外部引用的符号被剥离 - 你几乎搞砸了。您可以做的最好的事情是对函数访问可以传递参数的各个位置的方式应用一些二元分析。

这只会告诉您参数的数量和每个参数值的大小,而不是类型或名称/含义。当对某些程序进行逆向工程(例如恶意软件分析或安全审计)时,识别传递给函数的参数的类型和含义是主要工作之一。最近我遇到了一些我不得不为调试目的而反向的驱动程序,你无法相信我在Linux内核模块中找到C ++符号这一事实让我感到震惊(你不能以理智的方式在Linux内核中使用C ++) ),但也松了一口气,因为C ++名称错误为我提供了大量的信息。

答案 1 :(得分:4)

不,这是不可能的。函数的签名在运行时并不意味着什么,它在编译时有助于编译器验证程序。

答案 2 :(得分:3)

在Linux(或Mac)上,您可以使用" nm"和" c ++ filt" (对于C ++库)

  

nm mylibrary.so | C ++ FILT

  

nm mylibrary.a | C ++ FILT

"纳米"会给你一个错位的形式和" c ++ filt"试图将它们置于更易于阅读的格式中。您可能希望使用nm中的某些选项来过滤结果,特别是如果库很大(或者您可以" grep"最终输出以查找特定项目)

答案 3 :(得分:1)

你做不到。库可以在标题中发布公共API,也可以通过其他方式知道签名。

答案 4 :(得分:0)

较低级别的函数参数取决于您考虑的堆栈帧中的堆栈参数数量以及解释方式。因此,一旦将函数编译为目标代码,就不可能获得这样的签名。一种远程可能性是反汇编代码并读取其功能如何工作以了解参数的数量,但仍然难以或无法确定类型。总之,这是不可能的。

答案 5 :(得分:0)

此信息不可用。甚至调试器都不知道:

$ cat foo.c
#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[])
{
    char foo[10] = { 0 };
    char bar[10] = { 0 };
    printf("%s\n", "foo");
    memcpy(bar, foo, sizeof(foo));
    return 0;
}

$ gcc -g -o foo foo.c
$ gdb foo
Reading symbols from foo...done.
(gdb) b main
Breakpoint 1 at 0x4005f3: file foo.c, line 5.
(gdb) r
Starting program: foo 

Breakpoint 1, main (argc=1, argv=0x7fffffffe3e8) at foo.c:5
5   {
(gdb) ptype printf
type = int ()
(gdb) ptype memcpy
type = int ()
(gdb)