反向分析本地库

Reading time: 9 minutes

tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE) 学习和实践 Azure 黑客技术:HackTricks Training Azure Red Team Expert (AzRTE)

支持 HackTricks

更多信息请参见: https://maddiestone.github.io/AndroidAppRE/reversing_native_libs.html

Android 应用可以使用本地库,通常用 C 或 C++ 编写,用于性能关键的任务。恶意软件作者也滥用这些库,因为 ELF 共享对象比 DEX/OAT byte-code 更难反编译。 本页侧重于使逆向 Android .so 文件更容易的实用工作流程和近期工具改进(2023–2025)。


新提取 libfoo.so 的快速初筛流程

  1. 提取库文件
bash
# From an installed application
adb shell "run-as <pkg> cat lib/arm64-v8a/libfoo.so" > libfoo.so
# Or from the APK (zip)
unzip -j target.apk "lib/*/libfoo.so" -d extracted_libs/
  1. 识别架构与保护
bash
file libfoo.so        # arm64 or arm32 / x86
readelf -h libfoo.so  # OS ABI, PIE, NX, RELRO, etc.
checksec --file libfoo.so  # (peda/pwntools)
  1. 列出导出符号与 JNI 绑定
bash
readelf -s libfoo.so | grep ' Java_'     # dynamic-linked JNI
strings libfoo.so   | grep -i "RegisterNatives" -n   # static-registered JNI
  1. 在反编译器中加载 (Ghidra ≥ 11.0, IDA Pro, Binary Ninja, Hopper or Cutter/Rizin) 并运行自动分析。 较新的 Ghidra 版本引入了对 AArch64 的反编译器,能够识别 PAC/BTI stubs 和 MTE tags,大幅改善使用 Android 14 NDK 构建的库的分析。
  2. 决定使用静态还是动态逆向: 被剥离符号或混淆的代码通常需要插桩(Frida, ptrace/gdbserver, LLDB)。

动态插桩 (Frida ≥ 16)

Frida 16 系列带来了一些针对 Android 的改进,当目标使用现代 Clang/LLD 优化时这些改进很有用:

  • thumb-relocator 现在可以 hook 由 LLD 的激进对齐(--icf=all)生成的小型 ARM/Thumb 函数。
  • 在 Android 上,枚举并重新绑定 ELF import slots 已可行,当内联 hooks 被拒绝时,可对每个模块进行 dlopen()/dlsym() 修补。
  • 修复了 Java hooking 针对新的 ART quick-entrypoint 的兼容性,该入口在 Android 14 上使用 --enable-optimizations 编译的应用中被采用。

Example: enumerating all functions registered through RegisterNatives and dumping their addresses at runtime:

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 will work out of the box on PAC/BTI-enabled devices (Pixel 8/Android 14+) as long as you use frida-server 16.2 or later – earlier versions failed to locate padding for inline hooks.

Process-local JNI telemetry via preloaded .so (SoTap)

当完整功能的 instrumentation 过于繁重或被阻止时,你仍然可以通过在目标进程内预加载一个小型 logger 来获得本地级别的可见性。SoTap 是一个轻量级的 Android native (.so) 库,记录同一应用进程内其他 JNI (.so) 库的运行时行为(不需要 root)。

Key properties:

  • 初始化较早,观察加载它的进程内的 JNI/native 交互。
  • 使用多个可写路径持久化日志,当存储受限时优雅回退到 Logcat。
  • 源代码可定制:编辑 sotap.c 来扩展/调整要记录的内容,并按 ABI 重新构建。

Setup (repack the APK):

  1. 将对应 ABI 的构建放入 APK,以便 loader 能解析 libsotap.so:
  • lib/arm64-v8a/libsotap.so (for arm64)
  • lib/armeabi-v7a/libsotap.so (for arm32)
  1. 确保 SoTap 在其他 JNI 库之前加载。尽早注入一次调用(例如,在 Application 子类的静态初始化器或 onCreate 中),以便 logger 优先初始化。Smali snippet example:
smali
const-string v0, "sotap"
invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
  1. 重新构建/签名/安装,运行应用,然后收集日志。

Log paths (checked in order):

/data/user/0/%s/files/sotap.log
/data/data/%s/files/sotap.log
/sdcard/Android/data/%s/files/sotap.log
/sdcard/Download/sotap-%s.log
# If all fail: fallback to Logcat only

注意事项与故障排查:

  • ABI 对齐是强制的。不匹配会抛出 UnsatisfiedLinkError,且 logger 无法加载。
  • 现代 Android 常见存储限制;如果文件写入失败,SoTap 仍会通过 Logcat 输出。
  • 行为/详细程度可自定义;在编辑 sotap.c 后需从源码重新构建。

该方法适用于恶意软件初筛和 JNI 调试,在需要从进程启动就观察本地调用流但无法使用 root/系统级 hooks 时尤其有用。


在 APK 中值得寻找的近期漏洞

YearCVEAffected libraryNotes
2023CVE-2023-4863libwebp ≤ 1.3.1堆缓冲区溢出,可由解码 WebP 图像的本地代码触发。若干 Android 应用捆绑了易受影响的版本。当在 APK 中看到 libwebp.so 时,请检查其版本并尝试利用或修补。
2024MultipleOpenSSL 3.x series多项内存安全和 padding-oracle 问题。许多 Flutter & ReactNative 包会自带它们自己的 libcrypto.so

当你在 APK 中发现第三方 .so 文件时,务必将其哈希与上游通报进行交叉核对。移动端很少做 SCA (Software Composition Analysis),因此过时且易受攻击的构建非常普遍。


反逆向与加固趋势(Android 13-15)

  • Pointer Authentication (PAC) & Branch Target Identification (BTI): Android 14 在支持的 ARMv8.3+ 硬件上在系统库启用了 PAC/BTI。反编译器现在会显示与 PAC 相关的伪指令;对于动态分析,Frida 在去除 PAC 后注入 trampolines,但你的自定义 trampolines 在必要时应调用 pacda/autibsp
  • MTE & Scudo hardened allocator: 内存标记(memory-tagging)是可选的,但许多关注 Play-Integrity 的应用使用 -fsanitize=memtag 构建;使用 setprop arm64.memtag.dump 1 加上 adb shell am start ... 来捕获标记错误。
  • LLVM Obfuscator (opaque predicates, control-flow flattening): 商业 packers(例如 Bangcle、SecNeo)越来越多地保护 native 代码,而不仅仅是 Java;在 .rodata 中预计会看到伪造的控制流和加密的字符串块。

资源

参考资料

tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE) 学习和实践 Azure 黑客技术:HackTricks Training Azure Red Team Expert (AzRTE)

支持 HackTricks