【bugku】Take the maze

首先运行,提示输入key
【bugku】Take the maze_第1张图片
查壳显示无,拖入 IDA 根据字符串定位到main函数
【bugku】Take the maze_第2张图片


这里可以看出基本格式:24位十六进制
另外在字符串里可以看到很多print ticket的字样
(这个操作有点像今年信息安全国赛的“欢迎来到加基森”呢,吓了我一跳)
【bugku】Take the maze_第3张图片
下面可以看出来是写入文件的操作,如果直接把这个东西dump出来不就得到flag了吗~ 
二进制复制下来,发现是张二维码,惊喜的扫码识别:
Congratulations! The flag is your input + “Docupa”

哦 好吧= =我就知道没这么简单

乖乖分析check函数吧
流程挺清晰的,每次读取两个输入,分别通过switch进行保存
【bugku】Take the maze_第4张图片
【bugku】Take the maze_第5张图片
通过数组byte_541168再次转换作为最终值 
【bugku】Take the maze_第6张图片
第一次输入决定调用的函数,第二次输入则决定参数2

查看发现4个函数结构基本相同,大致如下
   【bugku】Take the maze_第7张图片
可以看出来,参数a2(即第二次输入)就是计数器,表示循环次数 
每次循环都会对i += 26

但是注意,i只是一个局部变量,把它保存回a1上是有条件的:
i/26<=10
dword_540548[i] ^ dword_540068[i] ==0

这两个条件很明显,前者是限定范围,后者则是限定路径

限定路径的方法是要求这两个数组的对应值相同才以该方向移动 
eg: 坐标为(4, 5)向下移动,则判断dword_540548[4*26+5] ^ dword_540068[4*26+5]==0,
相等则合法,移动成功,
不等则直接return 移动失败(i未写入a1)

同样查看另外三个函数可以得出限定范围 
x∈[1, 24] 
y∈[1, 10]

4个函数的数组彼此都不相同,也就是说每个坐标的可移动方向是不同的,因此要分别判断

这里刚开始卡了我很久想不通,本来以为是先改变i再校验路径合法性的 
其实i+=26是在循环体结束时调用的,而结束循环的计数器校验放在循环体的开头 
这也就意味着顺序其实是:
校验计数器→校验当前i的路径合法性→改变i→校验计数器 

改变i和校验路径合法性的先后顺序决定着到底是“ 一个地方可以向某个方向移动” 还是“ 一个地方可以由某个方向移动来  ”
真正的方式是前者

移动方式找到了,下一步寻找终止check,也就是终点 
反编译的伪代码中没有对该变量的调用,去反汇编中找到 
【bugku】Take the maze_第8张图片

311 = 11*26 + 25 
也就是说长宽至少为12*26,从左上角移动到右下角即可

于是下一步是dump出路径 
通过IDC脚本可以直接输出合法性的数组: 

以一个为例:

IDC>auto i;for(i=0;i<26*12;i++){if(Byte(0x540548+4*i)==Byte(0x540068+4*i))Message("1, ");else Message("0, ");}
1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,


然后将4个方向的都dump下来以后,复制到脚本中进行处理从而生成地图
刚开始是用的4位二进制(1111)来表示,感觉不太清晰,于是直接用了箭头,看起来舒服点 
【bugku】Take the maze_第9张图片

【bugku】Take the maze_第10张图片
(这里要感谢一波出题人没有用复杂的迷宫来为难,否则就只好再写一个走迷宫的算法了OTZ
写出解法字符串:
06360836063b0839073e0639

将其输入程序,发现还是不行 

于是动态调试,发现这里对input进行了处理

input[16] ^= 1u;                            
   sub_45C748((int)input);


sub_45C748内部很复杂,感觉有点类似VMP 
【bugku】Take the maze_第11张图片
分析起来太麻烦了,干脆直接动态调试猜结果 
输入测试字符串一串1时发现结果挺有规律的,下硬件断点也看到在进行一些花指令包裹的异或操作

于是输入测试字符串,将结果dump出来进行异或,发现这个函数的操作就是input  ^ i  ~

于是将结果字符串进行转换,得到flag前段
[Python] 
a = list("06360836063b0839073e0639")
a[16] = chr(ord(a[16])^1)
for i in range(24):
    print(chr(ord(a[i])^i))

解出来为:

07154=518?9i<5=6!&!v$#%.


还记得之前的二维码么,最后要拼接上Docupa哦 

所以最后flag为

zsctf{07154=518?9i<5=6!&!v$#%.Docupa}

你可能感兴趣的:(ctf)