CorCTF 2024 Rev DigestMe
字数 1430 2025-08-19 12:41:22
CorCTF 2024 DigestMe 逆向分析完整解析
题目概述
本题是CorCTF 2024中的一个逆向题目,名为"DigestMe"。题目描述了一位名为FizzBuzz101的开发者在编写一个秘密编译器时电脑被Crowdstrike攻击,恢复密钥被放在他自己编写的一个哈希器中。挑战者需要逆向分析这个哈希器来获取恢复密钥。
初步分析
文件特征
- 程序是一个非常大的C代码编译而成
- IDA反编译提示函数过大(0x1090到0xED97F,约90万行汇编)
- 代码分为两部分:
- 开头的普通处理逻辑
- 大量重复的操作序列
初始逆向技巧
- Patch技巧:将程序中间某处patch成ret,阻断IDA对后续逻辑的分析,以便使用F5查看简化逻辑
- 输入验证分析:
- 输入必须以"corctf{"开头,以"}"结尾
- 输入长度至少18个字符(从s[7]~s[17])
- 特定约束关系:
s[8] == s[17] s[9] == s[11] s[7] == s[16] + 1 s[14] == s[16] + 4 - 循环边界表明输入长度很可能是19字节
深入逆向分析
内存操作模式
程序对一个大内存空间(称为tmp或v3)进行大量操作,主要分为四种类型:
- 与操作:两个32字节连续内存空间的与处理
- 或操作:两个32字节连续内存空间的或处理
- 异或操作:两个32字节连续内存空间的异或处理
- 混合操作:与/或/异或处理,同时混入0B40h和0B41h进行操作
加法运算识别
通过分析汇编代码,发现以下模式:
mov cl, [rax+87h]
xor cl, [rax+7] ; a[af] = a[87]^a[7]
mov [rax+0A7h], cl
mov cl, [rax+87h]
and cl, [rax+7] ; a[B40] = a[87]&a[7]
mov [rax+0B40h], cl
mov cl, [rax+86h]
xor cl, [rax+6] ; a[b41] = a[86]^a[6]
mov [rax+0B41h], cl
mov cl, [rax+0B41h]
xor cl, [rax+0B40h] ; a[ae] = a[b40]^a[b41]
mov [rax+0A6h], cl
这实际上是模拟加法运算:
- a[87]^a[7]:当前位的和
- a[87]&a[7]:进位位
- 将进位影响下一位的运算
数据排列方式
- 单个数据按小端序排列
- 4个数字也按小端序排序
- 示例:
0x1000000: [0 0 0 0 1 0 0 1] [1 0 0 0 1 0 1 0] # a[0] = 0x9, a[1] = 0x8a 0x1000008: [0 0 0 0 1 0 0 0] [1 0 0 0 1 0 0 0] # a[2] = 0x8, a[3] = 0x88 组合后: a = 0x88088a09
循环位移操作
发现代码中存在循环右移操作:
mov cl, [rax+98h]
or cl, [rax+118h]
mov [rax+0CCh], cl
实际操作为:
def rotate_right(num, shift_amount):
shift_amount = shift_amount % 32
return (num >> shift_amount) | (num << (32 - shift_amount)) & 0xFFFFFFFF
其中shift_amount由被操作数和操作数的偏移计算得出:
def reverse_origin_offset(offset):
return (offset//8)*8 + (8 - (offset%8))
算法识别
MD5特征值
在dump出的数据中发现MD5的魔数:
- 0xd76aa478
- 0xe8c7b756
- 其他MD5特征常量
MD5变换函数对比
程序逻辑与MD5的md5_transform函数高度相似:
static void md5_transform(const unsigned char block[64]) {
int i, j;
UINT4 a,b,c,d,tmp;
const UINT4 *x = (UINT4 *) block;
a = state[0];
b = state[1];
c = state[2];
d = state[3];
/* Round 1 */
for (i = 0; i < 16; i++) {
tmp = a + F(b, c, d) + le32_to_cpu(x[i]) + T[i];
tmp = ROTATE_LEFT(tmp, s1[i & 3]);
tmp += b;
a = d; d = c; c = b; b = tmp;
}
/* 类似结构处理Round 2-4 */
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
}
最终验证
程序最后比较两个特定的哈希值:
cmp dword ptr [rsp+4C8h+var_4A8+8], 19C603BAh ; tmp[2] == 0xba03c619
cmp dword ptr [rsp+4C8h+var_4A8+0Ch], 14353CE4h ; tmp[3] == 0xe43c3514
解题方法
约束条件总结
- 输入格式:
corctf{...},长度19字节 - 有效内容长度:11字节(扣除固定部分)
- 特定约束:
- flag[8] == flag[17]
- flag[9] == flag[11]
- flag[7] == flag[16] + 1
- flag[14] == flag[16] + 4
爆破策略
由于Z3求解器无法在合理时间内解决(约1e12种可能性),采用CUDA加速爆破:
-
修改CUDA MD5爆破工具:
- 调整初始化逻辑以匹配题目要求
- 修改MD5计算函数以包含特定填充规则
-
关键修改点:
// 设置映射关系 char mapping_index[8] = {0,1,2,3,5,6,8}; // 应用约束条件 threadTextWord[9] = threadTextWord[0] - 1; threadTextWord[7] = threadTextWord[9] + 4; threadTextWord[10] = threadTextWord[1]; threadTextWord[4] = threadTextWord[2]; // 修改MD5填充 vals[i / 4] |= 0x80 << ((i % 4) * 8); vals[56 / 4] |= 0x58 << ((56 % 4) * 8); -
目标哈希:
- 查找产生特定MD5部分哈希(0xba03c619, 0xe43c3514)的输入
最终结果
经过约3小时的CUDA爆破,得到有效输入:
cPv3v8VfWbP
完整flag:
corctf{youtu.be/dQw4w9WgXcQ}
总结
-
逆向技巧:
- 对超大函数使用分段分析
- 识别重复模式并自动化处理
- 通过对比调试验证算法理解
-
算法识别:
- 注意特征常量和数据结构
- 对比已知算法(如MD5)的结构
-
解题策略:
- 当符号执行不可行时,考虑约束爆破
- 利用GPU加速大规模计算
-
工具使用:
- IDA Python脚本自动化分析
- CUDA编程实现高性能计算
本题展示了现代CTF逆向题目的典型特点:需要结合逆向工程、算法识别和高效计算技术才能解决。