
定位到 libcryptoDD.so文件
ida打开查看,发现不是静态注册
直接丢入unidbg,不做任何开启日志打印一下,发现了它的动态注册

地址是0x41ed8, 上ida中按下G, 跳转到这个地址,到此我们发现了so中函数的入口

将明文分组,解密后直接成为密文分组,分组之间没有关系
这里我是使用frida, hook,先打印一下参数
发现实际请求中 bArr2是固定的,且长度是32, 合理怀疑是key, 或者iv

而i10值有时候是2, 有时候是3, 这个分析java层代码可以知道,应该就是标识不同类型的请求的

所bArr这里就是我们的input_data, 我们自己构造加密判断一下它到底是ecb还是cbc
返回内容如下:

至此, 可以得知,他是ecb加密,而且是128位的分组加密
将参数增加,hook调用一下so中的localAESWork这个函数看看是否存在问题

看到了出现了问题,这时候打印一下trace日志,看看问题出现的具体位置

可以清晰的看到,pc指针执行到0x17e6c的位置的时候,出现了错误
我们直接到ida中按下G, 找到这个位置看看具体什么情况

看到了,他是一个free函数
而unidbg本质是无法处理free函数的(虽然有的so中有free它有时候不报错,好像是因为malloc和free同时使用的原因???)
所以这里我们在unidbg中直接hook掉free函数的使用看看
再次运行发现成功返回值

此时,使用unidbg打印一次加密的完整trace留待后用
返回值: 9b4174988fdd7be26d28bb9d970b63b9
trace文件也拿到了
直接在trace文件中查找 0xb9 这个关键词,然后从后往前看,可以发现:

直接根据trace中这些点的pc地址,在ida中查找,可以找到这个位置
发现代码中存在大量的while, if, elseif 等控制语句
这种情况就是内部进行了控制流平坦化的混淆

这里代码就对应了起来,这个函数是sub_189C4,在这里用unidbg console debugger下断点简单查看下

其参数x0寄存器中存储的就是input_data值,
所以,sub_189C4这里应该就是aes函数的主要逻辑部分了
这里面的代码也是混淆过的,不太容易看出来具体逻辑
但是发现其中有一些高频出现的函数,比如

这就和aes中的分组加密有点相似的意思了。
详细具体的查看一下几个高频函数内部逻辑
其中几乎都是异或操作,
是轮密钥加逻辑的可能性比较大

大量异或操作,
怀疑轮密钥加过程,也可能是列混淆过程

看着有点像是行行位移操作

这个就很明显了,字节代换

段落引用
aes有9轮循环运算和1轮最终轮运算,
循环运算中又有四个板块 :字节代换,行位移,列混淆,轮密钥加
最终轮缺少 列混淆
我需要查看一下一个加密过程中,这四个函数分别都被调用了多少次
直接在trace文件中进行搜索
0x219cc
0x223e0
0x21bec
0x22810
已经确定了sub_21BEC是字节代换的流程,先查找pc指针到0x21bec的次数

可以看到是10次
同理查找其他函数的次数,发现
0x219cc 33次
0x223e0 10次
0x21bec 10次
0x22810 9次
重点查看下0x219cc 次数这个,因为字节代换都是从77841行开始,所以77841行之前的0x219cc的计数不能算,
减去的话,就是刚好10次

至此,这四个函数的整体逻辑也算是明白了
这时候已经知道了四轮过程,只要通过dfa差分攻击即刻获取到aes的密钥值

到这里算法分析就算是结束了,至于差分攻击如何使用,可以详细问问chatgpt
整体分析流程涉及了jadx + unidbg + frida + ida + trace分析 + aes算法,
适合新手入门
let CryptoHelper = Java.use("com.luckincoffee.safeboxlib.CryptoHelper");
CryptoHelper["localAESWork"].implementation = function (bArr, i10, bArr2) {
console.log(`CryptoHelper.localAESWork is called: bArr=${bArr}, i10=${i10}, bArr2=${bArr2}`);
let result = this["localAESWork"](bArr, i10, bArr2);
return result;
};
let CryptoHelper = Java.use("com.luckincoffee.safeboxlib.CryptoHelper");
CryptoHelper["localAESWork"].implementation = function (bArr, i10, bArr2) {
console.log(`CryptoHelper.localAESWork is called: bArr=${bArr}, i10=${i10}, bArr2=${bArr2}`);
let result = this["localAESWork"](bArr, i10, bArr2);
return result;
};
var fragInstances = [];
const targetClassName = 'com.luckincoffee.safeboxlib.CryptoHelper';
console.log(`[+] 开始扫描堆,查找所有 ${targetClassName} 实例...`);
Java.choose(targetClassName, {
onMatch: function (instance) {
fragInstances.push(instance);
},
onComplete: function () { }
});
var frag = fragInstances && fragInstances.length > 0 ? fragInstances[0] : null;
let v1 = Java.array('byte', new Array(32).fill(0))
let v2 = 2
let v3 = Java.array('byte', [-39,-11,119,92,32,-11,-15,28,107,-40,-69,-126,113,-96,42,-30,7,64,65,79,-113,48,84,98,-86,49,-120,-43,105,70,-49,55]);
let res = frag.localAESWork(v1,v2,v3)
console.log("res:", res)
var fragInstances = [];
const targetClassName = 'com.luckincoffee.safeboxlib.CryptoHelper';
console.log(`[+] 开始扫描堆,查找所有 ${targetClassName} 实例...`);
Java.choose(targetClassName, {
[培训]传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!