反向工程本地库
Reading time: 5 minutes
更多信息请查看: https://maddiestone.github.io/AndroidAppRE/reversing_native_libs.html
Android 应用可以使用本地库,通常用 C 或 C++ 编写,以满足性能关键任务的需求。恶意软件创建者也滥用这些库,因为 ELF 共享对象仍然比 DEX/OAT 字节码更难以反编译。此页面专注于 实用 工作流程和 近期 工具改进(2023-2025),使反向工程 Android .so 文件变得更容易。
新提取的 libfoo.so 的快速分类工作流程
- 提取库
bash
# 从已安装的应用程序
adb shell "run-as <pkg> cat lib/arm64-v8a/libfoo.so" > libfoo.so
# 或从 APK(zip)
unzip -j target.apk "lib/*/libfoo.so" -d extracted_libs/
- 识别架构和保护
bash
file libfoo.so # arm64 或 arm32 / x86
readelf -h libfoo.so # OS ABI, PIE, NX, RELRO 等
checksec --file libfoo.so # (peda/pwntools)
- 列出导出符号和 JNI 绑定
bash
readelf -s libfoo.so | grep ' Java_' # 动态链接的 JNI
strings libfoo.so | grep -i "RegisterNatives" -n # 静态注册的 JNI
- 加载到反编译器中(Ghidra ≥ 11.0, IDA Pro, Binary Ninja, Hopper 或 Cutter/Rizin)并运行自动分析。更新的 Ghidra 版本引入了一个 AArch64 反编译器,能够识别 PAC/BTI 存根和 MTE 标签,极大地改善了对使用 Android 14 NDK 构建的库的分析。
- 决定静态反向工程还是动态反向工程: 被剥离和混淆的代码通常需要 插桩(Frida, ptrace/gdbserver, LLDB)。
动态插桩(Frida ≥ 16)
Frida 的 16 系列带来了几个 Android 特定的改进,帮助在目标使用现代 Clang/LLD 优化时:
thumb-relocator现在可以 挂钩由 LLD 的激进对齐(--icf=all)生成的小型 ARM/Thumb 函数。- 枚举和重新绑定 ELF 导入槽 在 Android 上有效,使得在内联钩子被拒绝时能够进行按模块的
dlopen()/dlsym()修补。 - Java 钩子已针对在 Android 14 上使用
--enable-optimizations编译的应用程序的新 ART 快速入口点 进行了修复。
示例:枚举通过 RegisterNatives 注册的所有函数并在运行时转储它们的地址:
javascript
Java.perform(function () {
var Runtime = Java.use('java.lang.Runtime');
var register = Module.findExportByName(null, 'RegisterNatives');
Interceptor.attach(register, {
onEnter(args) {
var envPtr = args[0];
var clazz = Java.cast(args[1], Java.use('java.lang.Class'));
var methods = args[2];
var count = args[3].toInt32();
console.log('[+] RegisterNatives on ' + clazz.getName() + ' -> ' + count + ' methods');
// iterate & dump (JNI nativeMethod struct: name, sig, fnPtr)
}
});
});
Frida将在启用PAC/BTI的设备(Pixel 8/Android 14+)上开箱即用,只要使用frida-server 16.2或更高版本——早期版本无法找到内联钩子的填充。 citeturn5search2turn5search0
最近值得在APK中寻找的漏洞
| 年份 | CVE | 受影响的库 | 备注 |
|---|---|---|---|
| 2023 | CVE-2023-4863 | libwebp ≤ 1.3.1 | 从解码WebP图像的本地代码可达的堆缓冲区溢出。多个Android应用程序捆绑了易受攻击的版本。当你在APK中看到libwebp.so时,检查其版本并尝试利用或修补。 |
| 2024 | 多个 | OpenSSL 3.x系列 | 多个内存安全和填充预言机问题。许多Flutter和ReactNative捆绑包自带libcrypto.so。 |
当你在APK中发现第三方 .so文件时,始终交叉检查它们的哈希与上游公告。SCA(软件组成分析)在移动设备上不常见,因此过时的易受攻击构建普遍存在。
反逆向与加固趋势(Android 13-15)
- 指针认证(PAC)和分支目标识别(BTI): Android 14在支持的ARMv8.3+硅片上启用PAC/BTI。反编译器现在显示与PAC相关的伪指令;对于动态分析,Frida在去除PAC后注入跳板,但你的自定义跳板应在必要时调用
pacda/autibsp。 - MTE和Scudo加固分配器: 内存标记是可选的,但许多Play-Integrity感知的应用程序使用
-fsanitize=memtag构建;使用setprop arm64.memtag.dump 1加上adb shell am start ...来捕获标记故障。 - LLVM混淆器(不透明谓词,控制流扁平化): 商业打包工具(例如Bangcle,SecNeo)越来越多地保护本地代码,而不仅仅是Java;预计在
.rodata中会出现虚假的控制流和加密字符串块。
资源
- 学习ARM汇编: Azeria Labs – ARM汇编基础
- JNI和NDK文档: Oracle JNI规范 · Android JNI技巧 · NDK指南
- 调试本地库: 使用JEB反编译器调试Android本地库
参考
- Frida 16.x变更日志(Android钩子,微型函数重定位) – frida.re/news citeturn5search0
libwebp溢出CVE-2023-4863的NVD公告 – nvd.nist.gov citeturn2search0
HackTricks