Python pickletools.dis 怎样查看 pickle 字节码结构
来源:17golang原创
时间:2026-11-01 08:59:11 148浏览 收藏
排查一个 .pkl 文件时,我更愿意先看它的协议操作码,再决定是否需要恢复对象。Python 的 pickletools.dis() 正好负责这一步:它把 pickle 字节流展开成带位置、操作码和参数的符号化文本,不要求先执行反序列化逻辑。
如果目标是“看 pickle 里面大概写了什么、用了哪个协议、哪些位置引用了 memo”,用pickletools.dis或python -m pickletools;如果目标是得到 Python 对象,才涉及pickle.load或pickle.loads。来源不可信时不要跨过这个安全边界。
官方文档:https://docs.python.org/3/library/pickletools.html
先把“查看结构”和“恢复对象”分开
pickle 是面向 Python 对象的二进制序列化协议,操作码里可能包含类型、字符串、容器构造和全局名称等信息。pickletools 的职责是分析这些操作码;它不等于把字节流恢复成原来的对象。
命令行最省事的写法如下。这里的 # 是 shell 注释,只说明命令用途,不是输入文件的一部分:
# 只展开 pickle 操作码,不调用 pickle.load 或 pickle.loads
python -m pickletools sample.pkl
我在处理来源不明的文件时会先保留这份文本输出。这样能先观察协议版本、字符串和容器相关操作码,再决定是否需要交给隔离环境做进一步处理。不要为了“确认内容”直接把文件交给 pickle.load。
最小配方:用 pickletools.dis 输出可读结构
dis 接受 pickle 字节串或文件对象,默认把符号化反汇编写到标准输出。下面的示例只用本地构造的可信数据生成演示字节流;它的重点是查看协议结构,而不是展示反序列化。
import io
import pickle
import pickletools
# 只为演示生成可信的本地 pickle 字节流,不要对不可信输入调用 loads。
payload = pickle.dumps(
{"name": "demo", "items": [1, 2]},
protocol=4,
)
# 把 bytes 包成二进制文件对象,dis 会把操作码写到标准输出。
pickletools.dis(io.BytesIO(payload), annotate=1)
输出通常会包含偏移位置、操作码名称、参数和栈级别信息。annotate=1 会在输出中追加简短的操作码说明,便于第一次阅读;如果你只想保留紧凑结果,可以省略它。

文件输入和输出位置怎么控制
分析现成文件时,不需要先把文件读成一个巨大的 bytes。直接把以二进制模式打开的文件对象传给 dis 即可。输出也可以从标准输出改到 io.StringIO,便于保存、截取或交给日志系统。
import io
import pickletools
# 以 rb 打开文件,避免文本编码层改变原始 pickle 字节。
with open("sample.pkl", "rb") as source:
report = io.StringIO()
# 把反汇编文本写入内存缓冲区,indentlevel 控制嵌套层级缩进。
pickletools.dis(
source,
out=report,
indentlevel=2,
annotate=1,
)
# 这里只读取分析文本,不会把 pickle 恢复成 Python 对象。
print(report.getvalue())
这里有三个容易混淆的参数:
out是文本输出对象,不是新的 pickle 文件;indentlevel影响嵌套结构的显示缩进,不改变原始字节流;annotate是注释列位置提示,非零值会让操作码旁边多出简短说明。
需要筛选记录时改用 pickletools.genops
dis 适合人眼通读;如果我想找出某个位置、某类操作码或某个参数,就会改用 genops。它逐项返回 (opcode, arg, pos),其中 opcode 是 OpcodeInfo 对象,arg 是已解码的参数,pos 是该操作码在字节流中的偏移。
import pickletools
# 逐项读取操作码记录,只做结构分析,不恢复对象。
with open("sample.pkl", "rb") as source:
for opcode, arg, pos in pickletools.genops(source):
# 只打印稳定的名称、参数和偏移,方便后续筛选。
print(f"pos={pos:04d} name={opcode.name} arg={arg!r}")
例如,排查协议版本时可以关注 PROTO;分析引用关系时可以关注 PUT、GET、BINPUT 或 BINGET。不要把这些名字当作 Python 业务字段,它们是 pickle 协议层的操作码。

连续分析和 optimize 该怎么选
如果多个 pickle 片段来自同一个 pickler,dis 的 memo 参数可以复用同一个字典,让连续反汇编共享引用上下文。它适合分析一组有关联的片段,不代表线程安全,也不会把多个文件自动合并。
import io
import pickletools
# 同一分析会话复用 memo,用来保留连续 pickle 片段的引用上下文。
memo = {}
first = io.BytesIO(b"...")
second = io.BytesIO(b"...")
# 示例中的字节流必须替换为来自同一 pickler 的可信数据。
pickletools.dis(first, memo=memo, out=io.StringIO())
pickletools.dis(second, memo=memo, out=io.StringIO())
如果你的目标是得到语义等价但更紧凑的 pickle 字节串,可以了解 pickletools.optimize()。它会返回一个新的等价字节串并移除未使用的 PUT 操作码;这属于整理序列化结果,不是查看报告的必要步骤,也不应拿来处理未经信任的输入后再继续反序列化。
这几个边界最容易踩坑
| 目标 | 适合的入口 | 需要注意 |
|---|---|---|
| 查看协议和操作码 | python -m pickletools 或 pickletools.dis | 输出是结构说明,不是恢复后的对象 |
| 筛选偏移和参数 | pickletools.genops | arg 已被解析成 Python 值,但仍处于分析上下文 |
| 保留连续引用信息 | dis(..., memo=同一个字典) | 只对具有关联的连续片段有意义 |
| 恢复 Python 对象 | pickle.load 或 pickle.loads | 只处理明确可信且完整的输入 |
特别要注意,pickletools 的“更安全”只表示它专注于反汇编、不执行 pickle 字节码;它不是对输入来源的认证。文件是否被篡改、内容是否符合业务预期,仍需要来源控制、签名或隔离环境来解决。
最后的使用判断
把任务拆成“先看结构、再决定动作”后,选择就很清楚:人眼阅读用 dis,程序筛选用 genops,连续片段才考虑 memo,只有在信任边界已经明确时才考虑恢复对象。对大文件或不熟悉来源的文件,我会先保存反汇编文本和偏移位置,避免把一次排查变成一次不可逆的执行。
常见延伸问题
- 为什么
pickletools.dis看不到原始对象的完整业务结构? 因为它展示的是协议操作码和参数,不负责执行对象构造,也不保证还原出业务层字段关系。 genops返回的pos有什么用? 它对应操作码在 pickle 字节流中的位置,适合把异常或可疑结构定位回原始文件偏移。- 看完反汇编后能直接调用
pickle.loads吗? 只有输入来源、完整性和运行环境都可信时才考虑;反汇编本身不能替代信任判断。
-
383 收藏
-
141 收藏
-
文章 · python教程 | 25分钟前 | 标准库 · 调试 · python · Python 装饰器 functools.wraps functools.update_wrapper __wrapped__ inspect.unwrap174 收藏
-
282 收藏
-
118 收藏
-
203 收藏
-
218 收藏
-
文章 · python教程 | 2小时前 | 标准库 · python · ip地址 · Python IPv6 ipv4 CIDR ipaddress summarize_address_range345 收藏
-
153 收藏
-
326 收藏
-
205 收藏
-
344 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习