声明
仅供学习交流使用,切勿用于违法犯罪.
抓包
看登录请求要逆向的参数有password,deviceUniqueid,sign,deviceid,还有头部的Authorization,Authorization是后来发现的,这个如果正确登录了之后就失效了,需要重新请求获取,这个比较简单,在这先说明一下,如果登录成功再登录一次,不换Authorization就会出现下面的
这里参数比较多,先hook看一下有没有java层的加密,有的话可以减少在java层花费的时间,有更多时间还原so层的加密,算法自吐或者算法助手,我比较喜欢用算法助手.这个sign算法除了登录需要,其他搜索类的等等接口都需要.
password
搜一下0e031dcac2bfa6decad46f54abeac50e,我输入的是123456
点进去一个看看
搜一下明文779226276cd4d68ebb04f827b0a158cb,可能加密了多层
很明显了,拼接了两层DHX_password:
password分析完毕
deviceUniqueid
搜一下b9a4fdc908cdfff3158361cf512fb9f8
明文是unknown26:2c:5d:67:ca:95,后面那串可能是mac地址或者蓝牙地址
sign
76e166ab73f32c4bd5984822ba9b1d24||d,由前面32位和"||d"拼接组成的
搜索下前32位试试,搜了下没有结果,大概率就是so层的算法了.接下来就是定位加密位置了.
搜索大法
太多了,肯定不可能一个一个点过去看,这样太费劲了.换个思路hook
hashmap.put
var hashMap = Java.use("java.util.HashMap");
hashMap.put.implementation = function (a, b) {
// if (a != null && a.equals("sign")) {
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()))
console.log("hashMap.put: ", a, b);
// }
return this.put(a, b);
}
搜一下没有结果,换个hook点
StringBuilder.toString
var sb = Java.use("java.lang.StringBuilder");
sb.toString.implementation = function () {
var retval = this.toString();
if (retval.indexOf("||d") != -1) {
console.log("StringBuilder.toString: ", retval);
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()))
}
return retval;
}
sign后面有个"||d",所以最终的sign字符串可能是通过StringBuilder.append添加上去的,可以hook stringBuilder.toString() 输出最终的字符串
有结果,32位数字来自str2,经过了getmd5方法,但是java层的md5没有hook到,应该是so的md5
就在下面
let EncryptImpl = Java.use("com.android.icredit.utils.EncryptImpl");
EncryptImpl["getMd5"].implementation = function (str) {
console.log(`EncryptImpl.getMd5 is called: str=${str}`);
let result = this["getMd5"](str);
console.log(`EncryptImpl.getMd5 result=${result}`);
return result;
};
hook下这个native方法
先验证下是不是md5,万一它连盐都没有加直接就出结果了就可以不用看so了
验证是标准的md5 sign分析完毕,入参由deviceId+时间戳+盐值组成
除此以外,如果你觉得他是so的算法,也可以hook jni函数,比如NewStringUTF
因为很多大厂的算法你通过上面介绍的方法很有可能hook不到,但是hook NewStringUTF有可能会出现你想要的字符串,但是如果结果通过字节数组返回的,那也会出现hook不到的情况.
NewStringUTF
var addrNewStringUTF = null;
var symbols = Module.enumerateSymbolsSync("libart.so");
for (var i = 0; i < symbols.length; i++) {
var symbol = symbols[i];
if (symbol.name.indexOf("NewStringUTF") >= 0 && symbol.name.indexOf("CheckJNI") < 0) {
addrNewStringUTF = symbol.address;
console.log("NewStringUTF is at ", symbol.address, symbol.name);
}
}
if (addrNewStringUTF != null) {
Interceptor.attach(addrNewStringUTF, {
onEnter: function (args) {
var c_string = args[1];
var dataString = c_string.readCString();
if (dataString) {
if (dataString.length==32) { //这里写要对字符串的筛选
console.log(dataString);
//读取当前是在那个so文件,那个地址
// console.log(Thread.backtrace(this.context, Backtracer.FUZZY).map(DebugSymbol.fromAddress).join('\n') + '\n');
//先用下面的
// console.log(Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n') + '\n');
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
}
}
},
});
}
中间有个if判断 if (dataString.length==32),可以输出你想要的字符串的堆栈信息如果输出太多app可能会崩溃.
deviceid
这个似乎不是java层的,而是webview页面生成的.它的另一个名称是qcc_deviceid
并且把app清理缓存后这个值就不一样了,搜索也可以看到有js文件,这个不是重点了,感兴趣的自己试着还原一下吧
最后
案例比较简单,我也是挑的相对热门一点的需要爬虫业务的app来分析的
主要就是hook定位关键代码,有搜索大法,不过这个已经越来越不使用了,hashmap.put,StringBuilder.toString,base64,string.getbytes等等,以及so的NewStringUTF,还是需要根据经验来定位.
文章老是莫名被某sdn删掉,然后还审核不通过,后面估计发自己博客了