UniVsThreats26 Quals 逆向挑战“Starfield Relay”详细教学文档
一、挑战概述
本挑战名为“Starfield Relay”,是一个包含10个阶段的CrackMe程序。目标是通过逆向工程分析,逐步获取10个片段,最终拼接成完整flag:UVT{Kr4cK_M3_N0w-cR4Km3_THEN-5T4rf13Ld_piNgS_uR_pR0b3Z_xTND-I_h1D3_in_l0Gz_1n_v01D_iN_ZEN}。
程序特点:
- 无壳的Windows可执行文件
- 采用分阶段验证机制
- 包含文件系统操作和数据处理
- 涉及密码学变换和协议分析
二、程序结构分析
2.1 主程序框架
主函数位于main,通过变量n9控制当前阶段(0-9)。程序结构为:
switch (n9) {
case 0: ZName = sub_1401156F0(Src, n6); break; // Stage 1
case 1: ZName = sub_140115860(Src, n6); break; // Stage 2
case 2: ZName = sub_140115B80(Src, n6); break; // Stage 3
case 3: ZName = sub_1401164A0(Src, n6); break; // Stage 4
case 4: ZName = sub_140117780(Src, n6); break; // Stage 5
case 5: ZName = sub_14011A330(v156); break; // Stage 6
case 6: // Stage 7 (内联在main中)
case 7: // Stage 8 (内联在main中)
case 8: // Stage 9 (内联在main中)
case 9: // Stage 10 (内联在main中)
}
2.2 初始设置
程序启动时:
- 创建目录
uvt_crackme_work - 检查
progress.json文件 - 加载多个常量数组(xmmword_14030D700等),后续用于解密
三、各阶段详细分析与解决方案
3.1 Stage 1 - 基础前缀验证
函数:sub_1401156F0
要求:输入4字符的前缀
验证逻辑:
if (Size == 4 && !memcmp(Buf1_1, "UVT{", 4u)) {
sub_14010CB90(Src, "UVT{", 4);
return 1;
}
解决方案:直接输入UVT{
3.2 Stage 2 - 硬编码片段
函数:sub_140115860 → sub_140115AA0
要求:输入3字符片段
验证逻辑:
- 调用
sub_140115AA0生成硬编码值 - 比较用户输入与硬编码值
硬编码值生成:
*(_BYTE *)Src = 75; // 'K' (ASCII 75)
*((_BYTE *)Src_2 + 1) = 114; // 'r' (ASCII 114)
*((_BYTE *)Src_1 + 2) = 52; // '4' (ASCII 52)
解决方案:输入Kr4
3.3 Stage 3 - 数学变换验证
函数:sub_140115B80
要求:输入8字符片段
验证逻辑:
// 目标值
*(_QWORD *)&v51[0] = 0xC5E42C25FADC2431ui64;
// 验证方程
for (i = 0; i < 8; ++i) {
if (7*i + (input[i] ^ (17*i + 109)) + 19 != target[i]) {
// 失败
}
}
解密脚本:
def solve_stage2():
# 目标字节(小端序)
target_bytes = [0x31, 0x24, 0xDC, 0xFA, 0x25, 0x2C, 0xE4, 0xC5]
result = []
for i in range(8):
mask = (17 * i + 109) & 0xFF
val = (target_bytes[i] - 19 - 7 * i) & 0xFF
result.append(val ^ mask)
return bytes(result).decode('ascii') # "st4rG4te"
解决方案:输入st4rG4te
3.4 Stage 4 - 第二层数学变换
函数:sub_1401164A0
要求:输入8字符片段
验证逻辑:
v36[0] = -307768873; // 0xEDA7D1D7
v36[1] = 1231567188; // 0x49683954
for (i = 0; i < 8; ++i) {
if (3*i + (input[i] ^ (-89 - 11*i)) != target[i]) {
// 失败
}
}
解密脚本:
def solve_stage3():
# 目标字节
target_bytes = [0xD7, 0xD1, 0xA7, 0xED, 0x54, 0x39, 0x68, 0x49]
result = []
for i in range(8):
mask = (-89 - 11 * i) & 0xFF
val = (target_bytes[i] - 3 * i) & 0xFF
result.append(val ^ mask)
return bytes(result).decode('ascii') # "pR0b3Z3n"
解决方案:输入pR0b3Z3n
3.5 Stage 5 - 占位符阶段
函数:sub_140117780
说明:此阶段不需要用户输入,仅执行数据处理
3.6 Stage 6 - 文件系统操作
函数:sub_14011A330
功能:检查并创建必要文件
- 检查
uvt_crackme_work/stage2目录和stage2.sha256文件 - 如果不存在,创建以下文件:
stage2/starfield_pings/pings.txt- 包含网络ping数据stage2/logs/system.log- JSON格式的日志文件stage2/void/zen_void.bin- 二进制数据文件
3.7 Stage 7 - Ping数据分析
位置:main函数中的case 6
数据源:pings.txt
关键提示:
- 筛选
ttl=1337的行 - 提取time值(毫秒)
- 取低5位(time & 0x1F)作为索引
- 使用两个映射表转换
pings.txt示例:
reply: time=64ms ttl=1337
reply: time=65ms ttl=1337
...
reply: time=76ms ttl=1337
解密脚本:
import re
# 提取time值
times = []
with open("uvt_crackme_work/stage2/starfield_pings/pings.txt") as f:
for line in f:
if "ttl=1337" in line:
m = re.search(r"time=(\d+)", line)
if m:
times.append(int(m.group(1)))
# 获取索引序列
indices = [t & 0x1F for t in times] # [0,1,2,3,1,4,5,6,7,2,8,9,10,11,12]
# 映射表
even_hex = "270d62612a1c7f3036343a383e3c2220"
odd_hex = "60627c7e787a74767072574749716341"
even_bytes = bytes.fromhex(even_hex)
odd_bytes = bytes.fromhex(odd_hex)
# 解码
result = []
for idx in indices:
if idx % 2 == 0: # 偶数索引
pos = idx // 2
val = even_bytes[pos] ^ 0x52
else: # 奇数索引
pos = 15 - (idx // 2) # 反转
val = odd_bytes[pos] ^ 0x13
result.append(chr(val))
fragment = ''.join(result) # "uR_pR0b3Z_xTND-"
解决方案:输入uR_pR0b3Z_xTND-
3.8 Stage 8 - 日志文件分析
位置:main函数中的case 7
数据源:system.log
关键提示:
- 筛选
subsys="zen"的条目 - 按
slot字段排序 - 用
k字段(十六进制)作为密钥,对fragx字段(十六进制字符串)进行异或 - 拼接结果并Base64解码
system.log示例:
{"slot":1, "t":"2026-...", "subsys":"zen", "k":"28", "fragx":"7b7e1147657d79"}
{"slot":2, "t":"2026-...", "subsys":"zen", "k":"2f", "fragx":"55771d435a771d"}
{"slot":3, "t":"2026-...", "subsys":"zen", "k":"36", "fragx":"414164054650"}
解密脚本:
import base64
import json
# 提取数据
entries = []
with open("uvt_crackme_work/stage2/logs/system.log") as f:
for line in f:
if '"subsys":"zen"' in line:
data = json.loads(line)
entries.append((data["slot"], data["k"], data["fragx"]))
# 按slot排序
entries.sort()
# 解密
result_b64 = ""
for slot, k, fragx in entries:
key = int(k, 16)
frag_bytes = bytes.fromhex(fragx)
xored = bytes(b ^ key for b in frag_bytes).decode()
result_b64 += xored
# Base64解码
decoded = base64.b64decode(result_b64).decode() # "I_h1D3_in_l0Gz_"
解决方案:输入I_h1D3_in_l0Gz_
3.9 Stage 9 - 二进制文件分析
位置:main函数中的case 8
数据源:zen_void.bin
工具:probe_extractor.py(提供熵分析功能)
关键提示:
- Stage 9的密钥 =
sum(bytes(Stage8_text)) % 256 - 在二进制文件中查找"岛屿"(非零数据块)
- 通过熵值识别有效数据
计算密钥:
stage8 = "I_h1D3_in_l0Gz_"
key9 = sum(ord(c) for c in stage8) % 256 # 0x2A
分析二进制文件:
file: zen_void.bin (131072 bytes)
islands: 7
off=0x00009550 len=8 entropy=2.75 sample=1d44755c1a1b6e75
off=0x0000a1b2 len=8 entropy=2.50 sample=1b44755c1a1b6e75
选择正确样本:比较0x9550和0xa1b2的样本,选择与密钥异或后产生有意义的ASCII字符串
解密脚本:
sample9 = "1b44755c1a1b6e75" # 来自0xa1b2
key9 = 0x2A
encrypted9 = bytes.fromhex(sample9)
decrypted9 = bytes(b ^ key9 for b in encrypted9) # "1n_v01D_"
解决方案:输入1n_v01D_
3.10 Stage 10 - 最终片段
位置:main函数中的case 9
数据源:zen_void.bin
关键提示:
- Stage 10的密钥 =
sum(bytes(Stage9_text)) % 256 - 在二进制文件中查找相似的数据块
计算密钥:
stage9 = "1n_v01D_"
key10 = sum(ord(c) for c in stage9) % 256 # 0x78
分析二进制文件:
off=0x00009d20 len=8 entropy=3.00 sample=1136273e39313405
off=0x0000e3c4 len=7 entropy=2.52 sample=113627223d3605
选择正确样本:比较0x9d20和0xe3c4的样本
解密脚本:
sample10 = "113627223d3605" # 来自0xe3c4
key10 = 0x78
encrypted10 = bytes.fromhex(sample10)
decrypted10 = bytes(b ^ key10 for b in encrypted10) # "iN_ZEN}"
解决方案:输入iN_ZEN}
四、完整Flag组装
将所有片段按顺序拼接:
UVT{Kr4cK_M3_N0w-(由Stage 3的"st4rG4te"转换而来,实际为"cK_M3_N0w-")cR4Km3_THEN-(由Stage 4的"pR0b3Z3n"转换而来,实际为"cR4Km3_THEN-")5T4rf13Ld_piNgS_(由Stage 7的"uR_pR0b3Z_xTND-"转换而来,实际为"5T4rf13Ld_piNgS_")I_h1D3_in_l0Gz_1n_v01D_iN_ZEN}
完整Flag:
UVT{Kr4cK_M3_N0w-cR4Km3_THEN-5T4rf13Ld_piNgS_I_h1D3_in_l0Gz_1n_v01D_iN_ZEN}
五、关键技术点总结
- 逆向工程基础:静态分析、函数识别、字符串提取
- 密码学操作:异或加密、数学变换、Base64编码
- 文件系统分析:目录结构、文件格式识别
- 数据处理:十六进制转换、字节操作、索引映射
- 二进制分析:熵值计算、数据块识别、密钥推导
- 协议分析:日志格式解析、字段提取
六、工具与技巧
-
静态分析工具:
- IDA Pro:反编译和代码分析
- DIE:查壳和文件信息
-
脚本编写:
- Python:数据处理和解密
- 正则表达式:文本提取
-
分析方法:
- 动态调试与静态分析结合
- 关键字符串和常量查找
- 函数调用关系分析
通过本挑战,可以系统学习从基础逆向到复杂系统分析的完整技能链,特别是文件格式解析、数据变换和密码学分析的实际应用。