ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

nssctf_TTTTTTTTTea

nssctf_TTTTTTTTTea 下载、查壳、64位、IDA打开tea算法的特征非常明显TEATiny Encryption Algorithm微型加密算法是由剑桥大学的 David Wheeler 和 Roger Needham 于 1994 年提出的一种轻量级分组对称加密算法。它的设计目标是代码极简、易于实现、占用资源极少非常适合嵌入式设备、逆向 CTF 题目等场景。核心参数表格项标准 TEA 默认值本题对应实现分组长度64 bit8 字节拆为 2 个 32 位整数v0, v1一致每组 2 个 int8 字节密钥长度128 bit16 字节拆为 4 个 32 位整数k[0]~k[3]一致16 字节密钥经典轮数32 轮一致循环0x1F即 32 轮魔数 delta0x9E3779B9源于黄金分割比例保证混淆性等价补码写法0x61C88647标准 TEA 加密原理TEA 基于迭代混淆 - 扩散的思想对两个 32 位明文块v0、v1进行多轮运算每轮通过位移、异或、加法和密钥混合逐步打乱明文特征。加密流程32 轮初始化累加值sum 0delta 为固定魔数每轮执行sum sum delta; v0 v0 ( ((v1 4) k[0]) ^ (v1 sum) ^ ((v1 5) k[1]) ); v1 v1 ( ((v0 4) k[2]) ^ (v0 sum) ^ ((v0 5) k[3]) );32 轮结束后v0、v1就是最终的密文分组核心设计(v 4) ^ (v 5)是位移混淆通过左移 4 位和右移 5 位的异或打散比特位分布加法和异或交替使用实现非线性混淆增加破解难度sum累加值每轮递增保证每轮的密钥混合因子都不同避免相同轮函数重复本题中的 TEA 代码对应你 IDA 反汇编里的tea_encrypt函数本质就是标准 TEA 的轻微改写对应关系如下变量映射v6 左 32 位明文 / 密文v0v5 右 32 位明文 / 密文v1v4 轮累加值suma2 128 位密钥数组首地址kdelta 的写法差异代码里写的是v4 - 1640531527即sum - 0x61C88647它和标准 TEA 的sum 0x9E3779B9运算结果完全等价因为 32 位无符号整数运算下-0x61C88647 mod 2^32 0x9E3779B9只是编译器把正数加法优化成了负数减法的补码形式。3.密钥索引的差异标准 TEA 固定用k[0]/k[1]计算 v0、k[2]/k[3]计算 v1本题用sum 3和(sum 11) 3动态取密钥下标属于 CTF 中很常见的 TEA 变种写法本质还是 4 个密钥字循环参与运算。TEA 解密逻辑解密是加密的严格逆过程初始sum设为加密结束时的最终值32 轮后 sum 32 × delta每轮倒序执行加密操作先还原 v1再回退 sum最后还原 v0循环 32 轮后得到原始明文这也是为什么之前的解密脚本里sum 初始值是负数、运算顺序和加密反过来的原因。代码解析这是一个flag 验证程序的主函数核心逻辑是读取用户输入 → 按 TEA 算法加密 → 和内置密文对比判断输入是否正确。我们按执行顺序拆解1. 变量声明int v4[16]; // 整数数组共16个int用于存放「内置密文」和「加密后的输入」 char v5[44]; // 字符缓冲区存放用户输入的flag字符串 int k, j, i; // 循环计数器 int *v9; // int型指针用于把字符串按4字节整数读取v4是核心数组前半部分存正确答案密文后半部分存用户输入加密后的结果最后逐位对比。v5是输入缓冲区最大 44 字节但实际只会用到前 24 字节6 个 int。2. 输入读取puts(please input your flag); scanf(%s, v5); v9 (int *)v5;提示用户输入 flag读取字符串存入v5。v9 (int *)v5把字符数组的地址强制转为 int 指针。作用是把输入的字符串按4 字节一个 32 位整数的方式来读取x86 平台默认小端序。3. 输入拆分字符串 → 整数数组for ( i 0; i 5; i ) v4[i 8] *v9;循环 6 次把输入的 flag 按 4 字节切分成6 个 int 整数依次存入v4[8] ~ v4[13]。6 个 int 24 字节说明 flag 的有效长度最多 24 字节。4. 核心分组 TEA 加密for ( j 0; j 2; j ) tea_encrypt(v4[2 * j 8], key);循环 3 次每次取**2 个 int8 字节**作为一组调用tea_encrypt进行加密。j0加密v4[8], v4[9]第 1 组j1加密v4[10], v4[11]第 2 组j2加密v4[12], v4[13]第 3 组tea_encrypt是原地加密直接修改传入地址里的数值加密完成后v4[8]~v4[13]就从明文变成了密文。这符合 TEA 算法的特征64 位8 字节分组加密每次处理 2 个 32 位整数。5. 内置正确密文v4[0] -1054939302; v4[1] -1532163725; v4[2] -165900264; v4[3] 853769165; v4[4] 768352038; v4[5] 876839116;v4[0] ~ v4[5]是程序写死的正确密文标准答案。这里是有符号 int 显示实际参与运算时是 32 位无符号整数。6. 校验对比for ( k 0; k 5; k ) { if ( v4[k] ! v4[k 8] ) { printf(ERROR!); exit(9); } } printf(you are right!);循环 6 次逐位对比左边v4[k]内置的正确密文右边v4[k8]用户输入加密后的结果只要有一位不相等就打印ERROR!并退出全部相等则打印you are right!说明输入的 flag 正确。整体逻辑总结用户输入flag字符串 ↓ 按4字节拆成6个int整数存入v4[8]~v4[13] ↓ 分成3组每组2个int分别调用TEA加密 ↓ 加密结果 和 程序内置的6个正确密文 逐位对比 ↓ 全部相等 → 正确否则 → 错误然后解析加密算法这是一个32 轮迭代的 64 位分组对称加密算法变种 TEA输入是 8 字节明文拆为 2 个 32 位整数和 16 字节密钥拆为 4 个 32 位整数经过 32 轮混淆运算后输出 8 字节密文属于原地加密直接修改输入的内存数据。二、变量映射与初始化1. 输入参数unsigned int *a1明文指针指向连续 2 个unsigned int共 8 字节分别是明文的左 32 位、右 32 位__int64 a2密钥指针指向连续 4 个unsigned int共 16 字节即 128 位密钥2. 内部变量对应表格变量对应 TEA 核心概念作用v6左半部分v0存储加密过程中的左 32 位数据v5右半部分v1存储加密过程中的右 32 位数据v4轮累加值sum每轮递变的常量保证每轮运算因子不同i轮计数器控制 32 轮循环3. 初始化步骤v6 *a1; // 读取明文左32位 v5 a1[1]; // 读取明文右32位 v4 0; // 轮累加值 sum 初始化为 0循环for ( i 0i64; i 0x1F; i )0x1F 31循环执行32 轮0~31 共 32 次和标准 TEA 的轮数一致。三、单轮加密核心运算逐行拆解每一轮循环严格分为三步更新左半 v6 → 更新 sum 值 v4 → 更新右半 v5。第 1 步更新左半部分 v6v6 (((v5 5) ^ (16 * v5)) v5) ^ (*(_DWORD *)(4i64 * (v4 3) a2) v4);从内到外逐层拆解运算逻辑比特位移混淆(v5 5) ^ (16 * v5)v5 5v5 逻辑右移 5 位高位补 016 * v5等价于v5 4v5 左移 4 位低位补 0两者异或把 v5 的高低比特位交叉打散是 TEA 算法的核心混淆单元目的是破坏明文的统计规律非线性叠加((位移异或结果) v5)把混淆后的结果再加上 v5 本身通过加法引入非线性进一步扩散比特位密钥 轮值混合(*(_DWORD *)(4i64 * (v4 3) a2) v4)v4 3取 sum 的低 2 位得到 0/1/2/3 的下标动态选择 4 个密钥字中的一个4i64 * 下标每个密钥字占 4 字节计算对应的字节偏移量加上密钥基地址a2取出对应的 32 位密钥字再加上当前轮的 sum 值v4让密钥和轮值绑定相同密钥字在不同轮的作用不同最终合并(非线性叠加结果) ^ (密钥轮值混合结果)结果累加到 v6 上用异或把数据和密钥混合再通过加法更新左半部分完成一轮左半部分的扩散第 2 步更新轮累加值 sumv4v4 - 1640531527;常量1640531527的十六进制为0x61C88647在 32 位无符号整数运算中减去0x61C88647等价于加上0x9E3779B9标准 TEA 的经典 delta 常量源于黄金分割数作用每轮让 sum 变化一个固定值保证每一轮的密钥混合因子都不相同避免轮函数重复第 3 步更新右半部分 v5v5 (((v6 5) ^ (16 * v6)) v6) ^ (*(_DWORD *)(4i64 * ((v4 11) 3) a2) v4);和 v6 的更新结构对称但有两个关键区别输入是更新后的 v6用刚更新完的左半部分 v6 来计算右半部分形成迭代依赖这是分组密码实现扩散的核心密钥索引方式不同(v4 11) 3把 sum 右移 11 位后再取低 2 位和 v6 更新时的v4 3索引不同目的是让左右两半使用不同的密钥字调度增加算法的非线性和破解难度四、结果写回32 轮循环结束后*a1 v6; // 加密后的左32位写回原地址 a1[1] v5; // 加密后的右32位写回原地址 return v5; // 返回右半部分主函数未使用该返回值这是原地加密输入的明文内存直接被覆盖为密文不需要额外的输出缓冲区。五、算法核心特性总结64 位分组一次处理 8 字节数据符合 TEA 家族的基本设计32 轮迭代通过多轮重复的简单运算累积实现足够的混淆和扩散位移混淆单元左移4 ^ 右移5是 TEA 的标志性设计用极简单的运算实现比特打散动态密钥调度通过 sum 的不同位选择密钥字相比标准 TEA 的固定密钥搭配变种性更强轮值 sum每轮变化的累加值让相同密钥在不同轮产生不同的加密效果这个算法本质是标准 TEA 的轻量变种保留了 TEA 代码极简、易于实现的特点修改了密钥调度和运算顺序是 CTF 逆向题的高频出题模板。提取key:最后写解密脚本importstructdeftea_decrypt(v0,v1,key): 变种TEA解密函数严格逆向反汇编中的加密逻辑 v0: 密文左32位对应加密中的v6 v1: 密文右32位对应加密中的v5 key: 128位密钥4个32位无符号整数 delta0x61C88647# 加密中的递减常量 1640531527# 加密32轮结束后sum的最终值初始0每轮减delta共减32次sum_val(0-32*delta)0xFFFFFFFFfor_inrange(32):# --------------------------# 第1步逆加密的最后一步v5更新# 加密v5 ( ((v65) ^ (v64)) v6 ) ^ ( key[(sum11)3] sum )# 解密v5 减去该增量# --------------------------# 位移混淆部分左移4位必须截断32位模拟C语言溢出mix((v05)^((v04)0xFFFFFFFF))0xFFFFFFFF# 密钥 轮累加值k_sum(key[(sum_val11)3]sum_val)0xFFFFFFFF# 总增量inc(mixv0)0xFFFFFFFF^k_sum# 逆加法v1(v1-inc)0xFFFFFFFF# --------------------------# 第2步逆sum的更新# 加密sum - delta → 解密sum delta# --------------------------sum_val(sum_valdelta)0xFFFFFFFF# --------------------------# 第3步逆加密的第一步v6更新# 加密v6 ( ((v55) ^ (v54)) v5 ) ^ ( key[sum3] sum )# 解密v6 减去该增量# --------------------------mix((v15)^((v14)0xFFFFFFFF))0xFFFFFFFFk_sum(key[sum_val3]sum_val)0xFFFFFFFFinc(mixv1)0xFFFFFFFF^k_sum# 逆加法v0(v0-inc)0xFFFFFFFFreturnv0,v1# --------------------------# 1. 提取密钥数据段字节 → 小端解析为4个32位无符号整数# --------------------------key_bytesbytes([0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x0B,0x0A,0x09,0x08,0x0F,0x0E,0x0D,0x0C])keylist(struct.unpack(4I,key_bytes))# 表示小端序# --------------------------# 2. 提取密文有符号int → 32位无符号整数# --------------------------cipher[-1054939302,-1532163725,-165900264,853769165,768352038,876839116]cipher_uint[x0xFFFFFFFFforxincipher]# --------------------------# 3. 分3组解密每组8字节/2个int# --------------------------groups[(cipher_uint[0],cipher_uint[1]),(cipher_uint[2],cipher_uint[3]),(cipher_uint[4],cipher_uint[5])]# --------------------------# 4. 逐组解密拼接为flag字符串# --------------------------flag_bytesbforv0,v1ingroups:plain0,plain1tea_decrypt(v0,v1,key)# 小端打包回字节序列flag_bytesstruct.pack(2I,plain0,plain1)# 输出结果print(解密得到的flag,flag_bytes.decode(ascii))运行得到
返回列表