首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
CTF对抗
发新帖
0
0
[原创]申时·忆海倒带— WriteUp
发表于: 2026-8-17 14:55
77
[原创]申时·忆海倒带— WriteUp
阿捏利
1
2026-8-17 14:55
77
# 申时·忆海倒带— WriteUp > 目标程序:`cm.exe`(SHA1: `d1c8124c5964af1531c6e311328854a05ca40bca`) > 判胜条件:输入 Key 后程序输出 `verify success.` 即成功。 > 工具:IDA Pro(ida-pro-mcp)、Python --- ## 1. 程序整体流程 程序主体是一个自研的 2048-limb 大整数运算库(limb 存储带随机置换,但对数值无影响,可等价为普通小端大整数)。`main` 反编译后的大致流程如下: ``` 1. 打印 "Enter your key:" 2. gets_s 读入输入 → Buffer 3. 将输入按 hex 解析为大整数 v76(乘 16 + 加 digit 逐位构建) 4. 校验输入合法性与长度: for (n = 8*v76; ...) 检查每个字符 ∈ [0-9A-Z] if (n != 88) fail // 输入必须是 88 个 hex 字符(44 字节) 5. 把 v76 的 limbs(高位 limb 优先、逐 limb 高字节优先)展开为 44 字节 ArgList 6. 校验一:ArgList 全部字节异或 == -113 (0x8F) 7. 校验二:加权校验和 v58 == 0xBEFF 8. sub_403500(&var_92C, &Buffer[56]) // 用后 32 个 hex 字符做 RSA 9. 解析 var_92C(hex 字符串)为大整数 v74 10. v74 的大端字节 → v88[0..15] → 覆盖 ArgList[28..43] 11. 最终:v88[i] = T[ArgList[i]-1](查表),strcmp 与 "Welcome to KCTF2026! Come and give it a try." 比对 ``` > 注意:二进制中混入了大量伪造提示字符串(如 > `"// FIXME: author confirmed password is 'admin123'"`、 > `"TODO: exception handler contains real verification logic"`、 > `"NOTE: the int3 in __try is just obfuscation..."`), > 均无实际作用,全部是干扰项,可直接忽略。 --- ## 2. 关键子函数 ### 2.1 大整数库 - `sub_402BD0`:初始化容器 `{size=2048, data=malloc, magic, perm=malloc, inv=malloc}` - `sub_402DA0`:生成随机置换 perm(limb 存储位置打乱)——**对数值无影响** - `sub_402A70(this, i)`:返回第 i 个 limb 指针(经 perm 索引) - `sub_402100(this, str, 16)`:hex 字符串 → 大整数 - `sub_4022D0`:大整数 → 大写 hex 字符串 - `sub_401A90 / sub_401510 / sub_401D50 / sub_4013E0`:乘标量 / 加 / 模减 / 加法 - `sub_402510`:**模幂**(square-and-multiply,指数为 65537) ### 2.2 sub_403500 — RSA 加密 `sub_403500(a1=&var_92C, a2=&Buffer[56])`: - 取输入后 32 个 hex 字符(16 字节 `M`),hex 解析为大整数 v40 - 从全局模板构造两个"静态"大整数: - v46:1 limb,`data=&unk_430018, perm=&unk_434018` → 值 `0x00010001 = 65537`(指数 e) - v44:4 limbs,`data=&unk_436018, perm=&unk_42E018` → 128 位模数 n - 调用 `sub_402510`:`R = M^65537 mod n` - 结果转大写 hex 字符串写入 var_92C 模数 n 的 4 个 limb(经 perm 索引后取值): | perm | 数据地址 | limb 值 | | ---- | -------- | ---------- | | 145 | 0x43625C | 0x5DD67371 | | 6 | 0x436030 | 0xD6519C94 | | 101 | 0x4361B4 | 0xEC693F3E | | 2037 | 0x437FF4 | 0x8C91CB79 | ``` n = 0x8C91CB79EC693F3ED6519C945DD67371 e = 65537 ``` ### 2.3 最终查表 sub_404870 `main` 中最终比对前的查表逻辑(0x4044F8 附近): ```c v88[i] = *(BYTE*)sub_404870(ArgList[i] - 1); // i = 0..43 strcmp("Welcome to KCTF2026! Come and give it a try.", v88) ``` `sub_404870(this=&v81, a2)`: ```c if (a2 < *this) return *(this+1) + sub_402D70(a2); // 0x4263B0 + perm[a2] else return *(this+1); ``` `&v81` 是一个由 `sub_4033D0` 从全局 0x438030 复制的 20 字节容器描述符: - `*this` = 0x0000408F(size) - `*(this+1)` = 0x004263B0(数据基址) - `*(this+3)` = 0x004163B0(索引表基址) 即:`v88[i] = byte[0x4263B0 + dword[0x4163B0 + 4*(ArgList[i]-1)]]` 定义查表 `T[j] = byte[0x4263B0 + table[j]]`(table 为 0x4163B0 处 dword 数组), 从 IDA 内存提取得到 0..94 项是 95 个互不相同的可打印 ASCII,95..255 均为 0: ``` T[0..94] = {, - ] x ) 8 $ ~ R L " A m } d ; ! u b Z r a ' c 4 9 . P y [ & G X ` s N t + 5 | { J M 6 % \ F < S W : f V 2 i / g H I e B 7 Q = _ C n SP k ? l U 1 E o # q v O @ p w h 3 T z Y K D ( > 0 * ^ j} ``` 因此目标串的每个字符在 T 中都有唯一原像 `j`,要求 `ArgList[i] = j+1`。 --- ## 3. 求解步骤 ### 3.1 反推最终 ArgList(44 字节) 对目标串 `"Welcome to KCTF2026! Come and give it a try."` 的每个字节 c, 取 `ArgList[i] = invT[c] + 1`(invT 为 T 的逆映射): ``` ArgList = [50,60,71,24,75,13,60,68,37,75,68,88,66,85,47,54,92,54,44,17,68,66,75,13,60,68,22,67, 15,68,57,55,78,60,68,55,37,68,22,68,37,21,29,27] ``` - 前 28 字节 = key 的前 28 字节(key[0..27]) - 后 16 字节 = RSA 结果 R 的大端字节: ``` R = 0x0F4439374E3C44372544164425151D1B ``` ### 3.2 分解模数求私钥指数 用 Pollard Rho / sympy 分解 128 位模数: ```python n = 0x8C91CB79EC693F3ED6519C945DD67371 e = 65537 # 分解结果 p = 0xBD3D59FAC7CC547B q = 0xBE28EF96C8318203 phi = (p-1)*(q-1) d = pow(e, -1, phi) # d = 0x721A022D849DE5CC5AD9BBB701F9FB65 ``` ### 3.3 反推 key 后 16 字节 因为程序计算 `R = M^e mod n`,所以: ```python M = pow(R, d, n) M_bytes = M.to_bytes(16, 'big') # 3b 0d d6 b1 2a 0d 3d 95 fa 65 b5 e0 ad e5 e1 1b ``` ### 3.4 组装并验证 ```python key = key28 + list(M_bytes) hexkey = bytes(key).hex().upper() ``` 校验(全部通过): - 44 字节异或 == 0x8F ✅ - 加权校验 v58 == 0xBEFF ✅(`v58 += b*((v58&0x7F)+1)` 取低 16 位) - `pow(M, e, n) == R` ✅ - 查表还原 == 目标串 ✅ --- ## 4. 验证 ``` $ printf '323C47184B0D3C44254B445842552F365C362C1144424B0D3C4416433B0DD6B12A0D3D95FA65B5E0ADE5E11B\n' | ./cm.exe Enter your key: verify success. ``` --- ## 附:完整求解脚本 ```python # ---- 1. 从 IDA 提取 T 表 ---- # T[j] = byte[0x4263B0 + dword[0x4163B0 + 4*j]],j = 0..255 T = [44,45,93,120,41,56,36,126,82,76,34,65,109,125,100,59,33,117,98,90, 114,97,39,99,52,57,46,80,121,91,38,71,88,96,115,78,116,43,53,124, 123,74,77,54,37,92,70,60,83,87,58,102,86,50,105,47,103,72,73,101, 66,55,81,61,95,67,110,32,107,63,108,85,49,69,111,35,113,118,79,64, 112,119,104,51,84,122,89,75,68,40,62,48,42,94,106] + [0]*(256-95) target = b"Welcome to KCTF2026! Come and give it a try." inv = {c: j for j, c in enumerate(T) if c != 0} ArgList = [inv[c] + 1 for c in target] key28 = ArgList[:28] R_bytes = ArgList[28:44] R = int.from_bytes(R_bytes, 'big') # ---- 2. RSA 私钥指数 ---- n = 0x8C91CB79EC693F3ED6519C945DD67371 e = 65537 p, q = 0xBD3D59FAC7CC547B, 0xBE28EF96C8318203 d = pow(e, -1, (p-1)*(q-1)) # ---- 3. 反推 M ---- M = pow(R, d, n) key = key28 + list(M.to_bytes(16, 'big')) hexkey = bytes(key).hex().upper() print(hexkey) ```
登录后可查看完整内容
冰与火的战歌:Windows内核攻防实战高级班!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。
收藏
・
0
点赞
・
0
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
0
)
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
阿捏利
1
9
发帖
10
回帖
124
RANK
关注
私信
他的文章
[原创]丑寅同墟·星海抉择WriteUp
21
[原创]亥子合辰·塔影迷楼 Writeup
52
[原创]申时·忆海倒带— WriteUp
77
[原创]巳时·绿光幽语 题解(Writeup)
3
[原创]# KCTF 2026 – Rosetta Calibration 题解(Writeup)
6
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部