首页
社区
课程
招聘
[原创]KCTF 2025 第九题 WriteUp - LLM 一血直解
发表于: 2025-9-2 09:26 3162

[原创]KCTF 2025 第九题 WriteUp - LLM 一血直解

2025-9-2 09:26
3162

一道增加了分析障碍的算法题,LLM 一眼识破障眼法,告诉最终的核心内容,拿到算法代码后 LLM 又直接给出了解题代码,仅一小时十多分便完成解题拿下一血。

其实我已经四五年没打过 CTF了,今年参与看雪 CTF 2025 就是想亲自感受下 LLM 对 CTF 比赛二进制方向解题的影响。

可以看到 LLM 进步如此之快,现在靠算法给 CTF 逆向题目上强度与难度已经不容易了,这是出题人亟需面对和思考的问题。

拖进反编译器发现不太对劲,查壳显示加了 Enigma Virtual Box 保护。

随后让 LLM 进行分析字符串和导入表:

Gemini 2.5 Pro

LLM 轻松地判断出来程序是 .NET 和 Python 混淆编程并经过打包的,Enigma Protector 壳的话,如果看防守方出题规则可以知道是不让用第三方保护加壳的,简单搜下 Enigma Virtual Box 看到出现解包字样,判断程序只是进行了打包操作。

搜索到 Python 解包工具 pyinstxtractor,解包出相关文件夹和文件,注意到关键文件 20250805Calc-pub.pyc,反编译内容为

搜素到 Enigma Virtual Box 解包工具 evbunpack,解包出 dnlib.dllhtg_Crackme.exe,分析发现后者不能识别为 .NET 程序。

稍加思索,决定换种方法试下,使用 dnspy 选择附加到进程,点击调试->窗口->模块找到 htg_Crackme.exe,选中右击从内存中打开模块,及保存模块,成功获取到可以直接分析的 .NET 代码逻辑。

现在已经得到几乎接近源码的题目算法代码,剩下的交给 LLM 即可,先贴上 .NET 和 Python 混合编程的代码让其梳理分析下。

Gemini 2.5 Pro

逻辑一览无余,让其写出注册机代码。

Gemini 2.5 Pro

运行下发现结果和题目中给的对不上,提问反馈

Gemini 2.5 Pro

按回答所说初始值改为 0, 0, 1,得到了比题目所给 SN 多了一个 l 的结果,继续提问反馈:

Gemini 2.5 Pro

两轮修正后算是可以了,运行得到答案

修正后求解代码如下:

这一道ctf逆向题目的字符串、导入表内容,进行梳理分析
(贴上所有字符串和导入表内容)
这一道ctf逆向题目的字符串、导入表内容,进行梳理分析
(贴上所有字符串和导入表内容)
好的,我们来对这个CTF逆向题的字符串和导入表进行一次全面的梳理和分析。从你提供的信息来看,这个程序结构相当复杂,包含了多层封装和保护。
 
### **一、 总体概览**
 
这是一个高度混淆和保护的应用程序。核心逻辑可能是由Python和.NET混合编写的,然后使用 **PyInstaller** 将Python脚本打包成EXE,最后再用 **Enigma Protector** 对整个EXE文件进行了加壳和虚拟化保护。
 
*   **保护/加壳层:** Enigma Protector (证据:`.enigma1`, `.enigma2` 区段,大量`Zw*` API钩子,反调试字符串)。
*   **打包层:** PyInstaller (证据:大量 `pyi-` 前缀的字符串,关于解压归档、初始化Python环境的错误信息)。
*   **核心逻辑层:**
    *   **Python:** 作为“胶水”代码,负责协调和执行流程。
    *   **.NET程序:** 名为 `_2024003_Crackme.exe` 或 `20250805Calc-pub.exe`,很可能包含了题目的核心算法。
 
### **二、 字符串分析**
 
我们可以将有价值的字符串分为几个大类来分析:
 
#### **1. PyInstaller 打包器特征**
 
这些字符串是 PyInstaller 生成的可执行文件的典型特征,它们揭示了程序是如何从一个Python脚本构建的。
 
*   `pyi-runtime-tmpdir`, `PYI_ARCHIVE_FILE`, `_PYI_PARENT_PROCESS_LEVEL`: PyInstaller在运行时使用的环境变量和参数,用于定位和管理解压后的临时文件。
*   `Failed to extract %s: ...`, `Could not allocate memory for archive structure!`: 这些是PyInstaller的引导程序(bootloader)在尝试从自身解压Python库和脚本文件时可能出现的错误信息。
*   `ailed to load Python DLL '%ls'.`, `Py_Initialize`, `Py_Finalize`: 明确指出了程序正在加载和初始化一个嵌入式的Python解释器。
*   `base_library.zip`: 这是PyInstaller打包时用来存放标准Python库的压缩包。
*   `__main__`, `_pyi_main_co`: `__main__`是Python程序的入口点,`_pyi_main_co` 指的是主脚本的code object,这表明程序即将执行解压后的主Python代码。
 
**小结:** 程序本质上是一个Python脚本。逆向的关键一步是需要从这个加壳的EXE中提取出PyInstaller打包的数据,然后用 `pyinstxtractor` 这类工具来解包,获取Python字节码(.pyc文件)。
 
#### **2. .NET 应用程序特征**
 
这些字符串表明程序内部嵌入或调用了一个.NET程序集。
 
*   `mscoree.dll`, `_CorExeMain`: 这是启动.NET公共语言运行时(CLR)的入口点,是.NET程序的明确标志。
*   `mscorlib`, `System.Core`, `System.IO`, `System.Text`: 核心的.NET框架命名空间。
*   `_2024003_Crackme.exe`, `20250805Calc-pub`: 看起来是嵌入的.NET可执行文件的原始名称。`Crackme` 和 `Calc` (计算器) 暗示了题目的功能。
*   `KX_Z2V0X1F1b3RpZW50` -> base64解码为 -> `get_Quotient` (获取商)
*   `KX_c2V0X1F1b3RpZW50` -> base64解码为 -> `set_Quotient` (设置商)
*   `KX_VmVyaWZ5U2lnbmFs` -> base64解码为 -> `VerifySignal` (验证信号/签名)
*   `KX_RXh0cmFjdFJlc291cmNlVG9UZW1w` -> base64解码为 -> `ExtractResourceToTemp` (提取资源到临时文件)
 
**小结:** Python脚本很可能在运行时,从自身资源中解压出这个.NET程序,然后通过进程调用或 `pythonnet` 库与之交互。核心的校验逻辑(例如,一个复杂的计算)就封装在这个.NET程序中。Base64编码的字符串是一种简单的混淆手段,逆向时需要重点关注这些解码后的函数名。
 
#### **3. Enigma Protector 保护器特征**
 
这些字符串来自于最外层的保护壳,其目的是反调试、反篡改和虚拟化代码。
 
*   `.enigma1`, `.enigma2`: Enigma Protector添加的自定义区段名。
*   `Memory Protection failed...`, `IsDebuggerPresent`: 典型的反调试检测。
*   `FPC 3.2.2 [...] for x86_64 - Win64`: 这表明Enigma Protector的加载器(stub)部分可能是用Free Pascal编译器编写的。
*   `ZwClose`, `ZwCreateFile`, `ZwProtectVirtualMemory`, `TNtDllHook`: 大量出现 `ntdll.dll` 中的底层 `Zw*` 函数名,并且有 "Hook" 字样,这强烈暗示它通过API Hooking来控制和虚拟化程序的文件、注册表和进程访问,从而实现保护和反分析。
*   `virtualboxunit`, `CVirtualBox`: 这些字符串非常有趣。Enigma Protector的虚拟化引擎可能借用了VirtualBox的一些概念或名称。这套系统会在内存中创建一个“虚拟机”,让被保护的代码在这个受控的环境中运行,极大地增加了调试和分析的难度。
 
**小结:** Enigma壳是逆向工作需要面对的第一个巨大障碍。常规的静态分析工具很难穿透它。你需要借助专门的脱壳工具,或者使用调试器(如x64dbg)配合ScyllaHide等反-反调试插件,手动跟过保护壳的初始化阶段,找到程序的原始入口点(OEP),然后从内存中dump出解密后的程序主体(即PyInstaller打包的那个文件)。
 
### **三、 导入表分析**
 
导入表(IAT)主要反映了最外层壳(Enigma Loader)的功能需求。
 
*   **`kernel32.dll`**: 导入了几乎所有的基础功能函数,包括:
    *   **内存管理**: `VirtualAlloc`, `VirtualProtect`, `ReadProcessMemory` - 这是保护壳动态加载、解密和修改代码所必需的。
    *   **文件操作**: `CreateFileW`, `ReadFile`, `WriteFile` - 用于读取自身数据,也可能用于创建临时文件。
    *   **进程/线程**: `CreateThread`, `SuspendThread`, `ResumeThread`, `OpenProcess` - 表明它有复杂的多线程行为,并且可能对子进程进行注入或控制。
    *   **库加载**: `LoadLibraryA`, `GetProcAddress` - 动态加载其他DLL并获取函数地址,这是所有模块化程序和保护壳的基础。
*   **`ntdll.dll`**: 导入了非常底层的系统API,如 `ZwProtectVirtualMemory`。这再次印证了它在通过直接的系统调用(syscall)来绕过高层API钩子,实现更隐蔽的内存保护和修改。这是高级保护软件的典型特征。
*   **`user32.dll`**: 导入了 `MessageBoxA` 等,说明保护壳在遇到错误(如检测到调试器)时可能会弹出对话框。
*   **`ole32.dll`, `oleaut32.dll`**: COM和自动化支持,这可能是为了后续与.NET环境交互做准备。
 
**小结:** 导入表清晰地描绘出一个强大的原生加载器形象,它具备深度操作系统交互能力,能够精细地控制内存、进程和文件,这完全符合一个现代软件保护器的特征。
 
### **四、 逆向策略建议**
 
根据以上分析,推荐的逆向步骤如下:
 
1.  **脱壳**: 这是首要任务。尝试使用通用脱壳工具或脚本。如果失败,则需要手动脱壳。在调试器中加载程序,重点关注内存区域的变化,在执行到可疑的`jmp`或`call`指令后,查看内存中是否出现了新的PE头(`MZ`标志),这通常是被解密出的原始程序。找到后,使用Scylla等工具从内存中dump出来。
2.  **提取Python脚本**: 对脱壳后的文件(应该是PyInstaller打包的程序)使用 **pyinstxtractor**。这会解压出`base_library.zip`、主程序的`.pyc`文件以及其他依赖项。
3.  **反编译Python脚本**: 使用 **uncompyle6** 或其他Python字节码反编译器,将`.pyc`文件转换为可读的`.py`源代码。分析Python代码的逻辑,特别是它如何处理和调用.NET程序。
4.  **分析.NET程序**: 在Python脚本的解压文件中找到`.exe`后缀的.NET程序集。使用 **dnSpy-unofficial**, **ILSpy** **dotPeek** 等.NET反编译工具打开它。
5.  **攻克核心算法**: 在.NET程序的源代码中,找到类似 `VerifySignal` 的关键函数,分析其内部的计算和验证逻辑,这应该就是解出这道CTF题目的最终环节。
 
总而言之,这道题是一个综合性的挑战,考验了逆向工程师对加壳、打包、以及Python和.NET混合应用等多方面的知识。
好的,我们来对这个CTF逆向题的字符串和导入表进行一次全面的梳理和分析。从你提供的信息来看,这个程序结构相当复杂,包含了多层封装和保护。
 
### **一、 总体概览**
 
这是一个高度混淆和保护的应用程序。核心逻辑可能是由Python和.NET混合编写的,然后使用 **PyInstaller** 将Python脚本打包成EXE,最后再用 **Enigma Protector** 对整个EXE文件进行了加壳和虚拟化保护。
 
*   **保护/加壳层:** Enigma Protector (证据:`.enigma1`, `.enigma2` 区段,大量`Zw*` API钩子,反调试字符串)。
*   **打包层:** PyInstaller (证据:大量 `pyi-` 前缀的字符串,关于解压归档、初始化Python环境的错误信息)。
*   **核心逻辑层:**
    *   **Python:** 作为“胶水”代码,负责协调和执行流程。
    *   **.NET程序:** 名为 `_2024003_Crackme.exe` 或 `20250805Calc-pub.exe`,很可能包含了题目的核心算法。
 
### **二、 字符串分析**
 
我们可以将有价值的字符串分为几个大类来分析:
 
#### **1. PyInstaller 打包器特征**
 
这些字符串是 PyInstaller 生成的可执行文件的典型特征,它们揭示了程序是如何从一个Python脚本构建的。
 
*   `pyi-runtime-tmpdir`, `PYI_ARCHIVE_FILE`, `_PYI_PARENT_PROCESS_LEVEL`: PyInstaller在运行时使用的环境变量和参数,用于定位和管理解压后的临时文件。
*   `Failed to extract %s: ...`, `Could not allocate memory for archive structure!`: 这些是PyInstaller的引导程序(bootloader)在尝试从自身解压Python库和脚本文件时可能出现的错误信息。
*   `ailed to load Python DLL '%ls'.`, `Py_Initialize`, `Py_Finalize`: 明确指出了程序正在加载和初始化一个嵌入式的Python解释器。
*   `base_library.zip`: 这是PyInstaller打包时用来存放标准Python库的压缩包。
*   `__main__`, `_pyi_main_co`: `__main__`是Python程序的入口点,`_pyi_main_co` 指的是主脚本的code object,这表明程序即将执行解压后的主Python代码。
 
**小结:** 程序本质上是一个Python脚本。逆向的关键一步是需要从这个加壳的EXE中提取出PyInstaller打包的数据,然后用 `pyinstxtractor` 这类工具来解包,获取Python字节码(.pyc文件)。
 
#### **2. .NET 应用程序特征**
 
这些字符串表明程序内部嵌入或调用了一个.NET程序集。
 
*   `mscoree.dll`, `_CorExeMain`: 这是启动.NET公共语言运行时(CLR)的入口点,是.NET程序的明确标志。
*   `mscorlib`, `System.Core`, `System.IO`, `System.Text`: 核心的.NET框架命名空间。
*   `_2024003_Crackme.exe`, `20250805Calc-pub`: 看起来是嵌入的.NET可执行文件的原始名称。`Crackme` 和 `Calc` (计算器) 暗示了题目的功能。
*   `KX_Z2V0X1F1b3RpZW50` -> base64解码为 -> `get_Quotient` (获取商)
*   `KX_c2V0X1F1b3RpZW50` -> base64解码为 -> `set_Quotient` (设置商)
*   `KX_VmVyaWZ5U2lnbmFs` -> base64解码为 -> `VerifySignal` (验证信号/签名)
*   `KX_RXh0cmFjdFJlc291cmNlVG9UZW1w` -> base64解码为 -> `ExtractResourceToTemp` (提取资源到临时文件)
 
**小结:** Python脚本很可能在运行时,从自身资源中解压出这个.NET程序,然后通过进程调用或 `pythonnet` 库与之交互。核心的校验逻辑(例如,一个复杂的计算)就封装在这个.NET程序中。Base64编码的字符串是一种简单的混淆手段,逆向时需要重点关注这些解码后的函数名。
 
#### **3. Enigma Protector 保护器特征**
 
这些字符串来自于最外层的保护壳,其目的是反调试、反篡改和虚拟化代码。
 
*   `.enigma1`, `.enigma2`: Enigma Protector添加的自定义区段名。
*   `Memory Protection failed...`, `IsDebuggerPresent`: 典型的反调试检测。
*   `FPC 3.2.2 [...] for x86_64 - Win64`: 这表明Enigma Protector的加载器(stub)部分可能是用Free Pascal编译器编写的。
*   `ZwClose`, `ZwCreateFile`, `ZwProtectVirtualMemory`, `TNtDllHook`: 大量出现 `ntdll.dll` 中的底层 `Zw*` 函数名,并且有 "Hook" 字样,这强烈暗示它通过API Hooking来控制和虚拟化程序的文件、注册表和进程访问,从而实现保护和反分析。
*   `virtualboxunit`, `CVirtualBox`: 这些字符串非常有趣。Enigma Protector的虚拟化引擎可能借用了VirtualBox的一些概念或名称。这套系统会在内存中创建一个“虚拟机”,让被保护的代码在这个受控的环境中运行,极大地增加了调试和分析的难度。
 
**小结:** Enigma壳是逆向工作需要面对的第一个巨大障碍。常规的静态分析工具很难穿透它。你需要借助专门的脱壳工具,或者使用调试器(如x64dbg)配合ScyllaHide等反-反调试插件,手动跟过保护壳的初始化阶段,找到程序的原始入口点(OEP),然后从内存中dump出解密后的程序主体(即PyInstaller打包的那个文件)。
 
### **三、 导入表分析**
 
导入表(IAT)主要反映了最外层壳(Enigma Loader)的功能需求。
 
*   **`kernel32.dll`**: 导入了几乎所有的基础功能函数,包括:
    *   **内存管理**: `VirtualAlloc`, `VirtualProtect`, `ReadProcessMemory` - 这是保护壳动态加载、解密和修改代码所必需的。
    *   **文件操作**: `CreateFileW`, `ReadFile`, `WriteFile` - 用于读取自身数据,也可能用于创建临时文件。
    *   **进程/线程**: `CreateThread`, `SuspendThread`, `ResumeThread`, `OpenProcess` - 表明它有复杂的多线程行为,并且可能对子进程进行注入或控制。
    *   **库加载**: `LoadLibraryA`, `GetProcAddress` - 动态加载其他DLL并获取函数地址,这是所有模块化程序和保护壳的基础。
*   **`ntdll.dll`**: 导入了非常底层的系统API,如 `ZwProtectVirtualMemory`。这再次印证了它在通过直接的系统调用(syscall)来绕过高层API钩子,实现更隐蔽的内存保护和修改。这是高级保护软件的典型特征。
*   **`user32.dll`**: 导入了 `MessageBoxA` 等,说明保护壳在遇到错误(如检测到调试器)时可能会弹出对话框。
*   **`ole32.dll`, `oleaut32.dll`**: COM和自动化支持,这可能是为了后续与.NET环境交互做准备。
 
**小结:** 导入表清晰地描绘出一个强大的原生加载器形象,它具备深度操作系统交互能力,能够精细地控制内存、进程和文件,这完全符合一个现代软件保护器的特征。
 
### **四、 逆向策略建议**
 
根据以上分析,推荐的逆向步骤如下:
 
1.  **脱壳**: 这是首要任务。尝试使用通用脱壳工具或脚本。如果失败,则需要手动脱壳。在调试器中加载程序,重点关注内存区域的变化,在执行到可疑的`jmp`或`call`指令后,查看内存中是否出现了新的PE头(`MZ`标志),这通常是被解密出的原始程序。找到后,使用Scylla等工具从内存中dump出来。
2.  **提取Python脚本**: 对脱壳后的文件(应该是PyInstaller打包的程序)使用 **pyinstxtractor**。这会解压出`base_library.zip`、主程序的`.pyc`文件以及其他依赖项。
3.  **反编译Python脚本**: 使用 **uncompyle6** 或其他Python字节码反编译器,将`.pyc`文件转换为可读的`.py`源代码。分析Python代码的逻辑,特别是它如何处理和调用.NET程序。
4.  **分析.NET程序**: 在Python脚本的解压文件中找到`.exe`后缀的.NET程序集。使用 **dnSpy-unofficial**, **ILSpy** **dotPeek** 等.NET反编译工具打开它。
5.  **攻克核心算法**: 在.NET程序的源代码中,找到类似 `VerifySignal` 的关键函数,分析其内部的计算和验证逻辑,这应该就是解出这道CTF题目的最终环节。
 
总而言之,这道题是一个综合性的挑战,考验了逆向工程师对加壳、打包、以及Python和.NET混合应用等多方面的知识。
# Version: Python 3.12
 
from decimal import Decimal, getcontext
import os
import sys
 
def reciprocal(m, n, prec):
    getcontext().prec = int(prec)
    result = Decimal(m) / Decimal(n)
    return result
 
 
def main(arg1, arg2, arg3):
    sys.set_int_max_str_digits(int(arg3))
    reciprocal_value = reciprocal(arg1, arg2, arg3)
    print(reciprocal_value)
 
if __name__ == '__main__':
    arg1 = sys.argv[1]
    arg2 = sys.argv[2]
    arg3 = sys.argv[3]
    main(arg1, arg2, arg3)
    return None
# Version: Python 3.12
 
from decimal import Decimal, getcontext
import os
import sys
 
def reciprocal(m, n, prec):
    getcontext().prec = int(prec)
    result = Decimal(m) / Decimal(n)
    return result
 
 
def main(arg1, arg2, arg3):
    sys.set_int_max_str_digits(int(arg3))
    reciprocal_value = reciprocal(arg1, arg2, arg3)
    print(reciprocal_value)
 
if __name__ == '__main__':
    arg1 = sys.argv[1]
    arg2 = sys.argv[2]
    arg3 = sys.argv[3]
    main(arg1, arg2, arg3)
    return None
好的,我们来梳理分析这两段分别由Python和C#编写的代码。这看起来是一个典型的CTF(Capture The Flag)逆向工程题目,其中C#程序是主验证逻辑,它调用了一个内嵌的、由Python脚本编译成的程序来执行核心计算。
 
### 整体逻辑概览
 
C#程序是一个注册机验证程序(CrackMe)。它要求用户输入`Name`(用户名)和`SN`(序列号)。程序通过以下步骤来验证`SN`是否正确:
1.  根据输入的`Name`计算出一个数值,作为**被除数**
2.  对输入的`SN`进行一种自定义格式的解码,得到一个很长的数字字符串,作为**除数**
3.  从程序的资源中释放一个内嵌的.exe文件(实际上是由提供的Python脚本编译而成),并调用它来执行高精度除法运算(被除数 / 除数),得到一个非常长的小数结果(商)。
4.  对这个商进行分段,并对这些分段进行一个特定的数学关系验证。
5.  只有当这个数学关系在指定的循环次数下成立时,验证才算成功。
 
下面我们分步进行详细分析。
 
---
 
### 第一部分:Python脚本分析 (`reciprocal.py`)
 
这是被内嵌到C#程序中的计算核心。
 
**代码**:
---python
from decimal import Decimal, getcontext
import os
import sys
 
def reciprocal(m, n, prec):
    getcontext().prec = int(prec)  # 设置精度
    result = Decimal(m) / Decimal(n) # 执行高精度除法
    return result
 
def main(arg1, arg2, arg3):
    sys.set_int_max_str_digits(int(arg3)) # 设置整数转字符串的最大位数限制
    reciprocal_value = reciprocal(arg1, arg2, arg3)
    print(reciprocal_value) # 将结果输出到标准输出
 
if __name__ == '__main__':
    arg1 = sys.argv[1] # 参数1: 被除数
    arg2 = sys.argv[2] # 参数2: 除数
    arg3 = sys.argv[3] # 参数3: 精度
    main(arg1, arg2, arg3)
---
 
**功能分析**:
*   **用途**: 这个脚本的唯一功能是执行高精度除法。
*   **输入**: 它接收三个命令行参数:
    *   `arg1 (m)`: 被除数。
    *   `arg2 (n)`: 除数。
    *   `arg3 (prec)`: 运算的精度(小数点后的位数)。
*   **核心库**: 使用Python的`decimal`库,这个库专门用于进行需要极高精度的十进制浮点数运算。
*   **输出**: 脚本将计算出的商(一个很长的浮点数)打印到标准输出流(`stdout`)。
*   **与C#的关联**: C#程序会调用这个脚本(编译后的.exe),并从其标准输出中读取运算结果。`sys.set_int_max_str_digits`的设置是为了防止在处理超长数字时Python抛出异常。
 
---
 
### 第二部分:C#代码分析
 
这是主要的验证程序,包含了所有的逻辑分支。我将按功能模块进行解析。
 
#### 1. 主程序入口 (`KX_UHJvZ3JhbQ雪雪.KX_TWFpbg雪雪`)
 
这是程序的起点,控制着整个验证流程。
 
**流程**:
1.  提示用户输入`Name`和`SN`。
2.  **用户名处理**:
    *   将输入的`Name`字符串转换为UTF-8字节数组。
    *   对所有字节进行异或(XOR)运算,得到一个最终的字节`b`。
    *   计算 `num = b % 64`。
    *   如果`num`是偶数,则将其加1,确保`num`为一个奇数。
    *   这个`num`将作为高精度除法的**被除数** (`stringBuilder2`)。
3.  **序列号处理**:
    *   调用`KX_RGVjb2Rl(text2)`方法解码用户输入的`SN`。
    *   解码后的结果将作为高精度除法的**除数** (`stringBuilder`)。
4.  **执行高精度除法**:
    *   实例化 `KX_SGlnaFByZWNpc2lvbkRpdmlzaW9uMg雪雪` 类。
    *   设置精度 `Precision = (2040 + 1) * 2025 = 4133025`。这是一个极高的精度。
    *   调用 `KX_TXlEaXZpZGU雪` 方法,传入被除数和除数,执行除法并获取商。
5.  **验证商**:
    *   对得到的商(`Quotient`)进行循环验证。
    *   如果验证通过,则显示 "Congratulations!"
    *   否则,显示 "Failed!"
 
#### 2. SN解码函数 (`KX_UHJvZ3JhbQ雪雪.KX_RGVjb2Rl`)
 
这个函数实现了一种自定义的**游程编码(Run-Length Encoding, RLE)**的解码方案。
 
**解码规则**:
*   输入的`SN`由数字、`l` 和 `.` 组成。
*   编码格式为 `[数字字符][重复次数]l`。例如,`512l` 表示字符 '5' 重复 12 次。
*   `.` 字符可以直接出现在序列中,代表小数点。
*   函数会遍历输入的`SN`字符串,根据规则构建出解码后的原始数字字符串。
*   例如,一个SN `91l82l.35l` 会被解码为 `988.33333`。
*   函数包含大量的合法性检查,如SN不能以`0`开头,不能以`.`结尾,格式必须正确等。如果格式错误,程序会直接退出。
 
#### 3. 高精度除法调用 (`KX_SGlnaFByZWNpc2lvbkRpdmlzaW9uMg雪雪.KX_TXlEaXZpZGU雪`)
 
这个方法是C#与Python脚本交互的桥梁。
 
**流程**:
1.  调用`KX_RXh0cmFjdFJlc291cmNlVG9UZW1w()`方法。
2.  `KX_RXh0cmFjdFJlc291cmNlVG9UZW1w()`方法从程序资源中读取一个名为 `_20250805Calc` 的字节数组(这其实就是Python脚本编译后的.exe文件),并将其写入系统临时目录下的一个随机命名的.exe文件中。
3.  使用`Process.Start`启动这个刚创建的.exe文件。
4.  通过命令行参数,将被除数、除数和精度传递给这个.exe进程。
5.  设置`RedirectStandardOutput = true`来捕获子进程的输出。
6.  调用 `process.StandardOutput.ReadToEnd()` 读取Python脚本打印的计算结果。
7.  子进程结束后,删除临时的.exe文件。
8.  对返回的结果进行简单处理,然后存储在`Quotient`属性中。
 
#### 4. 验证逻辑 (`KX_UHJvZ3JhbQ雪雪.KX_VmVyaWZ5U2lnbmFs` 和主循环)
 
这是算法的核心,决定了什么样的SN才是有效的。
 
**流程**:
1.  主函数中设置了两个关键常量:`num2 = 2040` 和 `num3 = 2025`。
2.  程序将除法得到的商(一个极长的数字字符串)看作是由多个长度为 `num3`(2025)的数字块组成的序列。
3.  一个`while (j < num2)`循环从`j=0`开始迭代。
4.  在每次循环中,程序取出**连续的四个**数字块:
    *   `a = Quotient.Substring(j * num3, num3)`
    *   `b = Quotient.Substring((j + 1) * num3, num3)`
    *   `c = Quotient.Substring((j + 2) * num3, num3)`
    *   `d = Quotient.Substring((j + 3) * num3, num3)`
5.  调用`KX_VmVyaWZ5U2lnbmFs(a, b, c, d)`方法进行验证。
6.  `KX_VmVyaWZ5U2lnbmFs` **的验证规则是**: `d == (a * 5) + (b * 8) + (c * 9)`。
    *   注意:这里的加法和乘法都是通过`KX_QWRkU3RyaW5ncw雪雪`(大数加法)和`KX_TXVsdGlTdHJpbmdz`(大数乘法)实现的字符串运算。
7.  **循环成功/失败条件**:
    *   如果`KX_VmVyaWZ5U2lnbmFs`返回`true`,则`j++`,循环继续。
    *   如果`KX_VmVyaWZ5U2lnbmFs`返回`false`,则`j++`,然后`break`,循环终止。
    *   循环结束后,程序检查`if (j != num2 - 3)`,即 `if (j != 2037)`。
    *   **这意味着,要通过验证,循环必须在 `j` 的值正好是 `2037` 时结束。**
    *   结合循环逻辑,这要求:
        *   对于 `j = 0, 1, ..., 2035`,`KX_VmVyaWZ5U2lnbmFs`的校验**必须全部成功**
        *   当 `j` 增加到 `2036` 时,`KX_VmVyaWZ5U2lnbmFs`的校验**必须失败**。此时,`j`会再加1变为`2037`,然后循环中断。最终 `j == 2037`,验证成功。
 
### 总结
 
这个CTF题目的完整验证算法如下:
 
1.  **输入**: 用户名 `Name` 和序列号 `SN`。
2.  **生成被除数**: `dividend = f(Name)`,其中 `f` 是对`Name`各位字节进行异或,对结果模64,并确保为奇数的函数。
3.  **解码除数**: `divisor = Decode(SN)`,其中 `Decode` 是一个自定义的游程编码解码函数。
4.  **执行计算**: 使用外部Python程序计算 `quotient = dividend / divisor`,精度为 `4133025`。
5.  **校验**:
    *   将`quotient`的小数部分按每`2025`位进行切片,得到一个数组 `Q`。
    *   校验 `Q[i+3] == (Q[i] * 5) + (Q[i+1] * 8) + (Q[i+2] * 9)` 必须对 `i = 0` 到 `2035` 成立。
    *   该校验在 `i = 2036` 时必须不成立。
 
要编写一个注册机(Keygen),就需要逆向这个过程:首先构造一个满足校验条件的超长小数,然后根据选择的用户名(决定了被除数),反向计算出对应的除数,最后将这个除数用游程编码进行编码,得到最终的`SN`。
好的,我们来梳理分析这两段分别由Python和C#编写的代码。这看起来是一个典型的CTF(Capture The Flag)逆向工程题目,其中C#程序是主验证逻辑,它调用了一个内嵌的、由Python脚本编译成的程序来执行核心计算。
 
### 整体逻辑概览
 
C#程序是一个注册机验证程序(CrackMe)。它要求用户输入`Name`(用户名)和`SN`(序列号)。程序通过以下步骤来验证`SN`是否正确:
1.  根据输入的`Name`计算出一个数值,作为**被除数**
2.  对输入的`SN`进行一种自定义格式的解码,得到一个很长的数字字符串,作为**除数**
3.  从程序的资源中释放一个内嵌的.exe文件(实际上是由提供的Python脚本编译而成),并调用它来执行高精度除法运算(被除数 / 除数),得到一个非常长的小数结果(商)。
4.  对这个商进行分段,并对这些分段进行一个特定的数学关系验证。
5.  只有当这个数学关系在指定的循环次数下成立时,验证才算成功。
 
下面我们分步进行详细分析。
 
---
 
### 第一部分:Python脚本分析 (`reciprocal.py`)
 
这是被内嵌到C#程序中的计算核心。
 
**代码**:
---python
from decimal import Decimal, getcontext
import os
import sys
 
def reciprocal(m, n, prec):
    getcontext().prec = int(prec)  # 设置精度
    result = Decimal(m) / Decimal(n) # 执行高精度除法
    return result
 
def main(arg1, arg2, arg3):
    sys.set_int_max_str_digits(int(arg3)) # 设置整数转字符串的最大位数限制
    reciprocal_value = reciprocal(arg1, arg2, arg3)
    print(reciprocal_value) # 将结果输出到标准输出
 
if __name__ == '__main__':
    arg1 = sys.argv[1] # 参数1: 被除数
    arg2 = sys.argv[2] # 参数2: 除数
    arg3 = sys.argv[3] # 参数3: 精度
    main(arg1, arg2, arg3)
---
 
**功能分析**:
*   **用途**: 这个脚本的唯一功能是执行高精度除法。
*   **输入**: 它接收三个命令行参数:
    *   `arg1 (m)`: 被除数。
    *   `arg2 (n)`: 除数。
    *   `arg3 (prec)`: 运算的精度(小数点后的位数)。
*   **核心库**: 使用Python的`decimal`库,这个库专门用于进行需要极高精度的十进制浮点数运算。
*   **输出**: 脚本将计算出的商(一个很长的浮点数)打印到标准输出流(`stdout`)。
*   **与C#的关联**: C#程序会调用这个脚本(编译后的.exe),并从其标准输出中读取运算结果。`sys.set_int_max_str_digits`的设置是为了防止在处理超长数字时Python抛出异常。
 
---
 
### 第二部分:C#代码分析
 
这是主要的验证程序,包含了所有的逻辑分支。我将按功能模块进行解析。
 
#### 1. 主程序入口 (`KX_UHJvZ3JhbQ雪雪.KX_TWFpbg雪雪`)
 
这是程序的起点,控制着整个验证流程。
 
**流程**:
1.  提示用户输入`Name`和`SN`。
2.  **用户名处理**:
    *   将输入的`Name`字符串转换为UTF-8字节数组。
    *   对所有字节进行异或(XOR)运算,得到一个最终的字节`b`。
    *   计算 `num = b % 64`。
    *   如果`num`是偶数,则将其加1,确保`num`为一个奇数。
    *   这个`num`将作为高精度除法的**被除数** (`stringBuilder2`)。
3.  **序列号处理**:
    *   调用`KX_RGVjb2Rl(text2)`方法解码用户输入的`SN`。
    *   解码后的结果将作为高精度除法的**除数** (`stringBuilder`)。
4.  **执行高精度除法**:
    *   实例化 `KX_SGlnaFByZWNpc2lvbkRpdmlzaW9uMg雪雪` 类。
    *   设置精度 `Precision = (2040 + 1) * 2025 = 4133025`。这是一个极高的精度。
    *   调用 `KX_TXlEaXZpZGU雪` 方法,传入被除数和除数,执行除法并获取商。
5.  **验证商**:
    *   对得到的商(`Quotient`)进行循环验证。
    *   如果验证通过,则显示 "Congratulations!"
    *   否则,显示 "Failed!"
 
#### 2. SN解码函数 (`KX_UHJvZ3JhbQ雪雪.KX_RGVjb2Rl`)
 
这个函数实现了一种自定义的**游程编码(Run-Length Encoding, RLE)**的解码方案。
 
**解码规则**:
*   输入的`SN`由数字、`l` 和 `.` 组成。
*   编码格式为 `[数字字符][重复次数]l`。例如,`512l` 表示字符 '5' 重复 12 次。
*   `.` 字符可以直接出现在序列中,代表小数点。
*   函数会遍历输入的`SN`字符串,根据规则构建出解码后的原始数字字符串。
*   例如,一个SN `91l82l.35l` 会被解码为 `988.33333`。
*   函数包含大量的合法性检查,如SN不能以`0`开头,不能以`.`结尾,格式必须正确等。如果格式错误,程序会直接退出。
 
#### 3. 高精度除法调用 (`KX_SGlnaFByZWNpc2lvbkRpdmlzaW9uMg雪雪.KX_TXlEaXZpZGU雪`)
 
这个方法是C#与Python脚本交互的桥梁。
 
**流程**:
1.  调用`KX_RXh0cmFjdFJlc291cmNlVG9UZW1w()`方法。
2.  `KX_RXh0cmFjdFJlc291cmNlVG9UZW1w()`方法从程序资源中读取一个名为 `_20250805Calc` 的字节数组(这其实就是Python脚本编译后的.exe文件),并将其写入系统临时目录下的一个随机命名的.exe文件中。
3.  使用`Process.Start`启动这个刚创建的.exe文件。
4.  通过命令行参数,将被除数、除数和精度传递给这个.exe进程。
5.  设置`RedirectStandardOutput = true`来捕获子进程的输出。
6.  调用 `process.StandardOutput.ReadToEnd()` 读取Python脚本打印的计算结果。
7.  子进程结束后,删除临时的.exe文件。
8.  对返回的结果进行简单处理,然后存储在`Quotient`属性中。
 
#### 4. 验证逻辑 (`KX_UHJvZ3JhbQ雪雪.KX_VmVyaWZ5U2lnbmFs` 和主循环)
 
这是算法的核心,决定了什么样的SN才是有效的。
 
**流程**:
1.  主函数中设置了两个关键常量:`num2 = 2040` 和 `num3 = 2025`。
2.  程序将除法得到的商(一个极长的数字字符串)看作是由多个长度为 `num3`(2025)的数字块组成的序列。
3.  一个`while (j < num2)`循环从`j=0`开始迭代。
4.  在每次循环中,程序取出**连续的四个**数字块:
    *   `a = Quotient.Substring(j * num3, num3)`
    *   `b = Quotient.Substring((j + 1) * num3, num3)`
    *   `c = Quotient.Substring((j + 2) * num3, num3)`
    *   `d = Quotient.Substring((j + 3) * num3, num3)`
5.  调用`KX_VmVyaWZ5U2lnbmFs(a, b, c, d)`方法进行验证。
6.  `KX_VmVyaWZ5U2lnbmFs` **的验证规则是**: `d == (a * 5) + (b * 8) + (c * 9)`。
    *   注意:这里的加法和乘法都是通过`KX_QWRkU3RyaW5ncw雪雪`(大数加法)和`KX_TXVsdGlTdHJpbmdz`(大数乘法)实现的字符串运算。
7.  **循环成功/失败条件**:
    *   如果`KX_VmVyaWZ5U2lnbmFs`返回`true`,则`j++`,循环继续。
    *   如果`KX_VmVyaWZ5U2lnbmFs`返回`false`,则`j++`,然后`break`,循环终止。
    *   循环结束后,程序检查`if (j != num2 - 3)`,即 `if (j != 2037)`。
    *   **这意味着,要通过验证,循环必须在 `j` 的值正好是 `2037` 时结束。**
    *   结合循环逻辑,这要求:
        *   对于 `j = 0, 1, ..., 2035`,`KX_VmVyaWZ5U2lnbmFs`的校验**必须全部成功**
        *   当 `j` 增加到 `2036` 时,`KX_VmVyaWZ5U2lnbmFs`的校验**必须失败**。此时,`j`会再加1变为`2037`,然后循环中断。最终 `j == 2037`,验证成功。
 
### 总结
 
这个CTF题目的完整验证算法如下:
 
1.  **输入**: 用户名 `Name` 和序列号 `SN`。
2.  **生成被除数**: `dividend = f(Name)`,其中 `f` 是对`Name`各位字节进行异或,对结果模64,并确保为奇数的函数。
3.  **解码除数**: `divisor = Decode(SN)`,其中 `Decode` 是一个自定义的游程编码解码函数。
4.  **执行计算**: 使用外部Python程序计算 `quotient = dividend / divisor`,精度为 `4133025`。
5.  **校验**:
    *   将`quotient`的小数部分按每`2025`位进行切片,得到一个数组 `Q`。
    *   校验 `Q[i+3] == (Q[i] * 5) + (Q[i+1] * 8) + (Q[i+2] * 9)` 必须对 `i = 0` 到 `2035` 成立。
    *   该校验在 `i = 2036` 时必须不成立。
 
要编写一个注册机(Keygen),就需要逆向这个过程:首先构造一个满足校验条件的超长小数,然后根据选择的用户名(决定了被除数),反向计算出对应的除数,最后将这个除数用游程编码进行编码,得到最终的`SN`。
好的,我们来基于之前的分析,使用Python为用户名 `FCCE23A5A04E0EA5` 编写一个注册机。
 
### 注册机构建思路
 
注册机的任务是逆向执行验证过程:
1.  **计算被除数 (`dividend`)**: 根据给定的用户名 `FCCE23A5A04E0EA5`,执行与C#程序中相同的算法。
2.  **构造合法的商 (`quotient`)**: 我们需要主动构造一个满足 `d = 5*a + 8*b + 9*c` 关系式的、非常长的小数。这个关系是一个线性递推关系。我们可以选择初始的几个数据块,然后递推出后续的所有块,并在最后故意制造一个不匹配的块来满足程序的退出条件。
3.  **计算除数 (`divisor`)**: 有了被除数和商,我们可以通过 `divisor = dividend / quotient` 来反推出所需的除数。这同样需要高精度计算。
4.  **编码序列号 (`SN`)**: 将计算出的大整数除数,使用程序自定义的游程编码(Run-Length Encoding)格式进行编码,生成最终的序列号。
 
下面是完整的Python实现代码。
 
---python
import sys
import itertools
from decimal import Decimal, getcontext
 
# --- 核心参数 (从C#代码中分析得出) ---
# 商被分割成多个块,每个块的长度
BLOCK_SIZE = 2025
# 商的总块数,循环到 num2-4
# 验证逻辑是 j 从 0 到 2035 成功, j=2036 失败, j最后等于 2037 (num2 - 3)
NUM_BLOCKS = 2040
# 用于高精度计算的精度设置
# 精度需要大于总小数位数 (NUM_BLOCKS * BLOCK_SIZE)
PRECISION = (NUM_BLOCKS * BLOCK_SIZE) + 10 # 留一些余量
 
def calculate_dividend(username: str) -> int:
    """
    根据用户名计算被除数。
    算法与C#程序中的逻辑完全一致。
    """
    print(f"[*] 步骤 1: 根据用户名 '{username}' 计算被除数...")
     
    encoded_username = username.encode('utf-8')
     
    xor_result = 0
    for byte in encoded_username:
        xor_result ^= byte
         
    dividend = xor_result % 64
     
    if dividend % 2 == 0:
        dividend += 1
         
    print(f"    - UTF-8 字节异或结果: {xor_result}")
    print(f"    - 模 64 并确保为奇数后,得到被除数: {dividend}\n")
    return dividend
 
def construct_valid_quotient() -> Decimal:
    """
    构造一个满足验证条件的、极长的商 (quotient)。
    Q[i+3] = 5*Q[i] + 8*Q[i+1] + 9*Q[i+2]
    """
    print(f"[*] 步骤 2: 构造满足递推关系的商 (Quotient)...")
     
    # 设置Python处理大整数转字符串的限制
    sys.set_int_max_str_digits(PRECISION)
     
    Q = [0] * NUM_BLOCKS
     
    # 1. 初始化前三个块 (可以任意选择,但为了简单和避免进位复杂性,选择小数字)
    #    并将其格式化为指定长度的字符串 (前面补零)
    Q[0] = 1
    Q[1] = 2
    Q[2] = 3
     
    print(f"    - 初始化前3个数据块: Q[0]=1, Q[1]=2, Q[2]=3")
 
    # 2. 使用递推关系生成后续的块,直到倒数第二个块
    #    循环必须在 j=2036 时失败,这意味着 Q[2036]...Q[2039] 这一组校验不通过
    #    所以我们需要生成到 Q[0]...Q[2038] 都是正确的
    for i in range(NUM_BLOCKS - 4): # 保证 Q[i+3] 不会越界
        # Q[i+3] = 5*Q[i] + 8*Q[i+1] + 9*Q[i+2]
        next_val = 5 * Q[i] + 8 * Q[i+1] + 9 * Q[i+2]
        Q[i+3] = next_val
 
    print(f"    - 已使用递推公式生成 {NUM_BLOCKS - 1} 个数据块...")
 
    # 3. 构造最后一个数据块,使其校验失败
    #    我们先计算出正确的 Q[2039],然后给它加1,使其必然不满足等式
    correct_last_val = 5 * Q[NUM_BLOCKS - 4] + 8 * Q[NUM_BLOCKS - 3] + 9 * Q[NUM_BLOCKS - 2]
    Q[NUM_BLOCKS - 1] = correct_last_val + 1
     
    print(f"    - 故意构造最后一个数据块 Q[{NUM_BLOCKS - 1}] 使验证在最后一步失败。")
 
    # 4. 将所有块拼接成一个巨大的小数字符串
    #    格式为 "0.Q[0]Q[1]Q[2]..."
    #    每个Q[i]都需要补零到 BLOCK_SIZE 的长度
    fractional_part = "".join([str(val).zfill(BLOCK_SIZE) for val in Q])
    quotient_str = "0." + fractional_part
     
    print(f"    - 拼接完成,商的前50位为: {quotient_str[:50]}...\n")
     
    return Decimal(quotient_str)
 
def calculate_divisor(dividend: int, quotient: Decimal) -> str:
    """
    根据被除数和商,计算除数。
    divisor = dividend / quotient
    """
    print(f"[*] 步骤 3: 使用高精度计算反推除数 (Divisor)...")
     
    # 设置 decimal 库的精度
    getcontext().prec = PRECISION
     
    divisor_decimal = Decimal(dividend) / quotient
     
    # 四舍五入到最接近的整数,并转为字符串
    # 使用 quantize(Decimal('1')) 来进行标准的四舍五入
    divisor_int_str = str(divisor_decimal.quantize(Decimal('1')))
     
    print(f"    - 计算得到的除数 (前50位): {divisor_int_str[:50]}...")
    print(f"    - 除数总长度: {len(divisor_int_str)}\n")
     
    return divisor_int_str
 
def encode_sn(divisor_str: str) -> str:
    """
    将大整数除数进行游程编码 (Run-Length Encoding),生成最终的SN。
    格式: <数字><重复次数>l,例如 "222" -> "23l"
    """
    print(f"[*] 步骤 4: 对除数进行游程编码生成SN...")
     
    if not divisor_str:
        return ""
         
    parts = []
    # 使用 itertools.groupby 来高效地对连续相同字符进行分组
    for digit, group in itertools.groupby(divisor_str):
        count = len(list(group))
        parts.append(f"{digit}{count}l")
         
    sn = "".join(parts)
    print(f"    - 编码完成!SN (前100位): {sn[:100]}...\n")
    return sn
 
def main():
    username = "FCCE23A5A04E0EA5"
     
    # 步骤 1: 计算被除数
    dividend = calculate_dividend(username)
     
    # 步骤 2: 构造商
    quotient = construct_valid_quotient()
     
    # 步骤 3: 计算除数
    divisor = calculate_divisor(dividend, quotient)
     
    # 步骤 4: 编码SN
    serial_number = encode_sn(divisor)
     
    print("="*50)
    print("注册机生成成功!")
    print(f"用户名: {username}")
    print(f"序列号 (SN): {serial_number}")
    print("="*50)
 
if __name__ == "__main__":
    main()
---
 
### 代码逻辑解释
 
1.  **`calculate_dividend`**: 完全复刻了C#代码中的逻辑,对用户名的UTF-8字节进行异或,然后对结果模64并保证为奇数。
2.  **`construct_valid_quotient`**: 这是注册机的核心。
    *   我们选择了最简单的`1, 2, 3`作为递推序列的初始值。
    *   然后使用 `Q[i+3] = 5*Q[i] + 8*Q[i+1] + 9*Q[i+2]` 公式,循环生成了后续的数据块,保证了从`j=0`到`j=2035`的验证都能通过。
    *   最关键的一步是,我们计算出按公式本应生成的最后一个数据块 `Q[2039]` 的值,然后给它加1。这确保了当验证程序检查到 `j=2036` 这一组数据时,等式不再成立,从而满足程序“在最后一步失败”的成功条件。
    *   最后,将所有这些整数块补零并拼接成一个巨大的`Decimal`对象。
3.  **`calculate_divisor`**: 利用Python强大的`decimal`库,设置了超高精度,然后执行 `被除数 / 商` 的运算,反推出除数。结果需要四舍五入为整数。
4.  **`encode_sn`**: 这个函数是C#中`KX_RGVjb2Rl`的逆过程。它使用`itertools.groupby`,这是一个非常适合处理游程编码的工具,能将连续的相同字符分组。然后将每组的字符和数量格式化为`"数字+数量+l"`的形式,最后拼接起来得到SN。
 
将以上代码保存为`.py`文件并运行,即可得到用户名 `FCCE23A5A04E0EA5` 对应的有效序列号。
好的,我们来基于之前的分析,使用Python为用户名 `FCCE23A5A04E0EA5` 编写一个注册机。
 
### 注册机构建思路
 
注册机的任务是逆向执行验证过程:
1.  **计算被除数 (`dividend`)**: 根据给定的用户名 `FCCE23A5A04E0EA5`,执行与C#程序中相同的算法。
2.  **构造合法的商 (`quotient`)**: 我们需要主动构造一个满足 `d = 5*a + 8*b + 9*c` 关系式的、非常长的小数。这个关系是一个线性递推关系。我们可以选择初始的几个数据块,然后递推出后续的所有块,并在最后故意制造一个不匹配的块来满足程序的退出条件。
3.  **计算除数 (`divisor`)**: 有了被除数和商,我们可以通过 `divisor = dividend / quotient` 来反推出所需的除数。这同样需要高精度计算。
4.  **编码序列号 (`SN`)**: 将计算出的大整数除数,使用程序自定义的游程编码(Run-Length Encoding)格式进行编码,生成最终的序列号。
 
下面是完整的Python实现代码。
 
---python
import sys
import itertools
from decimal import Decimal, getcontext
 
# --- 核心参数 (从C#代码中分析得出) ---
# 商被分割成多个块,每个块的长度
BLOCK_SIZE = 2025
# 商的总块数,循环到 num2-4
# 验证逻辑是 j 从 0 到 2035 成功, j=2036 失败, j最后等于 2037 (num2 - 3)
NUM_BLOCKS = 2040
# 用于高精度计算的精度设置
# 精度需要大于总小数位数 (NUM_BLOCKS * BLOCK_SIZE)
PRECISION = (NUM_BLOCKS * BLOCK_SIZE) + 10 # 留一些余量
 
def calculate_dividend(username: str) -> int:
    """
    根据用户名计算被除数。
    算法与C#程序中的逻辑完全一致。
    """
    print(f"[*] 步骤 1: 根据用户名 '{username}' 计算被除数...")
     
    encoded_username = username.encode('utf-8')
     
    xor_result = 0
    for byte in encoded_username:
        xor_result ^= byte
         
    dividend = xor_result % 64
     
    if dividend % 2 == 0:
        dividend += 1
         
    print(f"    - UTF-8 字节异或结果: {xor_result}")
    print(f"    - 模 64 并确保为奇数后,得到被除数: {dividend}\n")
    return dividend
 
def construct_valid_quotient() -> Decimal:
    """
    构造一个满足验证条件的、极长的商 (quotient)。
    Q[i+3] = 5*Q[i] + 8*Q[i+1] + 9*Q[i+2]
    """
    print(f"[*] 步骤 2: 构造满足递推关系的商 (Quotient)...")
     
    # 设置Python处理大整数转字符串的限制
    sys.set_int_max_str_digits(PRECISION)
     
    Q = [0] * NUM_BLOCKS
     
    # 1. 初始化前三个块 (可以任意选择,但为了简单和避免进位复杂性,选择小数字)
    #    并将其格式化为指定长度的字符串 (前面补零)
    Q[0] = 1
    Q[1] = 2
    Q[2] = 3
     
    print(f"    - 初始化前3个数据块: Q[0]=1, Q[1]=2, Q[2]=3")
 
    # 2. 使用递推关系生成后续的块,直到倒数第二个块
    #    循环必须在 j=2036 时失败,这意味着 Q[2036]...Q[2039] 这一组校验不通过
    #    所以我们需要生成到 Q[0]...Q[2038] 都是正确的
    for i in range(NUM_BLOCKS - 4): # 保证 Q[i+3] 不会越界
        # Q[i+3] = 5*Q[i] + 8*Q[i+1] + 9*Q[i+2]
        next_val = 5 * Q[i] + 8 * Q[i+1] + 9 * Q[i+2]
        Q[i+3] = next_val
 
    print(f"    - 已使用递推公式生成 {NUM_BLOCKS - 1} 个数据块...")
 
    # 3. 构造最后一个数据块,使其校验失败
    #    我们先计算出正确的 Q[2039],然后给它加1,使其必然不满足等式
    correct_last_val = 5 * Q[NUM_BLOCKS - 4] + 8 * Q[NUM_BLOCKS - 3] + 9 * Q[NUM_BLOCKS - 2]
    Q[NUM_BLOCKS - 1] = correct_last_val + 1
     
    print(f"    - 故意构造最后一个数据块 Q[{NUM_BLOCKS - 1}] 使验证在最后一步失败。")
 
    # 4. 将所有块拼接成一个巨大的小数字符串
    #    格式为 "0.Q[0]Q[1]Q[2]..."
    #    每个Q[i]都需要补零到 BLOCK_SIZE 的长度
    fractional_part = "".join([str(val).zfill(BLOCK_SIZE) for val in Q])
    quotient_str = "0." + fractional_part
     
    print(f"    - 拼接完成,商的前50位为: {quotient_str[:50]}...\n")
     
    return Decimal(quotient_str)
 
def calculate_divisor(dividend: int, quotient: Decimal) -> str:
    """
    根据被除数和商,计算除数。
    divisor = dividend / quotient
    """
    print(f"[*] 步骤 3: 使用高精度计算反推除数 (Divisor)...")
     
    # 设置 decimal 库的精度
    getcontext().prec = PRECISION
     
    divisor_decimal = Decimal(dividend) / quotient
     
    # 四舍五入到最接近的整数,并转为字符串
    # 使用 quantize(Decimal('1')) 来进行标准的四舍五入
    divisor_int_str = str(divisor_decimal.quantize(Decimal('1')))
     
    print(f"    - 计算得到的除数 (前50位): {divisor_int_str[:50]}...")
    print(f"    - 除数总长度: {len(divisor_int_str)}\n")
     
    return divisor_int_str
 
def encode_sn(divisor_str: str) -> str:
    """
    将大整数除数进行游程编码 (Run-Length Encoding),生成最终的SN。
    格式: <数字><重复次数>l,例如 "222" -> "23l"
    """
    print(f"[*] 步骤 4: 对除数进行游程编码生成SN...")
     
    if not divisor_str:
        return ""
         
    parts = []
    # 使用 itertools.groupby 来高效地对连续相同字符进行分组
    for digit, group in itertools.groupby(divisor_str):
        count = len(list(group))
        parts.append(f"{digit}{count}l")
         
    sn = "".join(parts)
    print(f"    - 编码完成!SN (前100位): {sn[:100]}...\n")
    return sn
 
def main():
    username = "FCCE23A5A04E0EA5"
     
    # 步骤 1: 计算被除数

传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2025-9-2 10:26 被0x指纹编辑 ,原因:
收藏
免费 1
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回