如何从已编译的二进制文件中删除字符串(.so)

时间:2010-05-20 09:57:05

标签: android linux gcc compilation android-ndk

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

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

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

4 个答案:

答案 0 :(得分:25)

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

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

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

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

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

  3. 如果你有一个功能:

    int foo(int a, int b);
    

    然后隐藏标记的语法是:

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

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

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

    有关详细信息,请参阅this document,这是有关此主题的绝佳信息来源。

答案 1 :(得分:7)

有一些commercial obfuscators可以实现这一目标。基本上,他们在旅途中重写所有符号。像这样:

void foo()

变为

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

变量名也被给予相同的处理,结构/联合的成员(取决于您设置的混淆程度)。

他们中的大多数都是通过扫描你的代码库,建立一个字典,然后在输出中用乱码混乱代替符号名称,然后可以照常编译。

我不建议使用它们,但它们可用。简单地模糊有意义的符号名称​​不会阻止那些决心发现你的库/程序如何工作的人。此外,您无法对跟踪系统调用的人员执行任何操作。真的,重点是什么?有人认为它有助于防止“随意的观察者”陷入困境,我认为有人ltrace stracestrings通常不是偶然的。

除非您的意思是 字符串文字 ,否则不是 符号 ?关于它们没有什么可以做的,除非你以加密的格式存储文字,你编码在使用之前必须解密。这不仅仅是一种浪费,而是一种极度浪费,无论如何都没有任何好处。

答案 2 :(得分:3)

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

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

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

答案 3 :(得分:-2)

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