本项目聚焦于 Android Native 侧的 so 追踪能力:基于 QBDI(指令级插桩)与 Frida Gum(Hook 能力)构建 libqdbi.so,配套 Frida 脚本用于自动加载与触发 trace。
xwTrace/
├── native/ # libqdbi.so 源码与预编译依赖
│ ├── CMakeLists.txt
│ ├── core/ # 追踪核心与桥接层
│ ├── hook/ # Gum 扩展与 Hook
│ ├── soinfo/ # ELF/符号解析
│ ├── svc_call/ # SVC/系统调用相关
│ ├── include/ # 对外头文件
│ ├── record/ # 记录/输出辅助
│ └── lib/ # 预编译静态库(QBDI / Frida Gum)
├── scripts/ # Frida 脚本
│ ├── trace.js
│ └── hook.js
├── build.sh # NDK CMake 一键构建脚本(输出到 out/)
└── README.md- Android SDK(包含 NDK)
- CMake(脚本会优先使用
$ANDROID_SDK_ROOT/cmake/3.22.1/bin/cmake,找不到则回退到系统cmake) - macOS / Linux
- Android 设备(arm64)
- Frida:设备端
frida-server+ 本机frida-tools - 需要向
/data/local/tmp写入文件(常见场景需要 root 或可写权限)
在项目根目录执行:
./build.sh成功后会输出:
out/libqdbi.soadb push out/libqdbi.so /data/local/tmp/libqdbi.sotrace 模式与参数由 trace.js 的 Trace.configure() / Trace.start*() 控制;脚本本身不打印任何运行日志,所有 trace 过程日志统一通过 qbdi/native 的 logcat 输出。
symbol:按符号名追踪(调用 nativeTrace_symbol/Trace_symbol_replace)offset:按偏移追踪(调用 nativeTrace_offset/Trace_offset_replace)extcalls:按偏移追踪外部调用(调用 nativeTrace_offset_extcalls)auto:自动选择(默认):soOffset != 0则 offset,否则 symbol
traceSoPath:Trace so 在设备上的路径(默认/data/local/tmp/libqdbi.so)backend:trace 后端选择(传给 nativegqb_set_backend)targetSoName:要追踪的目标 so 名称(用于等待 dlopen 以及传给 native)symbolName:mode=symbol时的符号名(如JNI_OnLoad)soOffset:mode=offset/extcalls时的偏移(十六进制或数字)argCount:目标函数参数个数replaceMode:是否启用 replace 版本(1=replace,0=hook)fullHexdump:详细日志/更大输出(1=开启,0=关闭)startTimes:从第几次命中开始记录(例如 3 表示前两次命中不记录)preserveSp:是否保留 SP(1=开启,0=关闭)autoStart:是否自动在目标 so dlopen 后触发(1/0)autoStartDelayMs:自动触发延迟(毫秒)
脚本加载后可在 Frida 控制台执行:
Trace.configure({
traceSoPath: "/data/local/tmp/libqdbi.so",
targetSoName: "libexecmain.so",
mode: "symbol",
symbolName: "JNI_OnLoad",
argCount: 2,
fullHexdump: 0,
replaceMode: 0,
startTimes: 1,
preserveSp: 0,
backend: 1,
autoStart: 1,
autoStartDelayMs: 0
});也可手动启动(会立即触发,不依赖 autoStart):
Trace.start();
// 或 Trace.startSymbol("libexecmain.so", "JNI_OnLoad", 2, 1, 0, 0, 0);
// 或 Trace.startOffset("libexecmain.so", 0x1234, 2, 1, 0, 0, 0);任务结束后(用于触发最终统计输出):
Trace.stop();注入方式示例:
# 启动目标 App 并注入
frida -U -f com.example.target -l scripts/trace.js --no-pause
# 或附加到已运行进程
frida -U com.example.target -l scripts/trace.js- trace 文件:默认写入
/sdcard/Download/<soName>_trace.log(若失败则回退到/data/local/tmp/gqb_trace.log) - 运行日志:通过 logcat 输出,包括
- 每分钟一条:
YYYY-MM-DD HH:MM:SS | trace_file=... | size=xx.xx MB | rate=yy.yyyy MB/s | bytes=... - 结束汇总:
YYYY-MM-DD HH:MM:SS | trace_done | final_size=... | avg_rate=... | total_time=... | path=...
- 每分钟一条:
hook.js 是一个偏“定点偏移 hook”的脚本示例(需要自行修改 soName 与 offBlr),适合快速定位某条指令/调用点的回溯与上下文。
- 设置
ANDROID_SDK_ROOT或ANDROID_HOME - 或在根目录创建
local.properties并写入sdk.dir=...
- 安装 NDK,并设置
ANDROID_NDK_HOME(或让 SDK 下存在ndk/<version>目录)
- 确认设备架构为 arm64
- 确认 so 上传路径与
traceSoPath一致 - 确认 SELinux/权限允许访问目标路径