【CTF reverse】[HDCTF2019]Maze-去除乐色代码

606 阅读3分钟

PETools查看exe概况

petools.JPG

入口点:Section: [UPX1], EP: 0x00004EC0,显然有upx壳。

upx壳可直接用命令脱壳:.\upx.exe -d maze_behind_junk.exe

作者:hans774882968以及hans774882968

IDA+x64dbg识别并去除乐色代码

一载入IDA就看到我们在main函数这里。

.text:00401000 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401000 _main:
.text:00401000                 push    ebp
.text:00401001                 mov     ebp, esp
.text:00401003                 sub     esp, 18h
.text:00401006                 push    ebx
.text:00401007                 push    esi
.text:00401008                 push    edi
.text:00401009                 push    offset aGoThroughTheMa ; "Go through the maze to get the flag!\n"
.text:0040100E                 call    sub_401140
.text:00401013                 add     esp, 4
.text:00401016                 lea     eax, [ebp-10h]
.text:00401019                 push    eax
.text:0040101A                 push    offset a14s     ; "%14s"
.text:0040101F                 call    _scanf
.text:00401024                 add     esp, 8
.text:00401027                 push    eax
.text:00401028                 xor     eax, ecx
.text:0040102A                 cmp     eax, ecx
.text:0040102C                 jnz     short near ptr loc_40102E+1
.text:0040102E
.text:0040102E loc_40102E:
.text:0040102E                 call    near ptr 0EC85D78Bh

这里有一些怪异的点:

  1. jnz short near ptr loc_40102E+1是真的很奇怪,跳转到一个指令的中间。
  2. IDA有一段“数据”突兀地出现在代码区。但载入x64dbg后,发现对应地址的“数据”其实是代码。

这只能说明一件事:IDA没能识别许多代码。而原因就在于:0EC85D78Bh本身其实是一条汇编指令,而IDA被call near ptr对应的1字节数据干扰了。

解决:使用Edit -> Patch program -> Change byte,把0x40102e处的E8改成90(对应nop)即可。

这一操作之后,IDA能够识别那些数据了。但还不能F5。于是我们手工创建函数:在函数的第一个字节所对应的行,右键,Create function。这样就能F5了。

sub_401140就是printf。

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v4; // [sp+0h] [bp-24h]@0
  int v5; // [sp+0h] [bp-24h]@1
  signed int i; // [sp+10h] [bp-14h]@1
  char v7[16]; // [sp+14h] [bp-10h]@1

  sub_401140((int)aGoThroughTheMa, v4);
  scanf(a14s, v7);
  for ( i = 0; i <= 13; ++i )
  {
    switch ( v7[i] )
    {
      case 'w':
        ++dword_40807C;
        break;
      case 's':
        --dword_40807C;
        break;
      case 'a':
        --dword_408078;
        break;
      case 'd':
        ++dword_408078;
        break;
      default:
        continue;
    }
  }
  if ( dword_408078 != 5 || dword_40807C != -4 )
  {
    sub_401140((int)aTryAgain___, v5);
  }
  else
  {
    sub_401140((int)aCongratulation, v5);
    sub_401140((int)aHereIsTheFlagF, (int)v7);
  }
  return 0;
}
/*
dword_408078 dd 7
dword_40807C dd 0
*/

找迷宫地图

题目名《Maze》但这段代码没有用到“迷宫”?于是我运行了程序,输入aaddaasswwssssadaadaswssssws发现它们都被认为是flag。

这说明迷宫得自己找,直接在IDA打开Strings window即可找到迷宫字符串,shift+E导出,长度为70。

*******+********* ******    ****   ******* **F******    **************

行和列怎么求?其实凭直觉我们也很容易求出,但这里想用更加数学的方法。

约定:x,y分别表示行、列索引,0-indexed。

namoid = c*x+y。又根据以上代码和“单机游戏常识”,s键使得行增加,a使得列减少。所以我们不难得出终点(5,-4)的-4表示行坐标。于是有以下代码:

def main():
    maze = "*******+********* ******    ****   ******* **F******    **************"
    sid = maze.index("+")
    eid = maze.index("F")
    ex,ey = -4,5
    c = (eid - ey) // (-ex)
    r = len(maze) // c
    print(r,c)
    for i in range(r):
        print(maze[c*i:c*(i+1)])

if __name__ == "__main__":
    main()

迷宫如下:

*******+**
******* **
****    **
**   *****
** **F****
**    ****
**********

flag{ssaaasaassdddw}