首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
CTF对抗
发新帖
0
7
[原创]KCTF2026 - 第五题:申时·忆海倒带 题解(AI)
发表于: 2026-8-17 14:47
569
[原创]KCTF2026 - 第五题:申时·忆海倒带 题解(AI)
星野安全
1
2026-8-17 14:47
569
## Summary 32 位 PE,无壳,校验逻辑里**没有一处反调试**。程序把大数的 limb 和字符表都存在一个带**索引置换**的 容器里(本文称 `RewindArray`),于是 RSA 的 `n`/`e` 在 `.data` 里直接 dump 完全看不出来。 跟着置换表把参数捞出来,分解 128 位的 `n`,反推序列号即可,**总体难度较低**。 ## Solution ### Step 1: 题眼 —— RewindArray PDB 路径没清有些难绷:`E:\kanxue\KCTF\2026\rsa_tf-20260802\Release\ca_tf6.pdb` → **RSA**。 `main` 里反复出现 `for (i=0; i<2048; ++i) *sub_402A70(i) = 0;`。 跟进 `sub_402A70`:  ```c struct RewindArray { u32 size; T* buf; u32 seed; u32* perm; u32* inv; }; // 20 字节 // 元素 i 的真实存放位置 = buf[perm[i]] struct BigNum { u32 limb_count; RewindArray<u32> arr; }; // 24 字节 ``` 顺序 dump `n` 的数据段:  按 `perm` 取回来就齐了(第 4 个 limb 在 `perm[3] = 2037` 槽): | | buf | perm | inv | limb_count | 值 | |---|---|---|---|---|---| | `n` | `0x436018` | `0x42E018` | `0x432018` | 4 | `0x8C91CB79EC693F3ED6519C945DD67371` | | `e` | `0x430018` | `0x434018` | `0x42C018` | 1 | `65537` | `.data` 里 6 个 `0x2000` 字节的缓冲区从 `0x42C018` 步长 `0x2000` 连续排到 `0x436018`, 但**同组的三块并不相邻**(见上表)。`inv` 是 `perm` 的逆,脚本里拿来当校验。 注意那四段 perm/inv 本身是完整的 0..2047 置换表,不是零 —— 只有两段 buf 才是「一片零加几个孤立值」。 ### Step 2: 四道校验,坑在第四道之前  第三道要留意:累加器是 **32 位**,但只比较低 16 位(正确答案的 32 位和是 `0x6BEFF`)。 第四道的映射表在 `0x438030`,`buf=0x4263B0`、`perm=0x4163B0`、`inv=0x406380`, 物理表 `0x4000` 项(`perm + 0x10000` 严格接上 `buf`;结构体的 `size` 字段运行期是 `0x408F`, 只用于越界判断,与结论无关)。索引是 `(signed char)v - 1`,只有 `v ∈ [1,127]` 走置换分支; `v = 96..127` 共 32 个值一律映到 `0`,而**所有非零输出各只有唯一原像** —— 目标串不含 `0` 字节,所以这 44 个字节被唯一钉死:  **真正的坑在这里**:  `Buffer` 在 `ebp-0x858`,`var_820` 在 `ebp-0x820`,差 `0x38 = 56`。 **RSA 只作用于输入的后 32 个 hex 字符**,前 56 个原样进终检 (`sub_403500` 返回后,`0x4043B7` 处的 16 次循环把模幂结果盖回 44 字节的最后 16 字节)。 Hex-Rays 在此把 `this` 和参数全丢成 `sub_403500();`,按「整个 88 位输入对 `n` 取模」去解, 会得到 `L` 和 `L + n` 两个都小于 `2^128` 的候选,第 ②③ 道校验一个都过不了。 于是解直接拆成两半:高 28 字节查表直出,低 16 字节 `L = C^d mod n`。 ### Step 3: 分解 n,出序列号 128 位半素数,mac 上 yafu / msieve / gmp-ecm 一个都没有,自己写反而更快: Brent-rho + `R = 2^128` 的 Montgomery(`mul256` 出 256 位积,`redc` 归约), 10 核并行不同种子,**几秒出解**(完整源码见 `rho128.c`): ```c static inline u128 redc(u128 lo, u128 hi) { // R = 2^128 u128 m = lo * Ninv, ml, mh; // Ninv = -N^{-1} mod 2^128 mul256(m, N, &ml, &mh); u128 s = lo + ml, t = hi + mh + (s < lo); return t >= N ? t - N : t; } ``` ``` p = 13636154180376482939 q = 13702465297157554691 ``` 完整求解链,直接从 `cm.exe` 读参数,端到端复现序列号: ```python #!/usr/bin/env python3 """KCTF 2026 第五题「申时·忆海倒带」求解链 —— 纯离线可复现。""" import pefile, struct pe = pefile.PE('cm.exe') ib = pe.OPTIONAL_HEADER.ImageBase rd = lambda va, n: pe.get_data(va - ib, n) # ── 1. 跟着 perm 走,取出被打乱存放的 RSA 参数 ────────────────────── A = struct.unpack('<2048I', rd(0x436018, 0x2000)) # n 的 limbs pA = struct.unpack('<2048I', rd(0x42E018, 0x2000)) # n 的 perm B = struct.unpack('<2048I', rd(0x430018, 0x2000)) # e 的 limbs pB = struct.unpack('<2048I', rd(0x434018, 0x2000)) # e 的 perm n = sum(A[pA[k]] << (32 * k) for k in range(4)) # BigNum.limb_count = 4 e = B[pB[0]] # BigNum.limb_count = 1 assert (n, e) == (186848929443298675924877642916120916849, 65537) # ── 2. 反解终检所需的 44 字节 ─────────────────────────────────────── # out[i] = charArr.buf[charArr.perm[(signed char)v[i] - 1]] N = 0x4000 cperm = struct.unpack('<%dI' % N, rd(0x4163B0, N * 4)) cbuf = rd(0x4263B0, N) rev = {} for v in range(1, 128): # v==0 或 v>=128 时索引为负 → 落回 buf[0]==0 rev.setdefault(cbuf[cperm[v - 1]], v) req = bytes(rev[t] for t in b'Welcome to KCTF2026! Come and give it a try.') assert len(req) == 44 # ── 3. 分解 n(Brent-rho + 128 位 Montgomery,见 rho128.c)────────── p, q = 13636154180376482939, 13702465297157554691 assert p * q == n d = pow(e, -1, (p - 1) * (q - 1)) # ── 4. 组装:高 28 字节直出,低 16 字节走 RSA ─────────────────────── # 关键:sub_403500 收到的是 Buffer+56,RSA 只吃输入的后 32 个 hex 字符 H = int.from_bytes(req[:28], 'big') C = int.from_bytes(req[28:], 'big') L = pow(C, d, n) mb = ((H << 128) + L).to_bytes(44, 'big') # ── 5. 复核 exe 的两道前置校验 ────────────────────────────────────── x = 0 for b in mb: x ^= b s = 0 for b in mb: s = (s + b * ((s & 0x7F) + 1)) & 0xFFFFFFFF assert x == 0x8F and (s & 0xFFFF) == 0xBEFF assert pow(L, e, n).to_bytes(16, 'big') == req[28:] print(mb.hex().upper()) ``` 真机验证: ```console $ echo 323C47184B0D3C44254B445842552F365C362C1144424B0D3C4416433B0DD6B12A0D3D95FA65B5E0ADE5E11B | ./cm.exe Enter your key: verify success. ``` ## 烟雾弹 题面和程序里的干扰项,逆的时候可以直接跳过: - **`GetTickCount()`**:全程只在 `sub_402100` 里调用一次,作为返回值传出去后被所有调用点丢弃, 不参与解析或运算 —— 题面的「0.5 秒时间窗」到此为止 - **反调试**:校验逻辑里一处也没有。`IsDebuggerPresent` 确实在导入表里,但唯一引用者是 CRT 的 `___scrt_fastfail`;`sub_403C60` 里那个 `__try/__except` 是空架子,try 体内没有任何会抛异常的 指令,`__except` 分支是死代码 - **假线索**:`admin123` / `r3v3rs3!` / `n0_4i_c4n_r34d_th1s!`,外加三条开发者注释 (FIXME / TODO / NOTE,那句「real check is in the handler」和「int3 is just obfuscation」 其实同属 NOTE 一条)。它们被 `sub_403180` / `sub_403230` 算成 0/1 后返回值全部丢弃, `r3v3rs3!` 的使用者 `sub_4032E0` 干脆在那段死代码里 - **花指令**:`.text` 里遍布 `retn` / `stc` / `db 8Bh,0FFh`,全被前面的 `jmp` 跳过(见图 1) 真正的题眼只有一个:**`RewindArray` 的索引置换**。。 ## 工具 | 工具 | 用途 | |---|---| | **Claude Code(Claude Opus 5)** | 全程驱动:反汇编阅读、结构还原、求解链编写与验证 | | IDA Pro + Hex-Rays | 静态反编译,经 `ida-pro-mcp` 以 MCP 形式直接暴露给 LLM 查询 | | pefile | 直接从 PE 映像读出置换表与 RSA 参数,全程不需要调试器 | | 自写 `rho128.c` | Brent-rho + `R = 2^128` 的 Montgomery 归约,分解 128 位的 `n` |
回复或点赞可查看完整内容
冰与火的战歌:Windows内核攻防实战高级班!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。
最后于
2026-8-18 11:33 被星野安全编辑 ,原因: 补充代码文件
上传的附件:
rho128.c
(3.73kb,4次下载)
收藏
・
0
点赞
・
7
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
git_51951meggadf3df
非常支持你的观点!
2026-9-12 07:35
mb_dvqqrcce
感谢你分享这么好的资源!
2026-9-11 17:28
晨曦。
为你点赞!
2026-9-9 07:26
mb_lthgjpwj
为你点赞!
2026-8-21 07:31
neilwu
为你点赞!
2026-8-19 09:15
huangyalei
感谢你的贡献,论坛因你而更加精彩!
2026-8-18 22:06
ONewTach
你的帖子非常有用,感谢分享!
2026-8-18 17:02
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
4
)
落音吹雪
雪 币:
205
活跃值:
(140)
能力值:
( LV2,RANK:10 )
在线值:
发帖
1
回帖
41
粉丝
0
关注
私信
落音吹雪
2
楼
好
2026-8-18 20:50
0
道友请留步
雪 币:
138
能力值:
( LV1,RANK:0 )
在线值:
发帖
1
回帖
45
粉丝
0
关注
私信
道友请留步
3
楼
111111111111111
2026-8-25 15:01
0
马继
雪 币:
0
能力值:
( LV1,RANK:0 )
在线值:
发帖
0
回帖
5
粉丝
0
关注
私信
马继
4
楼
大佬好能不能加个好友我很强
2026-9-11 09:46
0
rongkao
雪 币:
62
活跃值:
(1090)
能力值:
( LV2,RANK:10 )
在线值:
发帖
7
回帖
103
粉丝
0
关注
私信
rongkao
5
楼
感谢分享,思路清晰,学习 了!RewindArray 索引置换这个设计很有意思。
9小时前
0
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
星野安全
1
14
发帖
6
回帖
110
RANK
关注
私信
他的文章
[原创] 某 App 抽取壳的内存脱壳与请求签名逆向
17358
[原创] 基于某银行App的梆梆加固逆向实战
4034
[原创]KCTF2026 -第十题:卯时·曦光初现 题解(AI)
444
[原创]KCTF2026 - 第九题:丑寅同墟·星海抉择 题解(AI)
119
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
谁下载
×
huangyalei
rongkao
ONewTach
mb_lthgjpwj
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部