-
-
[分享][原创][SWPUCTF 2025 秋季新生赛]CodeRunner
-
发表于: 2天前 180
-
### [SWPUCTF 2025 秋季新生赛]CodeRunner
做这道题的时候我一直在网上找wp,根本没有
个人感觉超级难,一开始打开是这样的
运行之后发现脚底下是一个二维码,但是看不到全貌,这道题的提示说扫码就可以得到flag,是不是非常简单,实则不然,所以我想应该只有两种可能,一种是生成二维码,一种是二维码已经在了,所以我打算拆包,使用AssetStudio,但是没有找到二维码,但是找到了和黑块和白块,以及QRCodeBuilder函数,所以基本断定应该是制作二维码
后面我想使用 Il2CppDumper 来找找结构信息,看看能不能对解题产生什么帮助,但是这一步报错了,是没有其中一个文件,但是是有的,所以才发现是壳,蜕壳之后成功取得结构信息
把结构信息中的Assembly-CSharp.dll拉入ILSpy,找到QRCodeBuilder类,发现伪造的 DLL 是空壳,但拿到了函数的真实内存地址,将脱壳后的 GameAssembly.dll 拖入 IDA Pro。利用快捷键 G 跳至 ConstructQRCode 方法。按 F5 反编译后,发现这里只有“按图纸铺地砖”的双重 for 循环逻辑,通过读取 *(a1 + 72)(即 qrMatrix)来判断放黑块还是白块
跳入 QRCodeBuilder 的构造函数(.ctor),终于看到了决定性代码
v8[0] = 29;
v8[1] = 29; // 创建 29x29 的矩阵 (Version 3 二维码)
sub_181652960(v9, qword_182F726A8, 0); // 从内存深处拷贝数据
在 IDA 的 .data 段追踪 182F726A8,发现那里的数据是 80000003h。这并不是真实数据,而是 IL2CPP 引擎的 Metadata Token。它指向了存放在 global-metadata.dat 中的一串二进制死数据
既然我们知道数据在 global-metadata.dat 里,且二维码大小是 29×2,那干脆不按常理出牌,直接祭出 CTF 的终极奥义:特征码****扫描。 所有二维码的左上角都有一个 7x7 的定位方块(黑黑黑黑黑黑黑白)。在 int 数组中,这就意味着必然存在连续的 7 个 1 和 1 个 0。 编写 Python 脚本,直接在 global-metadata.dat 里进行地毯式爆破:
import os
from PIL import Image
def find_and_draw():
filepath = "global-metadata.dat"
# 二维码左上角特征:连续 7 个黑块(1) 和 1 个白块(0)
# 扫描模式 A:int 数组 (4字节 Little Endian)
pattern_int = (b'\x01\x00\x00\x00' * 7) + b'\x00\x00\x00\x00'
print(f"[*] 正在扫描 {filepath} ...")
with open(filepath, "rb") as f:
data = f.read()
offset = 0
while True:
offset = data.find(pattern_int, offset)
if offset == -1:
break
# 截取 29x29 = 3364 字节
chunk = data[offset : offset + 3364]
# 校验:确保这 3364 字节全是 0 或 1
if len(chunk) == 3364 and all(chunk[i:i+4] in (b'\x00\x00\x00\x00', b'\x01\x00\x00\x00') for i in range(0, 3364, 4)):
print(f"[+] 在偏移 {hex(offset)} 处成功捕获矩阵!")
# 画图还原
width, height = 29, 29
img = Image.new('1', (width, height), 1)
pixels = img.load()
for y in range(height):
for x in range(width):
val = chunk[(y * width + x) * 4]
if val == 1:
pixels[x, y] = 0 # 1 代表黑块
img = img.resize((290, 290), Image.NEAREST)
img.save("flag_qrcode.png")
print("[+] 二维码已生成!")
return
offset += 1
if __name__ == "__main__":
find_and_draw()