论坛首页 CTF竞赛交流区 阅读主题

[原创]看雪·2025 KCTF 第九题 WriteUP

297 浏览 0 回复
#1 楼主 2026-06-01 21:08:55
GPT-5 还是 Pro 好用,Thinking 老是走弯路,浪费了不少时间本文的分析由 GPT-5 Pro + Thinking 写成,我只干了脱壳的事(❁´◡`❁)LLM 万岁(雾用 DIE 查壳,发现加了 Enigma Virtual Box 的壳,用 EnigmaVBUnpacker 脱壳后可得到源代码。 对于释放出来的 20250805Calc, 直接在程序运行的时候从temp复制过来就行,或者 dnSpy 直接导出就可以得到原始可执行文件 对 20250805Calc.exe 使用 pyinstxtractor + PyLingual 还原原始代码 下面给你把这个 crackme 的校验流程完整梳理一遍,并给出 Name = KCTF 时应该如何反推出合法 SN 的办法。我也把关键源码位置都标好了,方便你对照。程序读入 Name 和 SN;若为空直接退出。随后把 Name 的 UTF‑8 字节逐个异或,取结果 % 64 得到一个数 num;如果 num 为偶数再 +1。接着把 num 的十进制字符串记为 A(作为被除数的“分子”)。
*对 Name = KCTF:ASCII 分别为 K=75, C=67, T=84, F=70,逐位 XOR:75^67^84^70 = 26,26 % 64 = 26,偶数 → num = 27,所以 A = "27"。*对输入的 SN 先做一次「特殊的游程解码」(只能包含数字、l 和 .)。这个解码器把形如的编码翻译为一个只由 数字和至多一个小数点组成的 B 字符串。B 后续将作为“分母”。长度<26 或 >50、以 0 开头、以 . 结尾、计数有前导 0 等情况都会被判无效。程序把一个随附的 Python 可执行(你提供的源码 20250805Calc-pub.py)释放到临时目录并执行,计算其中 Decimal 精度被设置为 (2040+1)×2025 = 4,133,025 个有效数字,并把结果打印到标准输出。C# 只接受 科学计数法(包含 E)的输出;它会把形如 1.xxxxxE-kkk 的结果,转成一个超长纯数字串:之后把 Quotient 从左到右按 每 2025 位一块切片,得到块 Q[0], Q[1], ...,然后在 j=0..2036 范围内检查下面的线性关系是否成立:Q[j+]⋅Q[j]+⋅Q[j+]+⋅Q[j+]Q[j+3] \stackrel{?}= 5\cdot Q[j] + 8\cdot Q[j+1] + 9\cdot Q[j+2]Q[j+3]=?5⋅Q[j]+8⋅Q[j+1]+9⋅Q[j+2]这里的乘法与加法是十进制大整数的真运算(程序自己实现的大整数字符串乘/加),并且不允许出现 Q[j]==Q[j+1]==Q[j+2] 的三连相等。期望的“第一次不满足”应当恰好发生在 j=2036(代码在不满足时会 j++ 再 break,随后检查 j == 2040-3),否则判定失败。小结:SN 的本质是让 B 经过上面的链条生成的 Quotient 满足 2025 位分块后的长距离线性递推关系,且第一次失败点恰好落在规定的位置。解码器的核心规则(从源码 KX_RGVjb2Rl 直接整理)如下:允许的字符:0–9、l、.;语法(伪 EBNF):约束:举例:"2""1""l""9""1""l""0""6""l" 会被解码为 29 后面 6 个 0,即 29000000。要点是:我们希望 R = 27/B 的十进制(科学计数法)转成的 Quotient(去掉小数点和 E- 后补零)在长度 4,133,025 的数字串上,按 2025 位分块满足前述线性递推,并且第一次不满足恰好出现在 j=2036。一个可操作的通用构造法:直接在“块空间”里造一个满足递推的序列:任选 3 个 2025 位的非全零首块 Q[0], Q[1], Q[2](避免三连相等),按Q[j+]=Q[j]+Q[j+]+Q[j+]Q[j+3] = 5Q[j] + 8Q[j+1] + 9Q[j+2]Q[j+3]=5Q[j]+8Q[j+1]+9Q[j+2]推到 j=2035。然后特意让 Q[2039] \ne 5Q[2036]+8Q[2037]+9Q[2038](比如把 Q[2039] 的末位改一下),从而把“第一次不满足”固定在 j=2036。把这些块首尾拼接成一个总长 2041×2025 = 4,133,025 位的数字串 D;定义R=D×−KR \;=\; 1.D \times 10^{-K}R=1.D×10−K其中选择一个足够大的 K(至少使 Python 以 E- 输出;比如 K≥7 就够了),这样 C# 会按它的逻辑把 R 还原回我们拼出来的 Quotient。令B=B \;=\; \frac{27}{R}B=R27​然后把十进制的 B 用上面的游程编码压缩成一个长度 26~50 的 SN。(为了让编码尽量短,可让 B 的十进制形态由极少种数字的长段组成,比如“前缀若干位 + 很长一段 0 + 很长一段 9 + …”,从而用极少几个 <digit><count>l 就能覆盖。)最后用源码里的同一套字符串乘/加校验一次(完全照抄 KX_TXVsdGlTdHJpbmdz 与 KX_QWRkU3RyaW5ncw雪雪 算法)以确保通过。说明:之所以这样构造是因为程序并不从 B 直接检验什么“数学性质”;它只关心通过 Python 得到的 数字串在 2025 位块上的线性关系。因此**先造数字串,再反推 B**是最稳妥的路线。你可以把自己想要的 Q[0..2039](或直接把最终的 B)喂进下面这段流程做本地验证:下面是我把思路 + 代码揉成一篇完整 write-up 的版本。它从还原校验逻辑开始,解释为什么“构造一个特定分母”能一招过关,然后给出确定性构造器的实现要点与可直接运行的脚本/命令。为便于核对,我在关键处都标了源码引用。给定脱壳后的 C# crackme 与它释放调用的 Python 计算器 20250805Calc-pub.py,当输入 Name="KCTF" 时,求一组满足校验的 SN。
校验大意是:把 A=int(NameRule) 与经“自定义游程编码”解出的 B 送去 Python 做高精度除法,拿回科学计数法结果,再把 mantissa 去小数点、按指数左补 0 得到一个超长纯数字串 Quotient;将它按 2025 位切块,检查一个跨 2040 个块的线性递推是否成立,且“第一次不满足”出现在最后的窗口边界。程序把 Name 的 UTF-8 字节做异或、对 64 取模,若为偶数再加 1。A = num.ToString()。对 KCTF 来说,最后得到 A="27"。SN 只能含 数字/l/.,语法上是一串“数字 + 计数 + l”的 run,可在 run 与 run 之间插入 最多一个小数点。长度必须在 [26,50],不许以 0 开头,也不许以 . 结尾;计数不允许前导 0;收尾允许省略最后一个 run 的 l(源码尾段会补齐这一段)。这些条件不满足直接失败。程序还在进入主流程前强制:A 和 B 的最后一位都不能是 '0'

...(已截断)

---
来源: 看雪论坛
原文链接: https://bbs.kanxue.com/thread-288330.htm

暂无回复,快来抢沙发吧!

请登录后参与讨论

立即登录 注册账号