艾克赛尔
第一节:题目
走迷宫的EXE,flag就是最后走到终点需要的方式
第二节:过程
IDA打开之后,尝试反汇编(0x401604 起)
int main(void)
{
int start_r, start_c, goal_r, goal_c;
/* 扫 20×20:7=起点,8=终点(0x401606 双重循环) */
for (int r = 0; r < 20; r++)
for (int c = 0; c < 20; c++) {
if (maze[r][c] == 7) { start_r = r; start_c = c; }
if (maze[r][c] == 8) { goal_r = r; goal_c = c; }
}
puts("=== MAZE ===");
puts("Enter your path in flag{}:");
char path[128];
fgets(path, sizeof path, stdin);
path[strcspn(path, "\r\n")] = 0; /* 去掉行尾换行 */
int r = start_r, c = start_c, steps = 0;
for (int i = 0; path[i]; i++) {
int dr = 0, dc = 0;
switch (path[i]) { /* 0x401788 起逐字符判方向 */
case 'W': dr = -1; break; /* 上 */
case 'S': dr = +1; break; /* 下 */
case 'A': dc = -1; break; /* 左 */
case 'D': dc = +1; break; /* 右 */
default: puts("Invalid move!"); return 1;
}
r += dr; c += dc;
if (r < 0 || r > 19 || c < 0 || c > 19) { puts("Wrong path!"); return 1; }
int v = maze[r][c];
if (v == 0) { puts("Wrong path!"); return 1; } /* 撞墙 */
if (++steps > 55) { puts("Out of moves!"); return 1; } /* 0x37 = 55 */
if (2 < v && v <= 5) { /* 滑行格 3~5,不连锁 */
for (int k = 0; k < v; k++) {
r += dr; c += dc;
if (r < 0 || r > 19 || c < 0 || c > 19) { puts("Wrong path!"); return 1; }
if (maze[r][c] == 0) { puts("Wrong path!"); return 1; }
}
}
}
if (r == goal_r && c == goal_c) {
puts("\nSuccess!");
printf("Steps: %d\n\n", steps);
puts("Congratulations!");
printf("Flag: flag{%s}\n", path); /* 路径字母就是 flag 内容 */
} else {
puts("Wrong path!");
return 1;
}
}
分析上面的代码,我们发现了起点 (1,1)、终点 (17,10),0 是墙;3~5 会往那个方向挪对应格数,撞墙和滑行中撞墙都不行,其实这道题可以用BFS来做,因为要求最高只能走55次,所以做的时候可以断定这道题用BFS做出来最短的只有一个——唯一解
迷宫长这个样子
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 7 1 1 1 1 3 1 1 1 1 1 5 1 1 1 1 1 1 0
0 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 3 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0
0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0
0 1 1 1 1 1 4 1 1 1 1 1 1 3 1 1 1 1 1 0
0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 1 1 1 1 3 1 1 1 1 1 1 1 4 1 1 1 1 1 0
0 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 3 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0
0 1 1 1 1 1 4 1 1 1 1 1 1 3 1 1 1 1 1 0
0 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 0 1 1 0
0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 1 1 1 1 1 3 1 1 1 1 1 5 1 1 1 1 1 1 0
0 1 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 3 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0
0 1 1 1 1 0 1 1 1 0 1 1 1 0 1 1 1 1 1 0
0 1 1 1 1 1 1 1 1 1 8 1 1 1 1 1 1 1 1 0
0 1 1 0 1 1 1 0 1 1 1 0 1 1 0 1 1 1 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
起点7 终点8,用BFS来做即可
于是最终脚本(把脚本和siuumaze.exe放在同目录下)
import struct
from collections import deque
data = open('siuumaze.exe', 'rb').read()
maze = struct.unpack_from('<400i', data, 0x2820) # 0x404020 换成文件偏移就是 0x2820
def step(r, c, dr, dc): # 走一格;踩到 3~5 再同方向滑 v 格,滑行不连锁
r, c = r + dr, c + dc
if not (0 <= r < 20 and 0 <= c < 20) or maze[r * 20 + c] == 0:
return None
v = maze[r * 20 + c]
if 2 < v <= 5:
for _ in range(v):
r, c = r + dr, c + dc
if not (0 <= r < 20 and 0 <= c < 20) or maze[r * 20 + c] == 0:
return None
return r, c
start, goal = (1, 1), (17, 10)
prev = {start: None}
q = deque([start])
while q:
r, c = q.popleft()
if (r, c) == goal:
break
for ch, (dr, dc) in {'W': (-1, 0), 'S': (1, 0), 'A': (0, -1), 'D': (0, 1)}.items():
nxt = step(r, c, dr, dc)
if nxt and nxt not in prev:
prev[nxt] = ((r, c), ch)
q.append(nxt)
path = ''
cur = goal
while prev[cur]:
cur, ch = prev[cur]
path = ch + path
print(path, len(path))
print('flag{%s}' % path)
最后 flag:
flag{DDDDDDDDSAAAAAAAASSDDDDDDDDSWAAAAAAAASSSDDDDDDDSAAAAAAA}