unidbg基础使用
简单来说unidbg是通过模拟android虚拟机,来模拟运行apk依赖的动态库中函数的项目,可以快速调用so层中的函数,也可以实现一些hook操作
依赖
- jdk
- maven
- idea
安装教程请移步
导入unidbg
git clone https://github.com/zhkl0228/unidbg.git
或下载zip包,解压到自定义目录
打开idea,配置setting下maven的路径,如果你依赖那一步配置好的话,idea会自动识别,直接导入unidbg项目,导入时记得选导入maven项目,等待项目同步完成
项目中的测试用例:src/test/java/com/kanxue/test2/MainActivity.java,直接执行其中的main方法,得到下面输出,代表导入运行正常
load offset=4822ms
Found: XuE, off=2300ms
Process finished with exit code 0
unidbg使用
unidbg架构基本流程
- 创建32位模拟器实例,
emulator = AndroidEmulatorBuilder.for32Bit().build(); - 创建模拟器内存接口
final Memory memory = emulator.getMemory(); - 设置系统类库解析
memory.setLibraryResolver(new AndroidResolver(23)); - 创建 Android 虚拟机
vm = emulator.createDalvikVM(); - 加载 so 到虚拟内存,第二个参数的意思表示是否执行动态库的初始化代码
DalvikModule dm = vm.loadLibrary(new File("unidbg-android/src/test/java/com/xxx/xxx.so"),true); - 获取 so 模块的句柄
module = dm.getModule(); - 设置 JNI 需要继承AbstractJni
vm.setJni(this); - 打印日志
vm.setVerbose(true); - 调用 JNI_Onload
dm.callJNI_OnLoad(emulator); - 创建 jobject, 如果没用到的话可以不写 ,要用需要调用函数所在的Java类完整路径,比如a/b/c/d等等,注意.需要用/代替
cNative = vm.resolveClass("com/xxx/xxx")
参数构造
构造调用时会给其传递参数,参数构造常用如下:
- 基本方式
List<Object> args = new ArrayList<>(10);
- 兼容格式
// 参数1:JNIEnv *env
args.add(vm.getJNIEnv());
jobject 或 jclass
DvmObject<?> cnative = cNative.newObject(null);
args.add(cnative.hashCode());
如果用不到直接填0即可
args.add(0);
- 字符串对象
String input = "abcdef";
args.add(vm.addLocalObject(new StringObject(vm, input)));
- bytes 数组
String str= "abcdef";
byte[] str_bytes = str.getBytes(StandardCharsets.UTF_8);
ByteArray str_bytes _array = new ByteArray(vm,str_bytes );
args.add(vm.addLocalObject(str_bytes _array));
- bool
// false 填 0,true 填 1
args.add(1);
使用实例
以2024csicn android_re appdebug.apk为例子,程序本身输入38字节flag运行会crash,hook jni返回值也一样crash,AS调试也是当调用jni函数时会crash,排查系统日志adb shell logcat
后发现可能是某些编码错误,所以以上hook和调试手段不可行,此时可以用unidbg进行模拟直接调用so相关函数得到返回值。
题目jni相关代码如下:
package com.example.re11113;
public class jni {
public static native String getiv();
public static native String getkey();
static {
System.loadLibrary("Secret_entrance");
}
}
要hook getiv()、getkey()的返回值,拿到key、iv就可以进行DES CBC解密,得到flag
在项目src/test/java/
下新建目录和要hook的类(和so库中的类一致),下面是个样例(2024csicn android_re的脚本)
package com.example.re11113;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.LibraryResolver;
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.VM;
import com.github.unidbg.linux.android.dvm.jni.ProxyDvmObject;
import com.github.unidbg.memory.Memory;
import java.io.File;
/**
* <a href="https://bbs.pediy.com/thread-263345.htm">CrackMe</a>
*/
public class jni {
public static void main(String[] args) {
jni jnii = new jni();
jnii.crack();
}
private final AndroidEmulator emulator;
private final VM vm;
private final Module module;
private jni() {
emulator = AndroidEmulatorBuilder
//创建64位模拟器实例,
.for64Bit()
//添加后端,推荐使用Dynarmic,运行速度快,但并不支持某些新特性
.addBackendFactory(new DynarmicFactory(true))
//指定进程名,推荐以安卓包名做进程名
.setProcessName("com.example.re11113")
//生成AndroidEmulator实例
.build();
//获取内存操作接口
Memory memory = emulator.getMemory();
//指定Android SDK 版本,目前支持19和23两个版本
LibraryResolver resolver = new AndroidResolver(23);
//设置系统类库解析
memory.setLibraryResolver(resolver);
//创建虚拟机
vm = emulator.createDalvikVM();
//设置日志打印是否开启
vm.setVerbose(true);
//加载 so 到虚拟内存,第二个参数的意思表示是否执行动态库的初始化代码
DalvikModule dm = vm.loadLibrary(new File("unidbg-android/src/test/resources/re1/libSecret_entrance.so"), true);
module = dm.getModule();
dm.callJNI_OnLoad(emulator);
}
private void crack() {
//创建虚拟机中的对象
DvmObject<?> obj = ProxyDvmObject.createObject(vm, this);
//调用so库中相关函数
String iv = obj.callJniMethodObject(emulator, "getiv()Ljava/lang/String;").getValue().toString();
String key = obj.callJniMethodObject(emulator, "getkey()Ljava/lang/String;").getValue().toString();
System.out.println("iv:"+iv);
System.out.println("key:"+key);
}
}
运行结果:
Find native function Java_com_example_re11113_jni_getiv => RX@0x40001f58[libSecret_entrance.so]0x1f58
V2YzREx1cHM=
JNIEnv->NewStringUTF("Wf3DLups") was called from RX@0x40001fac[libSecret_entrance.so]0x1fac
Find native function Java_com_example_re11113_jni_getkey => RX@0x40001fec[libSecret_entrance.so]0x1fec
Keys match!
JNIEnv->NewStringUTF("A8UdWaeq") was called from RX@0x400021e8[libSecret_entrance.so]0x21e8
iv:Wf3DLups
key:A8UdWaeq