当前位置:首页 > 文章列表 > 文章 > python教程 > Python pickletools.dis 怎样查看 pickle 字节码结构

Python pickletools.dis 怎样查看 pickle 字节码结构

来源:17golang原创 2026-11-01 08:59:11 0浏览 收藏

排查一个 .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 会在输出中追加简短的操作码说明,便于第一次阅读;如果你只想保留紧凑结果,可以省略它。

pickle 字节流经过 io.BytesIO 交给 pickletools.dis 后展开为 OpcodeInfo、memo 与标准输出的静态结构图
图1:操作示意图。输入边界中的 pickle 字节流和 BytesIO 与 dis 的关系,以及反汇编结果如何落到操作码、memo 和文本输出;这是静态说明图,不是运行截图。

文件输入和输出位置怎么控制

分析现成文件时,不需要先把文件读成一个巨大的 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 协议层的操作码。

pickle 文件通过二进制文件对象进入 pickletools.genops 并拆成 OpcodeInfo、arg、pos 与 memo 结构记录的静态图
图2:结果示意图。文件输入和 genops 的关系,以及每条结构记录中的 OpcodeInfo、arg、pos 与 memo;这是静态说明图,不是运行证据。

连续分析和 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.genopsarg 已被解析成 Python 值,但仍处于分析上下文
保留连续引用信息dis(..., memo=同一个字典)只对具有关联的连续片段有意义
恢复 Python 对象pickle.load 或 pickle.loads只处理明确可信且完整的输入

特别要注意,pickletools 的“更安全”只表示它专注于反汇编、不执行 pickle 字节码;它不是对输入来源的认证。文件是否被篡改、内容是否符合业务预期,仍需要来源控制、签名或隔离环境来解决。

最后的使用判断

把任务拆成“先看结构、再决定动作”后,选择就很清楚:人眼阅读用 dis,程序筛选用 genops,连续片段才考虑 memo,只有在信任边界已经明确时才考虑恢复对象。对大文件或不熟悉来源的文件,我会先保存反汇编文本和偏移位置,避免把一次排查变成一次不可逆的执行。

常见延伸问题

  • 为什么 pickletools.dis 看不到原始对象的完整业务结构? 因为它展示的是协议操作码和参数,不负责执行对象构造,也不保证还原出业务层字段关系。
  • genops 返回的 pos 有什么用? 它对应操作码在 pickle 字节流中的位置,适合把异常或可疑结构定位回原始文件偏移。
  • 看完反汇编后能直接调用 pickle.loads 吗? 只有输入来源、完整性和运行环境都可信时才考虑;反汇编本身不能替代信任判断。
版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Python types.MappingProxyType 怎样创建只读映射视图Python types.MappingProxyType 怎样创建只读映射视图
上一篇
Python types.MappingProxyType 怎样创建只读映射视图
Python typing.Self 怎样标注链式调用的返回类型
下一篇
Python typing.Self 怎样标注链式调用的返回类型
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    821次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    814次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    904次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    876次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    696次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码