免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动!

内容参考于:图灵Python学院

工具下载:

链接:https://pan.baidu.com/s/1bb8NhJc9eTuLzQr39lF55Q?pwd=zy89

提取码:zy89

复制这段内容后打开百度网盘手机App,操作更方便哦

上一个内容:42.安卓逆向2-补环境-unidbg安装和简单使用

开始之前先说明一下,unidbg是一个系统虚拟环境,它并不会去找某个app,它只是模拟一个运行环境,unidbg用到app的类、包名全部是假的(并不是我们使用反编译工具看到的那样,unidbg它只是通过类名、方法去构建一个假的类,然后去调用so文件),unidbg它只是模拟的安卓系统(模型安卓系统标准库函数),如果so文件或java代码依赖了开发者自定义的逻辑,unidbg是没办法模拟的,需要我们自己手动处理

然后先设置一下ida的目录显示,如下图把压缩空的中间软件包取消勾选

首先创建一个目录(或者叫包,包和目录是一个东西),如下图红框的目录,在com路面选择新加软件包

目录名mmmm,输入完按回车

按完回车目录就创建好了,如下图红框

然后再创建一个java类(这里又创建了一个叫dac的目录,在它里面创建java类)

如下图java类名,输入完按回车

然后就创建好了

然后开始写unidbg的代码,首先是初始结构和模拟器

package com.mmmm.dac;// 导入Unidbg框架的核心类
// AndroidEmulator:Android模拟器的核心类,用于模拟Android运行环境
import com.github.unidbg.AndroidEmulator;
// Module:用于操作加载的SO文件(动态链接库)
import com.github.unidbg.Module;
// AndroidEmulatorBuilder:模拟器构建器,用于创建不同配置的模拟器
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
// AndroidResolver:用于解析Android系统库,模拟系统库调用
import com.github.unidbg.linux.android.AndroidResolver;
// AbstractJni:JNI抽象类,用于处理SO中的JNI调用(如Java方法调用)
import com.github.unidbg.linux.android.dvm.AbstractJni;
// DalvikModule:Dalvik虚拟机中的模块类,用于加载和处理SO文件
import com.github.unidbg.linux.android.dvm.DalvikModule;
// VM:Dalvik虚拟机类,模拟Android的Java虚拟机环境
import com.github.unidbg.linux.android.dvm.VM;
// Memory:内存操作接口,用于管理模拟器的内存
import com.github.unidbg.memory.Memory;// 用于文件操作的Java标准类
import java.io.File;/*** DcTest类:继承自AbstractJni,用于测试和分析目标SO文件* 作用:模拟Android环境,加载指定的SO文件,并获取其中的函数地址等信息*/
public class DcTest extends AbstractJni {// 成员变量声明// 模拟器实例:整个模拟环境的核心private final AndroidEmulator emulator;// 虚拟机实例:模拟Android的Java虚拟机private final VM vm;// 模块实例:代表加载的SO文件,用于操作其中的函数和符号private final Module module;/*** 构造方法:初始化模拟器、虚拟机和加载SO文件* 当创建DcTest对象时,会自动执行这些初始化操作*/public DcTest(){// 1. 创建Android模拟器实例// for64Bit():指定创建64位模拟器(如果目标SO是32位,需改为for32Bit())// setProcessName():设置模拟的进程名,通常设为目标SO所在的APP包名// build():完成模拟器构建emulator = AndroidEmulatorBuilder.for64Bit().setProcessName("com.xxx.news").build();// 2. 获取模拟器的内存操作接口// 内存接口用于管理模拟器的内存分配、库解析等final Memory memory = emulator.getMemory();// 3. 设置系统库解析器// AndroidResolver(23):指定模拟的Android系统版本为API 23(Android 6.0)// 作用:当SO调用系统库(如libc.so)时,模拟器能正确解析并模拟这些调用memory.setLibraryResolver(new AndroidResolver(23));// 4. 创建Dalvik虚拟机(Android的Java虚拟机)vm = emulator.createDalvikVM();// 5. 设置JNI处理器// 将当前类(DcTest)作为JNI调用的处理器// 当SO中调用JNI函数(如调用Java方法)时,会由当前类处理vm.setJni(this);// 6. 加载目标SO文件// 第一个参数:SO文件的路径(这里是相对路径,实际使用时需确保文件存在)// 第二个参数:false表示不自动调用JNI_OnLoad(后续会手动调用)// DalvikModule:用于在Dalvik虚拟机中管理SO文件DalvikModule dm = vm.loadLibrary(new File("utils/dc1127/libwtf.so"), false);// 7. 手动调用SO中的JNI_OnLoad函数// JNI_OnLoad是SO被加载时的初始化函数,通常用于注册JNI方法// 动态注册的JNI方法需要调用此函数才会生效,静态注册可以省略dm.callJNI_OnLoad(emulator);// 8. 获取Module实例// Module是操作SO文件的主要接口,通过它可以查找函数、获取基地址等module = dm.getModule();// 9. 打印SO文件的基地址// 基地址是SO加载到内存中的起始地址,函数的实际地址=基地址+偏移量System.out.println("SO文件基地址:" + module.base);// 10. 查找并打印指定函数的地址(C++函数,经过名称修饰)// _ZN3MD56updateEPKhj:是C++函数MD5::update(const unsigned char*, unsigned int)的名称修饰后的结果// findSymbolByName:通过函数名查找符号// getAddress():获取函数在内存中的地址int address = (int)module.findSymbolByName("_ZN3MD56updateEPKhj").getAddress();System.out.println("MD5::update函数地址(十六进制):" + Long.toHexString(address));// 11. 查找并打印Java native方法对应的C函数地址// Java_cn_thecover_lib_common_manager_SignManager_getSign:// 是Java类cn.thecover.lib.common.manager.SignManager中的getSign()本地方法对应的C函数名// 这是JNI静态注册的命名规则:Java_包名_类名_方法名int funaddr = (int)module.findSymbolByName("Java_cn_thecover_lib_common_manager_SignManager_getSign").getAddress();System.out.println("getSign函数地址(十六进制):" + Long.toHexString(funaddr));}
}

然后为了方便引入so文件,这里创建一个目录专门用来存放so文件,如下图鼠标右击根目录,选择新建目录

然后输入utils,然后按回车

然后在utils目录再创建一个目录,这个目录是为了区分不同app的so文件(比如两个app一个叫测试1一个叫测试2,然后创建一个测1目录,然后测试1里用到的so文件全部都放到测1目录里,然后创建一个测2目录,用来存放测试2app中使用的so文件)

如下图的目录和文件

如下图代码中的使用

如下图上图红框的so文件中存在getSign方法

然后可以得到一个类路径,如下图红框

然后使用unidbg获取这个SignManager类(可以理解为让unidbg使用cn/thecover/lib/common/manager/SignManager这个类路径创建一个虚拟的类),这里用的smail语法

vm.resolveClass("cn/thecover/lib/common/manager/SignManager");

获取方法名,首先要把下图红框的代码转成smail代码

如下图利用大模型转

# 声明一个公共静态 native 方法
.method public static native getSign(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
.end method

然后复制下图红框的代码

然后unidbg代码

//要调用的方法
String method = "getSign(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;";// 调用方法时传的参数
String 参数1 = "1";
String 参数2 = "2";
String 参数3 = "3";//调用方法和获取返回值
StringObject value = dvmClass.callStaticJniMethodObject(emulator, method, 参数1, 参数2, 参数3);

调用方法的完整代码

/*** 调用目标Java类的静态native方法getSign,并返回结果* 功能:通过Unidbg模拟调用SO中实现的getSign方法,传递三个字符串参数并获取返回值*/
public String getSign(){// 1. 加载并获取目标Java类的虚拟表示(DvmClass)// vm.resolveClass:让Unidbg的虚拟机(vm)查找并加载指定的Java类// 参数是类的全限定名(用斜杠分隔),对应真实Java类:cn.thecover.lib.common.manager.SignManager// 返回的DvmClass对象相当于这个类在虚拟环境中的"代言人",通过它可以操作这个类的静态方法DvmClass dvmClass = vm.resolveClass("cn/thecover/lib/common/manager/SignManager");// 2. 定义要调用的方法签名(方法的"身份证")// 格式:方法名(参数类型列表)返回值类型// 这里的签名对应Java方法:public static native String getSign(String, String, String)// 解析:// - Ljava/lang/String; 表示参数类型为String(Smali语法,所有引用类型都用这种格式)// - 三个Ljava/lang/String; 对应三个String参数// - 最后的Ljava/lang/String; 表示返回值为StringString method = "getSign(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;";// 3. 准备调用方法时需要传递的参数String 参数1 = "1";  // 第一个字符串参数,实际使用中可能是具体业务数据(如时间戳、设备ID等)String 参数2 = "2";  // 第二个字符串参数,可能是加密盐值、用户ID等String 参数3 = "3";  // 第三个字符串参数,可能是随机数、签名类型等// 4. 调用目标类的静态native方法// dvmClass.callStaticJniMethodObject:通过虚拟类对象调用静态方法// 参数说明:// - emulator:当前的Android模拟器实例(提供运行环境)// - method:上面定义的方法签名(指定要调用的具体方法)// - 参数1/2/3:传递给方法的实际参数// 返回值:StringObject(Unidbg中对String的包装类,包含方法调用的结果)StringObject value = dvmClass.callStaticJniMethodObject(emulator, method, 参数1, 参数2, 参数3);// 5. 提取返回结果并返回// value.getValue():将Unidbg的StringObject转换为Java原生Stringreturn value.getValue();
}

运行代码

package com.mmmm.dac;// 导入Unidbg框架的核心类
// AndroidEmulator:Android模拟器的核心类,用于模拟Android运行环境
import com.github.unidbg.AndroidEmulator;
// Module:用于操作加载的SO文件(动态链接库)
import com.github.unidbg.Module;
// AndroidEmulatorBuilder:模拟器构建器,用于创建不同配置的模拟器
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
// AndroidResolver:用于解析Android系统库,模拟系统库调用
import com.github.unidbg.linux.android.AndroidResolver;
// AbstractJni:JNI抽象类,用于处理SO中的JNI调用(如Java方法调用)
import com.github.unidbg.linux.android.dvm.*;
// DalvikModule:Dalvik虚拟机中的模块类,用于加载和处理SO文件
// VM:Dalvik虚拟机类,模拟Android的Java虚拟机环境
// Memory:内存操作接口,用于管理模拟器的内存
import com.github.unidbg.memory.Memory;// 用于文件操作的Java标准类
import java.io.File;/*** DcTest类:继承自AbstractJni,用于测试和分析目标SO文件* 作用:模拟Android环境,加载指定的SO文件,并获取其中的函数地址等信息*/
public class DcTest extends AbstractJni {// 成员变量声明// 模拟器实例:整个模拟环境的核心private final AndroidEmulator emulator;// 虚拟机实例:模拟Android的Java虚拟机private final VM vm;// 模块实例:代表加载的SO文件,用于操作其中的函数和符号private final Module module;/*** 构造方法:初始化模拟器、虚拟机和加载SO文件* 当创建DcTest对象时,会自动执行这些初始化操作*/public DcTest(){// 1. 创建Android模拟器实例// for64Bit():指定创建64位模拟器(如果目标SO是32位,需改为for32Bit())// setProcessName():设置模拟的进程名,通常设为目标SO所在的APP包名// build():完成模拟器构建emulator = AndroidEmulatorBuilder.for64Bit().setProcessName("com.xxx.news").build();// 2. 获取模拟器的内存操作接口// 内存接口用于管理模拟器的内存分配、库解析等final Memory memory = emulator.getMemory();// 3. 设置系统库解析器// AndroidResolver(23):指定模拟的Android系统版本为API 23(Android 6.0)// 作用:当SO调用系统库(如libc.so)时,模拟器能正确解析并模拟这些调用memory.setLibraryResolver(new AndroidResolver(23));// 4. 创建Dalvik虚拟机(Android的Java虚拟机)vm = emulator.createDalvikVM();// 5. 设置JNI处理器// 将当前类(DcTest)作为JNI调用的处理器// 当SO中调用JNI函数(如调用Java方法)时,会由当前类处理vm.setJni(this);// 6. 加载目标SO文件// 第一个参数:SO文件的路径(这里是相对路径,实际使用时需确保文件存在)// 第二个参数:false表示不自动调用JNI_OnLoad(后续会手动调用)// DalvikModule:用于在Dalvik虚拟机中管理SO文件DalvikModule dm = vm.loadLibrary(new File("utils/dcs/libwtf.so"), false);// 7. 手动调用SO中的JNI_OnLoad函数// JNI_OnLoad是SO被加载时的初始化函数,通常用于注册JNI方法// 动态注册的JNI方法需要调用此函数才会生效,静态注册可以省略dm.callJNI_OnLoad(emulator);// 8. 获取Module实例// Module是操作SO文件的主要接口,通过它可以查找函数、获取基地址等module = dm.getModule();// 9. 打印SO文件的基地址// 基地址是SO加载到内存中的起始地址,函数的实际地址=基地址+偏移量System.out.println("SO文件基地址:" + module.base);// 10. 查找并打印指定函数的地址(C++函数,经过名称修饰)// _ZN3MD56updateEPKhj:是C++函数MD5::update(const unsigned char*, unsigned int)的名称修饰后的结果// findSymbolByName:通过函数名查找符号// getAddress():获取函数在内存中的地址int address = (int)module.findSymbolByName("_ZN3MD56updateEPKhj").getAddress();System.out.println("MD5::update函数地址(十六进制):" + Long.toHexString(address));// 11. 查找并打印Java native方法对应的C函数地址// Java_cn_thecover_lib_common_manager_SignManager_getSign:// 是Java类cn.thecover.lib.common.manager.SignManager中的getSign()本地方法对应的C函数名// 这是JNI静态注册的命名规则:Java_包名_类名_方法名int funaddr = (int)module.findSymbolByName("Java_cn_thecover_lib_common_manager_SignManager_getSign").getAddress();System.out.println("getSign函数地址(十六进制):" + Long.toHexString(funaddr));}/*** 调用目标Java类的静态native方法getSign,并返回结果* 功能:通过Unidbg模拟调用SO中实现的getSign方法,传递三个字符串参数并获取返回值*/public String getSign(){// 1. 加载并获取目标Java类的虚拟表示(DvmClass)// vm.resolveClass:让Unidbg的虚拟机(vm)查找并加载指定的Java类// 参数是类的全限定名(用斜杠分隔),对应真实Java类:cn.thecover.lib.common.manager.SignManager// 返回的DvmClass对象相当于这个类在虚拟环境中的"代言人",通过它可以操作这个类的静态方法DvmClass dvmClass = vm.resolveClass("cn/thecover/lib/common/manager/SignManager");// 2. 定义要调用的方法签名(方法的"身份证")// 格式:方法名(参数类型列表)返回值类型// 这里的签名对应Java方法:public static native String getSign(String, String, String)// 解析:// - Ljava/lang/String; 表示参数类型为String(Smali语法,所有引用类型都用这种格式)// - 三个Ljava/lang/String; 对应三个String参数// - 最后的Ljava/lang/String; 表示返回值为StringString method = "getSign(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;";// 3. 准备调用方法时需要传递的参数String 参数1 = "1";  // 第一个字符串参数,实际使用中可能是具体业务数据(如时间戳、设备ID等)String 参数2 = "2";  // 第二个字符串参数,可能是加密盐值、用户ID等String 参数3 = "3";  // 第三个字符串参数,可能是随机数、签名类型等// 4. 调用目标类的静态native方法// dvmClass.callStaticJniMethodObject:通过虚拟类对象调用静态方法// 参数说明:// - emulator:当前的Android模拟器实例(提供运行环境)// - method:上面定义的方法签名(指定要调用的具体方法)// - 参数1/2/3:传递给方法的实际参数// 返回值:StringObject(Unidbg中对String的包装类,包含方法调用的结果)StringObject value = dvmClass.callStaticJniMethodObject(emulator, method, 参数1, 参数2, 参数3);// 5. 提取返回结果并返回// value.getValue():将Unidbg的StringObject转换为Java原生Stringreturn value.getValue();}public static void main(String[] args) {DcTest dcTest = new DcTest();String sign = dcTest.getSign();System.out.println(sign);}}

运行后会报错,这个错误是调用 getSign(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; so方法,它里面又调用了cn/thecover/lib/common/utils/LogShutDown类里的getAppSign方法,这个getAppSign方法找不到就会出下图中的错误

如下图还有一个Please.vm.setJni,这个与上方的错误一样,都是so方法中调用了java方法,然后找不到调用的java方法就报错了

这就需要补环境,再补环境之前,还有一个东西,上方是通过smail语法去调用的方法,接下来写通过so文件的方法地址来调用方法,如下图使用ida打开so文件,接下来就通过调用MD5的update方法做实例

上图红框并不是真正的函数名,真正的函数名是_ZN3MD56updateEPKhj

函数的地址43834

通过下图红框的代码也可以获取_ZN3MD56updateEPKhj方法地址

获取java方法的地址

使用地址调用方法

/*** 通过Unidbg调用原生函数获取签名结果* 功能:调用指定地址的原生函数,传入三个字符串参数,返回函数处理后的签名字符串*/
public String getSignAdd(){// 定义要调用的原生函数在目标模块中的内存地址(偏移量)// 0x45B48是通过逆向分析(如IDA、Ghidra)得到的函数地址long functionAddress = 0x45B48;/** 准备JNI环境相关对象* JNI(Java Native Interface)是Java与原生代码交互的接口*/// 获取JNI环境指针(JNIEnv*),这是调用任何JNI函数的第一个参数Pointer jniEnv = vm.getJNIEnv();// 创建三个字符串对象作为函数参数// StringObject是Unidbg中用于表示Java字符串的包装类StringObject data1 = new StringObject(vm, "1");  // 第一个字符串参数值为"1"StringObject data2 = new StringObject(vm, "2");  // 第二个字符串参数值为"2"StringObject data3 = new StringObject(vm, "3");  // 第三个字符串参数值为"3"// 构建函数调用的参数列表List<Object> args = new ArrayList<>();// 添加第一个参数:JNI环境指针(JNIEnv*),这是JNI函数的标准第一个参数args.add(jniEnv);// 添加后续参数:将字符串对象转换为DVM本地引用// vm.addLocalObject()会将对象添加到Dalvik虚拟机的本地引用表,返回引用ID// 原生函数通过这个引用ID可以访问到对应的Java对象args.add(vm.addLocalObject(data1));args.add(vm.addLocalObject(data2));args.add(vm.addLocalObject(data3));// 调用目标原生函数// module.callFunction():通过模块调用指定地址的函数// 参数说明:模拟器实例、函数地址、参数数组// 返回值:原生函数的返回结果(这里是一个对象引用ID)Number numbers = module.callFunction(emulator, functionAddress, args.toArray());// 将返回的引用ID转换为DVM中的对象// 原生函数返回的是Java对象引用,需要通过vm.getObject()获取实际对象DvmObject<?> object = vm.getObject(numbers.intValue());// 从DVM对象中提取字符串值// 假设原生函数返回的是String类型对象,通过getValue()获取其字符串内容String value = (String) object.getValue();// 返回获取到的签名结果return value;
}

完整代码

package com.mmmm.dac;// 导入Unidbg框架的核心类
// AndroidEmulator:Android模拟器的核心类,用于模拟Android运行环境
import com.github.unidbg.AndroidEmulator;
// Module:用于操作加载的SO文件(动态链接库)
import com.github.unidbg.Module;
// AndroidEmulatorBuilder:模拟器构建器,用于创建不同配置的模拟器
import com.github.unidbg.arm.backend.Backend;
import com.github.unidbg.arm.context.Arm64RegisterContext;
import com.github.unidbg.arm.context.RegisterContext;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
// AndroidResolver:用于解析Android系统库,模拟系统库调用
import com.github.unidbg.linux.android.AndroidResolver;
// AbstractJni:JNI抽象类,用于处理SO中的JNI调用(如Java方法调用)
import com.github.unidbg.linux.android.dvm.*;
// DalvikModule:Dalvik虚拟机中的模块类,用于加载和处理SO文件
// VM:Dalvik虚拟机类,模拟Android的Java虚拟机环境
// Memory:内存操作接口,用于管理模拟器的内存
import com.github.unidbg.linux.android.dvm.jni.ProxyDvmObject;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.pointer.UnidbgPointer;
import com.sun.jna.Pointer;// 用于文件操作的Java标准类
import java.io.File;
import java.util.ArrayList;
import java.util.List;/*** DcTest类:继承自AbstractJni,用于测试和分析目标SO文件* 作用:模拟Android环境,加载指定的SO文件,并获取其中的函数地址等信息*/
public class DcTest extends AbstractJni {// 成员变量声明// 模拟器实例:整个模拟环境的核心private final AndroidEmulator emulator;// 虚拟机实例:模拟Android的Java虚拟机private final VM vm;// 模块实例:代表加载的SO文件,用于操作其中的函数和符号private final Module module;/*** 构造方法:初始化模拟器、虚拟机和加载SO文件* 当创建DcTest对象时,会自动执行这些初始化操作*/public DcTest(){// 1. 创建Android模拟器实例// for64Bit():指定创建64位模拟器(如果目标SO是32位,需改为for32Bit())// setProcessName():设置模拟的进程名,通常设为目标SO所在的APP包名// build():完成模拟器构建emulator = AndroidEmulatorBuilder.for64Bit().setProcessName("com.xxx.news").build();// 2. 获取模拟器的内存操作接口// 内存接口用于管理模拟器的内存分配、库解析等final Memory memory = emulator.getMemory();// 3. 设置系统库解析器// AndroidResolver(23):指定模拟的Android系统版本为API 23(Android 6.0)// 作用:当SO调用系统库(如libc.so)时,模拟器能正确解析并模拟这些调用memory.setLibraryResolver(new AndroidResolver(23));// 4. 创建Dalvik虚拟机(Android的Java虚拟机)vm = emulator.createDalvikVM();// 5. 设置JNI处理器// 将当前类(DcTest)作为JNI调用的处理器// 当SO中调用JNI函数(如调用Java方法)时,会由当前类处理vm.setJni(this);// 6. 加载目标SO文件// 第一个参数:SO文件的路径(这里是相对路径,实际使用时需确保文件存在)// 第二个参数:false表示不自动调用JNI_OnLoad(后续会手动调用)// DalvikModule:用于在Dalvik虚拟机中管理SO文件DalvikModule dm = vm.loadLibrary(new File("utils/dcs/libwtf.so"), false);// 7. 手动调用SO中的JNI_OnLoad函数// JNI_OnLoad是SO被加载时的初始化函数,通常用于注册JNI方法// 动态注册的JNI方法需要调用此函数才会生效,静态注册可以省略dm.callJNI_OnLoad(emulator);// 8. 获取Module实例// Module是操作SO文件的主要接口,通过它可以查找函数、获取基地址等module = dm.getModule();// 9. 打印SO文件的基地址// 基地址是SO加载到内存中的起始地址,函数的实际地址=基地址+偏移量System.out.println("SO文件基地址:" + module.base);// 10. 查找并打印指定函数的地址(C++函数,经过名称修饰)// _ZN3MD56updateEPKhj:是C++函数MD5::update(const unsigned char*, unsigned int)的名称修饰后的结果// findSymbolByName:通过函数名查找符号// getAddress():获取函数在内存中的地址int address = (int)module.findSymbolByName("_ZN3MD56updateEPKhj").getAddress();System.out.println("MD5::update函数地址(十六进制):" + Long.toHexString(address));// 11. 查找并打印Java native方法对应的C函数地址// Java_cn_thecover_lib_common_manager_SignManager_getSign:// 是Java类cn.thecover.lib.common.manager.SignManager中的getSign()本地方法对应的C函数名// 这是JNI静态注册的命名规则:Java_包名_类名_方法名int funaddr = (int)module.findSymbolByName("Java_cn_thecover_lib_common_manager_SignManager_getSign").getAddress();System.out.println("getSign函数地址(十六进制):" + Long.toHexString(funaddr));}/*** 调用目标Java类的静态native方法getSign,并返回结果* 功能:通过Unidbg模拟调用SO中实现的getSign方法,传递三个字符串参数并获取返回值*/public String getSign(){// 1. 加载并获取目标Java类的虚拟表示(DvmClass)// vm.resolveClass:让Unidbg的虚拟机(vm)查找并加载指定的Java类// 参数是类的全限定名(用斜杠分隔),对应真实Java类:cn.thecover.lib.common.manager.SignManager// 返回的DvmClass对象相当于这个类在虚拟环境中的"代言人",通过它可以操作这个类的静态方法DvmClass dvmClass = vm.resolveClass("cn/thecover/lib/common/manager/SignManager");// 2. 定义要调用的方法签名(方法的"身份证")// 格式:方法名(参数类型列表)返回值类型// 这里的签名对应Java方法:public static native String getSign(String, String, String)// 解析:// - Ljava/lang/String; 表示参数类型为String(Smali语法,所有引用类型都用这种格式)// - 三个Ljava/lang/String; 对应三个String参数// - 最后的Ljava/lang/String; 表示返回值为StringString method = "getSign(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;";// 3. 准备调用方法时需要传递的参数String 参数1 = "1";  // 第一个字符串参数,实际使用中可能是具体业务数据(如时间戳、设备ID等)String 参数2 = "2";  // 第二个字符串参数,可能是加密盐值、用户ID等String 参数3 = "3";  // 第三个字符串参数,可能是随机数、签名类型等// 4. 调用目标类的静态native方法// dvmClass.callStaticJniMethodObject:通过虚拟类对象调用静态方法// 参数说明:// - emulator:当前的Android模拟器实例(提供运行环境)// - method:上面定义的方法签名(指定要调用的具体方法)// - 参数1/2/3:传递给方法的实际参数// 返回值:StringObject(Unidbg中对String的包装类,包含方法调用的结果)StringObject value = dvmClass.callStaticJniMethodObject(emulator, method, 参数1, 参数2, 参数3);// 5. 提取返回结果并返回// value.getValue():将Unidbg的StringObject转换为Java原生Stringreturn value.getValue();}/*** 通过Unidbg调用原生函数获取签名结果* 功能:调用指定地址的原生函数,传入三个字符串参数,返回函数处理后的签名字符串*/public String getSignAdd(){// 定义要调用的原生函数在目标模块中的内存地址(偏移量)// 0x45B48是通过逆向分析(如IDA、Ghidra)得到的函数地址long functionAddress = 0x45B48;/** 准备JNI环境相关对象* JNI(Java Native Interface)是Java与原生代码交互的接口*/// 获取JNI环境指针(JNIEnv*),这是调用任何JNI函数的第一个参数Pointer jniEnv = vm.getJNIEnv();// 创建三个字符串对象作为函数参数// StringObject是Unidbg中用于表示Java字符串的包装类StringObject data1 = new StringObject(vm, "1");  // 第一个字符串参数值为"1"StringObject data2 = new StringObject(vm, "2");  // 第二个字符串参数值为"2"StringObject data3 = new StringObject(vm, "3");  // 第三个字符串参数值为"3"// 构建函数调用的参数列表List<Object> args = new ArrayList<>();// 添加第一个参数:JNI环境指针(JNIEnv*),这是JNI函数的标准第一个参数args.add(jniEnv);// 添加后续参数:将字符串对象转换为DVM本地引用// vm.addLocalObject()会将对象添加到Dalvik虚拟机的本地引用表,返回引用ID// 原生函数通过这个引用ID可以访问到对应的Java对象args.add(vm.addLocalObject(data1));args.add(vm.addLocalObject(data2));args.add(vm.addLocalObject(data3));// 调用目标原生函数// module.callFunction():通过模块调用指定地址的函数// 参数说明:模拟器实例、函数地址、参数数组// 返回值:原生函数的返回结果(这里是一个对象引用ID)Number numbers = module.callFunction(emulator, functionAddress, args.toArray());// 将返回的引用ID转换为DVM中的对象// 原生函数返回的是Java对象引用,需要通过vm.getObject()获取实际对象DvmObject<?> object = vm.getObject(numbers.intValue());// 从DVM对象中提取字符串值// 假设原生函数返回的是String类型对象,通过getValue()获取其字符串内容String value = (String) object.getValue();// 返回获取到的签名结果return value;}public static void main(String[] args) {DcTest dcTest = new DcTest();String sign = dcTest.getSignAdd();System.out.println(sign);}}

补自定义环境在下一节中


img

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/95849.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/95849.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/95849.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Kubernetes知识点问答题】Pod 调度

1. 如何将特定 Pod 调度到指定的节点&#xff1f;可以使用下列方法中的任何一种来选择 K8s 对特定 Pod 的调度&#xff1a;① 与节点标签匹配的 nodeSelector&#xff1a;在 Pod 的规范中使用 nodeSelector 字段来指定节点标签&#xff0c;以便将 Pod 调度到具有特定标签的节点…

wordpress显示时间日期的几种常见的方式

在WordPress中&#xff0c;显示时间日期有多种常见方式&#xff0c;包括使用默认设置、模板标签、插件等&#xff0c;以下是详细介绍&#xff1a; 使用默认设置 WordPress的默认设置允许你在文章列表中显示文章的发布时间。登录到WordPress后台&#xff0c;在“设置”中找到“…

基于飞算JavaAI实现布隆过滤器防止缓存穿透:原理、实践与全流程解析

引言&#xff1a;当缓存失效时&#xff0c;系统如何避免“雪崩式崩溃”&#xff1f; 在互联网高并发场景中&#xff08;如电商秒杀、社交平台热点新闻&#xff09;&#xff0c;缓存是提升系统性能的核心手段——将频繁访问的数据&#xff08;如商品详情、用户信息&#xff09;存…

DeepResearch开源与闭源方案对比

在这个AI不再只是聊天工具的时代&#xff0c;"深度研究"已经成为大语言模型&#xff08;LLM&#xff09;的一项新能力。先进的LLM不再只是给出快速的一次性回答&#xff0c;而是可以像研究助手一样工作——搜索网上信息&#xff0c;调用各种工具&#xff08;比如搜索…

UniApp 页面传参方式详解

在 UniApp 开发中&#xff0c;页面间参数传递是核心功能之一。以下是 8 种常用的传参方式&#xff0c;每种方式都有其适用场景和特点&#xff1a;一、URL 拼接传参&#xff08;最常用&#xff09; 适用场景&#xff1a;简单数据传递&#xff0c;如 ID、状态值等基础类型数据 实…

音频分类标注工具

pyqt 分类标注工具&#xff1a;import glob import sys import json import os from PyQt5.QtWidgets import (QApplication, QMainWindow, QTableWidget, QTableWidgetItem,QSplitter, QVBoxLayout, QWidget, QPushButton, QRadioButton,QButtonGroup, QLabel, QHBoxLayout, Q…

云计算-Kubernetes+Istio 实现金丝雀发布:流量管理、熔断、流量镜像、ingreess、污点及pv案例实战

介绍 在微服务架构中,如何安全、高效地实现服务发布与流量管理是保障业务稳定性的核心挑战。金丝雀发布(Canary Release)、灰度发布等策略通过精细化的流量控制,可有效降低新版本上线风险, Istio 作为主流的服务网格(Service Mesh)工具。 此次Istio 在 Kubernetes 集群…

12.web api 3

定时器-间歇函数

ComfyUI进阶:EchoMimic插件全解析,让静态肖像实现音频驱动的精准口型动画

在数字内容创作中&#xff0c;让静态肖像“开口说话”并做出自然表情&#xff0c;是提升交互感与沉浸感的关键。传统动画制作需专业人员逐帧调整口型与表情&#xff0c;成本高且效率低。ComfyUI的EchoMimic插件通过音频驱动技术&#xff0c;实现了“输入音频→自动生成匹配口型…

链式前向星、vector存图

场景设定 想象你是一个社交达人&#xff0c;要记录你和所有朋友的关系&#xff08;这就是“图”&#xff09;。每个朋友是一个节点&#xff0c;关系是一条边。你需要快速回答&#xff1a;“我有哪些朋友&#xff1f;”&#xff08;遍历邻居&#xff09;。方式1&#xff1a;链式…

YAML 中定义 List 的几种方式

在 YAML 配置文件中定义 List 并在 Spring 应用中注入是非常常见的操作&#xff0c;下面详细介绍具体写法和注入方式。一、YAML 中定义 List 的几种方式1. 缩进式写法&#xff08;推荐&#xff09;最常用的方式&#xff0c;通过短横线 - 加空格表示列表项&#xff1a;yaml# app…

C# 反射和特性(自定义特性)

自定义特性 你或许已经注意到了&#xff0c;应用特性的语法和之前见过的其他语法有很大不同。你可能会觉得特 性是一种完全不同的结构类型&#xff0c;其实不是&#xff0c;特性只是一种特殊的类。 有关特性类的一些要点如下。 用户自定义的特性类叫作自定义特性。所有特性类都…

科目二的四个电路

一.K21电动机单连续运转接线(带点动控制)1.电路图2.主线路这可很明了,是一条直线,从上接到下就OK了,然后从热继电器出来,接到SB3按钮的常闭触点上接着往下走一端接到SB2的常闭触点上,接着往下走&#xff0c;走到接触器的线圈上,从L2借一条火线出来,从熔断器的上端接入,另一端接…

【位运算】查询子数组最大异或值|2693

本文涉及知识点 位运算、状态压缩、枚举子集汇总 3277. 查询子数组最大异或值 给你一个由 n 个整数组成的数组 nums&#xff0c;以及一个大小为 q 的二维整数数组 queries&#xff0c;其中 queries[i] [li, ri]。 对于每一个查询&#xff0c;你需要找出 nums[li…ri] 中任…

HTML DOM 方法

HTML DOM 方法 引言 HTML DOM&#xff08;文档对象模型&#xff09;是HTML文档的编程接口&#xff0c;它允许开发者通过JavaScript来操作HTML文档中的元素。DOM 方法是DOM编程的核心&#xff0c;它提供了丰富的操作手段来改变网页的结构、样式和行为。本文将详细介绍HTML DOM中…

w嵌入式分享合集68

自己的原文哦~ https://blog.51cto.com/whaosoft/14133002 一、一键开关机电路的设计方案 方案一&#xff1a;电路图 一键开关机电路分析如下&#xff1a; 电路工作流程如下&#xff1a; Key按下瞬间&#xff0c;Q2、Q1导通&#xff0c;7805输入电压在8.9V左右&…

FFmpeg QoS 处理

FFmpeg 中的 QoS (服务质量) 处理主要关注于实时流媒体传输中的时序控制、丢帧策略和网络适应等方面。以下是 FFmpeg 中 QoS 相关的关键机制和配置方法。1. 基本 QoS 机制丢帧策略 (Frame Dropping)cAVDictionary *options NULL; av_dict_set(&options, "framedrop&q…

TexStudio中的Latex,PDFLatex,XeLatex和LuaLatex的区别

多种LaTeX编译器一、多种LaTeX编译器 1.1 PDFLaTeX&#xff08;1994年&#xff09; 默认、最常用的引擎。 输入文件通常是 ASCII 或 UTF-8 编码&#xff08;但中文需要 CJK 宏包或 ctex 宏包支持&#xff09;。 字体选择受限&#xff1a;只能使用 TeX 自带的字体或者 Type 1…

容器化部署:用Docker封装机器翻译模型与服务详解

文章目录一、机器翻译容器化的技术栈选型1.1 为什么需要容器化MT模型&#xff1f;1.2 基础镜像选择对比1.3 典型依赖分层方案1.4 性能对比&#xff08;容器化 vs 原生部署&#xff09;二、关键部署模式2.1 轻量级API服务封装2.2 模型热更新策略三、Docker镜像构建3.1 编写Docke…

leetcode_42 接雨水

1. 题意 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 2. 题解 这个题不会做&#xff0c;全部是看得题解捏。 不过能看懂题解感觉自己也很棒了&#xff01; 看完题解后感觉最难的是如何求出有多少…