当前位置:首页 > 技术 > 正文内容

NewStar CTF MazE 题目逆向解题指南

访客 技术 2026年5月24日 3

NewStar CTF MazE 逆向分析-writeup

题目概述

这是一道来自NewStar CTF 2024的迷宫逆向题目。程序通过管道实现父子进程通信,玩家需要从迷宫的左上角移动到右下角。题目提示路径长度为936,flag格式为flag{md5(路径)}。

程序结构分析

该程序为64位无壳ELF文件,采用fork()创建子进程,通过管道进行进程间通信。父进程负责处理用户输入和显示,子进程负责迷宫生成和位置更新。

主函数逻辑

__int64 __fastcall main(int argc, char **argv, char **envp)
{
  // 创建两个管道用于双向通信
  if ( pipe(pipe_fd1) == -1 || pipe(pipe_fd2) == -1 ){
    perror("pipe failed");
    return 1;
  }
  
  pid = fork();
  
  if (pid > 0) {  // 父进程
    // 关闭不需要的管道端
    close(pipe_fd1[0]);  // 关闭读端
    close(pipe_fd2[1]);  // 关闭写端
    
    // 读取初始地图
    read(pipe_fd2[0], buffer, 0x1000);
    
    puts("这是一个迷宫游戏,你需要从左上角走到右下角");
    puts("当前界面只显示以当前位置为中心的3*3区域");
    puts("使用键盘的'w''a''s''d'移动");
    puts("你的flag是flag{md5(路径)},路径为最短路径");
    
    // 游戏循环
    while (1) {
      scanf("%s", input);
      write(pipe_fd1[1], input, strlen(input) + 1);
      
      if (!strcmp(input, "exit")) break;
      
      memset(buffer, 0, 0x64);
      read(pipe_fd2[0], buffer, 0x64);
      puts(buffer);
    }
  }
  else {  // 子进程
    close(pipe_fd2[0]);
    close(pipe_fd1[1]);
    
    pos_x = 1;
    pos_y = 1;
    
    // 生成初始迷宫视图
    generate_view(view_buffer, 1, 1);
    write(pipe_fd2[1], view_buffer, strlen(view_buffer) + 1);
    
    while (1) {
      memset(buffer, 0, 0x1000);
      read(pipe_fd1[0], buffer, 0x1000);
      
      if (!strcmp(buffer, "exit")) break;
      
      // 处理玩家移动
      process_input(buffer, strlen(buffer), &pos_x, &pos_y);
      
      // 生成新视图
      memset(view_buffer, 0, 0x1000);
      generate_view(view_buffer, pos_x, pos_y);
      write(pipe_fd2[1], view_buffer, strlen(view_buffer) + 1);
    }
  }
}

关键函数分析

generate_view函数 (sub_1469)

该函数根据当前位置生成3x3的迷宫视图。参数为视图缓冲区、x坐标和y坐标。

process_input函数 (sub_168F)

处理玩家输入的移动指令,根据w/a/s/d更新玩家坐标。

迷宫数据提取

通过逆向分析,发现迷宫数据存储在一个大型数组中,通过特定的位移和XOR运算解密。迷宫大小为99x99,起始位置为(1,1),终点为(97,97)。

迷宫重建脚本

根据逆向分析结果,编写Python脚本重建完整迷宫:

# 迷宫数据(从二进制中提取)
maze_data = [
  0x8B, 0x98, 0x8D, 0x9B, 0x9B, 0x99, 0xCA, 0xCA, 0x8B, 0x98,
  # ... (完整的迷宫数据)
]

# 方向偏移数组
x_offset = [-1, -1, -1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0]
y_offset = [-1, 0, 1, 0, -1, 0, 1, 0, -1, 0, 1, 0, 0, 0, 0, 0]

def decrypt_cell(data, x, y):
    key = "tgrddf55"
    index = (99 * x + y) // 8
    bit_pos = 7 - (99 * x + y) % 8
    value = data[index] ^ ord(key[index % 8])
    return (value >> bit_pos) & 0x1

def get_cell_char(data, x, y):
    cell = decrypt_cell(data, x, y)
    if cell == 0:
        return '0'  # 可通行
    elif cell == 1:
        return '1'  # 墙壁
    else:
        return 'e'  # 错误状态

def build_maze(data, size=99):
    maze = []
    for i in range(1, size, 3):
        row1, row2, row3 = [], [], []
        for j in range(1, size, 3):
            # 生成3x3块
            block = [get_cell_char(data, x_offset[k]+i, y_offset[k]+j) 
                    for k in range(11)]
            row1.extend(block[0:3])
            row2.extend(block[3:6])
            row3.extend(block[6:9])
        maze.extend([row1, row2, row3])
    return maze

# 构建迷宫并输出
full_maze = build_maze(maze_data)
for row in full_maze:
    print(''.join(row), end=',')

路径搜索算法

BFS广度优先搜索 (C++)

#include <iostream>
#include <queue>
#include <cstring>

using namespace std;

const int MAZE_SIZE = 99;
int maze_map[MAZE_SIZE][MAZE_SIZE];
int visited[MAZE_SIZE][MAZE_SIZE];

// 方向: 上右下左
int dir_x[] = {-1, 0, 1, 0};
int dir_y[] = {0, 1, 0, -1};
char dir_char[] = {'w', 'd', 's', 'a'};

struct Node {
    int x, y;
    string path;
};

string bfs_search() {
    queue<Node> q;
    q.push({1, 1, ""});
    visited[1][1] = 1;
    
    while (!q.empty()) {
        Node cur = q.front();
        q.pop();
        
        // 到达终点
        if (cur.x == 97 && cur.y == 97) {
            return cur.path;
        }
        
        // 探索四个方向
        for (int i = 0; i < 4; i++) {
            int nx = cur.x + dir_x[i];
            int ny = cur.y + dir_y[i];
            
            if (nx >= 0 && nx < MAZE_SIZE && 
                ny >= 0 && ny < MAZE_SIZE && 
                maze_map[nx][ny] == 0 && 
                visited[nx][ny] == 0) {
                
                visited[nx][ny] = 1;
                q.push({nx, ny, cur.path + dir_char[i]});
            }
        }
    }
    return "";
}

int main() {
    // 将迷宫数据加载到二维数组
    for (int i = 0; i < MAZE_SIZE; i++) {
        for (int j = 0; j < MAZE_SIZE; j++) {
            maze_map[i][j] = maze_binary[MAZE_SIZE * i + j];
        }
    }
    
    string result = bfs_search();
    cout << "最短路径: " << result << endl;
    cout << "路径长度: " << result.length() << endl;
    return 0;
}

DFS深度优先搜索 (C++)

#include <iostream>

using namespace std;

const int MAZE_SIZE = 99;
int maze_map[MAZE_SIZE][MAZE_SIZE];
char path_buffer[10000];
int path_len = 0;

// 方向: 上右下左
int dir_x[] = {-1, 0, 1, 0};
int dir_y[] = {0, 1, 0, -1};
char move_char[] = {'w', 'd', 's', 'a'};

bool is_valid(int x, int y) {
    return (x >= 0 && x < MAZE_SIZE && 
            y >= 0 && y < MAZE_SIZE && 
            maze_map[x][y] == 0);
}

void dfs(int x, int y) {
    // 到达终点
    if (x == 97 && y == 97) {
        for (int i = 0; i < path_len; i++) {
            cout << path_buffer[i];
        }
        cout << endl;
        return;
    }
    
    // 尝试四个方向
    for (int i = 0; i < 4; i++) {
        int nx = x + dir_x[i];
        int ny = y + dir_y[i];
        
        if (is_valid(nx, ny)) {
            path_buffer[path_len++] = move_char[i];
            maze_map[x][y] = 1;  // 标记为已访问
            
            dfs(nx, ny);
            
            maze_map[x][y] = 0;  // 回溯
            path_len--;
        }
    }
}

int main() {
    // 加载迷宫数据
    for (int i = 0; i < MAZE_SIZE; i++) {
        for (int j = 0; j < MAZE_SIZE; j++) {
            maze_map[i][j] = maze_binary[MAZE_SIZE * i + j];
        }
    }
    
    dfs(1, 1);
    return 0;
}

获取Flag

运行搜索算法后得到最短路径,长度为936字符。对该路径计算MD5值:

import hashlib

path = "sssswwww..."  # 搜索得到的路径
flag = "flag{" + hashlib.md5(path.encode()).hexdigest() + "}"
print(flag)

最终结果:

flag{4ed5a17ee7aeb95fcf12a3b96a9d4e6f}

总结

本题关键点在于:

  1. 理解父子进程通过管道通信的机制
  2. 逆向分析迷宫生成算法,重建完整迷宫数据
  3. 使用BFS或DFS找到最短路径
  4. 对路径计算MD5得到flag

由于路径长度为936,手动搜索是不可能的,必须通过程序算法求解。

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。