首页
社区
课程
招聘
[原创]看雪CTF.TSRC 2018 团队赛 第六题 追凶者也
2018-12-11 21:40 2240

[原创]看雪CTF.TSRC 2018 团队赛 第六题 追凶者也

2018-12-11 21:40
2240

一个Win32程序,双击跑起来没有反映,CPU倒是跑起来了。题目描述说Win10可能报错,看起来我是遇到BUG了,只能丢进Win7虚拟机。在Win7中可以成功运行,随便输入点东西会弹窗。
图片描述

 

程序比较小,把WinMain下面的函数都点开看看,立马可以找到出错的逻辑。
图片描述

 

看看其他函数,0x401290处有个很奇怪的数组初始化操作,xref到0x401A97,发现这里似乎是校验逻辑。结合动态调试可以分析出基本流程:拿到输入,进行check,check返回1的话就再hash一下,需要结果是0x5634d252,然后就会修改弹窗标题和弹窗内容,应该就是过关信息了。
图片描述

 

check的逻辑也很简单,要求我们的输入可以把数组转化为指定形式。
图片描述

 

calc就是执行我们的操作,分析可知,把那个全局数组视为一个3x3的方阵,我们可以将一个元素和附近的0进行位置交换。

char __cdecl calc(int op, int ele)
{
  char result; // al
  signed int col; // [esp+8h] [ebp-8h]
  signed int row; // [esp+Ch] [ebp-4h]

  if ( !ele )
    return 0;
  row = 0;
LABEL_4:
  if ( row >= 3 )
    return 0;
  for ( col = 0; ; ++col )
  {
    if ( col >= 3 )
    {
      ++row;
      goto LABEL_4;
    }
    if ( mat[row][col] == ele )
      break;
LABEL_6:
    ;
  }
  switch ( op )
  {
    case 0:                                     // w
      if ( row )
      {
        if ( mat[row - 1][col] )
        {
          result = 0;
        }
        else
        {
          mat[row - 1][col] = mat[row][col];
          mat[row][col] = 0;
          result = 1;
        }
      }
      else
      {
        result = 0;
      }
      break;
    case 1:                                     // d
      if ( col == 2 )
      {
        result = 0;
      }
      else if ( byte_4147D1[3 * row + col] )
      {
        result = 0;
      }
      else
      {
        byte_4147D1[3 * row + col] = mat[row][col];
        mat[row][col] = 0;
        result = 1;
      }
      break;
    case 2:                                     // s
      if ( row == 2 )
      {
        result = 0;
      }
      else if ( mat[row + 1][col] )
      {
        result = 0;
      }
      else
      {
        mat[row + 1][col] = mat[row][col];
        mat[row][col] = 0;
        result = 1;
      }
      break;
    case 3:                                     // a
      if ( col )
      {
        if ( mat[row][col - 1] )
        {
          result = 0;
        }
        else
        {
          mat[row][col - 1] = mat[row][col];
          mat[row][col] = 0;
          result = 1;
        }
      }
      else
      {
        result = 0;
      }
      break;
    default:
      goto LABEL_6;
  }
  return result;
}

那么现在思路就是先算出能把数组转换成功的输入,再计算hash看是否匹配。由于翻转操作要求我们指定元素的指定方向上的邻近元素是0,所以等价于不停地挪动0。因为程序有个hash的要求,所以说我们还不能只找最短路径,这里就先多打点答案出来。

import copy
MAT = [
    [4, 1, 3],
    [7, 2, 5],
    [8, 6, 0],
]

def check(mat):
    return mat[0][0] == 1 and mat[0][1] == 2 and mat[0][2] == 3 and mat[1][0] == 4 and mat[1][1] == 5 and mat[1][2] == 6 and mat[2][0] == 7 and mat[2][1] == 8 and mat[2][2] == 0


elements = [0, 1, 2, 3, 4, 5, 6, 7, 8]
step = [(0,-1), (0, 1), (-1, 0), (1, 0)]
ops = ['d', 'a', 's', 'w']

ans = []
def dfs(x, y, cur, mat):
    for i in range(4):
        s = step[i]
        xx = x+s[0]
        yy = y+s[1]
        if xx >= 0 and xx <= 2 and yy >= 0 and yy <= 2:
            e = mat[xx][yy]
            if len(cur) > 0 and e == int(cur[-1][1]):
                continue
            bak = copy.deepcopy(mat)
            bak[x][y] = e
            bak[xx][yy] = 0
            log = cur + [ops[i]+str(e)]
            if check(bak):
                ans.append(''.join(log))
                return
            elif len(log) >= 20:
                return
            else:
                dfs(xx, yy, log, bak)
        else:
            continue

dfs(2,2, [], MAT)
print ans

运行之后有不少解出来了,当然里面有很多是操作冗余的。

['d6d8s7a2a5w6d8s5d2s4a1w2w5a8', 'd6d8s7a2a5w6d8s5s1d4w2a1s4d2w1a4w5a8', 'd6d8s7a2s1d4w2a1a5w6d8s5s4d2w1a4w5a8', 'd6d8s7a2s1d4w2a1s4d2w1a4a5w6', 'd6d8s7a2w8a6s5d8d2s4a1w2a8w5d6s8a5w6', 'd6d8s7a2w8a6s5d8d2s4a1w2w6a5s8d6w5a8', 'd6d8s7a2w8a6s5d8w6a5s8d6d2s4a1w2w5a8', 'd6d8s7s4a1a3w5d2s3a5w2d3s5a2w3w6', 'd6d8s7s4a1w2a5w6', 'd6d8s7s4a1w2w8a6s5d8w6a5s8d6w5a8', 'd6s2d7w8a2s7d8w2a7s8d2s4a1w2a5w6', 's5d2w6a5s2d6w5a2s6d5w2d8s7s4a1w2a5w6']

这时候送给hash校验,

def hash_(s):                                                                     
    ret = len(s)
    for i in s:
        ret = ord(i) ^ ((ret>>28)&0xffffffff) ^ ((16 * ret)&0xffffffff)
        ret &= 0xffffffff
        if ret > 0x7fffffff:
            ret -= 0x100000000
    return ret
for i in ans:
    if hash_(i) == 0x5634D252:
        print i

结果只有一个,就是最短的d6d8s7s4a1w2a5w6,也就是最终答案了。
图片描述

 

PS:写python差点就被符号位右移坑了……


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
点赞1
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回