如何从已编译的二进制文件中删除字符串?目标是避免让人们阅读里面的函数/方法的名称。
它是一个动态库(.so),使用NDK工具(包括GCC)从Android的C ++代码编译
我使用-O3
编译并已使用arm-eabi-strip -g mylib.so
删除调试符号,但是当我strings mylib.so
时,所有函数/方法的名称仍然可读。
答案 0 :(得分:25)
这些字符串位于动态符号表中,在运行时加载库时使用。 readelf -p .dynstr mylib.so
会显示这些条目。
strip -g
将删除调试符号,但它无法从动态符号表中删除条目,因为在运行时可能需要这些条目。您的问题是您在动态符号表中有条目,用于永远不会从库外部调用的函数。除非您说明,否则编译器/链接器无法知道哪些函数构成外部API的一部分(因此需要动态符号表中的条目)以及哪些函数对您的库是私有的(因此不需要输入动态符号表),因此它只为所有非静态函数创建动态符号表条目。
有两种主要方法可以告知编译器哪些函数是私有的。
标记私有函数static
。显然,这仅适用于仅在单个编译单元中需要的函数,但对于某些库,这种技术可能就足够了。
使用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")));
有关详细信息,请参阅this document,这是有关此主题的绝佳信息来源。
答案 1 :(得分:7)
有一些commercial obfuscators可以实现这一目标。基本上,他们在旅途中重写所有符号。像这样:
void foo()
变为
void EEhj_y33() // usually much, much longer and clobbered
变量名也被给予相同的处理,结构/联合的成员(取决于您设置的混淆程度)。
他们中的大多数都是通过扫描你的代码库,建立一个字典,然后在输出中用乱码混乱代替符号名称,然后可以照常编译。
我不建议使用它们,但它们可用。简单地模糊有意义的符号名称不会阻止那些决心发现你的库/程序如何工作的人。此外,您无法对跟踪系统调用的人员执行任何操作。真的,重点是什么?有人认为它有助于防止“随意的观察者”陷入困境,我认为有人ltrace
strace
和strings
通常不是偶然的。
除非您的意思是 字符串文字 ,否则不是 符号 ?关于它们没有什么可以做的,除非你以加密的格式存储文字,你编码在使用之前必须解密。这不仅仅是一种浪费,而是一种极度浪费,无论如何都没有任何好处。
答案 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)
他们是不可避免的。这些字符串是加载程序在运行时链接共享库的方法。