当前位置: 首页 > article >正文

unidbg 实现 JNI 与 Java 交互

版权归作者所有,如有转发,请注明文章出处:https://cyrus-studio.github.io/blog/

Android 示例代码

在 com.cyrus.example.unidbg.UnidbgActivity 编写一个静态变量 a 和非静态变量 b,还有 base64 方法。

代码如下:

package com.cyrus.example.unidbg

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import android.util.Base64

class UnidbgActivity : AppCompatActivity() {

    companion object {
        // 静态变量 a
        var a: String? = null
    }

    // 非静态变量 b
    var b: String? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_unidbg)

        // 初始化静态变量 a 和 非静态变量 b
        a = "StaticA"
        b = "NonStaticB"
    }

    // Base64 方法
    fun base64(content: String): String {
        val combined = (a ?: "") + content + (b ?: "")
        return Base64.encodeToString(combined.toByteArray(), Base64.NO_WRAP)
    }
    
    // Native 方法 sign
    external fun sign(content: String): String
}

再实现一个 native 方法 sign 接受一个字符串 content,将 java 层 UnidbgActivity 中 静态变量 a + content + 非静态变量 b 拼接,再调用 java 层的 base64 方法传入拼接后的字符串得到加密串作为返回值。

cpp 源码如下:

#include <jni.h>
#include <string>

extern "C" JNIEXPORT jstring JNICALL
Java_com_cyrus_example_unidbg_UnidbgActivity_sign(JNIEnv *env, jobject thiz, jstring content) {
    // 获取 content 字符串
    const char *contentChars = env->GetStringUTFChars(content, nullptr);

    // 获取静态变量 a 和 非静态变量 b
    jclass clazz = env->GetObjectClass(thiz);
    jfieldID aField = env->GetStaticFieldID(clazz, "a", "Ljava/lang/String;");
    jfieldID bField = env->GetFieldID(clazz, "b", "Ljava/lang/String;");

    jstring a = (jstring) env->GetStaticObjectField(clazz, aField);
    jstring b = (jstring) env->GetObjectField(thiz, bField);

    // 将 a, content 和 b 拼接
    const char *aChars = env->GetStringUTFChars(a, nullptr);
    const char *bChars = env->GetStringUTFChars(b, nullptr);

    std::string combined = std::string(aChars) + contentChars + std::string(bChars);

    // 释放字符串
    env->ReleaseStringUTFChars(content, contentChars);
    env->ReleaseStringUTFChars(a, aChars);
    env->ReleaseStringUTFChars(b, bChars);

    // 调用 base64 方法
    jmethodID base64Method = env->GetMethodID(clazz, "base64", "(Ljava/lang/String;)Ljava/lang/String;");
    jstring combinedStr = env->NewStringUTF(combined.c_str());
    jstring base64Result = (jstring) env->CallObjectMethod(thiz, base64Method, combinedStr);

    return base64Result;
}

点击按钮 sign 调用 sign 方法加密字符串并 toast,编译运行得到 apk 文件。

word/media/image1.png

逆向分析 apk

把 apk 拖入 JEB , 经过反编译后可以找到 调用了 目标 jni 方法 sign

word/media/image2.png

JEB 下载地址:https://bbs.kanxue.com/thread-282148.htm

模拟目标 JNI 方法

把 apk 中目标 so 解压并放到 resources 目录下

通过 unidbg 加载 so 并调用 目标 jni 方法 sign,源码如下:

package com.cyrus.example;

import com.alibaba.fastjson.util.IOUtils;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.LibraryResolver;
import com.github.unidbg.Module;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.AbstractJni;
import com.github.unidbg.linux.android.dvm.DvmClass;
import com.github.unidbg.linux.android.dvm.DvmObject;
import com.github.unidbg.linux.android.dvm.VM;
import com.github.unidbg.memory.Memory;

import java.io.File;

public class Demo2 extends AbstractJni {

    public static void main(String[] args) {
        Demo2 demo = new Demo2();
        demo.run();
        demo.destroy();
    }

    private void destroy() {
        IOUtils.close(emulator);
    }

    private final AndroidEmulator emulator;

    private Demo2() {
        // 创建一个 64 位的 Android 模拟器实例
        emulator = AndroidEmulatorBuilder
                .for64Bit()  // 设置为 64 位模拟
                .setProcessName("com.cyrus.example") // 进程名称
                .build();    // 创建模拟器实例

        // 获取模拟器的内存实例
        Memory memory = emulator.getMemory();

        // 创建一个库解析器,并设置 Android 版本为 23(Android 6.0)
        LibraryResolver resolver = new AndroidResolver(23);

        // 将库解析器设置到模拟器的内存中,确保加载库时能够解析符号
        memory.setLibraryResolver(resolver);

    }

    public void run(){
        // 加载共享库 libunidbg.so 到 Dalvik 虚拟机中,并设置为需要自动初始化库
        Module module = emulator.loadLibrary(new File("unidbg-android/src/test/resources/example_binaries/cyrus/libunidbg.so"));

        // 创建一个 Dalvik 虚拟机实例
        VM vm = emulator.createDalvikVM();
        // 启用虚拟机的调试输出
        vm.setVerbose(true);

        // 绑定自定义 JNI 接口
        vm.setJni(this);

        // 调用 JNI_Onload
        vm.callJNI_OnLoad(emulator, module);

        // 注册 UnidbgActivity 类
        DvmClass unidbgActivityClass = vm.resolveClass("com/cyrus/example/unidbg/UnidbgActivity");

        // 创建 Java 对象
        DvmObject unidbgActivity = unidbgActivityClass.newObject(null);

        // 调用 Java 对象方法 sign 并传参 String
        DvmObject result = unidbgActivity.callJniMethodObject(emulator, "sign(Ljava/lang/String;)Ljava/lang/String;", "hello");

        System.out.println("sign result:" + result);
    }

}

运行输出如下:

[main]I/Unidbg: Shared library initialized (init).
[main]I/Unidbg: Shared library initialized (init_array1).
[main]I/Unidbg: Shared library initialized (init_array2).
JNIEnv->FindClass(com/cyrus/example/unidbg/UnidbgActivity) was called from RX@0x1202615c[libunidbg.so]0x2615c
JNIEnv->RegisterNatives(com/cyrus/example/unidbg/UnidbgActivity, unidbg@0xe4fff6d0, 1) was called from RX@0x120261a0[libunidbg.so]0x261a0
RegisterNative(com/cyrus/example/unidbg/UnidbgActivity, add(IIIIII)I, RX@0x12025f50[libunidbg.so]0x25f50)
Find native function Java_com_cyrus_example_unidbg_UnidbgActivity_sign => RX@0x1202633c[libunidbg.so]0x2633c
JNIEnv->GetStringUtfChars("hello") was called from RX@0x120262f4[libunidbg.so]0x262f4
JNIEnv->GetStaticFieldID(com/cyrus/example/unidbg/UnidbgActivity.aLjava/lang/String;) => 0x3914272a was called from RX@0x12026670[libunidbg.so]0x26670
JNIEnv->GetFieldID(com/cyrus/example/unidbg/UnidbgActivity.b Ljava/lang/String;) => 0x20b5fd89 was called from RX@0x120266b4[libunidbg.so]0x266b4
[01:04:14 656]  WARN [com.github.unidbg.linux.ARM64SyscallHandler] (ARM64SyscallHandler:410) - handleInterrupt intno=2, NR=-128432, svcNumber=0x192, PC=unidbg@0xfffe0a54, LR=RX@0x120266f0[libunidbg.so]0x266f0, syscall=null
java.lang.UnsupportedOperationException: com/cyrus/example/unidbg/UnidbgActivity->a:Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.getStaticObjectField(AbstractJni.java:103)
at com.github.unidbg.linux.android.dvm.AbstractJni.getStaticObjectField(AbstractJni.java:53)
at com.github.unidbg.linux.android.dvm.DvmField.getStaticObjectField(DvmField.java:106)
at com.github.unidbg.linux.android.dvm.DalvikVM64$142.handle(DalvikVM64.java:2334)
at com.github.unidbg.linux.ARM64SyscallHandler.hook(ARM64SyscallHandler.java:119)
at com.github.unidbg.arm.backend.UnicornBackend$11.hook(UnicornBackend.java:345)
at unicorn.Unicorn$NewHook.onInterrupt(Unicorn.java:128)
at unicorn.Unicorn.emu_start(Native Method)
at com.github.unidbg.arm.backend.UnicornBackend.emu_start(UnicornBackend.java:376)
at com.github.unidbg.AbstractEmulator.emulate(AbstractEmulator.java:378)
at com.github.unidbg.thread.Function64.run(Function64.java:39)
at com.github.unidbg.thread.MainTask.dispatch(MainTask.java:19)
at com.github.unidbg.thread.UniThreadDispatcher.run(UniThreadDispatcher.java:165)
at com.github.unidbg.thread.UniThreadDispatcher.runMainForResult(UniThreadDispatcher.java:97)
at com.github.unidbg.AbstractEmulator.runMainForResult(AbstractEmulator.java:341)
at com.github.unidbg.arm.AbstractARM64Emulator.eFunc(AbstractARM64Emulator.java:262)
at com.github.unidbg.Module.emulateFunction(Module.java:163)
at com.github.unidbg.linux.android.dvm.DvmObject.callJniMethod(DvmObject.java:135)
at com.github.unidbg.linux.android.dvm.DvmObject.callJniMethodObject(DvmObject.java:93)
at com.cyrus.example.Demo2.run(Demo2.java:71)
at com.cyrus.example.Demo2.main(Demo2.java:21)
[01:04:14 659]  WARN [com.github.unidbg.AbstractEmulator] (AbstractEmulator:417) - emulate RX@0x1202633c[libunidbg.so]0x2633c exception sp=unidbg@0xe4fff580, msg=com/cyrus/example/unidbg/UnidbgActivity->a:Ljava/lang/String;, offset=12ms @ Runnable|Function64 address=0x1202633c, arguments=[unidbg@0xfffe1730, 2074185499, 2109874862]
sign result:null

目前通过 resolveClass 注册的 UnidbgActivity 类实际上并没有 变量 a 、b 和 base 方法,所以当调用 jni 方法中实际访问到这些变量和方法时就会报 UnsupportedOperationException

自定义 JNI 调用逻辑

但我们可以通过 AbstractJni 自定义 jni 行为逻辑,流程大概如下:

  1. extends AbstractJni

  2. vm.setJni(this);

  3. 重写 getStaticObjectField / getObjectField / callObjectMethodV,自定义执行逻辑

1. 自定义静态字段访问逻辑

从反编译的代码中可以找到静态变量 a 的值

word/media/image3.png

重写 getStaticObjectField 方法,通过判断 signature 是否访问的是静态变量 a 并自定义返回字符串值

@Override
public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {
    System.out.println("getStaticObjectField:" + signature);
    // 通过 signature 判断自定义静态变量访问逻辑
    if (signature.equals("com/cyrus/example/unidbg/UnidbgActivity->a:Ljava/lang/String;")) {
        return new StringObject(vm, "StaticA");
    }
    return super.getStaticObjectField(vm, dvmClass, signature);
}

2. 自定义对象字段访问逻辑

对于对象中的字段,我们可以重写 getObjectField 方法类似的处理,处理逻辑也类似。

@Override
public DvmObject<?> getObjectField(BaseVM vm, DvmObject<?> dvmObject, String signature) {
    System.out.println("getObjectField:" + signature);

    if (signature.equals("com/cyrus/example/unidbg/UnidbgActivity->b:Ljava/lang/String;")) {
        return new StringObject(vm, "NonStaticB");
    }

    return super.getObjectField(vm, dvmObject, signature);
}

3. 自定义方法访问逻辑

重写 callObjectMethodV 方法,模拟 java 中的 base64 方法

public String base64(String content) {
    return Base64.getEncoder().encodeToString(content.getBytes());
}

@Override
public DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {
    System.out.println("callObjectMethodV:" + signature);

    if (signature.equals("com/cyrus/example/unidbg/UnidbgActivity->base64(Ljava/lang/String;)Ljava/lang/String;")) {
        // 取出第一个参数
        StringObject content = vaList.getObjectArg(0);
        // 调用本地的 base64 方法
        String result = base64(content.getValue());
        // 返回加密后的字符串对象
        return new StringObject(vm, result);
    }

    return super.callObjectMethodV(vm, dvmObject, signature, vaList);
}

完整源码

package com.cyrus.example;

import com.alibaba.fastjson.util.IOUtils;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.LibraryResolver;
import com.github.unidbg.Module;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.memory.Memory;

import java.io.File;
import java.util.Base64;

public class Demo2 extends AbstractJni {

    public static void main(String[] args) {
        Demo2 demo = new Demo2();
        demo.run();
        demo.destroy();
    }

    private void destroy() {
        IOUtils.close(emulator);
    }

    private final AndroidEmulator emulator;

    private Demo2() {
        // 创建一个 64 位的 Android 模拟器实例
        emulator = AndroidEmulatorBuilder
                .for64Bit()  // 设置为 64 位模拟
                .setProcessName("com.cyrus.example") // 进程名称
                .build();    // 创建模拟器实例

        // 获取模拟器的内存实例
        Memory memory = emulator.getMemory();

        // 创建一个库解析器,并设置 Android 版本为 23(Android 6.0)
        LibraryResolver resolver = new AndroidResolver(23);

        // 将库解析器设置到模拟器的内存中,确保加载库时能够解析符号
        memory.setLibraryResolver(resolver);

    }

    public void run() {
        // 加载共享库 libunidbg.so 到 Dalvik 虚拟机中,并设置为需要自动初始化库
        Module module = emulator.loadLibrary(new File("unidbg-android/src/test/resources/example_binaries/cyrus/libunidbg.so"));

        // 创建一个 Dalvik 虚拟机实例
        VM vm = emulator.createDalvikVM();
        // 启用虚拟机的调试输出
        vm.setVerbose(true);

        //
        vm.setJni(this);

        // 调用 JNI_Onload
        vm.callJNI_OnLoad(emulator, module);

        // 注册 UnidbgActivity 类
        DvmClass unidbgActivityClass = vm.resolveClass("com/cyrus/example/unidbg/UnidbgActivity");

        // 创建 Java 对象
        DvmObject unidbgActivity = unidbgActivityClass.newObject(null);

        // 调用 Java 对象方法 sign 并传参 String
        DvmObject result = unidbgActivity.callJniMethodObject(emulator, "sign(Ljava/lang/String;)Ljava/lang/String;", "hello");

        System.out.println("sign result:" + result);
    }

    @Override
    public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {
        System.out.println("getStaticObjectField:" + signature);
        // 通过 signature 判断自定义静态变量访问逻辑
        if (signature.equals("com/cyrus/example/unidbg/UnidbgActivity->a:Ljava/lang/String;")) {
            return new StringObject(vm, "StaticA");
        }
        return super.getStaticObjectField(vm, dvmClass, signature);
    }

    @Override
    public DvmObject<?> getObjectField(BaseVM vm, DvmObject<?> dvmObject, String signature) {
        System.out.println("getObjectField:" + signature);

        if (signature.equals("com/cyrus/example/unidbg/UnidbgActivity->b:Ljava/lang/String;")) {
            return new StringObject(vm, "NonStaticB");
        }

        return super.getObjectField(vm, dvmObject, signature);
    }

    public String base64(String content) {
        return Base64.getEncoder().encodeToString(content.getBytes());
    }

    @Override
    public DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {
        System.out.println("callObjectMethodV:" + signature);

        if (signature.equals("com/cyrus/example/unidbg/UnidbgActivity->base64(Ljava/lang/String;)Ljava/lang/String;")) {
            // 取出第一个参数
            StringObject content = vaList.getObjectArg(0);
            // 调用本地的 base64 方法
            String result = base64(content.getValue());
            // 返回加密后的字符串对象
            return new StringObject(vm, result);
        }

        return super.callObjectMethodV(vm, dvmObject, signature, vaList);
    }

}

运行输出如下:

[main]I/Unidbg: Shared library initialized (init).
[main]I/Unidbg: Shared library initialized (init_array1).
[main]I/Unidbg: Shared library initialized (init_array2).
JNIEnv->FindClass(com/cyrus/example/unidbg/UnidbgActivity) was called from RX@0x1202615c[libunidbg.so]0x2615c
JNIEnv->RegisterNatives(com/cyrus/example/unidbg/UnidbgActivity, unidbg@0xe4fff6d0, 1) was called from RX@0x120261a0[libunidbg.so]0x261a0
RegisterNative(com/cyrus/example/unidbg/UnidbgActivity, add(IIIIII)I, RX@0x12025f50[libunidbg.so]0x25f50)
Find native function Java_com_cyrus_example_unidbg_UnidbgActivity_sign => RX@0x1202633c[libunidbg.so]0x2633c
JNIEnv->GetStringUtfChars("hello") was called from RX@0x120262f4[libunidbg.so]0x262f4
JNIEnv->GetStaticFieldID(com/cyrus/example/unidbg/UnidbgActivity.aLjava/lang/String;) => 0x3914272a was called from RX@0x12026670[libunidbg.so]0x26670
JNIEnv->GetFieldID(com/cyrus/example/unidbg/UnidbgActivity.b Ljava/lang/String;) => 0x20b5fd89 was called from RX@0x120266b4[libunidbg.so]0x266b4
getStaticObjectField:com/cyrus/example/unidbg/UnidbgActivity->a:Ljava/lang/String;
JNIEnv->GetStaticObjectField(class com/cyrus/example/unidbg/UnidbgActivity, a Ljava/lang/String; => "StaticA") was called from RX@0x120266f0[libunidbg.so]0x266f0
getObjectField:com/cyrus/example/unidbg/UnidbgActivity->b:Ljava/lang/String;
JNIEnv->GetObjectField(com.cyrus.example.unidbg.UnidbgActivity@382db087, b Ljava/lang/String; => "NonStaticB") was called from RX@0x1202672c[libunidbg.so]0x2672c
JNIEnv->GetStringUtfChars("StaticA") was called from RX@0x120262f4[libunidbg.so]0x262f4
JNIEnv->GetStringUtfChars("NonStaticB") was called from RX@0x120262f4[libunidbg.so]0x262f4
JNIEnv->ReleaseStringUTFChars("hello") was called from RX@0x12026330[libunidbg.so]0x26330
JNIEnv->ReleaseStringUTFChars("StaticA") was called from RX@0x12026330[libunidbg.so]0x26330
JNIEnv->ReleaseStringUTFChars("NonStaticB") was called from RX@0x12026330[libunidbg.so]0x26330
JNIEnv->GetMethodID(com/cyrus/example/unidbg/UnidbgActivity.base64(Ljava/lang/String;)Ljava/lang/String;) => 0x526ba1f7 was called from RX@0x1202686c[libunidbg.so]0x2686c
JNIEnv->NewStringUTF("StaticAhelloNonStaticB") was called from RX@0x120268a0[libunidbg.so]0x268a0
callObjectMethodV:com/cyrus/example/unidbg/UnidbgActivity->base64(Ljava/lang/String;)Ljava/lang/String;
JNIEnv->CallObjectMethodV(com.cyrus.example.unidbg.UnidbgActivity@382db087, base64("StaticAhelloNonStaticB") => "U3RhdGljQWhlbGxvTm9uU3RhdGljQg==") was called from RX@0x12026990[libunidbg.so]0x26990
sign result:"U3RhdGljQWhlbGxvTm9uU3RhdGljQg=="

可以看到 sign 最后返回的结果和 app 是一样的。

复用现有 java 类模拟 JNI 方法

更简单的方案,直接把反编译后的 java 类复制过来,做一下简单的修改,去掉不需要的代码,只留下目标方法相关的代码(注意:类路径一定要保持一致!!)

package com.cyrus.example.unidbg;


import org.apache.commons.io.Charsets;

public final class UnidbgActivity {

    private static String a;
    private String b;

    public UnidbgActivity() {
        UnidbgActivity.a = "StaticA";
        this.b = "NonStaticB";
    }

    public final String base64(String content) {
        byte[] arr_b = content.getBytes(Charsets.UTF_8);
        String s1 = Base64.encodeToString(arr_b, 2);
        return s1;
    }

}

把依赖的 Base64.java 也复制过来

word/media/image4.png

设置 ProxyClassFactory

vm.setDvmClassFactory(new ProxyClassFactory());

创建 DvmObject 并调用目标方法

// 注册 UnidbgActivity 类
vm.resolveClass("com/cyrus/example/unidbg/UnidbgActivity");

// 创建 DvmObject
UnidbgActivity activity=new UnidbgActivity();
DvmObject object = ProxyDvmObject.createObject(vm, activity);

// 调用 Java 对象方法 sign 并传参 String
DvmObject result = object.callJniMethodObject(emulator, "sign(Ljava/lang/String;)Ljava/lang/String;", "hello");

System.out.println("sign result:" + result);

完整代码如下:

package com.cyrus.example;

import com.alibaba.fastjson.util.IOUtils;
import com.cyrus.example.unidbg.UnidbgActivity;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.LibraryResolver;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.linux.android.dvm.jni.ProxyClassFactory;
import com.github.unidbg.linux.android.dvm.jni.ProxyDvmObject;
import com.github.unidbg.memory.Memory;

import java.io.File;

public class Demo3 {

    public static void main(String[] args) {
        Demo3 demo = new Demo3();
        demo.run();
        demo.destroy();
    }

    private void destroy() {
        IOUtils.close(emulator);
    }

    private final AndroidEmulator emulator;

    private Demo3() {
        // 创建一个 64 位的 Android 模拟器实例
        emulator = AndroidEmulatorBuilder
                .for64Bit()  // 设置为 64 位模拟
                .setProcessName("com.cyrus.example") // 进程名称
                .build();    // 创建模拟器实例

        // 获取模拟器的内存实例
        Memory memory = emulator.getMemory();

        // 创建一个库解析器,并设置 Android 版本为 23(Android 6.0)
        LibraryResolver resolver = new AndroidResolver(23);

        // 将库解析器设置到模拟器的内存中,确保加载库时能够解析符号
        memory.setLibraryResolver(resolver);

    }

    public void run() {
        // 创建一个 Dalvik 虚拟机实例
        VM vm = emulator.createDalvikVM();
        // 启用虚拟机的调试输出
        vm.setVerbose(true);

        vm.setDvmClassFactory(new ProxyClassFactory());

        // 加载共享库 libunidbg.so 到 Dalvik 虚拟机中,并设置为需要自动初始化库
        DalvikModule module = vm.loadLibrary(new File("unidbg-android/src/test/resources/example_binaries/cyrus/libunidbg.so"), true);

        // 调用 JNI_Onload
        module.callJNI_OnLoad(emulator);

        // 注册 UnidbgActivity 类
        vm.resolveClass("com/cyrus/example/unidbg/UnidbgActivity");

        // 创建 DvmObject
        UnidbgActivity activity=new UnidbgActivity();
        DvmObject object = ProxyDvmObject.createObject(vm, activity);

        // 调用 Java 对象方法 sign 并传参 String
        DvmObject result = object.callJniMethodObject(emulator, "sign(Ljava/lang/String;)Ljava/lang/String;", "hello");

        System.out.println("sign result:" + result);
    }

}

运行输出如下:

[main]I/Unidbg: Shared library initialized (init).
[main]I/Unidbg: Shared library initialized (init_array1).
[main]I/Unidbg: Shared library initialized (init_array2).
JNIEnv->FindClass(com/cyrus/example/unidbg/UnidbgActivity) was called from RX@0x1202615c[libunidbg.so]0x2615c
JNIEnv->RegisterNatives(com/cyrus/example/unidbg/UnidbgActivity, unidbg@0xe4fff6d0, 1) was called from RX@0x120261a0[libunidbg.so]0x261a0
RegisterNative(com/cyrus/example/unidbg/UnidbgActivity, add(IIIIII)I, RX@0x12025f50[libunidbg.so]0x25f50)
Find native function Java_com_cyrus_example_unidbg_UnidbgActivity_sign => RX@0x1202633c[libunidbg.so]0x2633c
JNIEnv->GetStringUtfChars("hello") was called from RX@0x120262f4[libunidbg.so]0x262f4
JNIEnv->GetStaticFieldID(com/cyrus/example/unidbg/UnidbgActivity.aLjava/lang/String;) => 0x3914272a was called from RX@0x12026670[libunidbg.so]0x26670
JNIEnv->GetFieldID(com/cyrus/example/unidbg/UnidbgActivity.b Ljava/lang/String;) => 0x20b5fd89 was called from RX@0x120266b4[libunidbg.so]0x266b4
JNIEnv->GetStaticObjectField(class com/cyrus/example/unidbg/UnidbgActivity, a Ljava/lang/String; => "StaticA") was called from RX@0x120266f0[libunidbg.so]0x266f0
JNIEnv->GetObjectField(com.cyrus.example.unidbg.UnidbgActivity@5f71c76a, b Ljava/lang/String; => "NonStaticB") was called from RX@0x1202672c[libunidbg.so]0x2672c
JNIEnv->GetStringUtfChars("StaticA") was called from RX@0x120262f4[libunidbg.so]0x262f4
JNIEnv->GetStringUtfChars("NonStaticB") was called from RX@0x120262f4[libunidbg.so]0x262f4
JNIEnv->ReleaseStringUTFChars("hello") was called from RX@0x12026330[libunidbg.so]0x26330
JNIEnv->ReleaseStringUTFChars("StaticA") was called from RX@0x12026330[libunidbg.so]0x26330
JNIEnv->ReleaseStringUTFChars("NonStaticB") was called from RX@0x12026330[libunidbg.so]0x26330
JNIEnv->GetMethodID(com/cyrus/example/unidbg/UnidbgActivity.base64(Ljava/lang/String;)Ljava/lang/String;) => 0x526ba1f7 was called from RX@0x1202686c[libunidbg.so]0x2686c
JNIEnv->NewStringUTF("StaticAhelloNonStaticB") was called from RX@0x120268a0[libunidbg.so]0x268a0
JNIEnv->CallObjectMethodV(com.cyrus.example.unidbg.UnidbgActivity@5f71c76a, base64("StaticAhelloNonStaticB") => "U3RhdGljQWhlbGxvTm9uU3RhdGljQg==") was called from RX@0x12026990[libunidbg.so]0x26990
sign result:"U3RhdGljQWhlbGxvTm9uU3RhdGljQg=="

可以看到 sign 最后返回的结果和 app 是一样的。

原理是通过 ClassLoader.loadClass 加载类 并通过反射获取到字段或调用方法。这些在 ProxyClassFactory 中都已经封装好了。

word/media/image5.png

实现原理

比如调用 CallObjectMethodV 时

word/media/image6.png

那么 unidbg 就会走到 DalvikVM64 的 _CallObjectMethodV 的 handle 方法中

word/media/image7.png

找到目标方法的类和对象,通过 java 中反射相关 api 去实现 java 类中属性和方法的访问。

word/media/image8.png

完整源码

unidbg 完整源码:https://github.com/CYRUS-STUDIO/unidbg

android 示例完整源码:https://github.com/CYRUS-STUDIO/AndroidExample


http://www.kler.cn/a/570595.html

相关文章:

  • ffmpeg源码编译支持cuda
  • PyCharm 对接 DeepSeek 大模型的详细操作流程
  • ArcGIS操作:11 计算shp矢量面的质心坐标
  • 【JAVA】ThreadPoolTaskExecutor 线程池学习、后端异步、高并发处理
  • ArcGIS操作:08 计算shp面积并添加到属性表
  • 解释 CSS 盒模型的概念以及如何使用 box-sizing 属性
  • 厦大团队|报告:《读懂大模型概念、技术与应用实践》140 页 PPT(文末附链接下载)
  • 知识图谱的推荐实现方案(Vue)
  • 泛型边界的使用
  • Linux 基础---文件权限
  • 费曼学习法12 - 告别 Excel!用 Python Pandas 开启数据分析高效之路 (Pandas 入门篇)
  • 【iOS】关于自动引用计数的认识
  • Win10环境借助DockerDesktop部署单节点Redis6
  • 初识编辑框和按钮
  • 基于深度学习的网络摄像头图像实时分类实践:从理论到完整实现
  • 体育数据分析:竞技表现优化与商业价值挖掘的技术范式
  • SpringBoot系列之Spring AI+DeekSeek创建AI应用
  • docker常规命令和高级用法
  • nextjs+material UI实现换肤功能
  • 2继续NTS库学习(读取shapefile)