【Android安全-《安卓逆向这档事》第二十三课、黑盒魔法之Unidbg】此文章归类为:Android安全。

1.了解unicorn与unidbg
2.源码学习unidbg的常用api
3.了解unidbg_hook
4.了解unidbg_patch
1.教程Demo
2.IDEA
3.IDA

开源地址
Unicorn 是一个由新加坡南洋理工大学团队在2015年开源的CPU模拟器框架,它支持多种架构,包括X86/X64/ARM/ARM64/MIPS等。Unicorn 的主要特点是:
开源地址
Unidbg(Unicorn Debugger)是一个开源的轻量级模拟器,主要设计用于模拟执行Android平台上的Native代码。它由凯神在2019年开源,基于Maven构建,使用Java语言编写,可以在IDE中打开和运行。Unidbg能够模拟Android Native函数的执行,让逆向工程师和安全研究人员能够分析和理解二进制文件的运行行为。它支持模拟系统调用和JNI调用,使得可以在模拟环境中执行依赖这些调用的代码。Unidbg基于Unicorn项目,Unidbg的优势在于它提供了一种隐蔽的监控手段,可以模拟复杂的Native环境,帮助用户进行深入的动态分析。由于其开源特性,Unidbg得到了社区的广泛支持和持续更新,成为了Android Native逆向分析领域中一个强有力的工具。
竞争者: AndroidNativeEmu 和继任者 ExAndroidNativeEmu (Unidbg优点:模拟实现了更多的系统调用和 JNI)
1.下载idea
社区版下载链接
2.下载源代码,并用idea打开,并配置好sdk
3.文件结构解析:
23 和 19 分别对应于 sdk23(Android 6.0) 和 sdk19(Android 4.4)的运行库环境,处理 64 位 SO 时只能选择 SDK23。

基本类型直接传递,int、long、boolean、double 等。
对于其他数据类型需要借助resolveClass构造,例如Context
符号调用
偏移调用
1.获取 HookZz 实例:
2.wrap_hook函数:
wrap函数有两个重载,一个基于符号寻址,一个基于地址寻址,本质没区别,符号寻址的最终也是会调用symbol.getAddress()
参数里的WrapCallback的泛型接口有三个RegisterContext(函数 Hook)、HookZzArm32RegisterContext(针对ARM32位)和HookZzArm64RegisterContext(针对ARM64位)因为可以访问某个寄存器的值,所以适用于inline hook
而在HookZzArm64RegisterContext中则是通过以下的方法去获取对应的寄存器的值
3.instrument_inline_hook函数
4.replace替换函数*
Console Debugger(控制台调试器)是 Unidbg 提供的一个强大工具,允许用户在模拟执行过程中设置断点、单步调试、查看和修改内存及寄存器等操作,从而深入分析目标程序的行为。
替换返回值
Patch 就是直接对二进制文件进行修改,Patch本质上只有两种形式
百度云
阿里云
哔哩哔哩
教程开源地址
PS:解压密码都是52pj,阿里云由于不能分享压缩包,所以下载exe文件,双击自解压
白龙unidbg教程
凯神博客
app逆向
【转载】Unidbg Hook 大全
| 特性 | 描述 |
|---|---|
| 使用场景 | |
| 模拟执行 | 执行目标 SO 文件中用户关注的函数,获取与真机等价的结果;替代 Frida/Xposed Call 进行 RPC 调用。 |
| 监控观察 | 观察样本对环境的信息获取与修改;监控所有类型的外部信息访问,包括系统调用、库函数、JNI 调用、文件读写等。 |
| 辅助算法分析和还原 | 提供 Hook/Debug/Trace 等分析能力,结合时间旅行调试器(Time-Travel Debugging),无疑是Android Native 上强大的分析神器。 |
| 优点 | |
| 低成本 | 减少设备成本和改机成本,无需购置和维护大量真机或租借云手机。 |
| 灵活性 | 可以模拟或代理所有函数调用接口,方便模拟设备环境变化。 |
| 监控能力 | 能够监控 Native 层的详细执行流,包括 JNI 调用和文件访问。 |
| 分析能力 | 结合时间旅行调试器,提供强大的算法分析和还原能力。 |
| 缺点 | |
| 学习成本高 | 尤其是环境补全(补环境)部分,如果补得不好,即使跑出结果也无法使用。 |
| 执行速度慢 | 基于 Unicorn 的模拟执行速度相比真机慢很多,尽管有 Dynarmic 等方案可以提高速度,但牺牲了部分辅助算法还原的能力。 |
| 功能限制 | 没有为特定场景做专门的优化,也没有提供配置管理功能;没有实现对所有系统调用的良好模拟,可能导致某些逻辑处理失败。 |
| 扩展性差 | 作为一个 Java 项目,Unidbg 无法作为 IDA 或 Ghidra 插件,也难以轻松嵌入到其他项目中,不如 Python 项目灵活。 |
├── README.md # 项目介绍和使用指南
├── LICENSE # 开源许可证文件
├── .gitignore # Git 忽略文件配置
├── pom.xml # Maven 配置文件,定义了项目的依赖和构建配置
├── mvnw # 脚本文件,用于 Maven Wrapper (Linux/Mac)
├── mvnw.cmd # 脚本文件,用于 Maven Wrapper (Windows)
├── test.sh # 测试脚本 (Linux/Mac)
├── test.cmd # 测试脚本 (Windows)
├── .mvn/ # Maven 配置目录
│ └── wrapper/ # Maven Wrapper 相关配置
├── assets/ # 存放模拟过程中使用的资源文件
│ ├── *.dll # Windows 动态链接库文件
│ └── *.so # Linux/Android 动态链接库文件
├── backend/ # 后端逻辑实现,包含核心模拟功能
├── unidbg-api/ # 核心接口和抽象类模块
│ └── src/ # API 模块的源代码目录
├── unidbg-ios/ # iOS 应用模拟模块
│ └── src/ # iOS 模拟模块的源代码目录
├── unidbg-android/ # Android 应用模拟模块
│ ├── pom.xml # Maven 构建文件
│ ├── pull.sh # 拉取 Android 模拟所需依赖文件的脚本
│ └── src/ # unidbg-android 模块的源代码目录
│ ├── main/
│ │ ├── java/ # 核心 Java 源代码
│ │ │ └── com/github/unidbg/ # 包含核心模拟器、文件系统、虚拟机组件
│ │ │ └── net/fornwall/jelf # ELF 文件格式解析实现
│ │ └── resources/ # 资源文件,封装了 JNI 库、Android 系统库等
│ ├── test/
│ │ ├── java/ # 单元测试代码
│ │ ├── native/android/ # 测试 Android 原生库的 C/C++ 源代码
│ │ └── resources/ # 测试资源文件,包含预编译的二进制文件(log4j.properties这个是日志相关配置,可以对open,syscall这类的系统调用进行trace)
└── .mvn/ # Maven Wrapper 相关配置目录
├── README.md # 项目介绍和使用指南
├── LICENSE # 开源许可证文件
├── .gitignore # Git 忽略文件配置
├── pom.xml # Maven 配置文件,定义了项目的依赖和构建配置
├── mvnw # 脚本文件,用于 Maven Wrapper (Linux/Mac)
├── mvnw.cmd # 脚本文件,用于 Maven Wrapper (Windows)
├── test.sh # 测试脚本 (Linux/Mac)
├── test.cmd # 测试脚本 (Windows)
├── .mvn/ # Maven 配置目录
│ └── wrapper/ # Maven Wrapper 相关配置
├── assets/ # 存放模拟过程中使用的资源文件
│ ├── *.dll # Windows 动态链接库文件
│ └── *.so # Linux/Android 动态链接库文件
├── backend/ # 后端逻辑实现,包含核心模拟功能
├── unidbg-api/ # 核心接口和抽象类模块
│ └── src/ # API 模块的源代码目录
├── unidbg-ios/ # iOS 应用模拟模块
│ └── src/ # iOS 模拟模块的源代码目录
├── unidbg-android/ # Android 应用模拟模块
│ ├── pom.xml # Maven 构建文件
│ ├── pull.sh # 拉取 Android 模拟所需依赖文件的脚本
│ └── src/ # unidbg-android 模块的源代码目录
│ ├── main/
│ │ ├── java/ # 核心 Java 源代码
│ │ │ └── com/github/unidbg/ # 包含核心模拟器、文件系统、虚拟机组件
│ │ │ └── net/fornwall/jelf # ELF 文件格式解析实现
│ │ └── resources/ # 资源文件,封装了 JNI 库、Android 系统库等
│ ├── test/
│ │ ├── java/ # 单元测试代码
│ │ ├── native/android/ # 测试 Android 原生库的 C/C++ 源代码
│ │ └── resources/ # 测试资源文件,包含预编译的二进制文件(log4j.properties这个是日志相关配置,可以对open,syscall这类的系统调用进行trace)
└── .mvn/ # Maven Wrapper 相关配置目录
log4j.logger.com.github.unidbg.linux.file=DEBUG //把INFO改成DEBUG
log4j.logger.com.github.unidbg.linux.file=DEBUG //把INFO改成DEBUG
package com.kanxue.test2;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.arm.backend.DynarmicFactory;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.DalvikModule;
import com.github.unidbg.linux.android.dvm.DvmObject;
import com.github.unidbg.linux.android.dvm.ProxyDvmObject;
import com.github.unidbg.linux.android.dvm.VM;
import com.github.unidbg.memory.Memory;
import java.io.File;
public class MainActivity {
private final AndroidEmulator emulator; // 定义Android模拟器实例
private final VM vm; // 定义Dalvik虚拟机实例
public MainActivity() {
// 创建32位Android模拟器实例,使用Dynarmic后端
emulator = AndroidEmulatorBuilder.for32Bit()
.addBackendFactory(new DynarmicFactory(true))
.build();
// 获取模拟器的内存管理接口
Memory memory = emulator.getMemory();
// 设置系统类库
LibraryResolver resolver = new AndroidResolver(23);
memory.setLibraryResolver(resolver);
// 创建Dalvik虚拟机实例
vm = emulator.createDalvikVM();
// 设置是否输出详细的JNI调用日志
vm.setVerbose(false);
// 加载指定路径的SO库文件,不自动调用JNI_OnLoad函数
DalvikModule dm = vm.loadLibrary(new File("unidbg-android/src/test/resources/example_binaries/armeabi-v7a/libnative-lib.so"), false);
// 手动调用JNI_OnLoad方法
dm.callJNI_OnLoad(emulator);
}
public void crack() {
// 创建一个vm对象,模拟Java层的对象传递给JNI层
DvmObject<?> obj = ProxyDvmObject.createObject(vm, this);
// 记录开始时间
long start = System.currentTimeMillis();
// 遍历所有可能的三字符组合,尝试破解
for (char a : LETTERS) {
for (char b : LETTERS) {
for (char c : LETTERS) {
String str = "" + a + b + c;
// 调用JNI方法,传入当前组合,判断是否成功
boolean success = obj.callJniMethodBoolean(emulator, "jnitest(Ljava/lang/String;)Z", str);
if (success) {
// 如果成功,输出结果并结束
System.out.println("Found: " + str + ", off=" + (System.currentTimeMillis() - start) + "ms");
return;
}
}
}
}
}
private static final char[] LETTERS = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' }; // 定义字母表
public static void main(String[] args) {
//记录开始时间
long start = System.currentTimeMillis();
// 创建MainActivity实例
MainActivity main = new MainActivity();
//输出调用结果
System.out.println("load offset=" + (System.currentTimeMillis() - start) + "ms");
// 调用破解方法
main.crack();
}
}package com.kanxue.test2;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.arm.backend.DynarmicFactory;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.DalvikModule;
import com.github.unidbg.linux.android.dvm.DvmObject;
import com.github.unidbg.linux.android.dvm.ProxyDvmObject;
import com.github.unidbg.linux.android.dvm.VM;
import com.github.unidbg.memory.Memory;
import java.io.File;
public class MainActivity {
private final AndroidEmulator emulator; // 定义Android模拟器实例
private final VM vm; // 定义Dalvik虚拟机实例
public MainActivity() {
// 创建32位Android模拟器实例,使用Dynarmic后端
emulator = AndroidEmulatorBuilder.for32Bit()
.addBackendFactory(new DynarmicFactory(true))
.build();
// 获取模拟器的内存管理接口
Memory memory = emulator.getMemory();
// 设置系统类库
LibraryResolver resolver = new AndroidResolver(23);
memory.setLibraryResolver(resolver);
// 创建Dalvik虚拟机实例
vm = emulator.createDalvikVM();
// 设置是否输出详细的JNI调用日志
vm.setVerbose(false);
// 加载指定路径的SO库文件,不自动调用JNI_OnLoad函数
DalvikModule dm = vm.loadLibrary(new File("unidbg-android/src/test/resources/example_binaries/armeabi-v7a/libnative-lib.so"), false);
// 手动调用JNI_OnLoad方法
dm.callJNI_OnLoad(emulator);
}
public void crack() {
// 创建一个vm对象,模拟Java层的对象传递给JNI层
DvmObject<?> obj = ProxyDvmObject.createObject(vm, this);
// 记录开始时间
long start = System.currentTimeMillis();
// 遍历所有可能的三字符组合,尝试破解
for (char a : LETTERS) {
for (char b : LETTERS) {
for (char c : LETTERS) {
String str = "" + a + b + c;
// 调用JNI方法,传入当前组合,判断是否成功
boolean success = obj.callJniMethodBoolean(emulator, "jnitest(Ljava/lang/String;)Z", str);
if (success) {
// 如果成功,输出结果并结束
System.out.println("Found: " + str + ", off=" + (System.currentTimeMillis() - start) + "ms");
return;
}
}
}
}
}
private static final char[] LETTERS = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' }; // 定义字母表
public static void main(String[] args) {
//记录开始时间
long start = System.currentTimeMillis();
// 创建MainActivity实例
MainActivity main = new MainActivity();
//输出调用结果
System.out.println("load offset=" + (System.currentTimeMillis() - start) + "ms");
// 调用破解方法
main.crack();
}
}emulator = AndroidEmulatorBuilder.for32Bit() .addBackendFactory(new DynarmicFactory(true))
.build();
emulator = AndroidEmulatorBuilder.for32Bit() .addBackendFactory(new DynarmicFactory(true))
.build();
.setRootDir() //设置虚拟机的根目录,可以实现io重定向,例如:app读取/data/data/123.txt,rootDir设置为E:/unidbg,那么真正的目录是E:/unidbg/data/data/123.txt,
.setRootDir() //设置虚拟机的根目录,可以实现io重定向,例如:app读取/data/data/123.txt,rootDir设置为E:/unidbg,那么真正的目录是E:/unidbg/data/data/123.txt,
| 方法名 | 返回类型 | 描述 |
|---|---|---|
getMemory() |
Memory |
获取内存操作接口。 |
getPid() |
int |
获取进程的 PID。 |
createDalvikVM() |
VM |
创建虚拟机。 |
createDalvikVM(File apkFile) |
VM |
创建虚拟机并指定 APK 文件路径。 |
getDalvikVM() |
VM |
获取已创建的虚拟机。 |
showRegs() |
void |
显示当前寄存器状态,可指定寄存器。 |
getBackend() |
Backend |
获取后端 CPU。 |
getProcessName() |
String |
获取进程名。 |
getContext() |
RegisterContext |
获取寄存器上下文。 |
traceRead(long begin, long end) |
void |
Trace 读内存操作。 |
traceWrite(long begin, long end) |
void |
Trace 写内存操作。 |
traceCode(long begin, long end) |
void |
Trace 汇编指令执行。 |
isRunning() |
boolean |
判断当前 Emulator 是否正在运行。 |
LibraryResolver resolver = new AndroidResolver(23);
memory.setLibraryResolver(resolver);LibraryResolver resolver = new AndroidResolver(23);
memory.setLibraryResolver(resolver);| 方法名 | 返回类型 | 描述 |
|---|---|---|
setLibraryResolver(AndroidResolver resolver) |
void |
设置 Android SDK 版本解析器,目前支持 19 和 23 两个版本。 |
getStackPoint() |
long |
获取当前栈指针的值。 |
pointer(long address) |
UnidbgPointer |
获取指针,指向指定内存地址,可通过指针操作内存。 |
getMemoryMap() |
Collection<MemoryMap> |
获取当前内存的映射情况。 |
findModule(String moduleName) |
Module |
根据模块名获取指定模块。 |
findModuleByAddress(long address) |
Module |
根据地址获取指定模块。 |
loadLibrary(File file, boolean forceLoad) |
ElfModule |
加载 SO 文件,会调用 Linker.do_dlopen() 方法完成加载。 |
allocatestack(int size) |
UnidbgPointer |
在栈上分配指定大小的内存空间。 |
writestackstring(String value) |
UnidbgPointer |
将字符串写入栈内存中。 |
writestackBytes(byte[] value) |
UnidbgPointer |
将字节数组写入栈内存中。 |
malloc(int size, boolean runtime) |
UnidbgPointer |
分配指定大小的内存空间,返回指向该内存的指针。 |
vm常用Api |
// 创建Dalvik虚拟机实例vm = emulator.createDalvikVM();// 设置是否输出详细的JNI调用日志vm.setVerbose(false);
// 加载指定路径的SO库文件,不自动调用JNI_OnLoad函数DalvikModule dm = vm.loadLibrary(new File("unidbg-android/src/test/resources/example_binaries/armeabi-v7a/libnative-lib.so"), false);
// 手动调用JNI_OnLoad方法dm.callJNI_OnLoad(emulator);// 创建Dalvik虚拟机实例vm = emulator.createDalvikVM();// 设置是否输出详细的JNI调用日志vm.setVerbose(false);
// 加载指定路径的SO库文件,不自动调用JNI_OnLoad函数DalvikModule dm = vm.loadLibrary(new File("unidbg-android/src/test/resources/example_binaries/armeabi-v7a/libnative-lib.so"), false);
// 手动调用JNI_OnLoad方法dm.callJNI_OnLoad(emulator);| 方法名 | 返回类型 | 描述 |
|---|---|---|
createDalvikVM(File apkFile) |
VM |
创建虚拟机,指定 APK 文件,file可为空 |
setVerbose(boolean verbose) |
void |
设置是否输出 JNI 运行日志。 |
loadLibrary(File soFile, boolean callInit) |
DalvikModule |
加载 SO 模块,参数二设置是否自动调用 init 函数。 |
setJni(Jni jni) |
void |
设置 JNI 交互接口,推荐实现 AbstractJni。 |
getJNIEnv() |
Pointer |
获取 JNIEnv 指针,可作为参数传递。 |
getJavaVM() |
Pointer |
获取 JavaVM 指针,可作为参数传递。 |
callJNI_OnLoad(Emulator<?> emulator, Module module) |
void |
调用 JNI_OnLoad 函数。 |
addGlobalObject(DvmObject<?> obj) |
int |
向 VM 添加全局对象,返回该对象的 hash 值。 |
addLocalObject(DvmObject<?> obj) |
int |
向 VM 添加局部对象,返回该对象的 hash 值。 |
getObject(int hash) |
DvmObject<?> |
根据 hash 值获取虚拟机中的对象。 |
resolveClass(String className) |
DvmClass |
解析指定类名,构建并返回一个 DvmClass 对象。 |
getPackageName() |
String |
获取 APK 包名。 |
getVersionName() |
String |
获取 APK 版本名称。 |
getVersionCode() |
String |
获取 APK 版本号。 |
openAsset(String assetName) |
InputStream |
打开 APK 中的指定资源文件。 |
getManifestXml() |
String |
获取 AndroidManifest.xml 文件的文本内容。 |
getSignatures() |
CertificateMeta[] |
获取 APK 签名信息。 |
findClass(String className) |
DvmClass |
通过类名获取已经加载的类(DvmClass 对象)。 |
getEmulator() |
Emulator<?> |
获取模拟器对象 emulator。 |
/** * 加载指定名称的库文件。
* @param libname 库文件的名称,不包括前缀 "lib" 和后缀 ".so"(例如 "example" 对应 "libexample.so")。
* @param forceCallInit 是否强制调用库的初始化函数(如 JNI_OnLoad)。
* @return 加载后的 DalvikModule 对象,封装了加载的库模块。
*/
DalvikModule loadLibrary(String libname, boolean forceCallInit);
/** * 从原始字节数组中加载指定的库文件。
* @param libname 库文件的名称,仅用于标识该库,与文件路径无关。
* @param 传入buffer方便解析elf
*/
DalvikModule loadLibrary(String libname, byte[] raw, boolean forceCallInit);
/** * 从指定路径的加载ELF。
* @param elfFile 表示库的 ELF 文件,必须是有效的 ELF 格式文件。例如:new File("unidbg-android/src/test/resources/example_binaries/armeabi-v7a/libnative-lib.so")
* @param forceCallInit 是否强制调用库的初始化函数(如 JNI_OnLoad)。
*/
DalvikModule loadLibrary(File elfFile, boolean forceCallInit);
/** * 加载指定名称的库文件。
* @param libname 库文件的名称,不包括前缀 "lib" 和后缀 ".so"(例如 "example" 对应 "libexample.so")。
* @param forceCallInit 是否强制调用库的初始化函数(如 JNI_OnLoad)。
* @return 加载后的 DalvikModule 对象,封装了加载的库模块。
*/
DalvikModule loadLibrary(String libname, boolean forceCallInit);
/** * 从原始字节数组中加载指定的库文件。
* @param libname 库文件的名称,仅用于标识该库,与文件路径无关。
* @param 传入buffer方便解析elf
*/
DalvikModule loadLibrary(String libname, byte[] raw, boolean forceCallInit);
/** * 从指定路径的加载ELF。
* @param elfFile 表示库的 ELF 文件,必须是有效的 ELF 格式文件。例如:new File("unidbg-android/src/test/resources/example_binaries/armeabi-v7a/libnative-lib.so")
* @param forceCallInit 是否强制调用库的初始化函数(如 JNI_OnLoad)。
*/
DalvikModule loadLibrary(File elfFile, boolean forceCallInit);
//第一个参数传入模拟器实例//第二个参数传入要调用的函数在java层方法签名信息//第三个参数传入可变参数列表,这里的参数在旧版本有很多需要自己封装,新版则帮我们封装好了,详细看callJniMethod方法boolean success = obj.callJniMethodBoolean(emulator, "jnitest(Ljava/lang/String;)Z", str);
/** * 调用 JNI 方法的辅助函数。
*
* @param emulator 模拟器实例。
* @param vm Dalvik 虚拟机实例。
* @param objectType 调用方法的类(DvmClass)。
* @param thisObj 方法调用的对象实例(DvmObject)。
* @param method 要调用的方法签名(例如 "methodName(参数类型)返回类型")。
* @param args 方法的可变参数列表。
* @return 方法执行后的返回值(Number 类型),可能是整数或浮点数。
*/
protected static Number callJniMethod(Emulator<?> emulator, VM vm, DvmClass objectType, DvmObject<?> thisObj, String method, Object... args) {
// 查找对应的本地函数指针(Native 方法)
UnidbgPointer fnPtr = objectType.findNativeFunction(emulator, method);
// 将当前对象添加到本地引用表,防止被垃圾回收
vm.addLocalObject(thisObj);
// 创建用于存放函数参数的列表,初始容量为10
List<Object> list = new ArrayList<>(10);
// 添加 JNI 环境指针(JNIEnv*)
list.add(vm.getJNIEnv());
// 添加 this 对象的引用(jobject)
list.add(thisObj.hashCode());
// 处理传入的参数列表
if (args != null) {
for (Object arg : args) {
if (arg instanceof Boolean) {
// 如果参数是布尔值,转换为 JNI_TRUE 或 JNI_FALSE
list.add((Boolean) arg ? VM.JNI_TRUE : VM.JNI_FALSE);
continue;
} else if (arg instanceof Hashable) {
// 如果参数实现了 Hashable 接口,表示是 DvmObject 或其子类
list.add(arg.hashCode()); // 添加对象引用(jobject)
if (arg instanceof DvmObject) {
// 将 DvmObject 对象添加到本地引用表
vm.addLocalObject((DvmObject<?>) arg);
}
continue;
} else if (arg instanceof DvmAwareObject ||
arg instanceof String ||
arg instanceof byte[] ||
arg instanceof short[] ||
arg instanceof int[] ||
arg instanceof float[] ||
arg instanceof double[] ||
arg instanceof Enum) {
// 如果参数是 DvmAwareObject、字符串、数组或枚举等类型
// 创建一个代理 DvmObject 对象
DvmObject<?> obj = ProxyDvmObject.createObject(vm, arg);
// 添加对象引用(jobject)
list.add(obj.hashCode());
// 将对象添加到本地引用表
vm.addLocalObject(obj);
continue;
}
// 对于其他类型的参数,直接添加到参数列表
list.add(arg);
}
}
// 调用本地函数,传入参数列表,并返回结果
return Module.emulateFunction(emulator, fnPtr.peer, list.toArray());
}//第一个参数传入模拟器实例//第二个参数传入要调用的函数在java层方法签名信息//第三个参数传入可变参数列表,这里的参数在旧版本有很多需要自己封装,新版则帮我们封装好了,详细看callJniMethod方法boolean success = obj.callJniMethodBoolean(emulator, "jnitest(Ljava/lang/String;)Z", str);
/** * 调用 JNI 方法的辅助函数。
*
* @param emulator 模拟器实例。
* @param vm Dalvik 虚拟机实例。
* @param objectType 调用方法的类(DvmClass)。
* @param thisObj 方法调用的对象实例(DvmObject)。
* @param method 要调用的方法签名(例如 "methodName(参数类型)返回类型")。
* @param args 方法的可变参数列表。
* @return 方法执行后的返回值(Number 类型),可能是整数或浮点数。
*/
protected static Number callJniMethod(Emulator<?> emulator, VM vm, DvmClass objectType, DvmObject<?> thisObj, String method, Object... args) {
// 查找对应的本地函数指针(Native 方法)
UnidbgPointer fnPtr = objectType.findNativeFunction(emulator, method);
// 将当前对象添加到本地引用表,防止被垃圾回收
vm.addLocalObject(thisObj);
// 创建用于存放函数参数的列表,初始容量为10
List<Object> list = new ArrayList<>(10);
// 添加 JNI 环境指针(JNIEnv*)
list.add(vm.getJNIEnv());
// 添加 this 对象的引用(jobject)
list.add(thisObj.hashCode());
// 处理传入的参数列表
if (args != null) {
for (Object arg : args) {
if (arg instanceof Boolean) {
// 如果参数是布尔值,转换为 JNI_TRUE 或 JNI_FALSE
list.add((Boolean) arg ? VM.JNI_TRUE : VM.JNI_FALSE);
更多【Android安全-《安卓逆向这档事》第二十三课、黑盒魔法之Unidbg】相关视频教程:www.yxfzedu.com