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 文件。
逆向分析 apk
把 apk 拖入 JEB , 经过反编译后可以找到 调用了 目标 jni 方法 sign
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 行为逻辑,流程大概如下:
-
extends AbstractJni
-
vm.setJni(this);
-
重写 getStaticObjectField / getObjectField / callObjectMethodV,自定义执行逻辑
1. 自定义静态字段访问逻辑
从反编译的代码中可以找到静态变量 a 的值
重写 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 也复制过来
设置 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 中都已经封装好了。
实现原理
比如调用 CallObjectMethodV 时
那么 unidbg 就会走到 DalvikVM64 的 _CallObjectMethodV 的 handle 方法中
找到目标方法的类和对象,通过 java 中反射相关 api 去实现 java 类中属性和方法的访问。
完整源码
unidbg 完整源码:https://github.com/CYRUS-STUDIO/unidbg
android 示例完整源码:https://github.com/CYRUS-STUDIO/AndroidExample