170623 逆向-maze
来源:互联网 发布:软件测试简历范文 编辑:程序博客网 时间:2024/05/19 07:09
1625-5 王子昂 总结《2017年6月23日》 【连续第264天总结】
A.ciscn-maze
B.惯例先拖入PEiD,发现是VC++
看这个题目明显是走迷宫类型的,先运行一下试试吧
用CMD打开,毫无提示直接结束
期待显示迷宫的我似乎搞错了什么OTZ
这种类型OD就不太好分析了,算法太复杂了
直接送入IDA,找到main函数F5,发现先是判断参数存在则继续,否则直接结束
这就是CMD也毫无输出的原因了,试着在cmd后加上参数进行执行,终于返回了"play again!"的结果
再看main判定有参数以后:
两个初始化,一个if的条件函数,一个if正确时执行的函数就组成了整个程序
先试着看了一下4010B0和401000两个初始化函数,很长,大概分别是两个循环和一个循环+一个随机处理
再看条件函数401150,核心部分如下:
do
{
if ( v3 < 97 || v3 > 100 ) // 如果为abcd以外的字符则跳转(v4+1,结束)
goto LABEL_17;
v9 = v1[1] - 101; // -'e'
if ( v9 > 0x15 ) // >z时令v1=a1=输入字符,v4+1,结束
{
v1 = a1;
LABEL_17:
++v4;
goto LABEL_18;
}
v10 = v7;
switch ( v3 ) // 移动判定,v9=步数,v6列数,v5行数
// c上移,d下移,v7=0
// a左移,b右移,v7=1
{
case 97:
v7 = 0;
v6 = (signed int)(v6 - v9) % 22;
v8 = *(&maze[22 * v5] + v6);
break;
case 98:
v7 = 0;
v6 = (signed int)(v9 + v6) % 22;
goto LABEL_14;
case 99:
v11 = v5 - v9;
goto LABEL_13;
case 100:
v11 = v9 + v5;
LABEL_13:
v7 = 1;
v5 = v11 % 22;
LABEL_14:
v8 = *(&maze[22 * v5] + v6); // v8=当前所在地区的值
break;
default:
break;
}
*(&maze[22 * v5] + v6) = 0;
v4 = v12 + (v8 ^ 1) + (v10 == v7); // v4=v4+v8异或1+(v10==v7)
// 即当前所在区域值为1
// 且v10与v7不能相等
//
// 由于在每次输入之前会将v7赋值给v10
// 即,两次输入之间的v7不等
// 由于上下移动v7=0,左右移动v7=1
// 因此移动时需要上下左右交替进行
// (即无法连续进行同轴的移动)
// 事实上保证了多步前进只有一个正解
v1 = a1;
LABEL_18:
v3 = v1[2];
v1 += 2;
v12 = v4;
a1 = v1;
}
while ( v3 );
if ( v5 != 21 || v6 != 21 ) // 如果行列不都等于21,则v4+1
LABEL_21:
++v4;
result = v4 < 0; // v4=0则成功,<0则返回失败
LOBYTE(result) = v4 <= 0;
return result;
出现了switch 97-100,那么很明显这就是循环接收输入的地方了
整理一下,第一个字符为abcd方向,第二个字符为'x'-'e'的步长
要求每步都落在1上,并且Y轴与X轴移动交替进行(即最少步数)
最后一步需要走到(21,21)方可返回True
那么很明显if判断为真时就执行flag的输出了
想要知道该怎么走,那就需要看看初始化函数中迷宫是如何构造的了
先进入第一个初始化函数4010B0,第一段是这样的:
if ( dword_40FF38[0] != -1 )
{
v2 = dword_40FF38[0];
v3 = 0;
do
{
++v1;
*(&maze[22 * v2] + dword_40FF3C[v3]) = 1;
v3 = 2 * v1;
v2 = dword_40FF38[2 * v1];
}
while ( v2 != -1 );
}
那么很明显可以看出v2是行数,v3是列数。对部分40FF38和40FF3C存储的地方赋1
4010B0的第二段里只有对0的赋值,没有对任何地方赋1,因此不看了
再看第二个函数401000:
if ( dword_40FFD8[0] != -1 )
{
v2 = 0;
do
{
++v1;
*(&maze[22 * v0] + dword_40FFDC[v2]) = 1;
v2 = 2 * v1;
v0 = dword_40FFD8[2 * v1];
}
while ( v0 != -1 );
}
完全相同的结构,只是内存变为了40FFD8和40FFDC,也就是说这程序还分了两个函数来设置迷宫。。。
第二段:
do
{
v5 = rand() % 22;
v6 = rand();
v5 *= 22;
v7 = *(&maze[v5] + v6 % 22) == -1;
v8 = &maze[v5] + v6 % 22;
if ( v7 )
{
*v8 = 1;
++v4;
}
}
while ( v4 < 15 );
v9 = maze;
do
{
result = 0;
do
{
if ( v9[result] == -1 )
v9[result] = 0;
++result;
}
while ( result < 22 );
v9 += 22;
}
分别是随机赋1(如果该处为-1的话)和将剩余部分填充为0
随机部分有一个判断此处是否为-1,而40F0B0的第二段是对某些区域赋0
那么猜测应该是将正确路径全部赋0,防止此处随机赋1时产生额外的正解使得flag出错
同理,因为flag唯一,所以随机部分赋的1可以全部忽视掉,正解只由两个函数的第一段循环赋的1得出
四处内存的值由于没有反编译出来,所以只要用IDC自己跑出来:
IDC脚本:
IDC>
auto i;
auto h=0x40FF38;
auto l=0x40FF3C;
for(i=0;Dword(h)!=-1;i++)
//Dwrod函数是从from1这个地址取出4个字节的数据,还有Word和Byte这两个函数分别从指定地址取出字数据和字节数据
{Message("%d,%d],",Dword(h),Dword(l));
//IDA的IDC中的Message函数和C的printf差不多,都是向终端输出结果
h=h+4;
l=l+4;
}
[19,0],[0,19],[19,17],[17,3],[3,17],[17,3],[3,8],[8,11],[11,8],[8,11],[11,15],[15,21],[21,15],[15,21],[21,21],[21,-1],
第二个脚本只将对应内存区域改为0x40FFD8和DC即可
[12,0],[0,4],[4,0],[0,0],[0,12],[12,0],[0,9],[9,-1],
将内存得到的数据拖到python中画出地图:
maze=[[19,0],[0,19],[19,17],[17,3],[3,17],[17,3],[3,8],[8,11],[11,8],[8,11],[11,15],[15,21],[21,15],[15,21],[21,21],[12,0],[0,4],[4,0],[0,0],[0,12],[12,0],[0,9]]h=[[0 for i in range(22)] for i in range(22)]for k in maze: x=k[0] y=k[1] h[x][y]=1for m in h: for n in m: if(n==1): print('\033[31m%s'%n,end=",") else: print('\033[30m%s'%n,end=",") print()得到:
按要求画图:
d19 b17 c16 a9 d8 b7 d10 b6
再将数字+'e'即可得到flag:
C.明日计划
继续CISCN的reverse练习
- 170623 逆向-maze
- 【逆向学习】 maze writeup
- 170708 逆向-南邮CTF逆向(maze)
- 171210 逆向-Take the maze
- Codeforces 377A Maze【Bfs+逆向思维】
- 南京邮电大学网络攻防平台逆向writeup之[maze]
- 南邮CTF逆向题第五道maze解题思路
- Maze
- maze
- Maze
- maze
- Maze
- maze
- Maze
- Maze
- Maze
- 170623 逆向-暗号
- 逆向
- 【iOS开发干货】Objective-C基础知识点总结(一)
- Java线程中yield与join方法的区别
- 设计模式六大原则
- 三种工厂模式的分析以及C++实现
- foxmail设置
- 170623 逆向-maze
- Java就业企业面试问题-数据库
- Java就业企业面试问题-ssm框架
- pat练习
- Java就业企业面试问题-ssh框架
- Java就业企业面试基础+WEB问题
- awk练习
- Spring源码包的Spring配置信息文件路径
- Java就业企业面试问题-电商项目