查看: 230|回复: 1

纯Python实现XXTEA加解密:支持任意数据长度与文件批量解密

[复制链接]
发表于 3 小时前 | 显示全部楼层 |阅读模式
XXTEA(eXtended eXtended Tiny Encryption Algorithm)是TEA加密算法家族的第三代成员,由David Wheeler和Roger Needham于1998年提出。它修正了XTEA的缺陷,支持任意长度的数据块(不限于64位),并保持简单的Feistel结构。算法仅使用加法、减法、异或、移位等基本整数运算,非常适合嵌入式环境。密钥固定为128位(16字节),轮数根据数据长度动态计算:rounds = 6 + 52 // n,其中n是32位字的个数。

下面给出完整的纯Python实现,包含加密、解密以及自动PKCS#7填充处理,不依赖任何第三方库。同时提供一个实用的文件批量解密工具,可自动识别并剔除文件头签名。
  1. import os
  2. import sys
  3. import struct
  4. # ---------- 辅助函数:字节 ↔ 32位整数数组 ----------
  5. def _bytes_to_uint32(data: bytes) -> list:
  6.     """字节串 -> 小端序32位无符号整数列表,长度自动补齐到4的倍数"""
  7.     n = len(data)
  8.     if n % 4 != 0:
  9.         data += b'\x00' * (4 - n % 4)
  10.     return list(struct.unpack(f'<{len(data)//4}I', data))
  11. def _uint32_to_bytes(arr: list) -> bytes:
  12.     """小端序32位整数列表 -> 字节串"""
  13.     return struct.pack(f'<{len(arr)}I', *arr)
  14. # ---------- 轮函数辅助计算 ----------
  15. def _mx(z, y, sum_val, p, e, k):
  16.     return (((z >> 5) ^ (y << 2)) + ((y >> 3) ^ (z << 4))) ^ ((sum_val ^ y) + (k[(p & 3) ^ e] ^ z))
  17. # ---------- XXTEA 加密 ----------
  18. def xxtea_encrypt(plain: bytes, key: bytes) -> bytes:
  19.     """加密任意字节串,返回密文字节串(自动添加PKCS#7填充)
  20.     key: 任意长度的密钥,内部将截断或补零至16字节
  21.     """
  22.     if len(plain) == 0:
  23.         return b''
  24.     # PKCS#7 填充:补足到4字节整数倍
  25.     pad_len = 4 - (len(plain) % 4)
  26.     if pad_len == 4:
  27.         pad_len = 0
  28.     if pad_len:
  29.         plain += bytes([pad_len]) * pad_len
  30.     # 将密钥转换为4个32位整数
  31.     key_bytes = key.ljust(16, b'\x00')[:16]
  32.     k = _bytes_to_uint32(key_bytes)
  33.     # 将明文转换为32位整数数组
  34.     v = _bytes_to_uint32(plain)
  35.     n = len(v)
  36.     if n < 2:
  37.         v += [0] * (2 - n)
  38.         n = 2
  39.     DELTA = 0x9E3779B9
  40.     rounds = 6 + 52 // n
  41.     sum_val = 0
  42.     y = v[0]
  43.     for _ in range(rounds):
  44.         e = (sum_val >> 2) & 3
  45.         for p in range(n):
  46.             z = v[p-1] if p > 0 else v[-1]
  47.             mx = _mx(z, y, sum_val, p, e, k)
  48.             v[p] = (v[p] + mx) & 0xFFFFFFFF
  49.             y = v[p]
  50.         sum_val = (sum_val + DELTA) & 0xFFFFFFFF
  51.     return _uint32_to_bytes(v)
  52. # ---------- XXTEA 解密 ----------
  53. def xxtea_decrypt(cipher: bytes, key: bytes) -> bytes:
  54.     """解密密文字节串,自动去除PKCS#7填充"""
  55.     if len(cipher) == 0:
  56.         return b''
  57.     if len(cipher) % 4 != 0:
  58.         raise ValueError("密文长度必须是4的倍数")
  59.     # 密钥转换
  60.     key_bytes = key.ljust(16, b'\x00')[:16]
  61.     k = _bytes_to_uint32(key_bytes)
  62.     # 将密文转换为32位整数数组
  63.     v = _bytes_to_uint32(cipher)
  64.     n = len(v)
  65.     if n < 2:
  66.         raise ValueError("密文至少需要8字节")
  67.     DELTA = 0x9E3779B9
  68.     rounds = 6 + 52 // n
  69.     sum_val = (rounds * DELTA) & 0xFFFFFFFF
  70.     y = v[0]
  71.     # 解密主循环
  72.     for _ in range(rounds):
  73.         e = (sum_val >> 2) & 3
  74.         for p in range(n-1, -1, -1):
  75.             z = v[p-1] if p > 0 else v[-1]
  76.             mx = _mx(z, y, sum_val, p, e, k)
  77.             v[p] = (v[p] - mx) & 0xFFFFFFFF
  78.             y = v[p]
  79.         sum_val = (sum_val - DELTA) & 0xFFFFFFFF
  80.     # 转换回字节并去除PKCS#7填充
  81.     plain_bytes = _uint32_to_bytes(v)
  82.     pad_len = plain_bytes[-1]
  83.     if pad_len <= len(plain_bytes):
  84.         return plain_bytes[:-pad_len]
  85.     else:
  86.         return plain_bytes
  87. # ---------- 文件解密工具(含签名自动裁剪) ----------
  88. def decrypt_file(file_path, key, sign=None):
  89.     """解密单个文件,支持可选的文件头签名裁剪"""
  90.     if not os.path.exists(file_path):
  91.         print(f"[错误] 文件不存在: {file_path}")
  92.         return
  93.     print(f"[处理] 正在读取文件: {file_path}")
  94.     try:
  95.         with open(file_path, 'rb') as f:
  96.             encrypted_data = f.read()
  97.         if sign and encrypted_data.startswith(sign):
  98.             print(f"[检测] 发现文件头签名 '{sign.decode()}',已自动裁剪前 {len(sign)} 字节。")
  99.             encrypted_data = encrypted_data[len(sign):]
  100.         else:
  101.             print("[提示] 未发现已知文件头签名,尝试直接整包解密。")
  102.         decrypted_data = xxtea_decrypt(encrypted_data, key)
  103.         if not decrypted_data:
  104.             print("[失败] 解密结果为空(可能Key错误,或该文件并非XXTEA加密)。")
  105.             return
  106.         dir_name, file_name = os.path.split(file_path)
  107.         base_name, ext = os.path.splitext(file_name)
  108.         output_name = f"{base_name}_decrypted{ext}" if ext else f"{base_name}_decrypted"
  109.         output_path = os.path.join(dir_name, output_name)
  110.         with open(output_path, 'wb') as f:
  111.             f.write(decrypted_data)
  112.         print(f"[成功] 解密完成!已生成文件: {output_path}")
  113.     except Exception as e:
  114.         print(f"[报错] 解密过程中出现异常: {e}")
  115. if __name__ == "__main__":
  116.     # 配置密钥和可选签名
  117.     KEY = b"your_secret_key_16"
  118.     SIGN = b"4meJnPyl"
  119.     if len(sys.argv) > 1:
  120.         for path in sys.argv[1:]:
  121.             decrypt_file(path, KEY, SIGN)
  122.     else:
  123.         print("提示: 可以直接把文件拖到此工具上运行。")
  124.         path = input("或者请输入要解密的文件路径: ").strip('"')
  125.         if path:
  126.             decrypt_file(path, KEY, SIGN)
  127.         input("\n处理完毕,按下回车键退出...")
复制代码

代码说明:
- _bytes_to_uint32 / _uint32_to_bytes:实现小端序字节串与32位整数列表的相互转换,自动补齐到4字节倍数。
- _mx:XXTEA轮函数中的核心计算公式,将输入z、y、sum、位置p、e和密钥k组合,产生混淆值。
- xxtea_encrypt:加密函数,先进行PKCS#7填充(补足到4字节倍数),然后进行多轮加密。轮数由6 + 52 // n决定。加密时使用正向循环(0到n-1),每轮更新sum。
- xxtea_decrypt:解密函数,与加密对称。初始化sum = rounds * DELTA,使用逆向循环(n-1到0)依次更新每个字,最后去除填充字节。
- decrypt_file:实用的文件解密函数,自动读取文件、裁剪文件头签名、调用解密函数并输出解密后文件。

使用要点:
1. 密钥长度:建议使用16字节(128位),代码内部会自动截断或补零至16字节。
2. 数据长度:XXTEA内部要求数据为4字节倍数,加密时自动填充,解密后自动去除。
3. 端序:默认使用小端序,与QQ、微信等常用实现一致。对接其他系统时需确认端序匹配。
4. 安全性:XXTEA在2010年后被发现存在理论攻击,但对于非极敏感数据仍足够使用。现代应用更推荐AES。

该实现完全自包含,无需安装第三方库,适用于任何Python 3环境。文件工具支持拖拽或命令行参数批量处理,适合游戏资源解密、配置文件脱壳等场景。
回复

使用道具 举报

发表于 2 小时前 | 显示全部楼层

Re: 纯Python实现XXTEA加解密:支持任意数据长度与文件批量解密

感谢楼主的分享,非常详实的XXTEA纯Python实现!代码结构清晰,注释也很到位,从字节与整数数组的转换到加密解密的轮函数都一目了然。尤其注意到您处理了任意长度数据和PKCS#7填充,这在文件加密场景中非常实用。 想请教一个小问题:在加密函数中,如果明文长度正好是4的倍数,您将pad_len设为0并跳过填充,但XXTEA通常要求输入至少两个32位字(8字节),填充逻辑似乎没有处理明文长度小于8字节的情况?我看到后续有 `if n < 2: v += [0] * (2-n)` 的补零处理,这应该是为了满足算法的最小块要求吧?另外,文件批量解密工具里的“自动识别并剔除文件头签名”具体是支持哪些格式或自定义签名呢?能否举个例子? 再次感谢,这段代码很适合嵌入到轻量级项目中,收藏了!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-6-12 13:06 , Processed in 0.024909 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部