收获

  • 解决 exe 由于缺少 DLL 文件导致无法运行的问题

  • 迷宫题,不过这个题有点小坑,对迷宫进行了改动,所以看到迷宫不要傻乎乎的直接复制粘贴,多看看代码里有没有对迷宫做修改

  • 代码与程序输出结合分析,快速定位关键位置


(2023年4月16日)【GDOUCTF 2023】doublegame


思路

解压得到一个 doublegame.exe 程序,运行发现是一个贪吃蛇游戏

部分人可能像我一样,打开 exe 会弹出一个缺少 DLL 的错误,导致无法运行

详见本站的《运行程序遇到 “由于找不到 xxx.dll,无法继续执行代码”》一文

2023GDOUCTF-doublegame1.png

2023GDOUCTF-doublegame5.png

经过试验,后面蛇的速度会越来越快

用 64 位 IDA 打开,F5 反编译:

2023GDOUCTF-doublegame2.png

但是什么都没有

由于没有 main() 函数,shift + F12 查看一下字符串,定位到关键位置:

2023GDOUCTF-doublegame3.png

2023GDOUCTF-doublegame4.png

可以看到很多与 flag 有关的信息:
① 首先最关键的一点,告诉了 flag 的形式是 HZCTF{md5(path)+score}
② 注意下图,有一个由很多 '0'' ' 组成的类似迷宫的东西,结合 md5(path) 可以大致推断 path 就是走迷宫的路径
③ 同时,还告诉了 the first game tell the score,结合文件名 doublegame.exe 可知,这个程序应该有两个游戏,第一个游戏是贪吃蛇,第二个游戏是走迷宫,贪吃蛇得到 score,走迷宫得到 path

注意到字符串里的 "GAME OVER""?(y/n):" 与贪吃蛇游戏闯关失败时的输出有关,跟进函数位置:

2023GDOUCTF-doublegame6.png

根据 sub_1400111F9("GAME OVER") 可以推出 sub_1400111F9() 函数的功能是 printf()

重命名一下,定位到关键 if 判断:

2023GDOUCTF-doublegame7.png

跟进上面两个红框中 printf() 的输出内容:

2023GDOUCTF-doublegame8.png

内容是十六进制数据,结合游戏的输出,这应该是输出的某些中文内容,最后 %d 是一个游戏中的数据

重点在后面的一个 if 判断:

if ( dword_140022CD0 > 13371337 )
  sub_14001136B();
sub_1400110E6();

首先,最后的 sub_1400110E6() 函数会执行 sub_140012A40() 用于在本地生成一个 贪吃蛇最高得分记录.txt 的文本文件:

2023GDOUCTF-doublegame9.png

关键在于当 dword_140022CD0 > 13371337 时,会执行 sub_14001136B() 函数,sub_14001136B() 函数会执行 sub_140012CF0()

2023GDOUCTF-doublegame10.png

这个函数用来生成迷宫的,以及 switch() 中的 'w''a''s''d' 用来控制方向
由于这是第二个游戏,明显 dword_140022CD0 > 13371337 就是通关条件

(可能是分数 score > 13371337 就进入下一关,这里只是猜测,后面会验证,所以仅凭徒手玩游戏通关是几乎不可能的了)

sub_140012CF0() 函数提供的迷宫数据提取出来
但是这里有个小坑,很重要,没有发现的话是做不出来的

2023GDOUCTF-doublegame12.png

在上方的一堆 '0'' ' 都是迷宫的内容,注意最后有一个 v11[4] = 48,48 是 '0' 的 ASCii 码

找到 v11 的位置,也就是@11111110101111111110(为了看得更清楚,我把空格用 '1' 来表示) 这一行的第 5 个位置的空格改为 '0'

提取出修改后的迷宫:
(这里有个小技巧:由于 '0' 表示墙壁,' ' 表示路,可以在 Pycharm 中全选 ' ',空格会有高亮,路径会更显眼)

2023GDOUCTF-doublegame11.png

得到路径 pathdddssssddwwwwddssddwwwwwwddddssaassddddwwwwddwwwwddd
MD5 加密后为:811173b05afff098b4e0757962127eac

这里来分析一下,为什么是个走迷宫,具体信息是怎么样的:
① 首先这里有个 while 循环,判断的是当前位置是否走到了出口,结合(v15,v16)(15,0)对应 '@' 的位置,而(v17,v18)(7,20)对应最后出口的那个点

2023GDOUCTF-doublegame13.png

② 再往下,注意到有一步操作会影响到后面的 if 条件,往左走的时候,如果当前位置是 '*' 的话,就将 v7[20] = 48,否则后面的 if 不会执行,而是输出 "error",所以路径必须要经过 '*' 的位置

2023GDOUCTF-doublegame14.png

2023GDOUCTF-doublegame15.png

③ 最后,注意这个 if 条件

2023GDOUCTF-doublegame16.png

函数 sub_140011433(0) 会执行 sub_140011E10(unsigned int a1)

2023GDOUCTF-doublegame17.png

根据形式,sub_14001126C("%d", &v2) 应该是一个 scanf() 函数,让用户输入的 v2 就是 key,而前面又提示说 the score is saving cat's key!\n ,可以知道这个 v2 就是 score

由于传参 a1 = 0v2 初值为 0,最后返回 v2 的值,而 sub_140011433(0) 的返回值为 v24,且 v24 要等于 13376013,所以 v2 = 0x1DC4 ^ 13376013

2023GDOUCTF-doublegame18.png

最后得到 v2 = 13371337验证了前面 score > 13371337 就进入下一关的猜想
于是得到 score13371337


结果

NSSCTF{811173b05afff098b4e0757962127eac13371337}
(最终要求将 HZCTF 改为 NSSCTF)