从CTF逆向题到实战:手把手教你修复魔改的Python .pyc文件头(附Python 3.7修复脚本)
从CTF逆向题到实战手把手教你修复魔改的Python .pyc文件头附Python 3.7修复脚本当你从CTF比赛或安全分析中拿到一个被故意破坏的.pyc文件时最常见的恶作剧就是篡改文件头部的magic number。这种看似简单的改动会让uncompyle6等反编译工具直接报错就像给你一本加密的魔法书却撕掉了目录页。本文将深入Python字节码的底层结构带你从零构建一个通用的.pyc修复工具。1. Python字节码文件结构解析.pyc文件本质上是一个序列化的代码对象其结构可以分为三个关键部分Magic Number4字节标识Python版本和编译环境Timestamp4字节记录源文件最后修改时间Code Object包含实际字节码的Marshal数据以Python 3.7为例正常的文件头应该是import binascii print(binascii.hexlify(importlib.util.MAGIC_NUMBER)) # 输出420d0d0a当magic number被修改时常见的错误提示是ImportError: bad magic number in filename: bxxxx2. 实战逆向分析被破坏的.pyc文件假设我们拿到一个来自CTF比赛的损坏文件magic.pyc使用hexdump查看其头部xxd -l 16 magic.pyc输出显示异常magic number00000000: 1234 5678 7102 0000 0000 0000 0000 0000 .4Vxq.........2.1 确定正确的Python版本通过特征比对法可以确定原始版本# 常见Python版本magic number对照表 MAGIC_NUMBERS { 3.7: b\x42\x0d\x0d\x0a, 3.8: b\x55\x0d\x0d\x0a, 3.9: b\x61\x0d\x0d\x0a } def detect_version(modified_header): for ver, magic in MAGIC_NUMBERS.items(): if modified_header[:2] magic[:2]: # 前两位通常不变 return ver return None2.2 修复文件长度字段在示例文件中我们看到异常字段7102导致长度错乱。正确的修复步骤删除异常字段示例中为第4-7字节调整文件长度字段原始7334 → 修正为7332恢复标准magic number3. 自动化修复脚本开发以下是一个完整的修复工具实现import struct import sys PY37_MAGIC b\x42\x0d\x0d\x0a def repair_pyc(input_file, output_file): with open(input_file, rb) as f: data f.read() # 检查并移除异常字段 if data[4:6] b\x71\x02: repaired PY37_MAGIC data[8:] # 移除异常字段 # 修正长度字段假设原长度为7334 length struct.unpack(I, repaired[4:8])[0] - 2 repaired repaired[:4] struct.pack(I, length) repaired[8:] else: repaired PY37_MAGIC data[4:] # 仅修复magic number with open(output_file, wb) as f: f.write(repaired) if __name__ __main__: repair_pyc(sys.argv[1], sys.argv[2])使用方式python repair_pyc.py broken.pyc fixed.pyc4. 进阶处理更复杂的篡改情况某些CTF题目会采用多重破坏手段4.1 时间戳篡改检测import time from datetime import datetime def check_timestamp(pyc_data): ts struct.unpack(I, pyc_data[4:8])[0] print(f编译时间: {datetime.fromtimestamp(ts)}) if ts int(time.time()): print([!] 未来时间戳异常)4.2 字节码校验工具import dis import marshal def validate_code(pyc_data): try: code marshal.loads(pyc_data[12:]) dis.dis(code) return True except: print([!] 字节码损坏) return False5. 防御性编程建议为防止修复过程中出现意外建议备份原始文件所有操作在副本上进行分段验证每步修改后检查文件有效性版本兼容处理不同Python版本的差异def safe_repair(input_path): backup input_path .bak os.rename(input_path, backup) try: repair_pyc(backup, input_path) if not validate_code(open(input_path, rb).read()): raise ValueError(修复后验证失败) except Exception as e: print(f修复失败: {str(e)}) os.rename(backup, input_path)通过这套方法你不仅能解决CTF题目还能应对真实环境中遇到的.pyc文件异常问题。当反编译工具报错时不妨先用hex编辑器查看文件头或许问题就藏在那些被修改的魔法数字里。