首页
社区
课程
招聘
[原创]KCTF2026 - 第四题:未时·车流困城 题解(AI)
发表于: 1天前 478

[原创]KCTF2026 - 第四题:未时·车流困城 题解(AI)

1天前
478

32 位 PE,但真正的逻辑经 Heaven's Gate 跑在 x64 长模式,代码藏在可执行的 .data 段里,
外面裹着 jmp 链、计算跳转、常量掩码,特权指令当 VM opcode 用in/out 触发 #GP,
int3 触发 VEH,由 exe 导出的 Warning 函数统一分发)。

注册码校验是五层套娃:

图片描述

三个卡点,都不在算法本身:

main 的骨架一屏可见 —— 五次调用全部经同一个trampoline函数 sub_4010E0
第 4 个参数是 x64 函数指针:

图 1 main 全貌。红框 ①~⑤ 就是五层套娃。
注意 ⑤ 之前那一大段 mov eax, offset unk_4D40A0 / unk_7A4448 / unk_4240A0 + mov [ebp+var_28], 0Bh ——
三张表的指针和常数 11打包成一个结构体传给 GoodGoodStudy
这三张表分别是 CSR 的 data、VM 字节码、CSR 的 row_ptr,11 = 多项式系数个数(10 次首一多项式)。
另外 ② 和 ④ 后面各跟一个 test eax,eax / jz loc_4013DA —— 两个提前失败出口,
后面会看到它们其实不是"校验函数",而是伪装成函数的异常陷阱。

序幕就是一发 anti-VM,而且它同时承担了初始化职责

*图 2 in eax, dx 在 ring3 必定 #GP。真机走 SEH → int 3 → VEH,
VEH 里那次隐藏初始化把 .data 段建成可执行、注册 Warning 处理器;
而在 VMware 里 in 不 fault,直接 jmp 跳过 int3,初始化不做,后面全是 decoy。

trampoline 本体只有 10 条指令:

图 3 Heaven's Gate。push 0x33 压的是 64 位代码段选择子,
call $+5 + add [esp],5 + retf 把返回地址改成下一条指令再远返回,于是 CPU 切到长模式继续执行。
切过去之后是 mov rcx/rdx/r8 ← 前三个参数、call r9 ← 第四个参数(x64 函数指针)。
所以 sub_4010E0(a,b,c,fn) 的语义就是 fn(a,b,c),只不过 fn 跑在 64 位。

最终判定:

图 4 GoodGoodStudy 的返回值在 esi,非 0 才 cmovnz 选中 Successful!
没有任何花活,说明*真正的校验全在 ⑤ 里
。*

serial 去掉头 lI|0O 和尾 Il1|! 后,payload 每 4 字符解成 3 字节:

它的输出是白噪声,不是明文(熵 7.977 bit/byte,256/256 个字节值都出现)。
我一度以为解码结果就该是整数文本,被这一步误导了很久 —— 真正的明文在 ③ 之后。

unk_7A6920unk_8386B4 各自只有 2 个字节

in/out 在 ring3 触发 #GP(STATUS_PRIVILEGED_INSTRUCTION),
由 64 位 VEH Warningexe 的导出函数,VA 0x81139C)按触发指令的 opcode 字节分发:

处理完把结果写进 CONTEXT.RaxCONTEXT.Rip += 1(跳过那 1 字节),
返回 EXCEPTION_CONTINUE_EXECUTION,于是落到 ret —— 从调用方看就像一个普通函数返回了值。

这也是反调试的核心:挂调试器时 int3 被调试器先吃掉,Warning 根本没机会注册,
后面所有陷阱都会变成未处理异常。

这一层是全题的重心,也是我误判最多的地方(早期在 decoy 路径下观测,一度以为它是 no-op)。
Warning0xEC 计算跳转实现出来之后,它才真正跑起来 —— 从 ~80 条指令暴涨到数百万条、
118 次模式切换。去混淆后的结构:

结论:MengXin ≡ AES-128 解密,只是 S 盒换成了自定义的 256 置换,
标准的 InvShiftRows / InvMixColumns 一个没改。
所以我们要的加密方向就是对应的 AES-128 加密

主密钥来自 main 栈上的 var_54ebp-0x54,16 字节),
它在 scanf 读输入之前就由 sub_401160 设好,跨运行固定:

S 盒依赖 BeingDebugged,这点极其隐蔽:

0x40Warning 加的,+0x1A 由后面另一处再加、凑成 0x5A
MengXin 执行时还是 0x40,3.9 秒后才变 0x5A(那是 ⑤ 的取值)。
用错值提出来的 S 盒和正确的一个字节都不重合(0/256)。

fmix32 的乘数常量 0x5BD1E995 在 .data 的 x64 blob 里

图 5 0x808D19 处的 95 E9 D1 5B0x5BD1E995
这段代码在 IDA 里是未定义字节(合成 PE32+ 里 .data 没被自动分析成代码,
何况它被 jmp 链彻底打散),所以这里用 Hex View 给常量本身当证据,
控制流靠自写的去混淆追踪器还原。

拿到 100 个 14-bit 组号后,每组去 CSR 表取 11 个系数,拼成 10 次首一多项式,
把解析出的整数代回去做 Horner 求值,要求 P(x) == 0

图片描述

图 6 row_ptr 紧跟在 Successful! 字符串之后。每项 (offset & 0xFFFFFF) | (count << 24)
共 11×16384 项。全表 180224 项里 count == 0 的一个都没有。

图片描述

图 7 系数本体。看上去完全随机 —— 因为每个字节都被
mixbyte(idx*0x9E3779B9 ^ row*0x517CC1B7 ^ i*0x85EBCA6B) 异或过。
解密后才是「符号 + 位数 + 压缩 BCD」。这正是题面"路线矩阵"伪装成随机矩阵的地方。

解析规则:- 分隔,空格/换行忽略,NUL 结束,必须恰好 1000 个整数;
第 k 组用 nums[10k..10k+9]。它不检查升序、不检查组内互异,比的是数值不是文本。

反解方向很轻松:多项式已知且首一,直接对每组求整数根即可,不需要任何搜索

Warning 里那条 BeingDebugged += 0x40 挂在一串检查后面,决定性的一环是:

explorer.exe 的 djb2 hash 常量 0x9D1337F1

图 8 0x8136A1 处小端 F1 37 13 9D。我按 djb2(seed 0xF13093F0、乘数 33、tolower)
正向算了一遍:hash33("explorer.exe") == 0x9D1337F1hash33("powershell.exe") == 0x8EED4105
两个都对得上,确认了这是父进程名白名单。
Readme 里那句「运行方式:双击运行」不是废话,是硬约束。

这解释了我前期所有的"诡异现象":从 python/PowerShell 用 subprocess 拉起 CrackMe 时,
父进程不在白名单 → BeingDebugged 只到 0x1A → VM 字节码解密成垃圾 →
连作者公布的合法 Name/Serial 都显示 Failed。我曾因此怀疑样例损坏、怀疑 VBS、
甚至关掉 Hyper-V 重启机器,全是白费。

对应的本地验证器:用 explorer.exe 启动,AttachConsole + WriteConsoleInput 注入,
再读屏拿结论(verify_via_explorer.py)。这样验证不消耗看雪的提交次数。

pev2(自写的模式感知静态部分求值器)能完整跑通 MengXin,但它的 key schedule 依赖
ntdll 导出遍历,算出的轮密钥和真机不同。而真机的轮密钥是固定的,于是两边互补:

拼出来的干净实现一次通过全部硬验证:

最强的一次验证 —— 用完整链条逐字符重现作者公开的 Serial

这一步把"每一环都对"钉死了,后面出问题就只可能是 KCTF 特有的东西。

GoodGoodStudy检查完全部 100 组(不提前返回),某组失败时内层 break 会少算几个数,
所以耗时本身就是"通过了多少组"的探针。测「MengXin 完成 → 出结论」这一段:

三档分离得很干净,一下就把锅从 ⑤ 摘到了 ④。


冰与火的战歌:Windows内核攻防实战高级班!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。

最后于 1天前 被星野安全编辑 ,原因:
收藏
免费 13
打赏
分享
最新回复 (6)
雪    币: 0
活跃值: (1959)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
学习学习
1天前
0
雪    币: 7539
活跃值: (4334)
能力值: (RANK:1130 )
在线值:
发帖
回帖
粉丝
3
学习学习
1天前
0
雪    币: 225
活跃值: (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
牛逼
1天前
0
雪    币: 1827
活跃值: (7913)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
5
1
1天前
0
雪    币: 225
活跃值: (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
期待开源实现代码
21小时前
0
雪    币: 29
活跃值: (940)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
6666666
14小时前
0
游客
登录 | 注册 方可回帖
返回