python解析二进制文件的关键在于明确定义结构:需用十六进制编辑器确认偏移与填充,struct.unpack需显式指定字节序并补全padding;复杂格式应使用construct库声明式解析,调试时检查offset和magic头,校验和字段需跳过,模糊字段靠多样本比对反推。

Python 能解析复杂二进制文件,但关键不在“能不能”,而在“怎么定义结构”——没有明确的格式文档或协议说明,struct 和 ctypes 都会失效。
用 struct.unpack 解析固定偏移+定长字段时,必须手动对齐字节序和填充
很多二进制格式(如自定义日志头、传感器帧)字段紧凑、无分隔符,靠位置和长度识别。这时 struct.unpack 最直接,但容易错在:struct 默认不处理编译器填充,而 C 结构体导出的二进制常含 \x00 填充字节;同时 struct 的 @(native)模式会随平台变,(little-endian)或 <code>>(big-endian)才可靠。
- 先用十六进制编辑器(如
xxd或HxD)打开样本文件,确认字段起始偏移、长度、是否含 padding - 构造 format string 时显式指定字节序,例如
<i32sb> 表示小端 4 字节整数 + 32 字节字符串 + 1 字节无符号整数</i32sb> - 若字段间有 2 字节填充(如
uint32_t a; char b[16]; uint16_t c;在 gcc x86_64 下),需在 format string 中补H(<ih16sh>),否则 <code>unpack会越界读 - 字符串字段常以
\x00截断,用.split(b'\x00', 1)[0].decode('utf-8', 'ignore')安全解码
遇到嵌套结构或变长字段,优先用 construct 库而非手写状态机
当文件含重复块、长度前缀字符串、条件字段(如 flag=1 时后面跟 8 字节扩展)、或嵌套子结构时,struct 表达力迅速枯竭。硬写 read() + seek() 容易漏状态、难维护。
-
construct库用声明式语法描述结构,例如Struct("len"/Int32ul, "data"/Bytes(this.len))自动处理变长数组 - 它支持条件分支:
If(this.flag == 1, Struct("ext"/Int64ul)),也支持嵌套:"header"/HeaderStruct - 调试时用
obj = parser.parse_stream(f)后调obj._debug_print()可视化解析路径和字节消耗 - 注意:默认启用 lazy parsing,真正访问字段才解析;若需全量校验,加
build()或parse()后检查obj._io.tell()是否等于文件末尾
解析失败时,IOError 或 StreamError 往往是 offset 错了,不是数据坏了
常见报错如 struct.error: unpack requires a buffer of 4 bytes 或 construct.core.StreamError: stream read less than specified amount,90% 情况下不是文件损坏,而是解析起点偏移错误——比如跳过了 magic header、误把压缩段当明文、或没跳过前面的 CRC 校验区。
- 先用
hexdump -C file.bin | head -20确认前 32 字节是否匹配已知 magic(如b'\x89PNG\r\n\x1a\n') - 用
f.seek(0); f.read(16).hex()在 Python 中快速验证 - 如果格式含校验和(如 Adler32、CRC16),先跳过对应字段再解析主体,否则
construct会在校验位处卡住 - 某些固件镜像开头是 padding(如全
\xff),需用f.seek()手动定位到第一个非\xff字节后再开始解析
最麻烦的永远不是解析逻辑本身,而是格式定义模糊——比如文档说“时间戳为 Unix 时间”,却没写是秒还是毫秒、有无时区偏移、是否用 int64_t 还是 uint32_t 存储。这种时候,唯一可靠的方式是拿多个已知时间点的样本文件,比对二进制差异,反推字段含义。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











