unidbg 实现 JNI 与 Java 交互

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

Android 示例代码

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

代码如下:

kotlin 复制代码
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 源码如下:

ini 复制代码
#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 文件。

逆向分析 apk

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

JEB 下载地址:bbs.kanxue.com/thread-2821...

模拟目标 JNI 方法

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

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

java 复制代码
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);
    }

}

运行输出如下:

less 复制代码
[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 的值

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

typescript 复制代码
@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 方法类似的处理,处理逻辑也类似。

typescript 复制代码
@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 方法

typescript 复制代码
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);
}

完整源码

java 复制代码
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);
    }

}

运行输出如下:

less 复制代码
[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 类复制过来,做一下简单的修改,去掉不需要的代码,只留下目标方法相关的代码(注意:类路径一定要保持一致!!)

arduino 复制代码
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 也复制过来

设置 ProxyClassFactory

arduino 复制代码
vm.setDvmClassFactory(new ProxyClassFactory());

创建 DvmObject 并调用目标方法

ini 复制代码
// 注册 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);

完整代码如下:

java 复制代码
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);
    }

}

运行输出如下:

less 复制代码
[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 中都已经封装好了。

实现原理

比如调用 CallObjectMethodV 时

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

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

完整源码

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

android 示例完整源码:github.com/CYRUS-STUDI...

相关推荐
后端码匠4 小时前
MySQL 8.0安装(压缩包方式)
android·mysql·adb
梓仁沐白5 小时前
Android清单文件
android
海尔辛6 小时前
学习黑客5 分钟读懂Linux Permissions 101
linux·学习·安全
zizisuo6 小时前
面试篇:Spring Security
网络·数据库·安全
玉笥寻珍6 小时前
Web安全渗透测试基础知识之HTTP参数污染篇
网络·网络协议·安全·web安全·http
董可伦7 小时前
Dinky 安装部署并配置提交 Flink Yarn 任务
android·adb·flink
玉笥寻珍8 小时前
Web安全渗测试基础知识之SSL交互异常利用篇
网络协议·安全·web安全·网络安全·交互·ssl
每次的天空8 小时前
Android学习总结之Glide自定义三级缓存(面试篇)
android·学习·glide
恋猫de小郭8 小时前
如何查看项目是否支持最新 Android 16K Page Size 一文汇总
android·开发语言·javascript·kotlin
7yewh8 小时前
MCU程序加密保护(二)ID 验证法 加密与解密
单片机·嵌入式硬件·安全