今天我们要分析的app 是5Y2K5qyh5YWD54mI5pys5Y+3djUuMC44 ,小伙伴们可以去各大应用商定自行下载。
1.先抓个包
老规矩,先用charles 抓个包,可以看到该app用的更多是post请求,请求参数 叫 “data”,请求值很明显被加密了,那么今天研究的参数就是它,发现字符串是以"="结尾,根据经验大胆的猜测是被base64处理过,猜测估计是使用了AES/DES或者一些其他算法。
2.jadx静态分析
直接搜索"data" ,肯定会有一大堆返回结果。那么就换个思路,比如一些常用词,比如AES ,DES,base64,encryp。这里搜索AES
然后 看这里(SecretKeySpec secretKeySpec = new SecretKeySpec(randomString.getBytes(Charset.defaultCharset()), "AES");
)感觉很像,来点进去看一看。
看关键词,这里就很明显使用了AES加密的ECB模式,在ECB模式中只需要一个16位的key。
然后继续追进去看,发现key 是randomString.getBytes(Charset.defaultCharset())
这个方法返回来的,它又是randomString = getRandomString(i);
方法回来的,继续往上追。
这里来到了private static native String getRandomString(int i);
,看关键词 native,好吧,加密的key 是来自于so层的。这时候需要看看so里面的逻辑是咋写的,不过从so里面返回的只是key,理论上来说这个key 应该是固定的,所以说我们可以直接hook它在java层的返回结果。
3.frida动态调试
打开frida服务,运行命令:
frida -U com.banciyuan.bcywebview -l hook_banciyuan_java.js
js代码如下:
Java.perform(
function(){
var Encrypt = Java.use("com.banciyuan.bcywebview.utils.encrypt.Encrypt");
Encrypt.a.overload("java.lang.String").implementation = function (str){
console.log("↓↓↓↓↓↓↓↓↓↓hook开始↓↓↓↓↓↓↓↓↓↓↓↓↓↓");
console.log("AES加密前参数: "+str);
var res = Encrypt.a(str);
console.log("AES加密后结果: "+res);
console.log("↑↑↑↑↑↑↑↑↑↑↑hook结束↑↑↑↑↑↑↑↑↑↑↑↑↑");
return res;
}
Encrypt.getRandomString.implementation = function (a) {
console.log('getRandomString入参数: ', a);
var aes_key = this.getRandomString(a);
console.log('加密的key: ', aes_key);
return aes_key;
}
// Encrypt.b.implementation = function (str, str2){
// console.log("b方法参数1: "+ str);
// console.log("b方法参数2: "+ str2);
// // console.log("↓↓↓↓↓↓↓↓↓↓↓↓打印b方法调用的堆栈↓↓↓↓↓↓↓↓↓↓↓↓");
// // printStack();
// // console.log("↑↑↑↑↑↑↑↑↑↑↑↑打印b方法调用的堆栈↑↑↑↑↑↑↑↑↑↑↑↑");
// var bres = Encrypt.b(str, str2);
// console.log("方法返回结果: "+ bres);
// console.log("方法返回结果: "+ Object.keys(bres))
// send(bres)
// return bres;
// }
function printStack(){
var threadef = Java.use('java.lang.Thread');
var threadinstance = threadef.$new();
var stack = threadinstance.currentThread().getStackTrace();
for(var i = 0;i<stack.length;i++){
send("stack:" + stack[i].toString());
}
}
}
)
效果如图:
这里就很明显的能看到,输入的参数和输出的结果以及key。
然后key已经被hook出来了,接下来,打开对应的so文件,去验证下我们的猜想。
查看so文件
这里的so文件叫 librandom.so
,在导出表里面直接看到so层的方法名Java_com_banciyuan_bcywebview_utils_encrypt_Encrypt_getRandomString
,然后还找到对应的aes-key,也验证了hook出来的值。
4.python还原一下
该app使用的是AES加密算法,对该算法不清楚的可移步这篇文章(python 实现AES加密算法)。
可以看到data 计算的结果,和hook出来的值一样,然后请求也成功了。
完事,手工。