版权归作者所有,如有转发,请注明文章出处: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 文件。 image.png

下载完成后解压到 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。