Frida后记——看我是怎么不用脱壳&逆向来解密APP的数据

这篇文章其实是前面的Traceview+frida这对绝配的组合的一个案例,不了解的可以去可以去看我直接写的几篇文章,这次以之前的一个搞的投资APP为例,这个应用数据是加密的,而且apk本身是360加固的,当时一看麻烦又要脱壳又要逆向就没弄,这次正好拿它来开整。
点击投资这个页面(或者刷新)

方法一:我们可以直接hook Cipher.init这个关键函数就能获得所有的模式,key和iv了,下面给出脚本

Java.perform(function x() {   

function toHexString(byteArray) {
  return Array.prototype.map.call(byteArray, function(byte) {
     return ('0' + (byte & 0xFF).toString(16)).slice(-2);
  }).join('');
}

    var Cipher = Java.use('javax.crypto.Cipher');
    var iv_parameter_spec = Java.use("javax.crypto.spec.IvParameterSpec");
    Cipher.init.overload("int", "java.security.Key", "java.security.spec.AlgorithmParameterSpec").implementation = function (arg1,arg2,arg3) {
    send("arg1: "+arg1);

    //refer to https://11x256.github.io/Frida-hooking-android-part-5/
    var b = arg2.getEncoded();
    var printable_key = ""
    for (var i = 0; i < b.length; i++){
      printable_key += (b[i].toString(16) + "");
    }
    send("key: "+printable_key);
    
    var temp=new Uint8Array(Java.cast(arg3, iv_parameter_spec).getIV());
    send("iv: "+toHexString(temp));
    this.init(arg1,arg2,arg3);
    }; 
    
});

当然这只适用于密钥是可见明文的情况,下面给出更通用的方法。
方法二:对比AES的解密实现,重点关注下面三个地方
Desktop Preview

使用frida来hook这三个地方,注意1,3两处我们要hook构造函数,和普通的2处函数是有区别的,要用$init这种形式,并且要return this.$init(arg1,arg2)调用原始的函数实现。(其中 cipher.init(Cipher.DECRYPT_MODE, keySpec, iv)函数并不能找到这个重载)。 然后坑点又来了,这个bytes类型并不能直接打印,会输出[object Object],然后搜索了好多也没解决问题,这里应该用到一个Google搜索的技巧——双引号,这样精确匹配

使用Frida逆向分析Android应用与BLE设备的通信 按照这篇文章所说的稍作修改终于可以打印byte数组了(注意这里不能用js里的fromCharCode转换成String,因为APP的密钥等不一定是可见明文,应该输出原始的十六进制数据)……我们分别来hook AES的mode,Key,iv,脚本如下

# -*- coding:utf-8 -*-
import frida, sys


def on_message(message, data):
    if message['type'] == 'send':
        print("[*] {0}".format(message['payload']))
    else:
        print(message)

jscode = """
Java.perform(function x() {
    // Function to hook is defined here
    var UtiEncrypt = Java.use('com.bianlidai123.bc.encrypt.UtiEncrypt');

    // Whenever button is clicked
    UtiEncrypt.decryptAES.overload('java.lang.String').implementation = function (arg1) {
        // Show a message to know that the function got called
       

       var sign=this.decryptAES(arg1);
             
       send("arg1:"+arg1);
       send("sign:"+sign);
       return sign;
       
    };
    
    var Cipher = Java.use('javax.crypto.Cipher');
    Cipher.getInstance.overload('java.lang.String').implementation = function (arg1) {
         var sign2=this.getInstance(arg1);
         send("Instance:"+arg1);
         return sign2;
    
    };

    var SecretKeySpec = Java.use('javax.crypto.spec.SecretKeySpec');
    SecretKeySpec.$init.overload('[B', 'java.lang.String').implementation = function (arg1,arg2) {
     hexstr="";
    for (i=0;i<arg1.length;i++)
    {
        b=(arg1[i]>>>0)&0xff;
        n=b.toString(16);
        hexstr += ("00" + n).slice(-2)+" ";
    }
        send("Key: " + hexstr);
      //send("init1:"+arg1+arg2);
        return this.$init(arg1,arg2);
    
    };
    
    var IvParameterSpec = Java.use('javax.crypto.spec.IvParameterSpec');
    IvParameterSpec.$init.overload('[B').implementation = function (arg1) {
    hexstr="";
    for (i=0;i<arg1.length;i++)
    {
        b=(arg1[i]>>>0)&0xff;
        n=b.toString(16);
        hexstr += ("00" + n).slice(-2)+" ";
    }
        send("Iv: " + hexstr);
      //send("init4:"+arg1);
        return this.$init(arg1);
    
    };
    
});
"""

process = frida.get_usb_device().attach(8309)
script = process.create_script(jscode)
script.on('message', on_message)
print('[*] Running CTF')
script.load()
sys.stdin.read()

Desktop Preview

成功获取到Key和iv之后,我们就可以去解密数据了

这样就完成了一次不需要脱壳加逆向就能解密APP的数据了!!!