题目信息

项目 内容
赛事 MoeCTF 2021
题目 PEPEPE
分值 443 分
类型 Reverse / PE 文件还原
考点 已知明文攻击、XOR 流密码、PE 文件结构
平台 NSSCTF (https://www.nssctf.cn/problem/3391)

题目描述

题目提供了一个经过加密的 PE 文件和加密程序源码,需要分析加密算法,并恢复原始可执行文件。

加密后的文件头部已无 MZ 标志,用 file 命令识别为纯 data。

加密原理分析

查看加密器 main.c 的核心逻辑:

1
2
3
4
5
6
scanf("%s", input);                      // 输入 key
...
for (int i = 0; i < file_len - 1; i++) {
bank[i] = getc(f);
bank[i] = ~(bank[i] ^ input[i % input_len]); // 逐字节 XOR 后取反
}

加密公式:

1
enc[i] = ~(org[i] ^ key[i % key_len])

其中 key 是加密时输入的字符串,按 i % key_len 循环使用。这是一个循环 XOR 流密码(多了一步取反),强度完全取决于 key 的保密性。

解题思路:已知明文攻击

为什么能攻击?

PE 文件有固定不变的结构——文件头。任何 PE 文件都以 MZ(0x4D 0x5A)开头,后面跟着结构固定的 DOS Header(大量字节是固定的 0x00)。这就是”已知明文”。

PE文件结构示意图

前 64 字节的 DOS Header 中,大量字节是固定值或零值,非常适合作为已知明文。

Key 恢复

把加密公式变形:

1
key[i] = ~enc[i] ^ org[i]

用加密文件的前若干字节 + 已知的 PE 头部明文,恢复 key 的循环片段,检测周期得完整 key(其中 plain_head 取自原始文件 file_org 的前 64 字节,即标准 DOS Header):

1
2
3
4
5
6
def recover_key(data: bytes, plain_head: bytes) -> bytes:
ks = bytes([(~data[i]) & 0xFF ^ plain_head[i] for i in range(len(plain_head))])
for period in range(1, len(ks) // 2 + 1):
if all(ks[i] == ks[i % period] for i in range(len(ks))):
return ks[:period]
return ks

运行结果:

1
[+] recovered key = b'reverierwilllikeyou!' (len=20)

key = reverierwilllikeyou!(20 字节),呼应了 file_org.c 中的 “I think ReverierXu will like you!”。

完整还原

1
2
def decrypt(data: bytes, key: bytes) -> bytes:
return bytes([(~b) & 0xFF ^ key[i % len(key)] for i, b in enumerate(data)])

验证结果:

  • file_restored 被识别为 PE32 executable
  • 与原始文件对比,前 45559/45560 字节完全一致

最后一个字节差异:加密循环只处理 file_len - 1 个字节,末字节为未初始化内存(垃圾值),不影响 PE 完整性。

运行验证

1
2
3
$ cp file_restored file_restored.exe && echo "" | ./file_restored.exe
[root@Track.Sh]# Good job, I think ReverierXu will like you!
[root@Track.Sh]# moectf{P3_Structur3_1s_r3ally_fUnnY!}

Flag

注意:还原后的程序输出的是 moectf{} 格式,而 NSSCTF 平台要求使用 NSSCTF{} 格式提交(题目描述中已注明)。

1
NSSCTF{P3_Structur3_1s_r3ally_fUnnY!}

总结

  1. 识别加密算法为循环 XOR(+取反)流密码
  2. 利用 PE 文件头固定结构 进行已知明文攻击恢复 key
  3. 检测 key 循环周期得到完整 key
  4. 解密整个文件,还原出可运行的 PE,运行得到 flag
  5. 固定格式的数据结构会降低简单流密码的安全性,因此在设计加密方案时不能仅依赖 XOR 与固定密钥。

参考

相关项目与联系

项目SnowSword —— Windows 10/11 x64 内核分析与 Anti-Rootkit 工具,源码与发布包在 Gitee。

技术合作Windows 内核 / 驱动开发 / 逆向分析 / 兼容性开发,范围与边界见 技术合作。

联系2160752730@qq.com · QQ 2160752730 · 微信 lst_3258110