如何去除Android NDK .so文件中的符号?解决nm检测异常问题
如何去除Android .so文件中嵌入的硬编码符号
从你的命令输出能看出来,这些符号并不是存放在ELF的符号表(.symtab或.dynsym)里的——这就是为什么nm检测不到符号,strip也毫无作用的原因。你用strings看到的符号,其实是硬编码在代码段(比如.text)里的字符串常量,大概率是编译时编译器保留的名字(比如C++名字修饰后的函数名,可能用于异常信息、运行时类型识别(RTTI)或者日志输出)。
第一步:确认符号的存储位置
先验证这个判断,用Android NDK里的readelf和objdump工具:
# 查看.so的段信息,确认符号是否在.text或其他非符号表段 arm-linux-androideabi-readelf -S libMeow.so # 反汇编代码段,搜索目标符号,看是否作为字符串嵌入 arm-linux-androideabi-objdump -d libMeow.so | grep _ZN11SecretClass14SecretFunctionERKS_
如果输出里能看到这个符号出现在指令流旁边的字符串注释里,那就能确定是硬编码的字符串了。
第二步:去除这些符号的方法
方法1:从编译阶段彻底避免生成这类符号
这是最稳妥的方式,从根源解决问题:
- 关闭RTTI和异常:如果你的C++代码不需要运行时类型识别和异常处理,编译时添加
-fno-rtti和-fno-exceptions选项,编译器就不会生成包含类/函数名的字符串常量。 - 启用字符串混淆:使用支持代码混淆的编译工具,比如O-LLVM(Obfuscated LLVM),它可以将字符串常量替换为加密后的内容,运行时再解密,避免明文符号暴露。
- 自定义名字修饰:对于C代码,用
extern "C"包裹不需要C名字修饰的函数,或者手动修改类/函数名,减少敏感符号的出现。
方法2:对已编译的.so文件进行二进制替换
如果已经拿到编译好的.so,只能手动处理,但要注意必须保持字符串长度一致,否则会破坏二进制结构导致程序崩溃:
- 用十六进制编辑器打开
libMeow.so,找到目标符号字符串(比如_ZN11SecretClass14SecretFunctionERKS_)。 - 用等长度的无意义字符替换(比如用
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,长度要和原符号完全相同)。 - 批量替换所有类似的符号,完成后保存文件,记得测试.so是否还能正常加载运行。
也可以用脚本自动化这个过程,比如用sed处理二进制文件(注意指定二进制模式):
# 替换目标符号为等长度的X,注意替换后的字符串长度必须和原符号一致 arm-linux-androideabi-sed -i 's/_ZN11SecretClass14SecretFunctionERKS_/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/' libMeow.so
提示:替换前一定要备份原文件,并且替换后必须测试.so的可用性,任何长度变化都会导致程序崩溃。
为什么strip和nm没用?
最后补充解释下:nm只读取ELF文件的符号表(.symtab用于调试符号,.dynsym用于导出动态符号),如果符号不是存放在这些段里,nm就检测不到。而strip的作用是移除符号表中的调试符号和未使用的动态符号,对嵌入在代码段里的硬编码字符串完全没有影响。
内容的提问来源于stack exchange,提问作者Myria




