unidbg 是一个基于 unicorn 的逆向工具,可以黑盒调用安卓和 iOS 中的 so 文件。unidbg 是一个标准的 java 项目。
由于现在的大多数 app 把签名算法已经放到了 so 文件中,所以要想破解签名算法,必须能够破解 so 文件。但是我们知道,C++ 的逆向远比 Java 的逆向要难得多了,所以好多时候是没法破解的,那么这个时候还可以采用 hook 的方法,直接读取程序中算出来的签名,但是这样的话,需要实际运行这个应用,需要模拟器或者真机,效率又不是很高。
unidbg 就是一个很巧妙地解决方案,他不需要直接运行 app,也无需逆向 so 文件,而是通过在 app 中找到对应的 JNI 接口,然后用 unicorn 引擎直接执行这个 so 文件,所以效率也比较高。
上个代码看着比较方便,代码中有很多注释
public class du extends AbstractJni { //ARM模拟器 private final ARMEmulator emulator; //vm private final VM vm; //载入的模块 private final Module module; private final DvmClass TTEncryptUtils; //初始化 public du() throws IOException { //创建app进程,这里其实可以不用写的,我这里是随便写的,使用app本身的进程就可以绕过进程检测 emulator = new AndroidARMEmulator("com.du.du"); Memory memory = emulator.getMemory(); //作者支持19和23两个sdk memory.setLibraryResolver(new AndroidResolver(23)); memory.setCallInitFunction(); //创建DalvikVM,利用apk本身,可以为null //如果用apk文件加载so的话,会自动处理签名方面的jni,具体可看AbstractJni,利用apk加载的好处,// vm = emulator.createDalvikVM(new File("src/test/resources/du/du4160.apk"));我这里没有用到apk,主要是没有检测其他因素。 vm = emulator.createDalvikVM(null); //加载so,使用armv8-64速度会快很多,这里是so的文件路径,其实也可以利用apk自身的。 DalvikModule dm = vm.loadLibrary(new File("src/test/resources/du/libJNIEncrypt.so"), false); //调用jni dm.callJNI_OnLoad(emulator); module = dm.getModule(); //加载so的那个类 TTEncryptUtils = vm.resolveClass("com/duapp/aesjni/AESEncrypt"); } //关闭模拟器 private void destroy() throws IOException { emulator.close(); System.out.println("destroy"); } public static void main(String[] args) throws IOException { du t = new du(); t.encodeByte(); t.destroy(); } private String encodeByte() { //调试 // 这里还支持gdb调试, //emulator.attach(DebuggerType.GDB_SERVER); //附加调试器// emulator.attach(DebuggerType.SIMPLE);// emulator.traceCode(); //这里是打断点,原地址0x00005028->新地址0x40005028 新地址需要改成0x4 // emulator.attach().addBreakPoint(null, 0x40001188);//encode地址// emulator.attach().addBreakPoint(null, 0x40000D10); Number ret = TTEncryptUtils.callStaticJniMethod(emulator, "getByteValues()Ljava/lang/String;"); long hash = ret.intValue() & 0xffffffffL; StringObject st1 = vm.getObject(hash); //*这里要处理下字符串 String byteString = st1.getValue(); StringBuilder builder = new StringBuilder(byteString.length()); for (int i = 0; i < byteString.length(); i++) { if (byteString.charAt(i) == '0') { builder.append('1'); } else { builder.append('0'); } } //获取encodeByte地址 ret = TTEncryptUtils.callStaticJniMethod(emulator, "encodeByte(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", //传参,这里需要两个字符串,所以就传入两个参数 vm.addLocalObject(new StringObject(vm, "要加密的值")), vm.addLocalObject(new StringObject(vm, builder.toString()))); //ret 返回的是地址, hash = ret.intValue() & 0xffffffffL; //获得其值 StringObject str = vm.getObject(hash); System.out.println(str.getValue()); return str.getValue(); }}
上边代码有jni的类是哪一个需要知道,就是下面这个类,这个其实是和加载so有关系的。
TTEncryptUtils = vm.resolveClass("com/*/aesjni/AESEncrypt");
我们需要逆向app,这里不细说如何在app中寻找加载so的类。如下图,encodeByte是该app调用native层加密的入口,loadLibrary是java加载so的方法,这个类就是上述代码中填写的。
然后再看"encodeByte(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"这里,这是smali写法,不补基础,后面跟上需要传的参数,
getByteValues 这个方法是毒获取的一个01字符串,并且在java层进行了处理,然后再传进encodeByte里面,encodeByte这个方法最后获取的其实并不是最终需要的,需要md5才是最后的newSign。可以验证一下下。
测试结果通过。
启动java文件时候注意这个改成自己的平台!!!
VM options: -Djava.library.path=prebuilt/os -Djna.library.path=prebuilt/osWhere os may: linux64, win32, win64, osx64
最后,小编想说:我是一名python开发工程师,整理了一套最新的python系统学习教程,想要这些资料的可以关注私信小编“01”即可,希望能对你有所帮助。