某CTF一道逆向题分析
某个周末,公司群里发了个某CTF的逆向题,刚好没啥事就尝试做一下。
详情分析
首先,先运行看看情况,打开程序之后提示找不到VCRUNTIME140_1.dll,如下:
在此链接下载vcruntime140_1.dll
,并将其放到C:\Windows\System32\
目录下,再次打开程序,可以看到是个控制台程序,并且需要输入,如下:
vcruntime140_1.dll
,Win10下不受影响接着查壳,看看是否加壳,使用LoadPE
查看区段,发现区段正常,并无异常,应该是没有壳的,如下:
接下来用x64dbg
打开程序,并且搜索字符串,如下:
可以看到如下字符串:
猜测应该是输入特定的字符串,然后对输入一顿加密,最后比较加密后的结果是否与程序内设定的一致,一致则提示找到flag,否则就退出啥的。
接下来再用IDA
打开程序,找到main
函数,看下流程图,如下:
看到这种的时候,就是 IDA
关闭 ==> 虚拟机
关闭 ==> 爱奇艺
打开。追了会剧,吃了个饭,想了想,还是分析分析吧,毕竟还是想装装逼学习学习的。
整个程序分成以下几块:
- 获取输入
- 判断输入是否合法
- 判断第二段字符串是否正确
- 判断第一段字符串是否正确
- 判断第三段字符串是否正确
- 完
获取输入:
IDA
f5 大法之后,从104 ~ 155
行,是获取输入,如下(请忽略小学鸡的注释😂):
上图中,标注了两处地方,这两处地方是这个获取输入的地方最重要的地方,下面详细分析。
首先看第二处,第二处比较简单,也就是这个输入怎么结束。这里的逻辑是判断输入的字符是否为#
号,当输入了3个#
号之后,循环结束,完成输入。
再来看第一处,这里比较复杂。
if
判断的是两个全局变量,这两个变量一开始都为0,然后开始执行sub_140001F60
函数,该函数会将输入的字符存放在某个数组中,并且给三个变量赋值。
变量1
为存放字符的数组首地址。变量2
为该数组+1的地址,其实也就是输入字符串的长度,变量2
- 变量1
= 输入字符串长度
。变量3
不知道存放的是什么地址,它要么与变量2
相等,要么比变量2
大一些。
而if
判断的就是变量2
和变量3
,当相等时就去执行sub_140001F60
函数,不相等时则将变量2
+1。
这里最主要就是变量3
的变化,经测试,变量3
的变化主要与输入的字符长度有关,具体如下:
输入字符长度 | 变量3的值 |
---|---|
1 ~ 4 | 变量2+0,也就是字符串实际长度 |
5 ~ 6 | 变量2+1,也就是字符串实际长度+1 |
7 ~ 9 | 变量2+2,也就是字符串实际长度+2 |
10 ~ 13 | 变量2+3,也就是字符串实际长度+3 |
14 ~ 19 | 变量2+5,也就是字符串实际长度+5 |
20 ~ 28 | 变量2+8,也就是字符串实际长度+8 |
… | … |
可以看出点端倪了,这应该是斐波那契数列。当字符串长度大于等于 5 时,就将变量2
按照斐波那契数列依次累加,并且赋值给变量3
。
从这个获取输入的位置,可以得出以下结论:
- 输入的字符串,包含3个
#
号 - 输入的字符串长度应该有所限制
判断输入是否合法:
当接受完输入的字符串之后,接下来就是判断输入是否合法了,从这个标题应该就能看出,这个输入字符串是有格式的,我们接着来分析。
从156 ~ 162
行,是在进行输入合法性校验,代码如下:
这里也有两处比较重要的点。
第一处判断几个全局变量相加相减之后的值是否等于37,不等于直接退出。这几个变量就是上面分析的变量1
与变量3
,只不过有三组变量1
与变量3
。为什么有三组呢,因为前面输入的位置每遇到一个#
号,就进行一组的变量操作,三个#
号刚好三组。用每一组的变量3
- 变量1
,也就是将斐波那契数列处理过的数 - 字符串首地址,得到的差值再相加,必须等于37。下面是输入字符串长度与变量3
的关系,也就是上面的完善版:
输入字符长度 | 变量3与变量2的关系 | 最终的差值 |
---|---|---|
1 ~ 4 | 变量2+0,也就是字符串实际长度 | 字符串实际长度 |
5 ~ 6 | 变量2+1,也就是字符串实际长度+1 | 6 |
7 ~ 9 | 变量2+2,也就是字符串实际长度+2 | 9 |
10 ~ 13 | 变量2+3,也就是字符串实际长度+3 | 13 |
14 ~ 19 | 变量2+5,也就是字符串实际长度+5 | 19 |
20 ~ 28 | 变量2+8,也就是字符串实际长度+8 | 28 |
… | … |
从这个表格中可以看到,要想满足37的标准,最简单得是28+9,如果这样得话那么一段字符串为20 ~ 28位,一段字符串为7 ~ 9位。
第二处,是三个变量相比较,也就是三段字符串的长度,比较结果是第二段字符串长度最长,其次是第一段,最后是第三段。
从这个地方可得出以下结论:
- 输入为一段包含3个
#
号的字符串,刚好分成3段字符串,第二段字符串最长,其次是第一段,最后是第三段。 - 按照上面这个长度划分,再结合相加得为37来看,第二段字符串应该在20 ~ 28位之间,第一段和第三段加起来为9,且第一段大于第三段。
判断第二段字符串是否正确:
接着是判断第二段字符串是否正确,代码为163 ~ 189
、410 ~ 414
行。
先是执行410 ~ 414
行,这里这个while
循环,是遍历输入的第二段字符串,判断每个字符是否等于1或者等于2,不等于这两个直接退出,代码如下:
接着就是163 ~ 189
行,将第二段字符串进行MD5
哈希,并且判断是否与39c1ca4b6d64c40558425432c11624a8
相等,不相等则退出,代码如下:
从这里得到结论:
- 第二段字符串必须由
1
、2
组成 MD5
后必须等于39c1ca4b6d64c40558425432c11624a8
在这里将39c1ca4b6d64c40558425432c11624a8
放入到网上的彩虹表查询,都没有查询到结果。由于只由两个字符组成,这里直接采取暴力破解的方式,来找到对应的字符串,代码如下:
|
|
最后找到1221222221212121211122112111
,如下:
最终结论:
- 第二段字符串为
1221222221212121211122112111
- 最终字符串为:
?#1221222221212121211122112111#?
判断第一段字符串是否正确:
然后判断第一段字符串是否正确,这里的代码就比较长了,主要点在下图:
主要逻辑为先定义了一个加密字符数组,然后取数组内容相加,并与输入的字符一个一个异或,得到的结果再与当前字符所在的下标进行异或,最后将异或后的结果与定义的字符相比较,其中一个字符不相等则退出。
这里我用的笨办法,因为本来剩余的字符就不多了,所以我直接动态调试找到异或的加密字符,然后与正确字符异或就得到了输入的字符串,代码如下:
|
|
得到的结果为Rc44cR
:
可以看到,输入的字符有乱码,说明第一段字符串肯定不是9,那就可以用3+6的组合来实现,也就是第一段字符长度为6,第二段字符长度为3,这样既能满足37(6+28+3)的要求,又能满足第一段字符长度 > 第三段字符长度 && 第一段字符长度 < 第二段字符长度
的要求,所以最后第一段字符串为Rc44cR
。
这里如果用3+6的方法的话,根据上面表格总结出的关系,第一段字符串也可以是5个字符,所以这里可能有两种答案。
总结:
- 第一段字符串长度为6,正确答案是
Rc44cR
- 完整字符串为:
Rc44cR#1221222221212121211122112111#?
判断第三段字符串是否正确:
第三段字符串判断逻辑,与第一段字符串判断逻辑类似,只是字符串长度变短了,并且最后异或比较的时候,没有与字符所在下标进行异或了,所以代码如下:
|
|
得到结果为TCF
:
最终结果
最终的输入字符串为:Rc44cR#1221222221212121211122112111#TCF#
,由于上面的表格总结的关系,以及CTF已经关闭的缘故,没办法验证Flag是否正确,字符串也可能是Rc44c#1221222221212121211122112111#TCF#
。执行结果如下:
END
第一次写 CTF 逆向题的WriteUp,写得比较啰嗦,而且太久没搞逆向了,可能有些地方分析得也不对,如发现有任何错误,欢迎与我联系。