cuscuta的草稿 - 3
呃啊……
这种事情真的很奇怪啦……
好吧……
其实上一篇说的不太详细,如果我们把栈空间中连续读取的那一段直接dump出来的话,我们会发现下面的东西:
1 | 88 6A 3F 24 D3 08 A3 85 2E 8A 19 13 44 73 70 03 |
现在令我好奇的有两点:
- 后面10字节的内容中的
29 30 58 02到底是哪里来的…… - 时间戳后的
31 5C 18 C9 32 0E 38 60 EE 04 F0 14 38 39 E7 49 88 17 9C 5D 2A 6F 3E 0D 91 BA A1 5A A9 57 BA B5 01(33字节)似乎并不是随机的,我也想知道它到底是哪里来的……
再看看吧……
对于29 30 58 02,前两个字节是以立即数的形式直接硬编码在代码中,而后面两个字节经过了复杂的变换,目前表面上只知道有40 01和58 02两种选择……这里还是有待进一步分析……
补录:根据动态分析,在登录时,上面的值第一段是28 02,第二段是80 02……实在找不到什么规律……
至于31 5C 18 C9 32 0E 38 60 EE 04 F0 14 38 39 E7 49 88 17 9C 5D 2A 6F 3E 0D 91 BA A1 5A A9 57 BA B5 01这33字节,我并没有通过序列搜索直接找到它,但是考虑到它是memcpy过来的,并且最后的01还是在送进白盒前刻意加上去的,所以有效的部分只有前32字节(舒服了),我还能说些什么呢……
新的分析
前几天复现了第二段(后半段)内容的生成,现在我尝试故技重施来复现前半段内容的生成……
前半段内容的“加密”汇编和后半段内容惊人的相似,所以我将前一次使用的方法几乎原封不动地放到了这一次;并且它与后半段处理的区别似乎仅仅在于输出的地址不一样了
最大的不同在于输入的数据,在前一次(第二段)中,输入的数据如下:
1 | 88 6A 3F 24 D3 08 A3 85 2E 8A 19 13 44 73 70 03 |
而在这一次(第一段),输入的数据变成了这样:
1 | 88 6A 3F 24 D3 08 A3 85 2E 8A 19 13 44 73 70 03 |
不难发现,更改的地方只有两处,第一处是从0x28开始的Path缺失了,第二处就是0x98-0x99的两字节不同
并且,由于处理前后的两段汇编输入地址几乎毫无差别,所以我尝试了仅更改输入栈数据的内容,然后更换汇编,结果两段汇编执行结果的不同仅仅是输出到栈的地址不一样了,意料之外但是情理之中,我有理由怀疑这是同一个函数内联了两次,算法极有可能是一样的
接下来就是……
接下来就是那个难缠的BSS段,我尝试使用MemoryAccessMonitor来监控这个段的内存,但是不知道为什么,onAccess总是触发不了,而dump显示,这个在BSS段上的32字节又确确实实被改变了……(设备问题吗……?)
读写这段BSS内容的代码很多,我怀疑是被当全局变量用了……
顺便,在登录时,前面那段Hash(31 5C 18 C9 32 0E 38 60 EE 04 F0 14 38 39 E7 49 88 17 9C 5D 2A 6F 3E 0D 91 BA A1 5A A9 57 BA B5)仍然与调用content_bundle的值相同,有理由怀疑这个值和版本相关,并且这个BSS段的内容我推测极有可能是那种先写入再读取出来解密的模式……
由于MemoryAccessMonitor的败北,我不得不hook所有引用这个段的操作,不过万幸的是,经过过滤,我们可以认为这个段仅仅被操作了两次:
1 | suses_23874132 triggered! |
可以注意到,第二次trigger就是把内部的值拉出来之后进行处理的那一步(在超大函数内),而第一次trigger则是来自负责写入到这个BSS段内的操作的函数,这个函数没有输入参数,比较纯,目测可以轻易离体调用
回到超大函数内,根据静态分析,这里的操作十分复杂,涉及到多个十分令人迷惑的步骤,且地址分散,静态还原难度大,动态跟踪需要完整环境,目前这是最棘手的地方……我能想到的方法仅有每次都动态分析……
回去看看……?
为了在变化中寻求不变,我翻看了曾经的so文件和分析日志,有了一些新的发现,尽管这些都是表观上来看的
首先,令人欣慰的是,88 6A 3F 24 D3 08 A3 85 2E 8A 19 13 44 73 70 03 22 38 09 A4 D0 31 9F 29 98 FA 2E 08 89 6C 4E EC(也就是送进白盒中输入的”Header”)跨越了2个版本依旧不变,虽然我仍未理解这个常量的用途,但目前来看,结果是令人省心的
另外,在旧的分析日志中,29 30 40 01和29 30 58 02的输入尾没有改变,我可以比较“合理”的推测,在登录时的这个值也不会轻易变化……
小结……
目前的进展还算的上顺利,所以在这里做一个小结
首先,X-Random-Challenge的大致生成逻辑已经明了,大概分为以下步骤(不指定的话默认小端序 废话 ):
- 开辟两个160字节长的缓冲区,初始化为00,这里命名为B1,B2
- 往B1+0和B2+0写入32字节的常量C1:
88 6A 3F 24 D3 08 A3 85 2E 8A 19 13 44 73 70 03 22 38 09 A4 D0 31 9F 29 98 FA 2E 08 89 6C 4E EC - 往B1+32和B2+32写入8字节的UNIX时间戳
- 往B1+40写入32字节的版本相关常量C2:
31 5C 18 C9 32 0E 38 60 EE 04 F0 14 38 39 E7 49 88 17 9C 5D 2A 6F 3E 0D 91 BA A1 5A A9 57 BA B5,依版本而异 - 往B2+40写入Path,然后继续向B2+40+len(Path)写入常量C2
- 往B1+40+1写入01,往B2+40+len(Path)+1写入01
- 往B1+160-10以及B2+160-10写入10字节的常量C3:
29 30 40 01、29 30 58 02,依请求而异 - 将B1和B2分别作为输入,使用黑盒过程F处理,得到两个24字节的处理结果R1、R2
- 开辟一个56字节长的缓冲区,命名为O
- 向O+0写入8字节的UNIX时间戳,向O+8和O+32分别写入R1和R2
- 将O使用Base64编码,即得
X-Random-Challenge的值
接下来……?
很明显,只有这些可不行,我们接下来的要做的事情还有一些
- 首先是上文中提到的版本相关常量C2,上文也提到这个值无论是单so离体调用或者静态分析都比较难解出来
- 再者是上文中提到的请求相关常量C3,这个值的特征是(按目前的观测)永远以
29 30开头,并且似乎不会变,但生成原理和含义有待商榷 - 最后,黑盒过程F是一个很高纯度的函数——额,我指的是它很像一个纯函数,所以可以被我们提取出来加以利用,但是我们并不保证这个函数在未来的版本中不会变……
我们最终的目的,是利用这个过程,做到完全离体生成这个值……
直接调用超大函数并不现实,函数很大,引用了BSS段,并且在参数中还含有地址——我不敢乱动,或者说不太现实
我们现在最大的壁垒,在于版本相关常量C2,它的生成过程复杂到目前以我的三脚猫技术只能在软件正常运行时看到它——不过我相信实现脱离软件仅靠执行so文件来调出它只是时间问题……吗……?
哈啊……
好困ww