# 前提纪要
在 app 加密中,加密逻辑不单单出现在 java 方法中,也有可能出现在 native 方法中,wiki 中介绍,java native interface 是能将 java 代码运行在 jvm 同时可以让其他语言类似 C,C++ 本机调用的方法。
同时,在 java 代码中会以如下方式出现:
// 加载库 | |
static { | |
System.loadLibrary(o00OO.OooO00o.OooO00o(-592843821021945178L)); | |
} | |
// 加载 Native 方法 | |
public native String sign(String str); |
# Unidbg 介绍
unidbg 是一个基于 unicorn 的逆向工具,可以直接调用 Android 和 iOS 中的 so
文件。项目的 GitHub 地址为 https://github.com/zhkl0228/unidbg。
idea 配好 jdk 即可
本质上是 java 可以调用 c 中的方法,那么只要模拟其方法即可。
# Unidbg 使用
这里以 https://www.qinless.com/1173 提到的为例,在编写对应 java 代码的时候,我们需要先编写伪方法:
import java.nio.charset.StandardCharsets; | |
public class oasis { | |
public static void main(String[] args) { | |
String input1 = "aid=01A-khBWIm48A079Pz_DMW6PyZR8" + | |
"uyTumcCNm4e8awxyC2ANU.&cfrom=28B529501" + | |
"0&cuid=5999578300&noncestr=46274W9279Hr1" + | |
"X49A5X058z7ZVz024&platform=ANDROID×tamp" + | |
"=1621437643609&ua=Xiaomi-MIX2S__oasis__3.5.8_" + | |
"_Android__Android10&version=3.5.8&vid=10190135" + | |
"94003&wm=20004_90024"; | |
Boolean input2 = false; | |
oasis test = new oasis(); | |
String sign = test.s(input1.getBytes(StandardCharsets.UTF_8), input2); | |
System.out.println(sign); | |
} | |
public String s(byte[] barr, boolean z){ | |
return "Sign"; | |
}; | |
} |
就是相当于,先编写一段调用代码,保持与原生 app 调用方法一致,然后将调用 so 的逻辑写进去即可。
private final AndroidEmulator emulator; | |
private final VM vm; | |
private final Module module; | |
// 补充构造方法,初始化 jni 啥的 | |
oasis() { | |
// 创建模拟器实例,进程名建议依照实际进程名填写,可以规避针对进程名的校验 | |
emulator = AndroidEmulatorBuilder.for32Bit().setProcessName("com.sina.oasis").build(); | |
// 获取模拟器的内存操作接口 | |
final Memory memory = emulator.getMemory(); | |
// 设置系统类库解析 | |
memory.setLibraryResolver(new AndroidResolver(23)); | |
// 创建 Android 虚拟机,传入 APK,Unidbg 可以替我们做部分签名校验的工作 | |
vm = emulator.createDalvikVM(new File("unidbg-android\\src\\test\\java\\com\\lession1\\lvzhou.apk")); | |
// | |
// vm = emulator.createDalvikVM(null); | |
// 加载目标 SO | |
DalvikModule dm = vm.loadLibrary(new File("unidbg-android\\src\\test\\java\\com\\lession1\\liboasiscore.so"), true); // 加载 so 到虚拟内存 | |
// 获取本 SO 模块的句柄,后续需要用它 | |
module = dm.getModule(); | |
vm.setJni(this); // 设置 JNI | |
vm.setVerbose(true); // 打印日志 | |
dm.callJNI_OnLoad(emulator); // 调用 JNI OnLoad | |
}; |
接着修改调用逻辑里的参数
public String getS(){ | |
// 使用 List 构建参数列表 | |
// args list | |
List<Object> list = new ArrayList<>(10); | |
// arg1 env | |
list.add(vm.getJNIEnv()); | |
//arg2 jobject/jclazz 一般用不到,直接填 0 | |
list.add(0); | |
// arg3 bytes | |
String input = "xxx"; | |
byte[] inputByte = input.getBytes(StandardCharsets.UTF_8); | |
ByteArray inputByteArray = new ByteArray(vm,inputByte); | |
list.add(vm.addLocalObject(inputByteArray)); | |
//arg4 ,boolean false 填入 0 | |
list.add(0); | |
// 参数准备完成 | |
//call function 根据地址偏移调用参数 | |
Number number = module.callFunction(emulator, 0xC365, list.toArray()); | |
String result = vm.getObject(number.intValue()).getValue().toString(); | |
return result; | |
} |
这里的前两个参数 env, obj 一般都不用管,由于这里第三个参数是 byte 数组类型,所以要用 unidbg 内置的 ByteArray 实现,然后用 list 包裹进去,最后 list.toArray 封装
整体框架
package com.lession2;
// 导入通用且标准的类库
import com.github.unidbg.linux.android.dvm.AbstractJni;
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.linux.android.dvm.array.ByteArray;
import com.github.unidbg.memory.Memory;
import com.lession1.oasis;
import java.io.File;
// 这里的sina是文件名
public class sina extends AbstractJni{
private final AndroidEmulator emulator;
private final VM vm;
private final Module module;
sina() {
// 创建模拟器实例,进程名建议依照实际进程名填写,可以规避针对进程名的校验
emulator = AndroidEmulatorBuilder.for32Bit().setProcessName("com.sina.International").build();
// 获取模拟器的内存操作接口
final Memory memory = emulator.getMemory();
// 设置系统类库解析
memory.setLibraryResolver(new AndroidResolver(23));
// 创建Android虚拟机,传入APK,Unidbg可以替我们做部分签名校验的工作
vm = emulator.createDalvikVM(new File("unidbg-android\\src\\test\\java\\com\\lession2\\sinaInternational.apk"));
//
// vm = emulator.createDalvikVM(null);
// 加载目标SO
DalvikModule dm = vm.loadLibrary(new File("unidbg-android\\src\\test\\java\\com\\lession2\\libutility.so"), true); // 加载so到虚拟内存
//获取本SO模块的句柄,后续需要用它
module = dm.getModule();
vm.setJni(this); // 设置JNI
vm.setVerbose(true); // 打印日志
// 样本连JNI OnLoad都没有
// dm.callJNI_OnLoad(emulator); // 调用JNI OnLoad
};
public static void main(String[] args) {
sina test = new sina();
// 这里调用补充的方法
}
// 这里补充方法
}