文章中所有内容仅供学习交流使用,
不用于其他任何目的,敏感信息均已
做脱敏处理。
严正声明禁止用于商业和非法用途,
否则由此产生的一切后果与作者本人无关。
若有侵权,请在vx【amuncocoL】联系作者。
1
Quic协议抓包及sig3定位
版本:12.6.11
抓包思路为hook quic后,
协议降级进行http抓包
sig3定位通过
NSMutableDictionary类为突破口
如下:
frida-trace -UF (脱敏) -m "*[NSMutableDictionary setValue:forKey:]"
过程就不赘述了┗|`O′|┛ 嗷~~(体力活)
最终定位到sig也在此分析过程中
比较简单params排序后进行md5 略过~
sig3生成所在的函数 createSig3WithSig:
最终生成算法进入commonFramework
核心算法在c++层 进入sub_11EF6C
看函数开头就知道‘不复杂’
2
sub_11EF6C固定参数后主动调用和trace
固定入参:
由上图可以看到结果还是在变。
经过我认真且一丝不苟的猛猛分析
发现内部在获取随机数~
找到时间戳和随机生成盐值函数的调用。
固定一下,再次调用,固定成功:
生成一份trace文件,方便后续分析使用
并搜索下结果:
对应ida中120e40处如下:
可以看到v71是结果,v71又来自v56,
但是在ida中v56来自哪就没识别到了。
回到trace中,结果存储的地址为:0x2830f4940
搜索下:
ida对应如下:
上图sub_11D9C0方法的
第一个参数,已经有结果了。
继续找上层调用:
到ida中查看:
IDA又没识别出,这里直接打印a1
能看出a1就是结果。
继续看sub_1355D4方法的上层调用:
又回来了开始的地方,看v139逻辑,
正是结果生成地方:
跟IDA识别一样
v139 = (unsigned __int8 *)&v149;
sub_11DC48((__int64)&v138);
v137 = 0;
for ( k = 0; (unsigned __int64)k <= 0x16; ++k )
v137 += v139[k];
if ( v137 >= 256 )
v137 = -v137;
v135 = v137;
v140 |= (unsigned __int8)v137 << 24;
v155 = v140;
for ( x = 0; (unsigned __int64)x <= 0x16; ++x )
v139[x] ^= v135 ^ (unsigned __int8)x;
v135很容易算出来,那么核心就是找v149了
打印v149内容得到:
16e3c6060 41 51 2d 10 2a eb a8 14 27 00 00 00 7d f5 c4 d9 AQ-.*...'...}...
16e3c6070 30 b9 98 66 15 0d 00 21 00 00 00 00 00 00 00 00 0..f...!........
分割下
41 51 2d 10
2a eb a8 14
27 00 00 00
7d f5 c4 d9 // 不确定
30 b9 98 66
15 0d
上述只有第四部分不确定其他都是随机
和固定的随机数。
找下7d f5 c4 d9的生成逻辑。
3
CRC32
样本中有很多花指令感兴趣的可以去除一下
这直接看trace
结果来自0x120acc,进入IDA查看
进入sub_104D48方法:
frida跟踪下0x104D48:
参数1大小0x30字节,经过参数3生成最终结果。
参数1:
280cc4040 73 64 18 9a 37 3c 9b 1b 65 20 45 e8 0e b2 c1 7a sd..7<..e E....z
280cc4050 53 a9 ec cb b4 35 9f bc fe 5b 43 be 47 08 1e 59 S....5...[C.G..Y
280cc4060 d9 b0 c3 12 28 13 1c 9d 62 81 3f ec 70 ae c3 af ....(...b.?.p...
参数3
10ec80cc8 b7 1d c1 04 ff ff ff ff ff ff ff ff 01 01 00 00 ................
10ec80cd8 63 63 63 63 7c 7c 7c 7c 77 77 77 77 7b 7b 7b 7b cccc||||wwww{{{{
找下上一步参数3生成的地方,在上层函数里:
看到crc32标识,验证下:
验证通过
4
白盒AES
继续跟进上一步的参数1的生成逻辑。
也就是:
280cc4040 73 64 18 9a 37 3c 9b 1b 65 20 45 e8 0e b2 c1 7a sd..7<..e E....z
280cc4050 53 a9 ec cb b4 35 9f bc fe 5b 43 be 47 08 1e 59 S....5...[C.G..Y
280cc4060 d9 b0 c3 12 28 13 1c 9d 62 81 3f ec 70 ae c3 af ....(...b.?.p...
搜索下这块参数中的字段:
逐个排查,并分析此处体力活跳过
最终发现结果地方为sub_136ABC方法:
跟踪下发现结果填充了一堆0x10
改下入参为't'结果填充了一堆0xf
确认为pkcs7填充模式
继续看
跟下sub_1061C0:
看起来不是很清晰,这样改一下
// 入参
d2 cf bd c2
b1 e4 48 42
f2 8f a7 b9
ae 40 ce b4
// 返回值
d2 cf bd c2
e4 48 42 b1 //左移1
a7 b9 f2 8f // 左移2
b4 ae 40 ce //左移3
基本确定为白盒AES,进行dfa故障攻击
// dfa写入 字节
Interceptor.attach(base.add(0x1061C0), {
onEnter: function(args) {
counter++;
if(counter == 9){
var offset = getRandomNumberBetween(0,15);
var value = getRandomNumberBetween(0,0xff);
// console.log('getRandomNumberBetween:',offset,value);
args[0].add(offset).writeS8(value);
}
console.log(`onEnter 0x1061C0 ${counter}次:`,'\n',hexdump(args[0],{length:0x10}))
},
onLeave: function(retval) {
//console.log('onLeave 0x1061C0:',hexdump(retval));
}
});
function getRandomNumberBetween(minNum, maxNum) {
if (arguments.length === 1) {
return parseInt(Math.random() * minNum + 1, 10);
}else if (arguments.length === 2) {
return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10);
}else {
return 0;
}
}
脚本如上通过主动调用触发,并重置counter。
拿到正确密文和故障后的密文结果
验证成功
5
Hmac-sha256
跟上述中sub_106234的入参a2:
d2 b1 f2 ae cf e4 8f 40 bd 48 a7 ce c2 42 b9 b4 .......@.H...B..
66 3b 83 fc 14 cf 4c d2 35 a3 67 93 56 14 57 bb f;....L.5.g.V.W.
通过trace的指令及调试分析
最终定位结果在sub_128F78生成
sub_128F78片段如下:
frida打印下入参和结果:
这块为标准的HMAC-SHA256加密
分析过程不再赘述~
Android端感兴趣的可以看如画大佬的文章
有详细分析过程.
致敬大佬
https://bbs.kanxue.com/thread-281317.htm
上图中此部分还原秘钥
验证如下:
6
总结
本次样本的分析还原就到这里啦~
ps:为了家里娃的奶粉,近期想换坑
有坑的大佬捞捞俺~
完结~撒花~ ✿✿ヽ(°▽°)ノ✿
下次再会。