使用 jnitrace 分析 native 方法调用过程
版权归作者所有,如有转发,请注明文章出处:https://cyrus-studio.github.io/blog/
jnitrace
jnitrace 可以动态跟踪 Java 层通过 JNI 接口调用 native 层(C/C++)代码的过程。它会记录并显示每一个 JNI 函数的调用,包括方法签名、参数、返回值等详细信息。
项目地址:https://github.com/chame1eon/jnitrace
安装 jnitrace
jnitrace 的使用依赖于 Frida。在使用 jnitrace 之前,需要确保 Frida 和 Python 环境已安装。可以参考下面两篇文章:
通过下面的命令安装 jnitrace
pip install jnitrace
解决 frida.NotSupportedError
运行 jnitrace 报错如下
jnitrace -l libnative-lib.so com.cyrus.exammple
Traceback (most recent call last):
File "D:\App\Miniconda3\envs\anti-app\lib\runpy.py", line 194, in _run_module_as_main
return _run_code(code, main_globals, None,
File "D:\App\Miniconda3\envs\anti-app\lib\runpy.py", line 87, in _run_code
exec(code, run_globals)
File "D:\App\Miniconda3\envs\anti-app\Scripts\jnitrace.exe\__main__.py", line 7, in \<module>
File "D:\App\Miniconda3\envs\anti-app\lib\site-packages\jnitrace\jnitrace.py", line 584, in main
pid = device.spawn([args.target], **aux_kwargs)
File "D:\App\Miniconda3\envs\anti-app\lib\site-packages\frida\core.py", line 26, in wrapper
return f(*args, **kwargs)
File "D:\App\Miniconda3\envs\anti-app\lib\site-packages\frida\core.py", line 140, in spawn
return self._impl.spawn(program, argv, envp, env, cwd, stdio, aux_options)
frida.NotSupportedError: need Gadget to attach on jailed Android; its default location is: C:\Users\cyurs\AppData\Local\Microsoft\Windows\INetCache\frida\gadget-android-arm64.so
看报错信息是缺少 gadget-android-arm64.so 文件导致的,去 frida releases 下载对应 frida 版本的 gadget 文件。
下载完成后解压到 C:\Users\cyurs\AppData\Local\Microsoft\Windows\INetCache\frida\gadget-android-arm64.so 就解决了。
使用 jnitrace 进行 JNI 跟踪
注意:在使用 jnitrace 之前需要先启动 frida-server。
1. jnitrace 跟踪 JNI 函数调用
执行下面命令附加到包名为 com.cyrus.exammple 的应用程序中,并开始跟踪 libnative-lib.so 中所有的 JNI 调用。 jnitrace -l libnative-lib.so com.cyrus.exammple
通过 -R host:port 自定义 firda-server 的端口号
jnitrace -R 127.0.0.1:1234 -l libnative-lib.so com.cyrus.example
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. 自定义跟踪特定的 JNI 函数
如果只想跟踪特定的 JNI 函数,例如 RegisterNatives,可以通过以下方式实现
jnitrace -R 127.0.0.1:1234 -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 */
551 ms [+] JNIEnv->RegisterNatives
551 ms |- JNIEnv* : 0x757bdfe180
551 ms |- jclass : 0xe9 { lte/NCall }
551 ms |- JNINativeMethod* : 0x7fc96e31a0
551 ms |: 0x747e514fa8 - IB([Ljava/lang/Object;)B
551 ms |- jint : 1
551 ms |= jint : 0
551 ms --------------------------------Backtrace--------------------------------
551 ms |-> 0x747e50d998: libGameVMP.so!0x6998 (libGameVMP.so:0x747e507000)
/* TID 26932 */
566 ms [+] JNIEnv->RegisterNatives
566 ms |- JNIEnv* : 0x757bdfe180
566 ms |- jclass : 0xe9 { lte/NCall }
566 ms |- JNINativeMethod* : 0x7fc96e31b8
566 ms |: 0x747e514fa8 - IC([Ljava/lang/Object;)C
566 ms |- jint : 1
566 ms |= jint : 0
566 ms --------------------------------Backtrace--------------------------------
566 ms |-> 0x747e50d998: libGameVMP.so!0x6998 (libGameVMP.so:0x747e507000)
/* TID 26932 */
580 ms [+] JNIEnv->RegisterNatives
580 ms |- JNIEnv* : 0x757bdfe180
580 ms |- jclass : 0xe9 { lte/NCall }
580 ms |- JNINativeMethod* : 0x7fc96e31d0
580 ms |: 0x747e514fa8 - IS([Ljava/lang/Object;)S
580 ms |- jint : 1
580 ms |= jint : 0
580 ms --------------------------------Backtrace--------------------------------
580 ms |-> 0x747e50d998: libGameVMP.so!0x6998 (libGameVMP.so:0x747e507000)
/* TID 26932 */
594 ms [+] JNIEnv->RegisterNatives
594 ms |- JNIEnv* : 0x757bdfe180
594 ms |- jclass : 0xe9 { lte/NCall }
594 ms |- JNINativeMethod* : 0x7fc96e31e8
594 ms |: 0x747e514fa8 - II([Ljava/lang/Object;)I
594 ms |- jint : 1
594 ms |= jint : 0
594 ms --------------------------------Backtrace--------------------------------
594 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)
3. 附加到正在运行的应用上
通过 -m <spawn|attach> 指定 Frida 附加方式,默认为 spawn(创建新的进程来启动目标应用程序并进行注入)。
执行下面命令通过 -m attach 附加到正在运行的应用上
jnitrace -R 127.0.0.1:1234 -m attach -l libnative-lib.so com.cyrus.example
4. 详细参数说明
参数说明:
-l <library>:指定要追踪的库,可以使用多次,例如 -l libnative-lib.so -l libanother-lib.so,或使用 -l * 追踪所有库。
<package>:目标应用的包名,必须已安装在设备上。
可选参数:
-R <host>:<port>:指定远程 Frida server 的位置(默认是 localhost:27042)。
-m <spawn|attach>:指定 Frida 附加方式,默认为 spawn,推荐使用。
-b <fuzzy|accurate|none>:控制追踪输出的堆栈信息,默认 accurate,可以选择 fuzzy 或 none 禁用。
-i <regex>:指定方法名称的正则表达式进行追踪,可以多次使用,如 -i Get -i RegisterNatives。
-e <regex>:排除指定方法名称的正则表达式,如 -e ^Find -e GetEnv。
-I <string>:指定库中的导出方法进行追踪,适用于只追踪部分导出方法,如 -I stringFromJNI。
-E <string>:排除指定导出方法的追踪,如 -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’。
输出说明
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。