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练习
 
原创粉丝点击