16

如何从/混淆编译的二进制文件中删除字符串?目标是避免让人们阅读里面的函数/方法的名称。

它是使用 NDK 工具(包括 GCC)从 Android 的 C++ 代码编译的动态库 (.so)

-O3我使用并已用于删除调试符号进行编译arm-eabi-strip -g mylib.so,但是当我这样做时,strings mylib.so所有函数/方法的名称仍然可读。

4

4 回答 4

26

这些字符串在动态符号表中,在运行时加载库时使用。 readelf -p .dynstr mylib.so将显示这些条目。

strip -g将删除调试符号,但它不能从动态符号表中删除条目,因为在运行时可能需要这些条目。您的问题是您在动态符号表中有永远不会从库外部调用的函数的条目。除非您告诉它,否则编译器/链接器无法知道哪些函数构成外部 API 的一部分(因此需要动态符号表中的条目)以及哪些函数是您的库私有的(因此不需要在动态符号表),所以它只是为所有非静态函数创建动态符号表条目。

有两种主要方法可以通知编译器哪些函数是私有的。

  1. 标记私有函数static。显然,这仅适用于仅在单个编译单元中需要的功能,尽管对于某些库而言,这种技术可能就足够了。

  2. 使用 gcc "visibility" 属性将函数标记为可见或隐藏。您有两个选择:要么将所有私有函数标记为隐藏,要么使用-fvisibility=hidden编译器选项将默认可见性更改为隐藏,并将所有公共函数标记为可见。后者可能是您的最佳选择,因为这意味着您不必担心意外添加功能而忘记将其标记为隐藏。

如果你有一个功能:

int foo(int a, int b);

那么将其标记为隐藏的语法是:

int foo(int a, int b) __attribute__((visibility("hidden")));

并将其标记为可见的语法是:

int foo(int a, int b) __attribute__((visibility("default")));

有关详细信息,请参阅此文档,这是有关此主题的极好信息来源。

于 2010-06-02T16:37:20.733 回答
7

有一些商业混淆器可以做到这一点。基本上,他们会在旅途中重新编写所有符号。像这样的东西:

void foo()

变成

void EEhj_y33() // usually much, much longer and clobbered

变量名称也被给予相同的处理,结构/联合的成员也是如此(取决于您设置的混淆级别)。

它们中的大多数通过扫描您的代码库、建立字典然后用乱码替换输出中的符号名称来工作,然后可以照常编译。

我不建议使用它们,但它们是可用的。简单地混淆有意义的符号名称不会阻止那些决心发现你的库/程序是如何工作的人。此外,您将无法对跟踪系统调用的人做任何事情。真的,有什么意义?有人争辩说,它有助于让“随便的观察者”远离,我认为有人在跑步ltrace strace,而且strings通常不是随便的。

除非您的意思是字符串文字,而不是符号?您对它们无能为力,除非您以加密格式存储文字,您的代码在使用之前必须对其进行解密。这不仅是一种浪费,而且是一种没有任何好处的严重浪费。

于 2010-05-20T14:32:17.507 回答
3

假设您正确地为所有源文件指定了对 g++ 的隐藏可见性(正如其他海报所建议的那样),您可能会遇到这个 GCC 错误: http: //gcc.gnu.org/bugzilla/show_bug .cgi?id=38643

尝试转储二进制文件中出现的符号(readelf -Wa mylib.so | c++filt | less);如果您在解组后只看到 vtable 和 VTT 符号,那么 gcc 错误可能是您的问题。

编辑:如果可以,请尝试 GCC 4.4.0 或更高版本,因为它似乎已修复。

于 2010-07-11T06:11:15.830 回答
-2

它们是不可避免的。这些字符串是加载程序在运行时链接共享库的方式。

于 2010-05-20T10:03:43.583 回答