应用安全 --- 调试安卓so的所有方法分类
调试安卓原生共享库(.so
文件,通常是C/C++代码)需要特定的工具和方法。以下是全面的安卓SO调试方法,涵盖主要工具和技术:
一、 核心调试工具链
-
Android Studio + LLDB (推荐)
-
配置:
-
确保
build.gradle
中启用调试支持:android { defaultConfig { ndk { debuggable true } } }
-
CMakeLists.txt
中设置调试符号:set(CMAKE_BUILD_TYPE Debug)
或add_compile_options(-g)
-
-
操作:
-
点击
Debug 'app'
按钮 ▶️ -
在
Debug
窗口选择LLDB
标签 -
设置断点、查看寄存器/内存、调用栈追踪
-
-
优势: 图形化界面,与Java/Kotlin调试无缝集成
-
-
命令行调试 (ADB + LLDB/GDB)
-
前置步骤:
adb push $NDK/prebuilt/android-arm64/gdbserver/gdbserver /data/local/tmp adb shell chmod 755 /data/local/tmp/gdbserver
-
启动调试:
# 在设备上启动gdbserver adb shell /data/local/tmp/gdbserver :5039 --attach <PID> # 在主机上启动LLDB lldb (lldb) platform select remote-android (lldb) platform connect connect://<设备IP>:5039 (lldb) target create /path/to/your/app.so
-
二、 日志与跟踪
-
Android Logcat (原生日志)
-
代码中插入:
#include <android/log.h> #define TAG "NativeLib" __android_log_write(ANDROID_LOG_DEBUG, TAG, "Debug message");
-
查看:
adb logcat | grep "NativeLib"
-
-
Simpleperf (CPU性能分析)
-
记录性能数据:
adb shell simpleperf record -p <PID> --call-graph fp -o /data/local/tmp/perf.data adb pull /data/local/tmp/perf.data
-
生成报告:
python3 $NDK/simpleperf/report_html.py perf.data
-
-
Perfetto (系统级跟踪)
-
启用: 设备开发者选项 → 系统跟踪
-
记录: 使用Perfetto命令行或Web UI捕获trace
-
分析: 加载
.perfetto-trace
文件到 ui.perfetto.dev
-
三、 内存调试
-
AddressSanitizer (ASan)
-
启用 (CMake):
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
-
运行时: 自动检测内存越界、use-after-free错误,日志输出到logcat
-
-
Valgrind (需Root)
-
交叉编译Valgrind: 使用NDK编译arm版
-
运行:
adb shell valgrind --tool=memcheck /path/to/app
-
-
Malloc Debug (Android原生)
-
启用:
adb shell setprop wrap.<package_name> '"LIBC_DEBUG_MALLOC_OPTIONS=backtrace logwrapper"'
-
分析: 崩溃时生成
/data/tombstones/tombstone_XX
文件
-
四、 崩溃分析
-
Tombstone 文件
-
位置:
/data/tombstones/tombstone_XX
-
获取:
adb pull /data/tombstones/tombstone_XX
-
分析工具:
$NDK/ndk-stack -sym /path/to/symbols -dump tombstone_XX
-
-
Breakpad (Google崩溃收集)
-
集成: 编译Breakpad库到项目
-
生成符号: 使用
dump_syms
工具 -
分析: 用
minidump_stackwalk
解析崩溃报告
-
五、 动态分析与逆向
-
Frida (动态插桩)
-
Hook Native函数:
// hook libnative.so中的foo函数 Interceptor.attach(Module.getExportByName("libnative.so", "foo"), { onEnter: function(args) { console.log("foo called with: " + args[0].toInt32()); } });
-
使用:
frida -U -l script.js com.example.app
-
-
IDA Pro/Ghidra (反汇编调试)
-
远程调试:
-
IDA加载
.so
文件 -
菜单: Debugger → Attach → Remote ARM Linux/Android debugger
-
配置主机IP和端口(需提前启动
android_server
)
-
-
用途: 无源码调试、漏洞分析、恶意代码研究
-
六、 高级技巧
-
检查OpenGL ES调用
-
启用:
adb shell setprop debug.egl.trace 1
-
日志: GLES错误会输出到logcat
-
-
调试JNI调用
-
启用JNI检查:
adb shell setprop dalvik.vm.checkjni true adb shell setprop debug.checkjni 1
-
常见错误:
JNI DETECTED ERROR IN APPLICATION
-
-
无源码调试
-
关键: 保留带符号的
.so
文件(obj/local/armeabi-v7a/libapp.so
) -
LLDB加载符号:
(lldb) target create --no-dependents --arch arm libapp.so
-
工具链对比表
工具 | 用途 | 优势 | 限制 |
---|---|---|---|
Android Studio+LLDB | 源码级调试 | 图形化集成,断点/变量查看 | 需完整工程 |
Simpleperf | CPU性能分析 | 低开销,火焰图生成 | 仅支持Android 5.0+ |
ASan | 内存错误检测 | 实时检测内存漏洞 | 性能开销约2倍 |
Frida | 动态Hook | 无需重启APP,脚本灵活 | 需注入进程 |
IDA Pro | 逆向与无源码调试 | 强大的反汇编能力 | 商业软件,学习曲线陡峭 |
关键注意事项
-
ABI匹配: 确保调试器(lldb/gdb)与设备架构(arm64/x86)一致
-
符号文件: 保留编译生成的
obj/local/<abi>/
下的未strip的.so文件 -
Android版本: NDK工具链版本需兼容目标系统API级别
-
Root权限: 部分工具(如Valgrind)需root设备
建议从Android Studio + LLDB开始,再逐步使用Simpleperf/ASan进行深度优化。逆向场景选择Frida/IDA Pro组合。
两种调试方式的本质区别
特性 | 附加调试 (Attach) | 调试模式启动 (Start in Debug Mode) |
---|---|---|
启动方式 | 附加到已运行进程 | 以调试模式启动新进程 |
调试时机 | 可调试运行中的代码 | 从APP入口点开始调试 |
JNI_OnLoad调试 | 可能错过早期初始化 | 可调试JNI_OnLoad等初始化函数 |
操作复杂度 | 简单直接 | 需要端口转发+jdb恢复执行 |
进程状态 | 进程已初始化完成 | 进程暂停在等待调试器状态 |
适用场景 | 运行时触发的函数调试 | 初始化过程/反调试绕过 |
核心区别图示:
附加调试: [APP已运行] → IDA附加 → 调试当前状态 调试启动: [启动命令] → APP暂停 → IDA附加 → jdb恢复 → 调试完整生命周期
调试方法选择矩阵
场景 | 推荐方法 | 工具组合 |
---|---|---|
JNI_OnLoad调试 | 调试模式启动 + IDA | IDA + jdb |
运行时函数分析 | 附加调试 + Frida Hook | Frida + IDA |
算法还原 | GDB指令级调试 | GDB + GEF插件 |
内存取证 | 内存Dump + 静态分析 | dd + 010 Editor |
自动化分析 | Frida脚本化调试 | Frida + Python |
源码级调试 | LLDB集成调试 |
Android Studio + LLDB
|