​ 在看雪学苑看到了一题py逆向,正好最近在学习,遂拿来做。

题目链接https://pan.baidu.com/s/1Rq4lJmANHSL1EKk8AZxqDw?pwd=0422

首先拿到一个附件,没有后缀

将其拖入010Editor查看文件头,发现其为4D5A开头,为windowsPE文件的MS-DOS 头,将文件后缀修改为PE文件的可执行系列.exe

image-20231121221724145

image-20231121221747202

image-20231121223453957

image-20231121223920738**

可以通过该文件图标特征判断该文件为PyInstaller打包的文件,也可以通过Exeinfo PE来查看文件提示,推荐查看

python打包的二进制文件反编译 - Hk_Mayfly - 博客园 (cnblogs.com)博客里也有一个pwn题

image-20231121224759773

打开exe程序发现是一道迷宫题,flag是用wasd表示的最短路径的md5值。

反编译成py文件

我们先把pyinstxtractor.py复制到exe文件同一目录,执行命令

image-20231123224357482

1
>python pyinstxtractor.py ran.exe

image-20231123224911879

可能的程序入口为5,且生成了一个_extracted文件夹,我们将其中的5和struct复制出来。

image-20231123224803830

将文件5后缀改为5.pyc,分别用winhex打开文件,

struct

image-20231123225620840

5.pyc

image-20231123225656700

struct比5.pyc多出16字节,将这16字节插入test.pyc的头部

修改后

image-20231123225950143

在5.pyc目录中打开cmd,用uncompyle6反编译

image-20231123230300476

1
2
>uncompyle6 -o  55.py 5.pyc
5.pyc

打开55.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# uncompyle6 version 3.7.4
# Python bytecode 3.7 (3394)
# Decompiled from: Python 3.8.0 (tags/v3.8.0:fa919fd, Oct 14 2019, 19:37:50) [MSC v.1916 64 bit (AMD64)]
# Embedded file name: 5.py
# Compiled at: 1995-09-28 00:18:56
# Size of source mod 2**32: 272 bytes
import random, msvcrt
row, col = (12, 12)
i, j = (0, 0)
maze = [
[
1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[
1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1],
[
1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1],
[
1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1],
[
1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1],
[
1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1],
[
1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1],
[
1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1],
[
1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1],
[
1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1],
[
1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1],
[
1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1],
[
1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1],
[
1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1],
[
1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1],
[
1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1],
[
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1],
[
1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1],
[
1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1],
[
1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1],
[
1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1],
[
1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
[
1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1],
[
1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 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, 1, 1, 1, 1, 1, 1, 0, 1]]
print('Mice walk in a maze: wasd to move,q to quit')
print("flag is the shortest path's md5,example:if the shortest path is wasdsdw,the flag is md5('wasdsdw')")
i, j = (0, 1)
n = 0
while 1:
if i == row * 2:
if j == col * 2 - 1:
print('ohhhh!!!!you did it')
break
print('your position:({},{})'.format(i, j))
inp = msvcrt.getch()
n += 1
ti, tj = i, j
if b'a' == inp and i > 0:
tj -= 1
else:
if b'w' == inp and j > 0:
ti -= 1
else:
if b's' == inp and j < row * 2:
ti += 1
else:
if b'd' == inp and i < col * 2:
tj += 1
else:
if b'q' == inp:
exit('bye!!')
else:
print('What???')
continue
if maze[ti][tj] == 1:
print(random.choice(['no wayy!!', "it's wall", 'nop']))
continue
elif maze[ti][tj] == 0:
print(random.choice(['nice!!', 'yeah!!', 'Go on']))
i, j = ti, tj

关于迷宫题的一些求解思路 (qq.com)得到bfs算法得出路径

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
//BFS——广度优先算法(Breadth First Search)
//简单来说,BFS算法会判断当前状态可以行走的方向。假设你是一只老鼠,如果只有一条路,你会沿着这条路一直走。当到交叉路口时,BFS算法会clone出多个小鼠同时向多个方向进攻。同时利用队列保存每一个小鼠对象。当检测到小鼠未到达出口时,逐个出列,小鼠继续前进。当有一只小鼠找到出口时,退出循环。此时小鼠走过的路径就是最短路径(但不唯一,因为队列里的小鼠走过的路径长度是相同的,可能队列中下一个小鼠也能到达出口)






#include <iostream>
#include <queue>
using namespace std;
char maze[25][26] = {//注意String类型有'/0'
"1S11111111111111111111111",
"1010000000000000000000101",
"1010111111111111101110101",
"1010000010001000001000101",
"1011111010101011111011101",
"1000100010101010100010001",
"1110111110101010101111101",
"1010001000100010100000101",
"1011101011111110111110101",
"1000001000001000001010001",
"1011111110101011101011101",
"1010000010101000100010001",
"1010111011101010111010111",
"1000101010001010001010101",
"1111101010111011101010101",
"1010001010001010001010101",
"1010101010101110111110101",
"1010100010101000100010001",
"1010111110101011101011101",
"1010001000101000101000101",
"1011101111101110101110101",
"1010001000101000101010101",
"1010111010101011101010101",
"1000000010001000000010001",
"11111111111111111111111E1"
};

//25*25
struct Point {
int row, col;
string path; // 路径跟踪变量
};

bool isValid(int row, int col) {
// 检查点是否在地图内并且是可行走的
return row >= 0 && row < 25 && col >= 0 && col < 25 && maze[row][col] != '1';//迷宫边界及墙
}

void bfs() {
queue<Point> q; //queue< typename > name ; 
q.push({ 0, 1, "" }); // 将起点放入队列中,入队操作
bool visited[25][26] = { false };
visited[0][1] = true; // 该变量用于记录图中的每个节点是否已被访问过,将入口点标为true

while (!q.empty()) {
Point p = q.front(); //访问队首元素,即最早被压入队列的元素。
q.pop();//队首元素出列
//q.pop()弹出队列的第一个元素,但不会返回被弹出元素的值。所以要用p存储 q.front()的返回值
int row = p.row;
int col = p.col;
string path = p.path;

// 判断是否到终点了
if (maze[row][col] == 'E') {
cout << path << endl;
return;
}

//进行移动操作
if (isValid(row - 1, col) && !visited[row - 1][col]) {
q.push({ row - 1, col, path + 'w' });
visited[row - 1][col] = true;//左移
}
if (isValid(row + 1, col) && !visited[row + 1][col]) {
q.push({ row + 1, col, path + 's' });
visited[row + 1][col] = true;//右移
}
if (isValid(row, col - 1) && !visited[row][col - 1]) {
q.push({ row, col - 1, path + 'a' });
visited[row][col - 1] = true;//下移
}
if (isValid(row, col + 1) && !visited[row][col + 1]) {
q.push({ row, col + 1, path + 'd' });
visited[row][col + 1] = true;//上移
}
}

cout << "No path found" << endl;
}

int main() {
bfs();
return 0;
}

输出结果:

image-20231124123738957

CyberChef(可部署再本地)通过md5解密得出image-20231124124207304

NSSCTF{69193150b15c87d39252d974bc323217}