当前位置: 首页> 汽车> 车展 > Unidbg使用指南

Unidbg使用指南

时间:2025/7/12 20:12:58来源:https://blog.csdn.net/weixin_43344005/article/details/141142508 浏览次数: 0次

Unidbg使用指南

  • 简介
  • 使用
  • Unidbg补环境
    • 仅含C语言
    • C调用 Java
  • 实操——车智赢+在unidbg实现执行so中的方法
  • 附——关于引用数据类型的转换
  • 附——静态注册和动态注册模板
    • 静态注册
    • 动态注册

现在很多的app使用了so加密,以后会越来越多。爬虫工程师可能会直接逆向app,看java代码,完成java层的算法破解,但是如果遇到so该怎么办呢?可能你会直接破解so,但是真的会有很多爬虫工程师会去破解so吗?有时候我们可以不用破解so,利用很多大佬写好的轮子即可完成so的调用。

说到调用,就有很多方法了,比如用frida+rpc、xposed+andserver、再者就是unicorn+web框架等等,今天要说的并不是这些,而是unidbg,这框架有什么好的地方呢?看看简介。

简介

unidbg是一个Java项目,可以帮助我们去模拟一个安卓或IOS设备,用于去执行so文件中的算法,从而不需要再去逆向他内部的算法。
关于so的解决方法:

  • 硬核分析+调试+破解
  • frida-rpc
  • unidbg

使用

  1. 在github上开源的项目:unidbg
    在这里插入图片描述
  2. 打开项目
    由于unidbg项目是由java编写的,所以需要用 Intellij IDEA 打开并操作。
    导入项目后我们运行下测试代码,看看我们的环境是否有问题
    在这里插入图片描述
    当我们运行测试代码后能出现sign值说明环境是没问题的。

Unidbg补环境

unidbg在运行so文件时会出现两类情况:

  • so算法都基于C语言实现,
  • so算法中还会读取Java中成员,

仅含C语言

这种直接基于unidbg来进行so文件即可。

在这里插入图片描述

C调用 Java

这种就需要补环境,将C中调用的Java中的功能给补上,这样so中的代码才能正常执行。
所谓的unidbg补环境,其实补的就是这个。

在这里插入图片描述

实操——车智赢+在unidbg实现执行so中的方法

在这里插入图片描述
在这里插入图片描述

  1. 创建类
    在这里插入图片描述

  2. 设备初始化
    在这里插入图片描述

    package com.com.demo;import com.github.unidbg.AndroidEmulator;
    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;public class che extends AbstractJni{public static AndroidEmulator emulator;public static Memory memory;public static VM vm;public static DalvikModule dm;public static Module module;che() {// 1.创建设备(32位或64位模拟器), 具体看so文件在哪个目录。 在armeabi-v7a就选择32位emulator = AndroidEmulatorBuilder.for32Bit().setProcessName("com.che168.autotradercloud").build();// 2.获取内存对象(可以操作内存)memory = emulator.getMemory();// 3.设置安卓sdk版本(只支持19、23)memory.setLibraryResolver(new AndroidResolver(23));// 4.创建虚拟机(运行安卓代码需要虚拟机,就想运行py代码需要python解释器一样)vm = emulator.createDalvikVM(new File("unidbg-android/apks/che/atc282.apk"));vm.setJni(this);//vm.setVerbose(true); //是否展示调用过程的细节// 5.加载so文件DalvikModule dm = vm.loadLibrary(new File("unidbg-android/apks/che/libnative-lib.so"), false);//dm.callJNI_OnLoad(emulator);// 6.dm代表so文件,dm.getModule()得到module对象,基于module对象可以访问so中的成员。module = dm.getModule();}public static void main(String[] args) {che obj = new che();}
    }
    
  3. 执行签名

    public void sign() {// 找到java中native所在的类,并加载DvmClass CheckSignUtil = vm.resolveClass("com/autohome/ahkit/jni/CheckSignUtil");// 方法的符号表示String method = "get3desKey(Landroid/content/Context;)Ljava/lang/String;"; //JNI签名//导入的包					返回值// 执行类中的静态成员StringObject obj = CheckSignUtil.callStaticJniMethodObject(emulator,method,vm.resolveClass("android/content/Context").newObject(null));String keyString = obj.getValue();System.out.println(keyString);}

在这里插入图片描述

通过上述操作我们就完成了在unidbg实现执行so中的方法。

附——关于引用数据类型的转换

在这里插入图片描述

附——静态注册和动态注册模板

静态注册

  • 根据函数名将Java代码中的native方法与so中的JNI方法一一对应,当Java层调用so层的函数时,如果发现其上有JNIEXPORT和JNICALL两个宏定义声明时,就会将so层函数链接到对应的native方法上。
  • 而native方法和so方法对应规则是:以字符串“Java”为前缀,并且用“_”下划线将包名、类名以及native方法名连接起来就是对应的JNI函数名了。
Java.perform(function () {var dlsymadd = Module.findExportByName("libdl.so", 'dlsym');Interceptor.attach(dlsymadd, {onEnter: function (args) {this.info = args[1];}, onLeave: function (retval) {//那个so文件 module.namevar module = Process.findModuleByAddress(retval);if (module == null) {return retval;}// native方法var funcName = this.info.readCString();if (funcName.indexOf("getHNASignature") !== -1) {console.log(module.name);console.log('\t', funcName);}return retval;}})
});// Application(identifier="com.rytong.hnair", name="海南航空", pid=14958, parameters={})
// frida -U -f  com.rytong.hnair  -l static_find_so.js

动态注册

  • 在调用System.loadLibrary()时会在so层调用一个名为JNI_OnLoad()的函数,我们可以提供一个函数映射表,再在JNI_Onload()函数中通过JNI中提供的RegisterNatives()方法来注册函数。这样Java就可以通过函数映射表来调用函数,而不必通过函数名来查找对应函数。
var symbols = Module.enumerateSymbolsSync("libart.so");
var addrRegisterNatives = null;
for (var i = 0; i < symbols.length; i++) {var symbol = symbols[i];if (symbol.name.indexOf("art") >= 0 &&symbol.name.indexOf("JNI") >= 0 &&symbol.name.indexOf("RegisterNatives") >= 0 &&symbol.name.indexOf("CheckJNI") < 0) {addrRegisterNatives = symbol.address;console.log("RegisterNatives is at ", symbol.address, symbol.name);}
}
console.log("addrRegisterNatives=", addrRegisterNatives);if (addrRegisterNatives != null) {Interceptor.attach(addrRegisterNatives, {onEnter: function (args) {var env = args[0];var java_class = args[1];var class_name = Java.vm.tryGetEnv().getClassName(java_class);// 只有类名为com.bilibili.nativelibrary.LibBili,才打印输出var taget_class = "com.xunmeng.pinduoduo.secure.DeviceNative";if (class_name === taget_class) {console.log("\n[RegisterNatives] method_count:", args[3]);var methods_ptr = ptr(args[2]);var method_count = parseInt(args[3]);for (var i = 0; i < method_count; i++) {// Java中函数名字的var name_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3));// 参数和返回值类型var sig_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize));// C中的函数指针var fnPtr_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize * 2));var name = Memory.readCString(name_ptr); // 读取java中函数名var sig = Memory.readCString(sig_ptr); // 参数和返回值类型var find_module = Process.findModuleByAddress(fnPtr_ptr); // 根据C中函数指针获取模块var offset = ptr(fnPtr_ptr).sub(find_module.base) // fnPtr_ptr - 模块基地址// console.log("[RegisterNatives] java_class:", class_name);console.log("name:", name, "sig:", sig, "module_name:", find_module.name, "offset:", offset);//console.log("name:", name, "module_name:", find_module.name, "offset:", offset);}}}});
}// frida -U -f  com.xunmeng.pinduoduo  -l dynamic_find_so.js
关键字:Unidbg使用指南

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: