浅析ByteCTF2024 Reverse
字数 1904 2025-08-23 18:31:08

ByteCTF2024 Reverse 题目解析与教学文档

1. babyapk 题目解析

1.1 初步分析

  • 这是一个基于 Flutter 架构的 APK 文件
  • 使用 DIE 工具扫描确认架构后,考虑使用 blutter 进行反编译

1.2 环境配置

配置 blutter 环境时常见问题:

  1. 网络问题:需要全局代理
  2. 编译环境缺失:确保安装完整的编译环境
  3. CMake 相关错误:检查 VS 模块是否安装完整

1.3 反编译与符号表恢复

  1. 使用 blutter 成功启动后,运行 addName 脚本恢复符号表
  2. 可能需要多次运行脚本才能完全恢复符号表
  3. 搜索 APK 名称字段可以找到相关函数

关键函数:

  • babyapk_main__MyHomePageState::test_264c0c:包含主要逻辑
  • 修复函数结构技巧:"先U再C再P"原则(Undefine, Code, Procedure)

1.4 Frida Hook 分析

  1. 使用真机而非模拟器(模拟器可能缺少 ARM 底层支持)
  2. 修改 blutter 提供的 hook 脚本地址进行注入
  3. 关键 Hook 点:
    • 0x198d18:flag 头检测
    • 0x198df8:flag 头验证
    • 0x264d88:flag 尾部验证

发现:Flutter 层仅进行 flag 格式验证,实际加密在 Rust 部分

1.5 Rust 部分分析

  1. librust_lib_babyapk.so 中找到关键字符串 "m3N4B5V6"
  2. 定位到加密函数 sub_3AEE0
  3. 发现 flag 格式验证和连接符验证

1.6 Z3 求解

加密部分是一系列方程式,使用 Z3 求解器:

from z3 import *

data = [0x1EE59, 0x22A, 0x1415, 0x40714, 0x13E0, 0x8B8, 0xFFFDCEA0, 0x313B, 
        0x3D798, 0xFFFFFE6B, 0xC4E, 0x23884, 0x8D, 0x1DB4, 0xFFFC1328, 0x1EAC, 
        0x43C64, 0x142B, 0xFFFFF622, 0x23941, 0xFFFFEF6D, 0x120C, 0xFFFBD30F, 
        0x1EBE, 0x45158, 0xFFFFEF66, 0x1D3F, 0x4C46B, 0xFFFFF97A, 0x1BFD, 
        0xFFFBA235, 0x1ED2]

for i in range(4):
    s = Solver()
    v46, v47, v45, v44, v48, v49, v50, v51 = BitVecs("v46 v47 v45 v44 v48 v49 v50 v51", 8)
    # 添加约束条件...
    if s.check() == sat:
        print(s.model()[v46], end=",")
        # 输出其他变量...

求解后得到字节数组:

byte = [51, 50, 101, 55, 53, 48, 99, 56, 102, 98, 50, 49, 52, 53, 54, 50, 
        97, 102, 50, 50, 57, 55, 51, 102, 98, 53, 49, 55, 54, 98, 57, 99]

1.7 Flag 格式确定

  1. 32字节数据 + 4个连接符 + "ByteCTF{}" = 45字符
  2. 猜测为 GUID 格式:"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
  3. 使用爆破脚本确定连接符位置:
from itertools import combinations

original_string = "32e750c8fb214562af22973fb5176b9c"
byte_18E46 = [...]  # 验证数组

def validate_hyphen_positions(input_str):
    # 验证逻辑...
    return True

# 生成所有可能的连接符位置组合
positions = list(combinations(range(len(original_string) + 1), 4))

for pos in positions:
    temp_str = original_string
    # 插入连接符...
    if validate_hyphen_positions(temp_str):
        print(f"Valid combination: {temp_str}")

最终得到 flag 格式:
32e750c8-fb21-4562-af22-973fb5176b9c

2. ByteBuffer 题目解析

2.1 文件分析

  1. 文件包含点和边的信息,推测需要绘制图形
  2. 结构体特征:
    • Edge 部分:前部是大数字,中间是两个四字节整数(端点编号)
    • Dot 部分:包含 x,y 坐标信息

2.2 数据提取脚本

import struct
import matplotlib.pyplot as plt

def u32(a):
    return struct.unpack('<I', a)[0]

with open("ByteBuffer.bin", 'rb') as fp:
    buffer = fp.read()
    
# 提取 Edge 数据
edges = []
addr = 0x3AC
while addr <= 0x1203:
    # 读取结构体...
    edges.append(bts)
    
# 提取 Dot 数据
dots = []
while addr <= 0x1f85:
    # 读取结构体...
    dots.append(bts)

# 提取坐标点
points = []
for i in dots:
    x_d = u32(i[4:8])
    y_d = u32(i[8:12])
    points.append((x_d, y_d))

# 绘制图形
for i in edges:
    p1 = u32(i[8:12])
    p2 = u32(i[12:16])
    p2p = [120 - p1, 120 - p2]  # 索引反转
    x_coords = [points[i][0] for i in p2p]
    y_coords = [points[i][1] for i in p2p]
    plt.plot(x_coords, y_coords, '-o')

plt.gca().invert_yaxis()
plt.show()

3. ByteKit 题目解析

3.1 文件分析

  1. 包含启动 QEMU 镜像的脚本和 qcow2 镜像
  2. 镜像中有一个 flag 验证程序,验证路径:
    /sys/firmware/efi/efivars/ByteCTFOut-93e91ed6-1a7a-46a1-b880-c5c281700ea2

3.2 提取 EFI 文件

方法:

  1. 使用 7zip 直接解压
  2. 使用 uefi_retool 工具:
    python uefi_retool.py get-images bin路径

3.3 加密分析

  1. 发现 "ByteCTFIn"、"KEY:" 等字符串
  2. 使用 IDA 分析 ModuleEntryPoint 函数
  3. 发现 11 位循环异或加密:
    • 密钥:2CE83EE0BC1A0956B9D994
    • 加密数据大小:0x6c4

3.4 解密脚本

from idaapi import *

startaddr = 0x3A37
xoraddr = 0x3A2C
size = 0x6c4

for i in range(size):
    patch_byte(startaddr + i, get_byte(startaddr + i) ^ get_byte(xoraddr + i % 11))

# 导出解密文件
fp = open("output", "wb+")
fp.write(get_bytes(startaddr, size))
fp.close()

3.5 二次解密

解密后的文件包含 "KEY:" 验证和异或加密:

enc = [0x4B, 0x27, 0x42, 0x55, 0x48, 0x6E, 0x41, 0x29, 0x1F, 0x5E, 0x04, 0x04, 
       0x6B, 0x3E, 0x57, 0x5F, 0x08, 0x07, 0x5F, 0x3A, 0x31, 0x17, 0x40, 0x30, 
       0x5F, 0x7A, 0x75, 0x67, 0x36, 0x36, 0x36, 0x36]
key = [...]  # 密钥数组

for i in range(len(key) // 3):
    v14 = key[i * 3 + 1]
    v15 = v14 + key[i * 3 + 2]
    while (v15 > v14):
        enc[v14] ^= key[i * 3]
        v14 += 1

flag = ''.join([chr(i) for i in enc])
print(flag)  # KEY:By71d@nnc6_Wan77_y@0_zug6666

4. 极限逃脱题目解析

4.1 初步分析

  1. 直接分析 IPA 文件中的 ByteCTFDemo 部分
  2. 符号表较完整,主要查看 ViewController

4.2 第一按钮验证

firsButtonClicked 方法:

  1. 生成随机数:arc4random_uniform(0x1F4)
  2. 验证输入是否匹配随机数
  3. 通过后显示第二个按钮

4.3 第二按钮验证

secondButtonClicked 方法:

  1. 验证 flag 格式(UUID 格式)
  2. 初始化数据(注意:第一部分数据实际使用 fifth 的数据)
  3. 拼接 flag 格式字符串并计算 SHA256
  4. 分段进行字符替换:
    • a → b
    • b → c
    • c → d
    • d → e
    • e → f

4.4 Flag 生成脚本

enc = 'a67be199da4b-b092-bd3e-e777-a67be199da4b'
hash = '6c9838a3c6810bdb2633ed5910b8547c09a7a4c08bf69ae3a95c5c37f9e8f57e'

p1 = ''
for i in range(1, 9):
    p1 += hash[i]
p1 = p1.replace('a', 'b')

p2 = ''
for i in range(9, 13):
    p2 += hash[i]
p2 = p2.replace('b', 'c')

p3 = ''
for i in range(5, 9):
    p3 += hash[i]
p3 = p3.replace('c', 'd')

p4 = ''
for i in range(5, 9):
    p4 += hash[i]
p4 = p4.replace('d', 'e')

p5 = ''
for i in range(5, 17):
    p5 += hash[i]
p5 = p5.replace('e', 'f')

flag = 'ByteCTF{' + p1 + '-' + p2 + '-' + p3 + '-' + p4 + '-' + p5 + '}'
print(flag)  # ByteCTF{c9838b3c-6810-8a3d-8a3c-8a3c6810bdb2}

总结

本教学文档详细分析了 ByteCTF2024 中的四个逆向题目,涵盖了:

  1. Flutter 应用逆向
  2. 二进制文件结构分析
  3. EFI 固件逆向
  4. iOS 应用逆向

关键技巧包括:

  • 使用 blutter 反编译 Flutter 应用
  • Frida 动态分析
  • Z3 求解器使用
  • 二进制结构体解析
  • 多阶段加密分析
  • iOS 运行时分析
ByteCTF2024 Reverse 题目解析与教学文档 1. babyapk 题目解析 1.1 初步分析 这是一个基于 Flutter 架构的 APK 文件 使用 DIE 工具扫描确认架构后,考虑使用 blutter 进行反编译 1.2 环境配置 配置 blutter 环境时常见问题: 网络问题 :需要全局代理 编译环境缺失 :确保安装完整的编译环境 CMake 相关错误 :检查 VS 模块是否安装完整 1.3 反编译与符号表恢复 使用 blutter 成功启动后,运行 addName 脚本恢复符号表 可能需要多次运行脚本才能完全恢复符号表 搜索 APK 名称字段可以找到相关函数 关键函数: babyapk_main__MyHomePageState::test_264c0c :包含主要逻辑 修复函数结构技巧:"先U再C再P"原则(Undefine, Code, Procedure) 1.4 Frida Hook 分析 使用真机而非模拟器(模拟器可能缺少 ARM 底层支持) 修改 blutter 提供的 hook 脚本地址进行注入 关键 Hook 点: 0x198d18:flag 头检测 0x198df8:flag 头验证 0x264d88:flag 尾部验证 发现:Flutter 层仅进行 flag 格式验证,实际加密在 Rust 部分 1.5 Rust 部分分析 在 librust_lib_babyapk.so 中找到关键字符串 "m3N4B5V6" 定位到加密函数 sub_3AEE0 发现 flag 格式验证和连接符验证 1.6 Z3 求解 加密部分是一系列方程式,使用 Z3 求解器: 求解后得到字节数组: 1.7 Flag 格式确定 32字节数据 + 4个连接符 + "ByteCTF{}" = 45字符 猜测为 GUID 格式:"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" 使用爆破脚本确定连接符位置: 最终得到 flag 格式: 32e750c8-fb21-4562-af22-973fb5176b9c 2. ByteBuffer 题目解析 2.1 文件分析 文件包含点和边的信息,推测需要绘制图形 结构体特征: Edge 部分:前部是大数字,中间是两个四字节整数(端点编号) Dot 部分:包含 x,y 坐标信息 2.2 数据提取脚本 3. ByteKit 题目解析 3.1 文件分析 包含启动 QEMU 镜像的脚本和 qcow2 镜像 镜像中有一个 flag 验证程序,验证路径: /sys/firmware/efi/efivars/ByteCTFOut-93e91ed6-1a7a-46a1-b880-c5c281700ea2 3.2 提取 EFI 文件 方法: 使用 7zip 直接解压 使用 uefi_ retool 工具: python uefi_retool.py get-images bin路径 3.3 加密分析 发现 "ByteCTFIn"、"KEY:" 等字符串 使用 IDA 分析 ModuleEntryPoint 函数 发现 11 位循环异或加密: 密钥: 2CE83EE0BC1A0956B9D994 加密数据大小:0x6c4 3.4 解密脚本 3.5 二次解密 解密后的文件包含 "KEY:" 验证和异或加密: 4. 极限逃脱题目解析 4.1 初步分析 直接分析 IPA 文件中的 ByteCTFDemo 部分 符号表较完整,主要查看 ViewController 4.2 第一按钮验证 firsButtonClicked 方法: 生成随机数: arc4random_uniform(0x1F4) 验证输入是否匹配随机数 通过后显示第二个按钮 4.3 第二按钮验证 secondButtonClicked 方法: 验证 flag 格式(UUID 格式) 初始化数据(注意:第一部分数据实际使用 fifth 的数据) 拼接 flag 格式字符串并计算 SHA256 分段进行字符替换: a → b b → c c → d d → e e → f 4.4 Flag 生成脚本 总结 本教学文档详细分析了 ByteCTF2024 中的四个逆向题目,涵盖了: Flutter 应用逆向 二进制文件结构分析 EFI 固件逆向 iOS 应用逆向 关键技巧包括: 使用 blutter 反编译 Flutter 应用 Frida 动态分析 Z3 求解器使用 二进制结构体解析 多阶段加密分析 iOS 运行时分析