首页
论坛
课程
招聘
[原创]看雪CTF2017秋季赛第五题Writeup
2017-11-2 00:42 3622

[原创]看雪CTF2017秋季赛第五题Writeup

2017-11-2 00:42
3622

相关工具:IDA、OD、python

一、按钮事件处理函数

由程序图标得知这是一个MFC程序。打开程序,随便输入sn,弹出失败对话框。

用OD打开CM,bp MessageBoxW,运行,输入sn,点击确定,触发MessageBoxW断点。栈回溯确定调用源0x407245,随即找到按钮处理函数0x4071fd。

主函数逻辑很简单,判断sub_406fc3的返回值。0弹出“失败”,1弹出“成功”,2弹出“你就差一点啦”

二、int 2d反调试

跟进0x406fc3函数,在正常流程中发现int 2d指令



int 2d反调试原理很简单,正常运行时int 2d触发异常,进入程序的异常处理函数。而当调试运行时,OD会处理该异常,将eip+1继续运行。

下图是CM设置的异常处理函数

patch掉int 2d,修改为jmp 0x40717d即可正常调试。

三、抽丝剥茧,理清程序脉络

异常处理函数中有3个关键判断

判断1:(失败返回0,成功则进入判断2)

判断2:(失败返回2,成功则进入判断3)

判断3:(失败返回2,成功则返回1,整个CM校验成功)

判断1是CM最重要的部分,下面我会详细分析判断1。

作者设置判断2是为了防止程序多解。事实上,如果在分析理清了作者的算法思路,那么逆推得出的sn会自然的pass掉判断2。

判断3的逻辑一目了然,eax指向sn字符串,判断条件:strlen(sn)=0xc。同判断2,这也是一个防止多解的判断。按作者思路逆推的sn也会pass掉判断3。
下面是判断1的详细分析:

判断1的成功条件:

为了便于描述,设var_d9f4开始,大小为0x30的字节流为BLOCK1。var_d934开始,大小为0x30的字节流为BLOCK2。

成功条件就是:BLOCK1和BLOCK2全等。

四、BLOCK1和BLOCK2的初始化

在0x40718b处下断,调试发现BLOCK1和BLOCK2都被初始化过。BLOCK1被初始化为0x00-0x2F依次递增排列,BLOCK2被初始化为0x00-0x2F的乱序排列。

往前翻代码,发现如下代码:

调试发现,sub_4084A3就是初始化BLOCK1和BLOCK2的函数。初始化时,sub_4084A3被调用(传入了字符串参数”KanXueCrackMe2017“,传入了BLOCK1的地址)(事实上,BLOCK1和BLOCK2在内存中是紧挨着的,实际上做变换的是BLOCK2),判断1前,sub_4084A3再次被调用(传入了我们的sn字符串,传入了BLOCK1的地址)。

为了使判断1成功,要让BLOCK2和BLOCK1全等。调试发现,sub_4084A3并不会打乱BLOCK1,只会打乱BLOCK2。(BLOCK1一直为0x00-0x2F的递增排列)

所以我们要找的sn可以说是字符串”KanXueCrackMe2017“在函数sub_4084A3作用下的”逆“。 ”KanXueCrackMe2017“ 负责打乱递增排列的BLOCK2,而我们的SN则负责还原BLOCK2。

五、SN变换

在打乱BLOCK之前,SN会经历4步变换

SN的第一次变换:

进入sub_4084A3

进入sub_40829c

对sn中的每一个char做判断:

在'0'~'9' 之间则减去'0'。结果范围[0,9]

在'A'~'Z'之间则减去'7'。结果范围[10,35]

在'a'~'z'之间则减去'='。结果范围[36,61]

变换1:由字符向[0,61]区间做1对1映射。

SN的第二次变换:

同样是在sub_40829c中

第一个call sub_403641作用是初始化变量。

第二个call sub_4038E1读取向[0,61]区间映射过的sn,按位权重乘以相应个数的62。

最后两个call将这些按62位权重的数求和。

变换2:转换为62进制数字

SN的第三次变换:

变换3:62进制转换为18进制

SN的第四次变换:

变换4:[0,17]向[0-9,'A'-'H']做1对1映射

验证代码:

为了确认这4次SN的变换,通过调试,得到输入字符串” ”KanXueCrackMe2017 “,得到输出字符串"EDAHE450C741GH441E11BH84"

验证代码如下(python实现):

输出如下:

验证通过

六、BLOCK变换

在之前的代码中,我们的SN被变换为SN1。下面我们来看SN1是如何引导BLOCK的变换的。

SN1变换后作为sub_4080DF的参数:

SN1又被转换为了[0,17]的数字序列,每个数字除以3的商和余数作为参数传递给sub_4080DF

进入sub_4080DF(函数较长,仅截取一部分):


这种函数中就调用了一个函数sub_40727D,余数决定了调用的次数,商决定了传给该函数的01块的地址。

余数的范围[0,2],调用次数为余数+1,即[1,3]。[0,17]除以3的范围是[0,5],相对应,也有6个不同的01块。

进入sub_4072:


调试发现sub_40727d就是BLOCK变换的执行者。下文简称基函数。上图基函数有两个变量,传递进来的都是BLOCK2的地址。

但事实上,最关键的一个参数IDA并没有识别出来,就是图中的v2,通过ecx传递进来的。这个v2是一个大小为0x2400的由0和1的DWORD值组成的块。

观察上图代码,基函数通过局部变量v10保存数据,退出时,用v10覆盖BLOCK2。v10中的每个值都是由01块v2和原BLOCK2对应求积再求和而来,这0x30个积中,01块有且仅有一个为1。每次算出一个值之后,会对01块进行的错位。通过这种方式来保证每个BLOCK2中的值都能通过01块映射到v10中。就这样,基变换完成了对BLOCK2的一次置换。

01块的初始化:

01块的初始化在sub_407307中:

上图只是一个01块的初始化(一共有6个01块)

6个01块,对应着6种基函数。把置换表提取出来:

七、拨云见日,算法模型浮出水面

提取出置换表之后,观察置换表特点

1、有一行置换后不变

2、有一行发生了偏移为2的移位。

3,、其余4行都只有3个位置产生了置换,剩余5个位置不变。

然后就。。。。

3X3魔方6个面对应6个置换表。旋转一个面时,对立面不变,旋转面偏移2移位,其余4面变换3位。

去除中心节点,魔方共有6X(9-1)=48=0x30个小方块。完美契合CM中的置换函数。

八、建立模型,还原魔方求SN

还是用python:

结果:

SN:CcLaoE37J45Y


PS:插入代码,换行变成了<br>,很奇怪。上篇帖子没有这个问题,这篇怎么改都不行,把py文件传上来算了。



看雪学院推出的专业资质证书《看雪安卓应用安全能力认证 v1.0》(中级和高级)!

上传的附件:
  • 1.py (0.83kb,15次下载)
  • 2.py (0.79kb,13次下载)
收藏
点赞0
打赏
分享
最新回复 (16)
雪    币: 622
活跃值: 活跃值 (484)
能力值: (RANK:520 )
在线值:
发帖
回帖
粉丝
netwind 活跃值 13 2017-11-2 10:51
2
0
分析的很精彩 
建议python代码直接贴出
雪    币: 561
活跃值: 活跃值 (14)
能力值: ( LV12,RANK:650 )
在线值:
发帖
回帖
粉丝
brichfire 活跃值 4 2017-11-3 12:58
3
0
厉害,膜拜一下大佬
雪    币: 8543
活跃值: 活跃值 (852)
能力值: ( LV15,RANK:1920 )
在线值:
发帖
回帖
粉丝
ccfer 活跃值 15 2017-11-3 13:12
4
0
厉害
雪    币: 8543
活跃值: 活跃值 (852)
能力值: ( LV15,RANK:1920 )
在线值:
发帖
回帖
粉丝
ccfer 活跃值 15 2017-11-3 13:28
5
0
brichfire 厉害,膜拜一下大佬[em_13]
靠运气啊
可是我解出的sb="46F9CFFF4E725CFAE"
SN=WgYgToL0J45Y
通不过椭圆验证,怎么办
雪    币: 601
活跃值: 活跃值 (34)
能力值: ( LV12,RANK:294 )
在线值:
发帖
回帖
粉丝
半盲道人 活跃值 2 2017-11-3 14:50
6
0
没有ECC这题解应该不少...  不过可以穷举,  看满足条件的解是否通过ECC
雪    币: 4298
活跃值: 活跃值 (383)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
大道在我 活跃值 2017-11-3 16:12
7
0
NB    这分析 
雪    币: 217
活跃值: 活跃值 (13)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
qincccken 活跃值 2017-11-3 20:04
8
0
真的高大上,分析得这么透彻,谢谢了
雪    币: 288
活跃值: 活跃值 (118)
能力值: ( LV5,RANK:65 )
在线值:
发帖
回帖
粉丝
sunsama 活跃值 2017-11-3 20:06
9
0
分析的很透彻,膜拜大佬
雪    币: 11434
活跃值: 活跃值 (490)
能力值: ( LV9,RANK:142 )
在线值:
发帖
回帖
粉丝
orz1ruo 活跃值 2017-11-4 07:30
10
0
厉害厉害,膜拜大佬
雪    币: 561
活跃值: 活跃值 (14)
能力值: ( LV12,RANK:650 )
在线值:
发帖
回帖
粉丝
brichfire 活跃值 4 2017-11-4 11:41
11
0
半盲道人 没有ECC这题解应该不少... 不过可以穷举, 看满足条件的解是否通过ECC
没有ECC就多解了
雪    币: 6817
活跃值: 活跃值 (153)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
聖blue 活跃值 2017-11-4 18:05
12
0
不错!!
雪    币: 455
活跃值: 活跃值 (149)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
ssarg 活跃值 2017-11-14 22:31
13
0
雪    币: 1138
活跃值: 活跃值 (73)
能力值: ( LV4,RANK:48 )
在线值:
发帖
回帖
粉丝
大只狼 活跃值 2017-11-20 20:49
14
0
除了膜,无fuck说
雪    币: 4
活跃值: 活跃值 (28)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
弟子规 活跃值 2018-3-20 22:56
15
0
patch掉int  2d,修改为jmp  0x40717d即可正常调试。
请教如何在IDA中定位到异常处理函数的地址0x40717d呢?
雪    币: 299
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
mxr 活跃值 2018-5-26 09:00
16
0
厉害!
雪    币: 1435
活跃值: 活跃值 (186)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
陈jack 活跃值 2018-7-22 23:16
17
0
写的很好
游客
登录 | 注册 方可回帖
返回