# 前提纪要

在 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&timestamp" +
                "=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();
        // 这里调用补充的方法
    }
    
    // 这里补充方法
}
Edited on

Give me a cup of [coffee]~( ̄▽ ̄)~*

Mr2 WeChat Pay

WeChat Pay