[CTF]Bugku Take the maze 200
{写博客不重要,记录思路最重要}
1. 拿到拖Die发现无壳,从文件名和解析结果来看就是用visual C写的
2. 运行,发现关键语句:“show me your key”,拖入IDA,搜索该字句,进入到函数
3. 发现该函数中有“done!....”表明输入正确,但是正确之后还有一些函数,进去后发现还有一些奇奇怪怪的东西如下:
根据提示,运行到此处会打印一个png文件,所以我们要让程序跑到这里
4. 拖入Ollydbg,开始运行,在输入处(搜索关键字句,很明显的%s后面是输入函数)设置断点,输入任意字符串后步进来到如下处:
这里和0x18进行比较,从代码中可以分析到这里是比较输入串的长度是否为0x18,不想分析也没关系,直接把寄存器的标志位改掉就可以骗过去
5. 然后来到这里:
观察这几个跳转的目的地发现,jge的跳转正好跳到done附近,于是我们在jge处将标志位改掉(具体改什么标志位可以查询一下汇编手册)使其跳转到如下位置:
这样一来,我们的输入就“对”了。虽然开了挂。
运行程序,得到一个二维码,上面却写着:Congratulations! The flag is your input + "Docupa"
得到了一个提示,看来我们还是要找到正确的input
6. 回到IDA,F5,报错(忘截图了,该图是网上找的):
是堆栈的问题,手动调试sp太麻烦,在Options -> General 中勾选Stack Pointer,然后来到报错的位置, 发现SP值有负数,这是不允许的,于是我们在负值前的最后一条语句处按Alt + K然后将其SP值改为左侧显示的应有的SP值,这时后面的负数就会变成0,按D再按C更新函数后如下:
只要没有负值就没有关系,虽然这样栈基本上还是乱的。但至少绕过了反逆向手段。
7. F5,如下:
其中很明显可以看出sub_45D846是输入函数,输入的结果保存在v4中,随后的sub_45C22F是strlen函数,判断长度是否为24,满足后,将v4的第16位反向后进入sub_45C748函数,暂时还不知道其功能
然后是一个循环,里面有一个判断,如果输入中出现了非法字符,就跳转,而字符的范围是0-9加上a-f
也就是说,正确的input应该为24个字符,经过处理后,是一个具有0-9 a-f的串,很明显,应该是一个16进制串。
如果我们能满足条件,就会进入最后一个if语句,此时,被处理后的v4进入sub_45E593函数,显然应该是一个比较函数,暂不知道其功能,一旦比较成功就获得了flag
8. 我们先来看一下sub_45E393函数,结果又报错:
找到0x463729处,同上修改463727的sp值为0x4即可(修完之后372A还是负数,再修改3729即可):
修完之后如下:
回到刚才的F5代码,进到比较函数中,不再报错,里面代码有些复杂,如下:
其中发现大量switch匹配式,考虑可能是走迷宫问题
9. 寻找大量switch语句中,找到一个数组:
delru正是down left right up四种走法(e不知道什么用)
而具体怎么走要看v6和v9的值,其中v6来自于第一个匹配,v9来自于第二个匹配,而v9有时根据v6来决定的,这样一来,重点就在于v6,考虑v6可能是迷宫的行走动作:
另外特别注意到这一段,v6的值是如下这样确定的,而该语句中的a2是用户的输入参数,也就是用来比较是否正确的那个user_input_key(经过处理),这种情况下显然v8就是一个指针用于逐个读入字符,也就是说,每一次循环,都会从key中读出2个字符,分别进入第一个匹配和第二个匹配:
如果第一个匹配中v6(来自于每两个字符中的前一个)是0或2或3或4不久正好是dlru吗?那就是说该字符决定方向
如果第二个匹配中v6(来自于每两个字符中的后一个)是5-15,那不刚好就是接着后面的0-9吗?那就是说该字符决定步数
所以,key决定了迷宫的走法,每两个字符一组,前一个决定方向,后一个决定步数
10. 接下来要做的就是走出迷宫,迷宫来自于如下四个函数(已改变量名):
v7来自于方向,方向包括dulr,分别对应ascii码为 100,117,108,114,减去一百就是图中的值
所以,对于不同的方向,这里调用了不同的迷宫函数进行操作(这是什么鬼操作.....)
其中,方向为‘d’时的迷宫函数如下,也就是说,如果向下走,就要走这个迷宫?
首先注意到之前的变量some_int在这里给了i的初始值,随后每次i都加上26,可以想象,26是迷宫的宽度(一维数组表示二维数组?)
随后some_int就是迷宫的格子的下标,根据汇编中如下处:
只有在Some_int是311的时候,eax是1,其它时候都是eax为0,也就是说,迷宫成功走出的最后是走到311格(其实是312格)
312=26*11,所以迷宫的长度为11
下面这里11,12,13行是当步数为0的时候,就不用走了,直接回去,然后result是i/26与10比较,这也证明了上面对迷宫的尺寸的推理是正确的,此时如果超出了第10行(其实是第11行),就返回i/26的值(一个比10大的非法行数)
否则,result被赋为当前的下标值并进行了如下异或运算
这就好理解了,如果这个异或运算算出来是1,就直接返回了(可以理解成撞墙了),否则继续循环(继续向下走)
所以这个异或运算就是在画迷宫
同样的,另外三个迷宫函数也有类似的东西,这样我们可以尝试一下画出这个迷宫:
新建一个idc文件,并且写如下代码(idc与C类似,具体语法可自行查手册):
然后在file -> Script file中运行该idc文件,得到如下结果:
值得注意的是,该迷宫不是一个常规迷宫,因为该迷宫图是由四个迷宫撮合而来,向不同方向走的时候要换不同的迷宫来判断是否可以走。该结果的使用方法是从左上(因为一开始i是0)开始,有哪个字母就表示该格可以往哪边走,比如i=0的格子只能往下走,i=1的格子可以往下或者往左走(每四个.或者字母是一个格子,这里特意用空格和换行区分开了格子)
11. 走迷宫(注意,只能是这一种走法,否则题目就错了):
以drul和步数来标识就是:d1 r1 d3 r1 d1 r6 d3 r4 d2 r9 d1 r4
根据前面我们在数组里看到的如下,d-0 r-3 1-6 2-7 3-8 4-9 5-a 6-b 7-c 8-d 9-e
转换成数组下标就是:06 36 08 36 06 3b 08 39 07 3e 06 39
得到我们要的key就是06360836063b0839073e0639
当然,是处理完之后的key,处理之前是什么呢?
12. 来到处理函数,F5进去,如下
这也太恶心了吧.....果断决定动态调试,如下,我输入了全1的字符串
发现在其中某一个函数中被处理成如下:
全2?
全3?
全0?
第1个字符总是不变的,第2个字符1-0 2-3 3-2 0-1,可以看出像是一种异或
第3个字符1-3 2-0 3-1 0-2,也是异或!刚才是异或0x1,现在是异或0x2
第4个字符1-2 2-1 3-0 0-3 异或0x3 !!!
于是我们就找到了正确的解法:
from i in (0,23)
input[i]^i
input[i] == 06360836063b0839073e0639
于是我们将其异或回来,脚本如下(千万别忘了,在这个加密函数之前还有一句异或哦!):
别忘记了还要加那个二维码里的提示和格式
终于搞定了