Scratch cat lost in the MAZE
原创2023年11月23日...大约 5 分钟
千層地獄?我選擇千層迷宮。
網頁: https://scratch.mit.edu/projects/919957145/
0x01 分析
同样是来到源码部分,看到每局都是从绿点走到红点,走过的路径是 seq
,在完成一张地图后通过计算上下左右的次数,计算出一个数放进 result
中
使用 result
计算出 flag
0x02 Exploit
根据以上信息,我们需要通过得到每一张地图的路径 seq
这里使用了 dfs 算法,通过递归的方式得到每一张地图的路径
然后计算出 result
,最后通过 result
计算出 flag
通过下载源码得到所有的地图
1. 裁切图片
附件中的图片并不是正方形,我们从过 Python 批量裁切多余部分
from PIL import Image
import os
def crop_images(input_folder, output_folder):
# 确保输出文件夹存在
if not os.path.exists(output_folder):
os.makedirs(output_folder)
# 处理输入文件夹中的每张图像
for filename in os.listdir(input_folder):
input_path = os.path.join(input_folder, filename)
output_path = os.path.join(output_folder, filename)
# 打开图像
img = Image.open(input_path)
# 裁剪图像从右往左至720*720
cropped_img = img.crop((0, 0, 720, 720))
# 保存裁剪后的图像
cropped_img.save(output_path)
if __name__ == "__main__":
input_folder = "maps" # 输入图像文件夹
output_folder = "maps-cut" # 输出图像文件夹
crop_images(input_folder, output_folder)
2. 图片像素化
为了更方便的将图片转换为路径,我们需要把图片像素话后,把像素点转成数组
from PIL import Image
import os
def pixelate_image(image, pixel_size):
# 获取图像的宽度和高度
width, height = image.size
# 计算图像中每个块的像素值
for y in range(0, height, pixel_size):
for x in range(0, width, pixel_size):
box = (x, y, x + pixel_size, y + pixel_size)
region = image.crop(box)
average_color = tuple(int(sum(channel) / len(channel)) for channel in zip(*region.getdata()))
# 将块中的所有像素设置为块的平均颜色
image.paste(average_color, box)
image = image.convert('RGB')
return image
def process_images(input_folder, output_folder, pixel_size=16):
# 确保输出文件夹存在
if not os.path.exists(output_folder):
os.makedirs(output_folder)
# 处理输入文件夹中的每张图像
for filename in os.listdir(input_folder):
if filename.endswith(".png"):
input_path = os.path.join(input_folder, filename)
output_path = os.path.join(output_folder, os.path.splitext(filename)[0] + ".jpg")
# 打开PNG图像
img = Image.open(input_path)
# 像素化图像
pixelated_img = pixelate_image(img, pixel_size)
# 保存为JPG格式
pixelated_img.save(output_path, "JPEG")
if __name__ == "__main__":
input_folder = "maps-cut" # 输入图像文件夹
output_folder = "maps-pix" # 输出图像文件夹
process_images(input_folder, output_folder)
3. 图片转文本
from PIL import Image
from collections import Counter
def convert_color_to_number(pixel):
# 定义颜色映射
color_mapping = {
(11, 64, 240, 255): 1, # 蓝色
(255, 0, 0, 255): 2, # 红色
(0, 255, 0, 255): 3, # 绿色
(255, 255, 255, 255): 0 # 白色
}
return color_mapping.get(pixel, -1) # 默认为-1表示未知颜色
def generate_txt_from_image(image_path, output_path):
# 打开图像
image = Image.open(image_path)
# 获取图像的宽度和高度
width, height = image.size
# 创建一个空白的txt文件
with open(output_path, 'w') as file:
for y in range(height):
for x in range(width):
# 获取像素颜色
pixel = image.getpixel((x, y))
# 将颜色映射为对应的数字
number = convert_color_to_number(pixel)
# 写入txt文件
file.write(f'{number} ')
file.write('\n')
def extract_values(matrix):
result = []
for i in range(0, len(matrix),16):
row_result = []
for j in range(0, len(matrix[i]), 16):
row_result.append(matrix[i][j])
result.append(row_result)
return result
def read_txt(filename):
with open(filename, 'r') as file:
lines = file.readlines()
matrix = [list(map(int, line.split())) for line in lines]
return matrix
def write_txt(filename, matrix):
with open(filename, 'w') as file:
for row in matrix:
file.write(','.join(map(str, row)) + '\n')
def main(image_path):
# image_path = 'maps-cut' # 实际的图像路径
output_path = 'test.txt' # 输出每个像素值的txt文件路径
generate_txt_from_image(image_path, output_path)
output_filename = "test-2output.txt" # 将上面txt文件中的每16个数字提取出来的输出文件名
input_matrix = read_txt(output_path)
output_matrix = extract_values(input_matrix)
write_txt(output_filename, output_matrix)
4. 使用 DFS 计算路径
#include <bits/stdc++.h>
using namespace std;
namespace fs = std::filesystem;
typedef long long int ll;
const int MAXN = 1e5 + 5;
const int INF = 1e9;
int n, m, ans = INF;
string path = "";
char mp[50][50]; // 地图
bool vis[50][50]; // 是否遍历
int dir[4][2] = {1, 0, -1, 0, 0, 1, 0, -1}; // 上下左右
char d[4] = {'d', 'u', 'r', 'l'}; // 上下左右
void DFS(int x, int y, int step) {
if (step > ans) return;
if (mp[x][y] == '2') {
ans = min(ans, step);
return;
}
for (int i = 0; i < 4; i++) {
int nx = x + dir[i][0], ny = y + dir[i][1];
if (nx >= 0 && nx < n && ny >= 0 && ny < m && mp[nx][ny] != '1' && !vis[nx][ny]) {
vis[x][y] = true;
path += d[i];
// cout<<path<<endl;
DFS(nx, ny, step + 1);
if (ans != INF) // 如果找到终点,直接返回,不再继续搜索
return;
// 该条路无法到达终点则退回
vis[x][y] = false;
path.pop_back();
}
}
}
int get_seq(string filename) {
path = "";
// 读取文件iostream
ifstream ifs(filename.c_str());
n = 45, m = 45;
if (!ifs.is_open()) {
std::cerr << "Error opening file: " << filename << std::endl;
return 1; // 返回错误代码
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
ifs >> mp[i][j];
}
}
int x = 0, y = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
vis[i][j] = false;
if (mp[i][j] == '3') // 标记起点
{
x = i;
y = j;
vis[i][j] = true;
}
}
}
ans = INF;
DFS(x, y, 0);
if (ans == INF) ans = -1; // 无法到达终点
// cout << ans << endl;
if (ans != -1) {
cout << "Path: " << path << endl;
// 新建filename_output文件
string output_filename = filename + "_output";
ofstream ofs(output_filename.c_str());
ofs << path;
}
return 0;
}
void TraverseDirectory(const std::string& path) {
for (const auto& entry : fs::directory_iterator(path)) {
if (fs::is_regular_file(entry)) {
// 处理文件
std::cout << "File: " << entry.path() << std::endl;
get_seq(entry.path().string());
}
}
}
int main() {
string directoryPath = "maps-right";
TraverseDirectory(directoryPath);
return 0;
}
5. 计算 flag
import json
import math
import os
# JSON 格式的字符串
json_str = '填入 .sb3 中图片名称与地图层数关系的json文件'
'''
{ "costumes": [ { "name": "1", "bitmapResolution": 2, "dataFormat": "png", "assetId": "1a8e468eaf36ba24a200783e113c0d07", "md5ext": "1a8e468eaf36ba24a200783e113c0d07.png", "rotationCenterX": 480, "rotationCenterY": 360 },
.......
{ "name": "1400", "bitmapResolution": 2, "dataFormat": "png", "assetId": "930b75957d9f7056fcc44c56c425473c", "md5ext": "930b75957d9f7056fcc44c56c425473c.png", "rotationCenterX": 480, "rotationCenterY": 360 } ] }
'''
ascii = [
'''
填入ascii数组
'''
]
results = [-1]
final_flag = ""
def moved(seq):
seq=" "+seq
i,cl,cu,cr,cd=0,0,0,0,0
for k in range(0,len(seq)-1):
i=i+1
if seq[i]=='l':
cl+=i
elif seq[i]=='u':
cu+=i
elif seq[i]=='r':
cr+=i
elif seq[i]=='d':
cd+=i
results.append(((cl%4)*64)+((cu%4)*16)+((cr%4)*4)+(cd%4))
flag()
def flag():
i=0
flag=""
for k in range(0,math.ceil((len(results)-1)/20)):
j=0
for l in range(0,20):
i=i+1
if(i>len(results)-1):
break
j=j+results[i]
j= j % 256
flag=flag+ascii[j+2]
print('flag: ',flag,len(flag))
# 反序列化
decoded_data = json.loads(json_str)
def read_txt(filename):
with open(filename, 'r') as file:
lines = file.readlines()
return lines[0]
# 按照name的顺序输出assetid
for i in decoded_data.get('costumes'):
# 读取文件txt
seq_file=i.get('assetId')+'_seq.txt'
seq=read_txt(seq_file)
print(seq)
moved(seq)
print(final_flag)
0x03 Flag
hkcert23{ePiC_10st_iN_tH3_infInItE_m4ZE_j0UrnEy_tHr0uGh_3he_lAbYRiNth}
Powered by Waline v3.3.1