爬虫工程师的unidbg入门教程

  • 如今不少的app使用了so加密,之后会愈来愈多。爬虫工程师可能会直接逆向app,看java代码,完成java层的算法破解,可是若是遇到so该怎么办呢?可能你会直接破解so,可是真的会有不少爬虫工程师会去而且会破解so吗?有时候咱们能够不用破解so,利用不少大佬写好的轮子便可完成so的调用。
  • 说到调用,就有不少方法了,好比用frida的rpc、xposed+andserver、再者就是unicorn+web框架等等,今天要说的并非这些,而是unidbg,这框架有什么好的地方呢?看看介绍。

介绍(来自逸飞)

unidbg 是一个基于 unicorn 的逆向工具,能够黑盒调用安卓和 iOS 中的 so 文件。unidbg 是一个标准的 java 项目。java

因为如今的大多数 app 把签名算法已经放到了 so 文件中,因此要想破解签名算法,必须可以破解 so 文件。可是咱们知道,C++ 的逆向远比 Java 的逆向要可贵多了,因此好多时候是无法破解的,那么这个时候还能够采用 hook 的方法,直接读取程序中算出来的签名,可是这样的话,须要实际运行这个应用,须要模拟器或者真机,效率又不是很高。linux

unidbg 就是一个很巧妙地解决方案,他不须要直接运行 app,也无需逆向 so 文件,而是经过在 app 中找到对应的 JNI 接口,而后用 unicorn 引擎直接执行这个 so 文件,因此效率也比较高。git

  • 这里重要的是目前利用unidbg+springboot作成了web服务。

食用

案例来自JXU2QkQyYXBwJTIwdjQuMTYuMA==
对于该app而言,是很是适合入门的一个app,未加固、算法简单、很容易找到so的jni。
先去凯神的github上下载https://github.com/zhkl0228/unidbg
下载完毕用idea打开,等待maven下载完毕。我这里已经建立好du的文件。github


上个代码看着比较方便,代码中有不少注释web

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的方法,这个类就是上述代码中填写的。spring


而后再看"encodeByte(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"这里,这是smali写法,不补基础,后面跟上须要传的参数,
getByteValues 这个方法是毒获取的一个01字符串,而且在java层进行了处理,而后再传进encodeByte里面,encodeByte这个方法最后获取的其实并非最终须要的,须要md5才是最后的newSign。能够验证一下下。springboot


测试结果经过。app

最后

启动java文件时候注意这个改为本身的平台!!!框架

VM options: -Djava.library.path=prebuilt/os -Djna.library.path=prebuilt/os
Where os may: linux64, win32, win64, osx64


最后这个文件放在https://github.com/zhaoboy9692/dailyanalysis喜欢的能够star,谢谢。

相关文章
相关标签/搜索