CTF逆向实战:魔改RC4、TEA、AES算法识别与破解全解析

📅 2026/7/4 18:32:01
CTF逆向实战:魔改RC4、TEA、AES算法识别与破解全解析
1. 项目概述当标准算法穿上“马甲”在CTF逆向赛题里尤其是那些中等偏上难度的题目出题人最喜欢干的一件事就是给经典的加密算法“穿马甲”。你可能会在IDA Pro里看到一堆似曾相识的循环、异或和移位操作感觉像是RC4又有点像TEA仔细一看常量好像不对S盒也长得有点怪。这就是所谓的“魔改”算法——它保留了原算法的核心流程和结构但修改了其中的关键参数如常量、初始向量、S盒、运算细节如加减法替换、轮数变化或者整体结构如混合多种算法。直接套用标准算法的解密脚本大概率会碰一鼻子灰。我参加过不少比赛也出过一些题目深知这里面的门道。魔改的目的一是增加逆向分析的难度和趣味性二是考察选手对算法原理的真正理解而不是只会调用现成的库。今天我们就聚焦于CTF中最常被魔改的“三巨头”流密码RC4、分组密码TEA及其变种XXTEA、以及分组密码AES。我会带你从特征识别开始一步步拆解魔改点并给出通用的分析思路和破解策略。无论你是刚入门逆向的新手还是想深化密码学理解的进阶者这篇实战总结都能让你在下次遇到“熟悉的陌生人”时心里有底手上有招。2. 核心思路从特征识别到针对性破解面对一个魔改算法盲目跟踪数据流往往会陷入细节的泥潭。一个高效的逆向分析流程应该是结构化的。我的思路通常分为四步快速特征扫描 - 算法家族归类 - 定位魔改点 - 构造解密脚本。整个过程就像侦探破案先确定“死者”算法的基本身份再勘查“现场”二进制代码寻找被篡改的痕迹最后还原“真相”原始数据。2.1 第一步静态特征快速扫描在拿到一个二进制文件通常是ELF或PE后我不会立刻深入每一行汇编。而是先用一些工具进行高层面的扫描寻找密码学算法的“指纹”。1. 字符串与常量搜索这是最快的一招。很多算法会使用独特的常量。打开IDA Pro或使用strings命令搜索以下特征TEA系列查找魔数0x9E3779B9黄金分割率衍生常数。这是TEA和XTEA算法的核心常量。如果找到基本可以锁定TEA家族。XXTEA也使用这个常量。AES查找AES的S盒Substitution Box和逆S盒。它们是256字节的固定表。在IDA的十六进制视图你可以尝试搜索一段连续的、看起来随机但固定的256字节数据。或者搜索AES的轮常量Rcon一个10字节左右的数组。MD5/SHA1虽然本次主题不是哈希但常一起出现。可以搜索其初始化向量IV如MD5的0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476。Base64表标准的ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789/也是明显标志。注意出题人可能会修改这些常量。所以没搜到标准常量不代表没有搜到了标准常量也要警惕是否是陷阱比如用了常量但算法流程被改。2. 函数名与导入表如果程序没有剥离符号Stripped那太幸运了。直接搜索RC4_、AES_、TEA_等函数名。即使剥离了符号也可以查看导入表如果引用了OpenSSL或libcrypto库中的相关函数如RC4_set_key,AES_set_encrypt_key那直接指明了方向。3. 代码模式识别IDA Pro眼力训练对于完全混淆的程序就需要练就“火眼金睛”。一些常见的模式大量循环与查表操作可能涉及S盒替换如AES。固定的循环次数AES-128是10轮TEA通常是64轮32次循环每次循环处理两轮。在反汇编代码中寻找for (i 0; i 64; i)或类似结构的循环。异或、加法、移位操作的密集组合这是TEA的典型特征。密钥调度算法Key Schedule看到一个函数在根据初始密钥扩展出一大串轮密钥Round Key这极有可能是AES或类似的分组密码。2.2 第二步动态调试验证猜想静态分析有了初步猜想后必须用动态调试来验证。我会使用GDB/LLDBLinux或x64dbg/OllyDbgWindows在疑似加密函数处下断点。关键操作输入输出追踪准备一段已知的、有特点的明文比如全零”\x00\x00…”或者可打印字符串”AAAABBBBCCCCDDDD”输入程序观察经过目标函数后密文是什么。反过来输入密文观察解密后的输出。观察数据流重点关注函数参数和返回值。哪个缓冲区是输入哪个是输出密钥存在哪里是硬编码在数据段还是动态生成的验证算法流程单步跟踪Step Into进入函数内部观察其操作是否与你猜测的算法如RC4的初始化S盒和伪随机生成流程匹配。可以对照标准算法的C实现代码在调试器中一步步比对。动态调试能最直观地告诉你“这里是不是加密函数”以及“它大概在干什么”。2.3 第三步定位与逆向魔改点确认了算法家族后真正的挑战开始魔改在哪里我通常会从以下几个层面进行对比分析常量对比将IDA中提取的常量与标准算法的常量进行逐字节对比。TEA的0x9E3779B9被改成了什么AES的S盒是否被重新排列或替换运算替换标准算法中的加法是否被换成了减法或乘法异或操作是否被与/或操作替代循环左移是否变成了循环右移例如TEA的核心运算sum delta; v0 ((v14) k0) ^ (v1 sum) ^ ((v15) k1);中的加法和异或可能被改动。结构微调轮数是否增加或减少AES的MixColumns步骤被移除了RC4的密钥调度算法KSA或伪随机生成算法PRGA的循环顺序变了组合与嵌套是否采用了“AES加密后RC4再加密”的套娃模式或者将TEA的加密结果作为AES的密钥这种结构上的魔改需要理清数据流的先后顺序。一个实用的技巧编写IDAPython脚本进行自动化比对。例如当你怀疑是魔改TEA时可以写一个脚本在二进制中搜索类似(v1 4) k0这样的模式或者提取所有使用的立即数常量与标准常量库进行匹配。这能极大提高分析效率。2.4 第四步构造解密脚本分析清楚魔改点后最后一步就是“以牙还牙”。有两种主要策略逆向算法重写解密函数如果魔改是可逆的大多数情况就根据分析出来的魔改算法用Python/C语言重新实现其解密过程。这是最根本、最通用的方法。利用侧信道或约束求解如果算法过于复杂或关键部分被混淆可以尝试侧信道如果程序有“解密成功/失败”的反馈如输出”Correct”或”Wrong”可以将其视为一个Oracle尝试选择密文攻击。约束求解使用Z3、Angr这样的符号执行工具将加密过程表示为一系列约束条件让求解器帮你找出满足条件的输入即Flag。这在算法流程清晰但密钥未知时特别有效。3. 三大算法魔改特征深度解析与实战下面我们进入实战环节分别拆解RC4、TEA和AES的常见魔改手法和破解之道。3.1 RC4流程简单魔改空间在S盒与密钥流RC4算法以其简洁著称主要分为**密钥调度算法KSA和伪随机生成算法PRGA**两部分。它的魔改通常围绕这两个部分展开。标准RC4特征速览状态一个256字节的S盒S[0]…S[255]初始化为0-255。KSA用密钥key打乱S盒。核心是一个循环for i in range(256): j (j S[i] key[i % keylen]) % 256; swap(S[i], S[j])。PRGA生成密钥流。i (i 1) % 256; j (j S[i]) % 256; swap(S[i], S[j]); t (S[i] S[j]) % 256; keystream_byte S[t]。加密/解密明文/密文与密钥流字节按位异或。常见魔改点与识别修改S盒初始化标准RC4的S盒初始化为顺序的0-255。魔改版可能初始化为其他固定序列或者用另一个密钥先对S盒进行一轮变换。识别在初始化代码段观察填充S盒的循环。如果不是简单的S[i] i而是从某个数组加载或经过计算那就是魔改点。破解在你的解密脚本中复制同样的初始化过程即可。修改KSA中的运算标准KSA使用加法。可能被改为减法、乘法或异或。识别在KSA循环中查找计算j值的语句。标准是j (j S[i] key[...])。如果变成了-或^就是魔改。破解在逆向出的算法中对应修改运算即可。注意取模256% 256通常保留。修改PRGA中的运算标准PRGA中密钥流字节取自S[(S[i] S[j]) % 256]。这里的加法可能被改或者索引计算方式完全变化例如S[S[i] ^ S[j]]。识别在生成密钥流并异或的代码附近重点看S盒的索引是如何计算的。破解同样在解密脚本中照搬修改后的索引计算方式。改变交换Swap操作标准RC4频繁交换S[i]和S[j]。魔改版可能取消交换或者改为更复杂的置换。识别跟踪S盒的更新看是否有典型的交换操作通常用临时变量temp实现。破解如果取消了交换S盒的状态变化会完全不同需要仔细逆向其状态更新规则。实战案例心得我曾遇到一个题目RC4的KSA和PRGA都用了异或代替加法。静态分析时看到满屏的xor指令就起了疑心。动态调试时我输入全零明文发现密文不是随机的而是有规律的变化这不符合标准RC4的特性。通过对比标准RC4的伪代码我定位到两处加法被替换成了异或。重写解密脚本时只需把换成^问题迎刃而解。关键点在于RC4的加法和异或都是在模256一个字节的范围内进行的而在这个范围内加法和异或的运算特性不同但算法结构不变因此破解的关键是精确识别被替换的运算符。3.2 TEA系列核心结构稳定魔改集中于运算与常量TEATiny Encryption Algorithm及其变种XTEA、XXTEA结构清晰代码短小是CTF出题人的心头好。其核心是Feistel网络结构通过多轮迭代的加、异或、移位操作来加密。标准TEA特征速览以加密为例处理单元每次加密64位8字节数据分成两个32位字v0和v1。密钥128位分成4个32位字k0, k1, k2, k3。核心循环通常32轮即64次Feistel轮for (i0; i32; i) { sum delta; // delta是常量0x9E3779B9 v0 ((v14) k0) ^ (v1 sum) ^ ((v15) k1); v1 ((v04) k2) ^ (v0 sum) ^ ((v05) k3); }常见魔改点与识别修改黄金分割常量delta这是最简单的魔改。0x9E3779B9可能被改成任意一个32位常数比如0x61C88647这是-0x9E3779B9的补码表示在一些实现中也会出现。识别在循环中寻找一个被累加sum delta的常量。在IDA的汇编或反编译代码中搜索这个立即数。破解在解密脚本中使用逆向分析得到的delta值。注意解密时delta的使用方式通常是sum - delta和初始sum值delta * 轮数需要根据加密逻辑调整。修改运算操作核心加密表达式中的加法、异或^、移位方向/可能被系统性地替换。识别仔细查看反编译出的加密表达式。例如(v14) k0可能变成(v14) ^ k0。需要逐项对比标准表达式。破解这是最考验细心的地方。你必须完全复现加密时的运算顺序和操作符。写解密函数时要推导出逆运算。如果加密是v0 E那么解密就是v0 - E。但E本身如果包含了复杂的运算需要确保在解密时E的计算值与加密时完全一致。修改轮数标准32轮可能被增加如64轮或减少如16轮。识别查看循环的终止条件。for (i0; i64; i)可能意味着64轮Feistel操作对应32次标准循环。破解在解密脚本中设置对应的轮数。同时初始的sum值会变为delta * 轮数。采用XTEA或XXTEA的变种XTEA和XXTEA本身是对TEA的改进它们有自己标准的运算格式。出题人可能魔改这些变种。识别XTEA的加密表达式更复杂但仍有delta常量。XXTEA可以处理更长的数据块其核心运算也不同于TEA。需要熟悉这些变种的标准实现。破解识别出是哪种变种后对照标准算法查找魔改点。实战避坑指南TEA魔改题最容易出错的地方在于运算的逆操作。比如加密时如果是v0 f(v1)解密时就是v0 - f(v1)。但这里的f(v1)必须在解密时的当前轮次用当前的v1值计算出来而这个v1值在解密开始前是未知的它是上一轮解密的结果。因此TEA的解密必须从最后一轮向第一轮反向进行并且sum的初始值是delta * 轮数。很多新手会在这里搞错顺序导致解密失败。我的经验是先完全理解标准TEA的解密流程再将其中的运算替换为魔改后的版本这样不容易乱。3.3 AES结构复杂魔改点多在S盒与轮常数AESAdvanced Encryption Standard结构规整但层次较多包括字节替换SubBytes、行移位ShiftRows、列混合MixColumns、轮密钥加AddRoundKey等步骤。魔改空间很大。标准AES-128特征速览轮数10轮128位密钥。核心组件S盒SubBytes一个固定的256字节替换表。逆S盒InvSubBytes用于解密的对应表。轮常数Rcon一个10个字的数组用于密钥扩展。列混合矩阵一个固定的4x4矩阵用于MixColumns变换。常见魔改点与识别自定义S盒这是最常见的魔改。出题人会用自己生成的或乱序的S盒替换标准S盒。识别在二进制中查找一段连续的256字节数据它被频繁用于查表操作movzx reg, byte ptr [table_base index]。可以尝试将这段数据提取出来与标准AES S盒进行对比。如果完全不同就是自定义S盒。破解将二进制中的这个S盒完整地dump出来在解密脚本中替换标准S盒。切记解密时必须使用对应的逆S盒。如果程序中只给了加密S盒你需要自己编写函数求逆或者动态调试从内存中dump出解密时使用的逆S盒如果程序自己计算并存下了的话。修改轮常数RconRcon用于密钥扩展。修改它会使得每一轮的轮密钥发生变化。识别在密钥扩展函数中寻找与Rcon数组相关的操作。标准Rcon是{0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36}。破解dump出修改后的Rcon数组在密钥扩展步骤中使用它。修改列混合MixColumns系数标准AES的MixColumns使用一个固定的多项式矩阵。魔改版可能修改这个矩阵的系数。识别MixColumns在代码中可能以查表T-Table方式实现也可能直接是矩阵乘法。观察计算过程中使用的常量乘法系数如0x02,0x03,0x01,0x01等是否被改变。破解这比较棘手。需要逆向出新的变换矩阵并计算其逆矩阵用于解密。通常这类题目会给出加密代码或足够提示。增减轮数例如改成8轮或12轮加密。识别数一数加密主循环的次数或者观察轮密钥扩展出了多少组轮数1组。破解在解密脚本中调整轮数并确保轮密钥的数目正确。移除或替换某个步骤例如移除MixColumns步骤类似于AES的ECB模式但更简单或者用其他操作替换SubBytes。识别需要对照AES的完整流程一步步跟踪数据看是否少了某个步骤或者某个步骤的输入输出不符合预期。破解如果只是移除步骤解密时也对应移除即可。如果是替换则需要逆向替换操作的可逆算法。高阶技巧利用白盒密码学思想在一些高难度赛题中AES可能被实现为“白盒密码”的形式即S盒、行移位、列混合和轮密钥加全部被合并、打乱、编码成巨大的查找表。识别起来极其困难看起来就像一堆杂乱无章的内存访问。对付这种通常有两种思路动态插桩Dynamic Instrumentation使用Intel PIN或Frida等工具记录下加密过程中所有内存读写的地址和值试图从海量数据中还原出算法逻辑或直接提取密钥。这需要较强的脚本能力和数据分析能力。代数攻击或侧信道如果白盒实现有缺陷可能通过求解方程组或利用缓存计时等侧信道信息来破解。这在CTF中较少见属于研究范畴。4. 实战工具链与自动化脚本工欲善其事必先利其器。一套顺手的工具能极大提升逆向效率。静态分析工具IDA Pro / Ghidra反汇编和反编译的主力。Ghidra的开源和反编译能力越来越强是IDA的优秀替代品。Binwalk用于固件或二进制文件的分析可能分离出加密算法相关的数据段。Strings / FLOSS提取二进制中的字符串和疑似编码数据。FLOSS能更好地识别混淆后的字符串。动态调试工具GDB (with Peda/Pwndbg/GEF)Linux下的调试利器增强插件能提供更好的反汇编视图、内存查看和ROP链构建功能。x64dbg / OllyDbgWindows平台下的强大调试器。Frida动态插桩框架可以Hook函数、修改内存、调用函数非常适合自动化测试和快速验证猜想。例如你可以写一个Frida脚本在加密函数被调用时自动打印出输入、输出和密钥。密码学分析工具CyberChef网页端的“密码学瑞士军刀”支持各种编码、哈希、经典加密算法。可以快速验证你的解密结果是否正确。Python pwntools编写解密脚本、进行网络交互的绝佳组合。pwntools库能方便地处理打包解包数据。Z3 / Angr当算法流程清晰但密钥或输入未知时使用约束求解器或符号执行引擎来“暴力”求解。例如你可以将加密算法用Z3的BitVec表示然后添加约束如解密后的明文包含flag{让Z3求解。自动化识别脚本IDAPython示例下面是一个简单的IDAPython脚本片段用于在IDA中搜索TEA算法可能使用的魔数0x9E3779B9或其常见变种0x61C88647。import ida_bytes import ida_search def find_tea_constants(): # 定义要搜索的常量列表十六进制小端序格式 constants [0xB9793779, 0x9E3779B9, 0x4788C661, 0x61C88647] # 注意小端序和大端序 found_addresses [] for const in constants: addr ida_search.find_binary(0, ida_idaapi.BADADDR, f{const:08X}, 16, ida_search.SEARCH_DOWN) while addr ! ida_idaapi.BADADDR: found_addresses.append((addr, f0x{const:08X})) print(fFound constant 0x{const:08X} at address {hex(addr)}) addr ida_search.find_binary(addr4, ida_idaapi.BADADDR, f{const:08X}, 16, ida_search.SEARCH_DOWN) if not found_addresses: print(No common TEA constants found.) return found_addresses find_tea_constants()这个脚本会搜索内存中是否包含这些特定的4字节模式。找到后你可以快速跳转到该地址查看其周围的代码很可能就是TEA算法的加密/解密循环。5. 常见问题排查与心法总结即使思路清晰工具齐全实战中还是会遇到各种“坑”。这里记录几个我踩过的坑和解决方法。问题1动态调试时加密函数被反复调用很多次看不清哪次是关键的策略在调用加密函数的上级函数入口处下断点观察传入的参数。通常关键加密的输入明文/密文长度固定如16、32字节且内容可能与你输入的测试数据相关。也可以结合字符串引用寻找可能输出“Correct”、“Success”等提示符的地方在其附近的加密调用更关键。问题2算法识别出来了魔改点也找到了但自己写的解密脚本结果不对排查清单字节序Endianness这是最大的坑x86/x64通常是小端序Little-Endian而很多算法描述包括本文使用大端序Big-Endian表示。在从内存中读取多字节数据如32位的v0,v1或写入数据时必须进行正确的字节序转换。Python的struct包I小端I大端是你的好朋友。轮次顺序像TEA这类Feistel网络解密必须是加密的逆序。确认你的解密循环是从最后一轮跑到第一轮并且sum的初始值正确。运算的完全复现确保你的解密脚本中的每一个加法、异或、移位操作都和IDA中反编译出来的完全一致包括运算符优先级。最好将IDA中的关键代码片段直接翻译成Python/C。密钥和初始向量的处理密钥是从哪里加载的是硬编码的字符串还是经过某种变换初始向量IV是否全零是否有额外的填充Padding这些细节必须与加密端严格匹配。使用中间值验证在动态调试中在加密函数的入口和出口记录下关键变量的值如TEA的v0,v1,sum。用你的加密脚本按照魔改算法实现对同样的输入进行计算对比中间结果。从第一轮开始比对哪里开始不一致哪里就是你的实现错误。问题3遇到极度混淆或虚拟化的代码根本看不到明显的算法逻辑策略这可能是虚拟机保护VMP或控制流扁平化。此时直接静态分析算法可能不现实。动态追踪数据流专注于输入和输出。在程序起始点如main函数输入你的数据在程序输出结果或判断结果的地方下断点。在这两点之间广泛下内存访问断点或硬件断点追踪你的输入数据是如何被读取、复制、变换的。虽然慢但可能理出关键的数据处理路径。符号执行如果程序逻辑不是特别复杂可以尝试使用Angr。让Angr自动探索执行路径并约束最终输出为期望值如包含”flag{“让它帮你求解输入。这对小型、独立的算法验证函数有时有奇效。Patch与绕过如果最终目标只是获取Flag而不是彻底逆向算法可以考虑修改程序逻辑。例如找到判断Flag是否正确的那个jump指令直接将其改为永远跳转到成功分支。或者将加密函数直接替换为一个将输入原样返回的空函数。逆向心法大胆假设小心求证先根据特征快速猜一个算法然后通过动态调试去验证或否定它。不要怕猜错。由外而内逐步深入先搞清楚程序的整体逻辑哪里输入、哪里加密、哪里比较、哪里输出再深入加密函数内部。重视输入输出准备有特点的测试数据全零、全F、递增序列、可打印字符串对比加密前后的变化往往能发现算法的蛛丝马迹比如是否是流密码、分组大小等。善用工具但不过度依赖工具能提高效率但最终的理解要靠自己的大脑。亲手用Python实现一遍标准算法会让你对它的细节和魔改点有刻骨铭心的认识。保持耐心与细致逆向工程尤其是密码学逆向是一个需要极度耐心和细致的工作。一个字节序的错误、一个运算符号的看错都可能导致前功尽弃。养成随时记录、反复验证的习惯。魔改算法的逆向破解是一场与出题人斗智斗勇的博弈。它考验的不仅是你的工具使用熟练度更是你对密码学原理的深刻理解、逆向工程的基本功以及那份在复杂代码中寻找规律的耐心和洞察力。希望这篇结合实战经验的长文能为你提供一套可复用的方法论和实用的技巧库。下次在CTF赛场上再遇到那些穿着“马甲”的RC4、TEA和AES时相信你能更加从容地揭开它们的真面目直取Flag。