首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
CTF对抗
发新帖
0
0
[原创]【KCTF 2026】第五题:申时·忆海倒带 Writeup:"中间语言",把 RSA 诱饵从假死代码里挖出来
发表于: 2026-8-17 17:31
175
[原创]【KCTF 2026】第五题:申时·忆海倒带 Writeup:"中间语言",把 RSA 诱饵从假死代码里挖出来
落音吹雪
2026-8-17 17:31
175
# 【KCTF 2026】 Writeup:一套自创的语言,把 RSA 诱饵从假死代码里挖出来 ## 引子 这道 Windows crackme 输入 88 位 hex 序列号,对了打印 `verify success.`。表面看就是"大整数解析 + 查表 + 几重校验"的常规套路,可偏偏就是那几个校验对不上,让我卡了很久。最后逼我发明了一套**压缩语言(我管它叫 Z-Lang)**,把整个 main 从头到尾翻译一遍、再做一次代数化简,才发现真正的机关藏在一个被当成"死代码"的地址里。 这篇文章就当分享这个思路,也希望能给同样卡在"校验矛盾"上的朋友一点启发。 ## 第一步:剥掉花指令 程序没有加壳,main 在 `0x403D90`,纯 MSVC 编译。麻烦的是里面塞满了花指令,典型几种: ```asm ; 模式A: jmp +6 跳过死代码 40435b: eb 06 jmp 0x404363 40435d: 8b c0 mov eax,eax ; ← 死代码开始 40435f: c3 ret 404360: ff (bad) 404361: ff 33 push [ebx] 404363: ... ; ← 真正代码 ; 模式C: jmp +2 跳过两个 nop 403f6d: eb 02 jmp 0x403f71 403f6f: 8b c9 mov ecx,ecx 403f71: 8b 01 mov eax,[ecx] ``` 写了个脚本按"无条件 jmp 跳过紧邻死代码"的规则线性清理,得到 522 条有效指令(`main_clean_full.asm`)。花指令本身不是难点,跳过。 ## 核心算法:三条约束打架 把 main 翻译完,逻辑其实不复杂。88 位 hex 先解析成 2048 位大整数,再字节反转成 44 字节缓冲区 `V`,然后要过三关: ``` hex(Key) → V (44字节) A) XOR(V) = 0x8F // 校验1,SSE2 XOR 折叠 B) 加权和(V)&0xFFFF = 0xBEFF // 校验2,Σ V[i]*((C&0x7F)+1) C) R[i] = D[P[V[i]-1]] = T[i] // 查表,T="Welcome to KCTF2026!..." ``` 查表对象 `D/P` 是静态表(`D=0x4263B0` 字节表,`P=0x4163B0` 偏移表),元素经大数对象的 `datap[perm[k]]` 间接寻址。程序里还有一大段 **RSA 模幂(exp=0x10001)和费雪耶茨洗牌**,看起来跟验证毫无关系——这就是后面最大的坑。 ## 卡点:查表把 V 锁死了,校验却过不去 约束 C 对目标串的每个字符,在 `D[P[j]]` 里恰好找到一个 `j` 命中,所以 **44 字节的 V 被 C 逐字节唯一确定**: ``` V = 32 3C 47 18 4B 0D 3C 44 25 4B 44 58 42 55 2F 36 5C 36 2C 11 44 42 4B 0D 3C 44 16 43 0F 44 39 37 4E 3C 44 37 25 44 16 44 25 15 1D 1B ``` 可是拿这个 V 去算 A、B: ``` XOR = 0x68 ≠ 0x8F ✗ 加权和 = 0x107F ≠ 0xBEFF ✗ ``` **查表唯一确定的 V,过不了校验。** 一个 CTF 题不可能无解,那一定是"三者作用于同一份 V"这个模型有问题。可汇编读来读去,A/B/C 都在读写同一个 `[ebp-0x470]`(V),怎么看都是同一份。 ## 转机:Z-Lang 化简第 18 步的"死代码" 卡了一下之后,我决定把所有内存写地址**用符号代数化简一遍**,不靠汇编直觉。重点看第 18 步——它要把 RSA 结果拷贝 16 字节,汇编里目标地址长得像"写到一个没用的地方": ```asm 40437f: mov esi, [ebp-0x938] ; esi = 11 404385: lea ecx, [ebp-0x88] ; ecx = &R 40438b: lea eax, [esi*4] ; eax = 44 404392: sub eax, ecx ; eax = 44 - &R 404394: lea eax, [ebp+eax-0x480] ; 目标 = ebp + 44 - &R - 0x480 40439b: mov [ebp-0x93c], eax ; ptr = 目标 ... 4043b7: mov [edx+ecx], al ; [ptr + idx] = R[i] ``` 把它写成 Z 记号再化简: ``` 写地址 = ptr + idx + i = (44 − &R + ebp − 0x480) + &R + i = 44 + ebp − 0x480 + i = ebp − 0x454 + i V 在 ebp−0x470 ⇒ 写地址 = V[28] + i ``` **这一化简,整个题就通了——那条"拷贝 16 字节"根本不是死代码,它在执行 `V[28..43] ← RSA 输出`,把 V 的尾部 16 字节覆盖了!** ## 真相:RSA 结果接进了主流程 重新梳理,三个约束其实针对的是**两份不同的 V**: ``` Key[0..27] = V[0..27] // C 查表锁定的 28 字节 Key[28..43] = base // RSA 的输入(Key 后 16 字节) V[28..43] = base^65537 mod mod // RSA 加密结果,覆盖 V 尾部 A/B 校验的是原始 hex(Key) 的 44 字节 ``` 也就是说: - **A、B** 校验 `Key` 本身; - **C** 查表用的是"覆盖后"的 V,其中 `V[0..27]` 来自 Key,`V[28..43]` 是 RSA 密文; - RSA 的输入 `base` 正是 `Key[28..43]`,是个**待求的未知数**。 这解释了一切矛盾。RSA 看似是诱饵,其实是**通过隐藏的缓冲区覆盖,把 `base` 解密的必要性接进了主流程**。 ## RSA 解密 `sub_403500` 从静态常量构造大数: ``` exp = 0x10001 = 65537(标准公钥指数) mod = 0x8C91CB79EC693F3ED6519C945DD67371(128位) 密文 = V[28..43] = 0x0F4439374E3C44372544164425151D1B ``` 128 位模数直接分解: ``` mod = 13636154180376482939 × 13702465297157554691 ``` 求私钥 `d = e⁻¹ mod φ(n)` 后解出 base: ``` base = 密文^d mod mod = 0x3B0DD6B12A0D3D95FA65B5E0ADE5E11B ``` ## 最终 Key ``` Key = hex(V[0..27]) || hex(base) = 323C47184B0D3C44254B445842552F365C362C1144424B0D3C4416433B0DD6B12A0D3D95FA65B5E0ADE5E11B ``` 验证全部通过: ``` A) XOR = 0x8F ✓ B) 加权和 = 0xBEFF ✓ C) 查表 = 匹配 ✓ 实机运行: "verify success." ✓ ``` ## 后记:一点心得 1. **"校验约束"和"查表约束"打架时,先找隐藏的缓冲区覆盖**。汇编里所有"写看似无效地址"的指令,都值得用符号化简把目标算出来——这次就是靠它翻盘的。 2. **大数对象 + 间接寻址**是这类题的常见混淆:元素不是 `datap[k]` 而是 `datap[perm[k]]`,读常量表要按 perm 解引用,字节序按最高 DWORD 大端在前。 3. **标准 RSA 指数 `0x10001` + 可分解模数**,基本可以放心直接分解求私钥,不用硬刚模幂。 4. 将汇编表述为中间语言,无论是自己看还是给ai都是更为方便 感谢阅读,欢迎交流。
登录后可查看完整内容
传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!!
最后于
2026-8-17 17:35 被落音吹雪编辑 ,原因:
收藏
・
0
点赞
・
0
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
0
)
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
落音吹雪
1
发帖
41
回帖
10
RANK
关注
私信
他的文章
[原创]【KCTF 2026】第五题:申时·忆海倒带 Writeup:"中间语言",把 RSA 诱饵从假死代码里挖出来
175
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部