【iOS逆向】某短视频sig3算法分析还原

 
 

    文章中所有内容仅供学习交流使用,

不用于其他任何目的,敏感信息均已

做脱敏处理。

    严正声明禁止用于商业和非法用途,

否则由此产生的一切后果与作者本人无关。

若有侵权,请在vx【amuncocoL】联系作者。

1‍

   Quic协议抓包及sig3定位‍‍‍‍‍‍‍‍‍‍

版本:12.6.11‍‍‍‍

抓包思路为hook quic后,

协议降级进行http抓包

sig3定位通过

NSMutableDictionary类为突破口

如下:

frida-trace -UF (脱敏) -m "*[NSMutableDictionary setValue:forKey:]"

9e058c94aa273afa2d970d3f404e2678.jpeg

过程就不赘述了┗|`O′|┛ 嗷~~(体力活)

最终定位到sig也在此分析过程中

比较简单params排序后进行md5 略过~

sig3生成所在的函数 createSig3WithSig:

0db8ecc0f9657001886fcca07a69ef77.jpeg

最终生成算法进入commonFramework

b685d772e87e21722bed1caca354a689.jpeg

核心算法在c++层 进入sub_11EF6C

ed2156e265ad65350fd8a8ce13f97ab9.jpeg

看函数开头就知道‘不复杂’

701d5e0b3ea1907d623db854bef06214.jpeg

 
 

2‍

sub_11EF6C固定参数后主动调用和trace‍‍‍‍‍‍‍‍‍‍

固定入参:

874302b79c5b779aa3d20cf9932ccf4e.jpeg

由上图可以看到结果还是在变。

经过我认真且一丝不苟的猛猛分析

发现内部在获取随机数~

找到时间戳和随机生成盐值函数的调用。

固定一下,再次调用,固定成功:

0e31387433b2d5010bd494f66b9899fe.jpeg

生成一份trace文件,方便后续分析使用

并搜索下结果:

02fccf7136eb0d031c025d6624876012.jpeg

对应ida中120e40处如下:

48e6415a9beaf31d729bdb0a9437b67b.jpeg

可以看到v71是结果,v71又来自v56,

但是在ida中v56来自哪就没识别到了。

回到trace中,结果存储的地址为:0x2830f4940

搜索下:

ff2d0263cc3795c2a09f0daf4b7f580c.jpeg

ida对应如下:

988603b0f9c9ff7a4884a9a5d56e97a1.jpeg

上图sub_11D9C0方法的

第一个参数,已经有结果了。

继续找上层调用:

7706ab7304160a38444f1175677d495f.jpeg

到ida中查看:

3309d79a7ffb6faf0858ae776898da74.jpeg

IDA又没识别出,这里直接打印a1

能看出a1就是结果。

继续看sub_1355D4方法的上层调用:

caa4a1c023057c8fa72bbb2f6d76b0a8.jpeg

又回来了开始的地方,看v139逻辑,

正是结果生成地方:

ddef2703ac9d0a7e48c6cf6ad1861ec6.jpeg

跟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

cb4b2e9f452c0e7a17de072780d35290.jpeg

结果来自0x120acc,进入IDA查看

7221dc8d40496e406251eadc69985afb.jpeg

进入sub_104D48方法:

5e57e861e20b02149520e0c51e34e630.jpeg

frida跟踪下0x104D48:

4c44f1d2f894e5ab69936131a506750b.jpeg

参数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生成的地方,在上层函数里:

8ec94d5d9802bc32946fb8d95cd1ad83.jpeg

看到crc32标识,验证下:

df5115842d47ab4781664376c60c4b62.jpeg

验证通过

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...

搜索下这块参数中的字段:

02279a8da71224561cd967ee6fbb280d.jpeg

逐个排查,并分析此处体力活跳过

最终发现结果地方为sub_136ABC方法:

ed309501ad0b441582d3a256df017317.jpeg

跟踪下发现结果填充了一堆0x10

fc1d54fca926eccfd227188d102dbe00.jpeg

改下入参为't'结果填充了一堆0xf

d2d4c0e39b509e99fdb663b85a5d6841.jpeg

确认为pkcs7填充模式

继续看

47399dd57465a98fdd433964dee24afc.jpeg

跟下sub_1061C0:

b8c1664ab3c49167ae92ec3a6471d89c.png

看起来不是很清晰,这样改一下

// 入参
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。

拿到正确密文和故障后的密文结果

cc4bcbb41fc944edb3bb1c4f865f92ac.jpeg

验证成功

47340ba054cc7af36c2d7bdb66540a95.jpeg

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的指令及调试分析

b77e5047ee8607ea3bf3e8d07a1fc6d5.jpeg

最终定位结果在sub_128F78生成

sub_128F78片段如下:

edec0b3a7c1e995578730ecbe0bdd855.jpeg

frida打印下入参和结果:

f724121a753480b46b24cb61786dd7f8.jpeg

这块为标准的HMAC-SHA256加密 

分析过程不再赘述~

Android端感兴趣的可以看如画大佬的文章

有详细分析过程.

致敬大佬

https://bbs.kanxue.com/thread-281317.htm

上图中此部分还原秘钥

11f190ec8c6b308bec2e694bc589f94f.jpeg

验证如下:

3edc1c7d5ec4217a44e75632b6adb603.jpeg

6
‍

总结‍‍‍‍‍‍‍‍‍‍

      本次样本的分析还原就到这里啦~

ps:为了家里娃的奶粉,近期想换坑

有坑的大佬捞捞俺~‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

        完结~撒花~ ✿✿ヽ(°▽°)ノ✿

        下次再会。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值