一、jni-trace
1. 前置
- jnitrace 是一个基于 Frida 的工具,用于跟踪 Android 应用程序中 JNI API 的使用;
- JNI 允许 Java 代码与 C/C++ 代码进行交互,而 jni-trace 通过动态分析,可以追踪这些交互过程中的 JNI 函数调用;
- 它会记录并显示每一个 JNI 函数的调用,包括方法签名、参数、返回值等详细信息;
- 项目地址:https://github.com/chame1eon/jnitrace
- 安装命令:
pip install jnitrace
- 注意:在使用 jnitrace 之前需要先启动 frida-server
- 各方面版本信息如下:
frida 16.5.7
frida-tools 13.6.0
hexdump 3.3
jnitrace 3.3.1
2. 使用方式
- 注意:在使用 jnitrace 之前需要先启动 frida-server;
2.1 跟踪 JNI 函数调用
- 执行下面命令附加到包名为 com.cyrus.exammple 的应用程序中,并开始跟踪 libnative-lib.so 中所有的 JNI 调用;
jnitrace -l libnative-lib.so com.cyrus.exammple
- trace的结果示例如下:
Tracing. Press any key to quit...
Traced library "libnative-lib.so" loaded from path "/data/app/com.cyrus.example-Jh9YgSVDqZ5bKxgv0f1E6w==/base.apk!/lib/arm64-v8a".
/* TID 13912 */
5973 ms [+] JNIEnv->NewStringUTF
5973 ms |- JNIEnv* : 0x7292c3c180
5973 ms |- char* : 0x7fc2a5b5d1
5973 ms |: Hello From Native
5973 ms |= jstring : 0x71 { Hello From Native }
5973 ms ------------------------------------Backtrace------------------------------------
5973 ms |-> 0x719d3b1080: libnative-lib.so!0x25080 (libnative-lib.so:0x719d38c000)
Stopping application (name=com.cyrus.example, pid=13912)...stopped.
2.2 跟踪特定的 JNI 函数
- 如果只想跟踪特定的 JNI 函数,例如 RegisterNatives,可以通过以下方式实现;
jnitrace -l libGameVMP.so -i RegisterNatives com.shizhuang.duapp
- 这个命令会仅跟踪 RegisterNatives 调用,从而帮助找到动态注册的 native 方法;
Tracing. Press any key to quit...
Traced library "libGameVMP.so" loaded from path "/data/app/com.shizhuang.duapp-fTxemmnM8l6298xbBELksQ==/lib/arm64".
/* TID 26932 */
478 ms [+] JNIEnv->RegisterNatives
478 ms |- JNIEnv* : 0x757bdfe180
478 ms |- jclass : 0xe9 { lte/NCall }
478 ms |- JNINativeMethod* : 0x7fc96e3128
478 ms |: 0x747e51293c - dI(I)I
478 ms |- jint : 1
478 ms |= jint : 0
478 ms --------------------------------Backtrace--------------------------------
478 ms |-> 0x747e50d998: libGameVMP.so!0x6998 (libGameVMP.so:0x747e507000)
/* TID 26932 */
493 ms [+] JNIEnv->RegisterNatives
493 ms |- JNIEnv* : 0x757bdfe180
493 ms |- jclass : 0xe9 { lte/NCall }
493 ms |- JNINativeMethod* : 0x7fc96e3140
493 ms |: 0x747e512aec - dS(Ljava/lang/String;)Ljava/lang/String;
493 ms |- jint : 1
493 ms |= jint : 0
493 ms --------------------------------Backtrace--------------------------------
493 ms |-> 0x747e50d998: libGameVMP.so!0x6998 (libGameVMP.so:0x747e507000)
/* TID 26932 */
508 ms [+] JNIEnv->RegisterNatives
508 ms |- JNIEnv* : 0x757bdfe180
508 ms |- jclass : 0xe9 { lte/NCall }
508 ms |- JNINativeMethod* : 0x7fc96e3158
508 ms |: 0x747e512ad0 - dL(J)J
508 ms |- jint : 1
508 ms |= jint : 0
508 ms --------------------------------Backtrace--------------------------------
508 ms |-> 0x747e50d998: libGameVMP.so!0x6998 (libGameVMP.so:0x747e507000)
/* TID 26932 */
522 ms [+] JNIEnv->RegisterNatives
522 ms |- JNIEnv* : 0x757bdfe180
522 ms |- jclass : 0xe9 { lte/NCall }
522 ms |- JNINativeMethod* : 0x7fc96e3170
522 ms |: 0x747e514fa8 - IV([Ljava/lang/Object;)V
522 ms |- jint : 1
522 ms |= jint : 0
522 ms --------------------------------Backtrace--------------------------------
522 ms |-> 0x747e50d998: libGameVMP.so!0x6998 (libGameVMP.so:0x747e507000)
/* TID 26932 */
537 ms [+] JNIEnv->RegisterNatives
537 ms |- JNIEnv* : 0x757bdfe180
537 ms |- jclass : 0xe9 { lte/NCall }
537 ms |- JNINativeMethod* : 0x7fc96e3188
537 ms |: 0x747e514fa8 - IZ([Ljava/lang/Object;)Z
537 ms |- jint : 1
537 ms |= jint : 0
537 ms --------------------------------Backtrace--------------------------------
537 ms |-> 0x747e50d998: libGameVMP.so!0x6998 (libGameVMP.so:0x747e507000)
/* TID 26932 */
609 ms [+] JNIEnv->RegisterNatives
609 ms |- JNIEnv* : 0x757bdfe180
609 ms |- jclass : 0xe9 { lte/NCall }
609 ms |- JNINativeMethod* : 0x7fc96e3200
609 ms |: 0x747e514fe8 - IF([Ljava/lang/Object;)F
609 ms |- jint : 1
609 ms |= jint : 0
609 ms --------------------------------Backtrace--------------------------------
609 ms |-> 0x747e50d998: libGameVMP.so!0x6998 (libGameVMP.so:0x747e507000)
/* TID 26932 */
623 ms [+] JNIEnv->RegisterNatives
623 ms |- JNIEnv* : 0x757bdfe180
623 ms |- jclass : 0xe9 { lte/NCall }
623 ms |- JNINativeMethod* : 0x7fc96e3218
623 ms |: 0x747e514fa8 - IJ([Ljava/lang/Object;)J
623 ms |- jint : 1
623 ms |= jint : 0
623 ms --------------------------------Backtrace--------------------------------
623 ms |-> 0x747e50d998: libGameVMP.so!0x6998 (libGameVMP.so:0x747e507000)
/* TID 26932 */
637 ms [+] JNIEnv->RegisterNatives
637 ms |- JNIEnv* : 0x757bdfe180
637 ms |- jclass : 0xe9 { lte/NCall }
637 ms |- JNINativeMethod* : 0x7fc96e3230
637 ms |: 0x747e515028 - ID([Ljava/lang/Object;)D
637 ms |- jint : 1
637 ms |= jint : 0
637 ms --------------------------------Backtrace--------------------------------
637 ms |-> 0x747e50d998: libGameVMP.so!0x6998 (libGameVMP.so:0x747e507000)
/* TID 26932 */
652 ms [+] JNIEnv->RegisterNatives
652 ms |- JNIEnv* : 0x757bdfe180
652 ms |- jclass : 0xe9 { lte/NCall }
652 ms |- JNINativeMethod* : 0x7fc96e3248
652 ms |: 0x747e514fa8 - IL([Ljava/lang/Object;)Ljava/lang/Object;
652 ms |- jint : 1
652 ms |= jint : 0
652 ms --------------------------------Backtrace--------------------------------
652 ms |-> 0x747e50d998: libGameVMP.so!0x6998 (libGameVMP.so:0x747e507000)
2.3 附加到正在运行的应用
- 通过 -m <spawn|attach> 指定 Frida 附加方式,默认为 spawn;
- 执行下面命令通过 -m attach 附加到正在运行的应用上;
jnitrace -m attach -l libnative-lib.so com.cyrus.example
3. 详细参数说明
参数说明:
- -l
:指定要追踪的库,可以使用多次,例如 -l libnative-lib.so -l libanother-lib.so,或使用 -l * 追踪所有库 :目标应用的包名,必须已安装在设备上;
- -l
可选参数:
- -R
: :指定远程 Frida server 的位置(默认是 localhost:27042); - -m <spawn|attach>:指定 Frida 附加方式,默认为 spawn,推荐使用;
- -b <fuzzy|accurate|none>:控制追踪输出的堆栈信息,默认 accurate,可以选择 fuzzy 或 none 禁用;
- -i
:指定方法名称的正则表达式进行追踪,可以多次使用,如 -i Get -i RegisterNatives; - -e
:排除指定方法名称的正则表达式,如 -e ^Find -e GetEnv; - -I
:指定库中的导出方法进行追踪,适用于只追踪部分导出方法,如 -I stringFromJNI; - -E
:排除指定导出方法的追踪,如 -E JNI_OnLoad -E nativeMethod; - -o <path/output.json>:指定追踪数据保存路径,以 JSON 格式存储;
- -p <path/to/script.js>:加载自定义 Frida 脚本,jnitrace 加载前执行,用于绕过反调试;
- -a <path/to/script.js>:加载自定义 Frida 脚本,jnitrace 加载后执行;
- --hide-data:减少输出,隐藏 hexdump 和字符串引用;
- --ignore-env:隐藏通过 JNIEnv 的调用;
- --ignore-vm:隐藏通过 JavaVM 的调用;
- --aux <name=(string|bool|int)value>:传递自定义参数,例如 --aux='uid=(int)10';
- -R
4. 输出说明
Traced library "libGameVMP.so" loaded from path "/data/app/com.xxx.duapp-fTxemmnM8l6298xbBELksQ==/lib/arm64".
/* TID 9541 */ # 表示线程 ID(TID)为 9541,当前的 JNI 调用是在这个线程上执行的。
613 ms [+] JNIEnv->RegisterNatives # 表示在 613 ms 时间点调用了 JNIEnv->RegisterNatives 方法
613 ms |- JNIEnv* : 0x757bdfe180 # 参数,JNIEnv 指针,0x757bdfe180 是该指针的内存地址。
613 ms |- jclass : 0xe9 { lte/NCall } # 参数,0xe9 为传递的 Java 类对象的句柄
613 ms |- JNINativeMethod* : 0x7fc96e3128 # 参数,指向 JNINativeMethod 数组的指针 0x7fc96e3128,这个数组定义了要注册的本地方法。
613 ms |: 0x747e54d93c - dI(I)I # 这是 JNINativeMethod 数组中的一项数据细节。0x747e54d93c 是本地函数的地址,dI(I)I 是方法的签名
613 ms |- jint : 1 # 参数,表示要注册的本地方法数量
613 ms |= jint : 0 # RegisterNatives 方法的返回值
613 ms --------------------------------Backtrace--------------------------------
613 ms |-> 0x747e548998: libGameVMP.so!0x6998 (libGameVMP.so:0x747e542000) # 这是调用 RegisterNatives 方法时的调用堆栈信息。
# 0x747e548998 是调用地址。
# libGameVMP.so!0x6998 表示 libGameVMP.so 库中的偏移地址 0x6998。
# (libGameVMP.so:0x747e542000) 指示库的基地址 0x747e542000。
alert("hello world")