TryHackMe THE GAME CHALLENGE 解题笔记

一、题目信息

  • 题目名称:THE GAME CHALLENGE
  • 题目文件Tetrix.exe-1741979048280.zip
  • 题目类型:逆向工程 / 游戏逆向 / 文件隐藏分析

题目描述:

Cipher has gone dark, but intel reveals he’s hiding critical secrets inside Tetris, a popular video game. Hack it and uncover the encrypted data buried in its code.

题目提供了一个 Windows 游戏程序,并在描述中提到 Tetris 和隐藏的加密数据。我的分析思路如下:

  1. 识别附件和可执行文件的基本信息;
  2. 检查 PE 节区及文件尾部是否存在额外数据;
  3. 定位并提取嵌入的 Godot PCK 资源包;
  4. 检查资源包结构和可能的加密特征;
  5. 使用 Ghidra 复现十六进制编辑器的全文件搜索;
  6. 排除误命中并定位 flag。

二、使用工具

类型工具 / 技术
操作系统Kali Linux
文件识别file
压缩包处理unzip
哈希校验sha256sum
PE 结构分析objdump
十六进制查看xxd、Bless Hex Editor
字符串搜索stringsgrep
逆向分析Ghidra
涉及格式PE、Godot PCK
关键知识点PE section、overlay、Godot PCK、Raw Binary 导入、ASCII 十六进制搜索

三、解压附件

首先识别压缩包类型:

1
file Tetrix.exe-1741979048280.zip

输出:

1
Tetrix.exe-1741979048280.zip: Zip archive data, at least v2.0 to extract, compression method=deflate

这是一个普通的 ZIP 压缩包,直接解压:

1
unzip -n Tetrix.exe-1741979048280.zip

解压后得到:

1
2
Tetrix.exe
__MACOSX/

__MACOSX 是 macOS 创建压缩包时附带的元数据目录,可以忽略。真正需要分析的是 Tetrix.exe

四、基础文件信息

4.1 识别文件类型

1
file Tetrix.exe

输出:

1
Tetrix.exe: PE32+ executable for MS Windows 5.02 (GUI), x86-64 (stripped to external PDB), 13 sections

由此可以判断,它是一个 64 位 Windows GUI 程序,并且包含 13 个 PE 节区。

4.2 计算文件哈希

1
sha256sum Tetrix.exe

输出:

1
64ba9f8a44b6d28026117497eedac76bf30189a09b5d8b7decf47fbd353ec96f  Tetrix.exe

记录哈希可以确认后续分析过程中样本没有发生变化。

4.3 查看文件大小

1
ls -lh Tetrix.exe

输出:

1
-rw-rw-rw- 1 meng meng 89M 2025年 3月15日 Tetrix.exe

一个简单的 Tetris 游戏体积接近 89 MB,说明文件中可能包含完整游戏引擎、资源包或其他附加数据。

五、分析 PE 节区

使用 objdump 查看 PE 节区:

1
objdump -h Tetrix.exe

关键输出:

1
2
3
4
5
6
7
Idx Name          Size      VMA               LMA               File off
0 .text 03ee1650 0000000140001000 0000000140001000 00000400
1 .data 0004a5c0 0000000143ee3000 0000000143ee3000 03ee1c00
2 .rdata 00da4400 0000000143f2e000 0000000143f2e000 03f2c200
3 pck 00000008 0000000144cd3000 0000000144cd3000 05036600
4 .pdata 00146df0 0000000144cd4000 0000000144cd4000 04cd0800
5 .xdata 001930a4 0000000144e1b000 0000000144e1b000 04e17600

这里最值得注意的是名为 pck 的节区。结合样本是游戏程序,可以联想到 Godot 引擎使用的 .pck 资源包。

该节区的文件偏移和声明大小分别为:

1
2
File offset = 0x05036600
Size = 0x8

声明的节区结束位置为:

1
0x05036600 + 0x8 = 0x05036608

六、检查文件尾部数据

查看真实文件大小,并将十六进制偏移转换为十进制:

1
2
stat -c '%s' Tetrix.exe
printf "%d\n" 0x05036608

输出:

1
2
93021728
84108808

文件真实大小明显大于 pck 节声明的结束位置,尾部还存在:

1
93021728 - 84108808 = 8912920 bytes

也就是约 8.5 MiB 的附加数据。

从 PE 结构角度看,pck 节只声明了 8 字节,后续内容属于文件尾部的 overlay;从 Godot 打包结构看,从 0x05036600 开始一直到文件末尾的数据共同组成了嵌入式 PCK 包。

七、定位 Godot PCK 包头

查看 pck 节起始位置附近的数据:

1
xxd -s $((0x05036600)) -l 32 Tetrix.exe

输出:

1
2
05036600: 4744 5043 0200 0000 0400 0000 0300 0000  GDPC............
05036610: 0000 0000 0300 0000 3017 0000 0000 0000 ........0.......

其中:

1
47 44 50 43 = GDPC

GDPC 是 Godot PCK 文件的 magic,可以确认 Tetrix.exe 中嵌入了 Godot 游戏资源包。

继续搜索文件中的所有 GDPC

1
grep -abo 'GDPC' Tetrix.exe | tail -20

部分结果:

1
2
3
4
5
6
7
45907339:GDPC
45907436:GDPC
45907536:GDPC
45907718:GDPC
46318144:GDPC
84108800:GDPC
93021724:GDPC

其中:

1
84108800 = 0x05036600

这与前面发现的 pck 节起始位置完全一致。文件末尾的 GDPC 则是 PCK 的尾部标记。

八、提取 PCK 资源包

从偏移 0x05036600 开始提取文件剩余内容:

1
tail -c +$((0x05036600 + 1)) Tetrix.exe > tetrix.pck

这里的 tail -c +N 从第 N 个字节开始读取,而文件偏移从 0 开始,因此需要加 1。

查看提取后的文件大小:

1
stat -c '%s' tetrix.pck

输出:

1
8912928

查看 PCK 头部:

1
xxd -l 128 tetrix.pck

输出:

1
2
3
4
5
6
7
00000000: 4744 5043 0200 0000 0400 0000 0300 0000  GDPC............
00000010: 0000 0000 0300 0000 3017 0000 0000 0000 ........0.......
00000020: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000030: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000040: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000050: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000060: 4600 0000 ...

可以初步解析为:

字段含义
MagicGDPCGodot PCK 标识
PCK format2PCK 格式版本
Godot version4.3.0引擎版本
Flags3PCK 标志位
File base0x1730文件数据区域基址
File count0x46共 70 个文件

九、检查 PCK 目录区

为了进一步检查 PCK 头部和目录区,我编写了下面的 Python 脚本。

pck_header.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
from pathlib import Path
import math
import struct


def entropy(buf):
if not buf:
return 0

frequencies = [0] * 256
for byte in buf:
frequencies[byte] += 1

result = 0
for count in frequencies:
if count:
probability = count / len(buf)
result -= probability * math.log2(probability)

return result


data = Path("tetrix.pck").read_bytes()

magic = data[:4]
fmt, major, minor, patch, flags, file_base = struct.unpack_from(
"<IIIIIQ", data, 4
)
file_count = struct.unpack_from("<I", data, 0x60)[0]
directory = data[0x64:file_base]

print("[+] magic:", magic)
print("[+] pck_format:", fmt)
print("[+] godot_version:", f"{major}.{minor}.{patch}")
print("[+] flags:", flags)
print("[+] file_base:", hex(file_base), file_base)
print("[+] file_count:", file_count)
print(
"[+] directory_region:",
hex(0x64),
"-",
hex(file_base),
"size =",
len(directory),
)
print("[+] directory_entropy:", round(entropy(directory), 4))
print("[+] directory_head:")
print(directory[:64].hex())

运行脚本:

1
python3 pck_header.py

输出:

1
2
3
4
5
6
7
8
9
10
[+] magic: b'GDPC'
[+] pck_format: 2
[+] godot_version: 4.3.0
[+] flags: 3
[+] file_base: 0x1730 5936
[+] file_count: 70
[+] directory_region: 0x64 - 0x1730 size = 5836
[+] directory_entropy: 7.9702
[+] directory_head:
28a995f8be6ab5a2771571f9eeadfd519816000000000000a9a9d11444480e8f...

目录区域的熵值为 7.9702,非常接近理论最大值 8,说明这部分数据具有很强的随机性。

高熵数据可能来自加密或压缩,不能只根据熵值直接下结论。不过结合 PCK 标志位和目录内容不可直接辨认,可以合理怀疑该 PCK 的目录区域经过了加密处理。

十、搜索 Godot 相关字符串

继续使用 strings 搜索 Godot、资源路径和加密相关字符串:

1
2
strings -a -n 5 Tetrix.exe \
| grep -Eai 'script_encryption|encryption_key|PackedData|FileAccessEncrypted|AES|encrypt|decrypt|key|godot|GDPC|pck|res://'

程序中可以找到 Godot 引擎版本:

1
Godot Engine v4.3.stable.official

同时还能看到一些资源路径和编译后的 GDScript 文件:

1
2
3
4
5
6
7
8
9
res://GUI.gdc
res://StoreSettings.gdc
res://Utils.gdc
res://blocks/I.gdc
res://blocks/J.gdc
res://blocks/L.gdc
res://blocks/O.gdc
res://blocks/S.gdc
res://blocks/T.gdc

这些信息进一步确认了样本是 Godot 游戏,并且包含 .gdc 编译脚本。

十一、尝试搜索明文密钥

Godot PCK 常见的加密密钥长度为 256 bit,也就是 32 字节。如果密钥以十六进制字符串保存,通常会表现为 64 个十六进制字符。

可以使用下面的命令进行初步搜索:

1
2
3
strings -a -n 32 Tetrix.exe \
| grep -Eio '[0-9a-fA-F]{64}' \
| sort -u

搜索结果主要是:

1
2
3
4
5
0000000000000000000000000000000000000000000000000000000000000000
1973197219711970196919681967196619651964196319621961196019591958
1989198819871986198519841983198219811980197919781977197619751974
3333333333333333333333333333333333333333333333333333333333333333
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff

这些内容大多是全 0、全 f、重复字符或年份序列,不像随机生成的有效密钥。

如果要沿着 PCK 解包路线继续分析,就需要逆向程序,寻找以原始二进制形式保存或在运行时生成的密钥。但这道题还有一条更直接的路线:对完整文件搜索 flag 特征。

十二、使用 Ghidra 进行全文件搜索

参考其他解题思路,可以使用 Bless Hex Editor 打开整个文件并搜索 THM。我希望使用 Ghidra 复现相同的搜索方式。

这里需要注意:

  • Bless Hex Editor 按原始文件偏移从头到尾显示数据;
  • Ghidra 默认按 PE 格式导入时,显示的是经过 PE Loader 映射后的地址空间;
  • 如果希望在 Ghidra 中像十六进制编辑器一样查看整个原始文件,应将样本作为 Raw Binary 导入。

重新导入 Tetrix.exe

  1. 选择 File -> Import File
  2. 选择 Tetrix.exe
  3. Format 设置为 Raw Binary
  4. Base Address 设置为 0x0
  5. Language 设置为 x86:LE:64:default

这样导入后,Ghidra 中显示的地址就等于文件偏移,与 Bless 左侧显示的偏移一致。由于这里的目标只是搜索原始字节,不需要运行自动分析。

十三、搜索 THM 特征

打开 Raw Binary 版本后,选择:

1
Search -> Memory

搜索下面的 ASCII 字节:

1
54 48 4D

它对应字符串:

1
THM

搜索结果中出现了多个候选地址:

1
2
3
4
5
041567f7
042502c6
043906fa
050cf1b6
058b159c

这些位置只是包含 THM 字节序列,不一定都是 flag。

13.1 排除误命中

查看第一个搜索结果附近的数据,发现它位于普通字符串:

1
FEATURE_ARITHMETIC

其中 ARITHMETIC 恰好包含:

1
ARI[THM]ETIC

这只是字符串子串造成的误命中。

TryHackMe flag 通常使用下面的格式:

1
THM{...}

其十六进制前缀为:

1
54 48 4D 7B

因此搜索时不仅要检查 THM,还要确认后面是否紧跟 {

十四、定位最终 flag

继续检查搜索结果,在文件偏移 0x058b159c 附近发现下面的完整字节序列:

1
54 48 4D 7B 49 5F 43 41 4E 5F 52 45 41 44 5F 49 54 5F 41 4C 4C 7D

转换为 ASCII:

1
T  H  M  {  I  _  C  A  N  _  R  E  A  D  _  I  T  _  A  L  L  }

最终得到:

1
THM{I_CAN_READ_IT_ALL}

flag

十五、总结

这道题属于游戏逆向和文件隐藏分析题。虽然最终可以通过搜索明文 THM{ 找到 flag,但完整分析过程中涉及了多个值得记录的知识点。

整体分析路线如下:

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
解压附件
|
识别 PE32+ Windows x64 程序
|
分析 PE section
|
发现可疑的 pck 节
|
定位 0x05036600 处的 GDPC
|
确认 EXE 中嵌入 Godot PCK
|
提取 tetrix.pck
|
分析 PCK 头部和目录区域熵值
|
判断资源包目录疑似经过加密
|
转向全文件特征搜索
|
使用 Ghidra Raw Binary 导入 EXE
|
搜索 THM 和 THM{
|
排除 ARITHMETIC 等误命中
|
在 0x058b159c 处找到 flag

本题的关键经验:

  • file 可以快速判断文件类型和目标架构;
  • objdump -h 可以查看 PE 节区布局;
  • 看到 pckGDPC 时,应联想到 Godot PCK 资源包;
  • 文件真实大小大于所有节区覆盖范围时,可能存在 overlay 数据;
  • 熵值接近 8 说明数据随机性很强,但需要结合格式标志和上下文判断是加密还是压缩;
  • Ghidra 默认 PE 导入不等价于十六进制编辑器的全文件视图;
  • 若要在 Ghidra 中按原始文件偏移搜索,应使用 Raw Binary 导入,并将 Base Address 设置为 0x0
  • 搜索 THM 可能出现普通单词子串造成的误报,应进一步确认 THM{
  • 最终 flag 明文位于原始文件偏移 0x058b159c 附近。

最终答案:

1
THM{I_CAN_READ_IT_ALL}