首页
社区
课程
招聘
[原创]KCTF 2026 · 第五题「申时·忆海倒带」Writeup
发表于: 2026-8-17 13:48 16

[原创]KCTF 2026 · 第五题「申时·忆海倒带」Writeup

2026-8-17 13:48
16

实机验证:

无 IDA / gdb,纯自建工具链静态解决:

踩坑:脚本一开始命名为 dis.py,遮蔽了标准库 dis,导致 import capstone 抛
AttributeError: module 'dis' has no attribute 'COMPILER_FLAG_NAMES'。改名即好。
另外 capstone 的 md.disasm(code, base) 遇到数据字节会直接停止(只出 608 条),
必须逐字节 while off < len 续扫才能覆盖全部 .text。

main(sub_403d90)开头把三条"开发者注释"当成 base-16 大数 parse 进三个 vector,
之后再也没被使用过,纯粹是给人看的假线索:

不存在 strcmp("admin123"),不存在起作用的 __except,int3 也不在校验路径上。

另一层难读化是遍地的假跳转桩,.text 里到处是:

以及 ret / 0xff / push [ebx] 混入指令流,专门用来打乱线性反汇编。直接无视即可。

parse 把 Key 当成 base-16 大整数存进 std::vector<uint32_t>,每 limb 8 个 hex 字符(32 bit)。

字符集与长度校验:

→ size = 11 limbs,输入恰好 88 个大写十六进制字符 = 44 字节。

接着把大数按 limb 从高到低、每 limb 大端展开到 buf2 = ebp-0x470:

也就是说 buf2[i] = int(input[2i:2i+2], 16) —— 就是 88 个 hex 字符的朴素解码,44 字节。

编译器把它向量化成了 SSE(pxor + psrldq 折叠),尾巴再补标量循环:

这是个非线性、依赖顺序的校验和,无法像纯 XOR 那样随便调字节。

input+56 正是 buf2[28..43] 对应的那 32 个 hex 字符。

sub_403500 内部有大量自修改代码(往 0x4380xx 一个字节一个字节写对象指针),拨开之后是:

回到 main,结果字符串又被 parse 成大数,大端展开到 ebp-0x88,
然后 覆盖回 buf2 的第 28..43 字节:

两个常量都不是明文立即数,而是"模板对象 + 置换表间接寻址":BigNum 读第 i 个 limb
实际是 data[perm[i]],perm 由 sub_402da0 用固定种子做 Fisher–Yates 洗牌生成。
按模板里的 data / perm 指针把 limb 一个个捞出来(dump_const.py):

标准 RSA-128 公钥运算。

其中 obj(ebp-0x86c)先用种子 0xA5A5A5A5 建好,再被 sub_4033d0 的 SMC 改成:

lookup 的实质是 obj->data + perm[x],所以:

dump_tables.py 把 x ∈ 0..94 全跑一遍,发现 f 在 0x20..0x7e 上是双射:

于是每个目标字符唯一确定一个 buf2 字节(= x + 1)。

目标串 "Welcome to KCTF2026! Come and give it a try." 长度恰好 44,与 Key 字节数一致。

反查表把整个 44 字节的"变换后 buf2"钉死:

→ 必须分解 128 位模数 C。C 无 20 万以内小因子、Pollard p−1(B=5×10⁵)不通,
但它是个"课本级"128-bit 半素数,factordb 已有完整记录(status: FF):

于是常规 RSA 解密:

拼接:

把这 44 字节代回校验 ① ②:

XOR 和加权和是"免费"满足的 —— 我们完全没有为它们求解,它们自动成立。
这说明出题人是先选定 Key、再反推出 0x8F / 0xBEFF 这两个常量,
也反证了整条数据流的还原完全正确(否则不可能三个独立约束同时命中)。

脚本 作用
pe.py 自写 PE32/PE32+ 解析器(rva2off / read_rva / cstr_rva / imports),不依赖 pefile
disasm_text.py 基于 capstone 5.0.7 的鲁棒 linear-sweep,反汇编整个 .text,遇非法字节跳过续扫,输出 dis_text.txt(7089 条指令 / 131 个函数入口)
dump_tables.py 重建被 SMC 写入的置换对象,dump PERM / DATA / INV / target 字符串,算出 f(x) 与反查表
dump_const.py 恢复模幂的指数 B 与模数 C 两个大数常量
solve2.py 一键求解 + 三重自校验
size data perm limbs 值
B(指数) 1 0x430018 0x434018 perm[0]=1374 → 0x00010001 65537
C(模数) 4 0x436018 0x42e018 perm=[145,6,101,2037] 0x8C91CB79EC693F3ED6519C945DD67371

传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!!

收藏
点赞 ・6
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回