cuscuta的草稿 - 5
额……
原本我还以为,cuscuta只是一个后端设计,没想到最终牵扯到的东西竟真如cuscuta一般了……
botrytis
等等等等……我知道要讲scirpophaga,但是但是,在这之前,我得先介绍一下我的新伙伴嘛……
botrytis是一个新仓库,其中包含了一些我常用的脚本和一些偷懒的方法,合理地使用它理论上可以略微加速逆向分析的速度……?
目前,botrytis只有很小一部分内容,包括了供frida使用的快捷函数,和一个叫做sweep_visualizer的小脚本(真的很小);其中frida脚本有general_utils和memory_tracking
1 | /* |
1 | /* |
具体的部分可以到botrytis查看~
分析,试验,和scirpophaga
这些小家伙的名字一个比一个难记对吧哈哈
scirpophaga是一个自动化的提取工具,它的终极目标是:仅通过离体的so文件,在任意版本下,推导出C2的值,而无需动态分析
在此之前……
众所周知,在现有版本的情况下,某傻逼东西的X-Random-Challenge的生成,除必要输入外,需要依赖4个常量,分别是C1、C2、C31、C32,在前几张草稿纸中也有提到,C1目前看来是一个铁打的常量,它不随版本的变化而改变;C31、C32推测是与请求有关的常量,不同请求的C31、C32不同,但不随版本变化
所以,目前唯一棘手的地方在于C2——它是一个版本相关常量,它在3个版本的值均不同,可以推测,它在每个版本的值也都不同,若没有scirpophaga或者其类似替代方案,我们只能使用frida脚本在动态环境下将其dump出来,但这么做需要一个完整的,且处于调试环境下的Android环境,并且其自动化较为繁琐
C2_pre1,和C2_pre2
在上上张草稿纸上写过,C2的值和两个常量相关,下面展示和C2有关键联系的汇编片段:
1 | ; 截取自某版本module.so文件 |
为了方便起见,我们将这里的Q4、Q5所代表的值命名为C2_pre1;将这里的Q0、Q1(前)所代表的值命名为C2_pre2(按分析顺序定的序号),结合汇编,不难看出C2就等于C2_pre1 XOR C2_pre2
C2_pre1
为了方便,这里先不管上上张草稿的发现,从头开始讲吧
出于各种原因,我当时选择先看看更神秘的C2_pre1,根据汇编,也不难看出C2_pre1来源于一个神秘的地址,即stru_1996E90,在IDA跟踪这个地址,发现它是一个BSS段;在IDA中搜索这个BSS段的引用,发现这个BSS段足足有75处引用
这种引用量按静态分析的话我要当场砸电脑了,所以我尝试使用frida监控这段内存,但是不巧的是,MemoryAccessMonitor并没有给我满意的结果——在我的机器上,它根本就没有显示任何结果(不知道是不是设备的原因);既然这样行不通的话,就要使用更土的法子了,那就是直接把所有引用到它的地方全部hook个遍,然后打印调用栈
结果是好的,这种方法拿到了MemoryAccessMonitor根本拿不到的东西:
1 | suses_23874132 triggered! |
在之前的草稿中说到,第二个trigger是我们刚刚的引用,那我们就去看看第一处引用到底往这个段里写了什么奇奇怪怪的玩意
这个函数没有输入参数,这十分令人欣慰;并且,在最末尾,可以清晰的看到写入操作:
1 | BL sub_16C4A54 |
至此,我们就可以直接通过模拟运行,最后读出Q0和Q1的值,即C2_pre1的值
C2_pre2
这个值十分棘手,我分析了栈的内存访问,还尝试了往一些栈空间里写一些奇奇怪怪的东西,但是都以失败告终
正当我不知道该怎么办的时候,我突然发现这个超大函数,前半段似乎没有外部引用……
并且根据分析,这个函数的4个输入参数均为指针,我尝试了直接运行这个函数,结果是出乎意料的,它……成功了一半
具体实现
刚刚的分析结果表明,C2_pre1的提取只需要简单地调用那个函数,最后取到Q0和Q1的值就行了……
但是等等!这个函数引用了一些Data和Rodata段的内容,而Elf文件直接装载到内存里时是不存在这些内容的,我们需要读Elf文件,然后读出所有PT_LOAD段,再写到内存里:
1 | struct ElfInit { |
至于Elf的文件结构方面,这里不再赘述
然后就是直接调用大函数得到C2_pre2对吧?这个我会!
……但是再等等!大函数中有一些外部引用,而我们只有裸的so文件,unicorn可不会自己下载libc.so然后自己就装载上去;大函数还有4个输入参数,怎么办?
上文说过,根据动态分析,那四个输入参数全都是地址……我们先假设这些地址不会干扰到我们的数值生成(且事实证明,这个假设是成立的)
1 | //... |
而对于那些外部引用,根据静态分析也不难得出,都是一些字符串操作相关的内容,和我们的C2_pre2提取也没有什么关系,所以……
1 | fn uc_fill(uc: &mut Unicorn<'_, ()>, from: u64, to: u64) -> Result<(), unicorn_engine::uc_error> { |
你知道我要干什么的……
C2_pre1和C2_pre2的值已经全部能够提取出来,也就能算出C2了,加上傻逼919防护意识淡薄,我们可以直接通过直接匹配来获取到这两个目标函数的位置……
1 | log::info!("[*] searching in input, len: 0x{:x}", input.len()); |
简单,粗暴,但能用(被打)
至此,距离实现scirpophaga的短期目标已经没有什么距离了
具体项目可以参考scirpophaga
之后……?
之后就先准备一下农业气象学考试(?)以及cuscuta的重构吧……
如果新来的大佬也觉得之前那个架构没问题的话,我就更有信心了……