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

44.安卓逆向2-补环境-使用unidbg(手动补环境)

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

内容参考于:图灵Python学院

工具下载:

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

提取码:zy89

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

上一个内容:43.安卓逆向2-补环境-使用unidbg(使用Smali语法调用方法和使用方法地址调用方法)

上一个内容里写了调用 getSign方法,然后在调用getSign方法时,它里面又调用了LogShutDown中的getAppSign方法,然后就报错了,本次就来补LogShutDown的getAppSign方法环境

首先使用jadx查看一下这个方法,看看它的参数和返回值是什么类型,如下图,它没有入参只有一个String类型的返回值

然后看下图红框的错误提示,它调用了 callStaticObjectMethodV 方法

然后我们的DcTest类也继承了AbstractJni类

手动补环境,就要根据错误提示来重写方法,也就是重写 callStaticObjectMethodV 方法,如下图重写callStaticObjectMethodV 方法

/*** 重写父类的callStaticObjectMethodV方法,用于处理特定的静态方法调用* @param vm 虚拟机实例,代表当前运行的Dalvik虚拟机* @param dvmClass 调用方法所属的类对象* @param signature 方法签名,格式为"类名->方法名(参数类型)返回值类型"* @param vaList 方法调用时的参数列表* @return 方法调用的返回值,封装为DvmObject类型*/
@Override
public DvmObject<?> callStaticObjectMethodV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {// 根据方法签名判断是否是需要特殊处理的方法switch (signature){// 匹配"cn/thecover/lib/common/utils/LogShutDown类的getAppSign()方法"case "cn/thecover/lib/common/utils/LogShutDown->getAppSign()Ljava/lang/String;":{// 当调用该方法时,返回一个值为"13"的字符串对象// new StringObject(vm, "13")表示在当前虚拟机中创建一个字符串对象return new StringObject(vm, "13");}}// 如果没有匹配到需要特殊处理的方法,调用父类的默认实现return super.callStaticObjectMethodV(vm, dvmClass, signature, vaList);
}

效果图:就会发现可以了(不要用函数地址的方式调用,要用smail语法的方式调用),这样就模拟调用了so文件中的getSign方法了,这样就不需要分析so文件的算法,因为我们可以直接运行这个方法了,只需要找到方法的入口、参数、返回值,不需要关心它里面具体的逻辑,唯一要注意的是so文件中调用的java代码

调用的java代码如下图

完整代码:

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;}@Overridepublic DvmObject<?> callStaticObjectMethodV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {switch (signature){case "cn/thecover/lib/common/utils/LogShutDown->getAppSign()Ljava/lang/String;":{return new StringObject(vm, "13");}}return super.callStaticObjectMethodV(vm, dvmClass, signature, vaList);}public static void main(String[] args) {DcTest dcTest = new DcTest();String sign = dcTest.getSign();System.out.println(sign);}}

img

http://www.dtcms.com/a/337953.html

相关文章:

  • JavaScript Array.prototype.at ():数组任意位置取值的新姿势
  • ReactNative开发实战——React Native开发环境配置指南
  • 使用 mdadm 创建 RAID 10(4块磁盘)
  • Buttercup:开源人工智能驱动系统检测并修补漏洞
  • React native 使用 JSI 库 实现 C++和JS互通
  • 设计模式之汇总
  • CNN-BiLSTM-Attention、CNN-BiLSTM、BiLSTM三模型多变量时序光伏功率预测
  • 物联网智能边缘架构:流数据处理与设备管理的协同优化
  • PHP如何利用GD库函数生成图片验证码?
  • 在Excel启动时直接打开多个Excel文件
  • golang读写锁和互斥锁的区别
  • 理解AQS的原理并学习源码
  • MongoDB新手教学
  • 2025 世界机器人大会:前沿科技闪耀,机器人板块未来可期
  • Android 圆形和圆角矩形总结
  • MyCAT完整实验报告
  • Unity作为库导入Android原生工程
  • AVB(Android Verified Boot)中vbmeta结构浅析
  • Unity2022打包安卓报错的奇葩问题
  • Java面试宝典:Redis 入门与应用
  • 【OpenAI】 GPT-4o-realtime-preview 多模态、实时交互模型介绍+API的使用教程!
  • 线程间同步机制与进程间通信
  • 数据处理和统计分析 —— Pandas 基础(附数据集)
  • SMTPman,smtp ssl助力安全高效邮件传输!
  • redhat9从github下拉软件包一直报错
  • petalinux2023.1编译pmu-rom-native...fetch error问题
  • 39-Linux下安装python
  • BPO(Business Process Optimization,业务流程优化)
  • FPGA驱动量子革命:微美全息(NASDAQ:WIMI)实现数字量子计算关键验证
  • 任务六 歌手页面功能开发