pickletools en Python: analiza pickles

Publicado el: 05/08/2026
Tempo de leitura: 6 minutos
Monitor con código binario que representa análisis de opcodes pickle con pickletools en Python

El formato pickle representa objetos Python como una secuencia de instrucciones. Cargar un archivo con pickle.load() ejecuta esas instrucciones y puede importar funciones o llamar constructores, por lo que los datos no confiables implican riesgo de ejecución de código. El módulo pickletools en Python desmonta el flujo en opcodes legibles sin ejecutar el pickle, ayudando a estudiar protocolos, investigar archivos y optimizar serializaciones.

Esta guía explica la interfaz de terminal, dis(), genops() y optimize(), además de sus límites de seguridad. Complementa nuestros artículos sobre pickle en Python, bytecode con dis, shelve, tracebacks y comparación de archivos.

Por qué pickle exige cuidado

Un pickle no es un contenedor pasivo de datos. Su flujo contiene operaciones para reconstruir objetos y puede referenciar globals, callables y mecanismos de reducción.

import pickle

with open("datos.pickle", "rb") as archivo:
    objeto = pickle.load(archivo)

Este código es seguro únicamente cuando el archivo procede de una fuente totalmente confiable y su integridad está protegida. Nunca cargues un upload, adjunto o archivo descargado al azar.

El papel de pickletools

Pickletools interpreta la estructura del formato y muestra los opcodes sin ejecutar la reconstrucción.

La documentación oficial de pickletools indica que el módulo resulta especialmente útil para desarrolladores de pickle y del núcleo de Python, aunque también sirve para auditoría y aprendizaje.

Crear un pickle de prueba

import pickle

contenido = {
    "nombre": "Ana",
    "puntos": [10, 20, 30],
}

datos = pickle.dumps(
    contenido,
    protocol=pickle.HIGHEST_PROTOCOL,
)

with open("ejemplo.pickle", "wb") as archivo:
    archivo.write(datos)

Usa datos generados localmente para aprender. No llames a pickle.loads() solo para descubrir el contenido de un archivo desconocido.

Desmontar desde la terminal

python -m pickletools ejemplo.pickle

La salida muestra posición, byte del opcode, nombre, argumento e información adicional. Al final informa el protocolo más alto requerido por las instrucciones encontradas.

pickletools frente a python -m pickle

python -m pickle carga el objeto para mostrar su representación. Esto ejecuta el flujo y no debe utilizarse con archivos no confiables.

python -m pickletools desmonta el formato y es la opción más segura para inspección estructural. Aun así, aplica límites de recursos a entradas hostiles.

Anotar cada opcode

La opción -a añade una descripción corta a cada línea.

python -m pickletools -a ejemplo.pickle

Las anotaciones ayudan a comprender operaciones como PROTO, FRAME, MARK, MEMOIZE, BINUNICODE y STOP.

Guardar la desmontagem

python -m pickletools \
  -o reporte.txt \
  ejemplo.pickle

No consideres el reporte textual datos sanitizados. Puede incluir strings, nombres y valores sensibles presentes en el flujo original.

Controlar la indentación

-l define la cantidad de espacios usada por cada nivel iniciado con MARK.

python -m pickletools -l 2 ejemplo.pickle

La indentación es únicamente visual y no modifica el pickle.

Varios archivos y preámbulo

La opción -p imprime un texto antes de cada archivo.

python -m pickletools \
  -p '=== siguiente pickle ===' \
  a.pickle b.pickle

Esto facilita leer reportes concatenados.

Conservar memo entre archivos

-m mantiene el memo al desmontar varios flujos.

python -m pickletools -m parte1.pickle parte2.pickle

Úsalo solo cuando los archivos fueron producidos por un proceso compatible que comparte memo. Los archivos independientes deben analizarse por separado.

Leer desde stdin

cat ejemplo.pickle | python -m pickletools -

No mezcles datos binarios y mensajes de log en el mismo stream. Impone un tamaño máximo antes de enviar uploads.

Usar dis() programáticamente

pickletools.dis() escribe una desmontagem simbólica en un objeto de archivo.

import io
import pickletools

salida = io.StringIO()
pickletools.dis(
    datos,
    out=salida,
    annotate=40,
)

print(salida.getvalue())

El argumento pickle puede ser bytes o un objeto file-like. La salida predeterminada es sys.stdout.

Memo programático

El parámetro memo recibe un diccionario compartido entre desmontajes.

memo = {}

pickletools.dis(datos_a, memo=memo)
pickletools.dis(datos_b, memo=memo)

El memo representa objetos guardados por el protocolo. Compartirlo entre flujos no relacionados puede producir interpretaciones incorrectas.

Iterar opcodes con genops()

genops() produce triples (opcode, argumento, posición).

for opcode, argumento, posicion in pickletools.genops(datos):
    print(
        posicion,
        opcode.name,
        argumento,
    )

Cada OpcodeInfo contiene nombre, código, documentación, efecto de pila, protocolo mínimo y otros metadatos de bajo nivel.

Crear un reporte estructurado

def reporte_pickle(datos: bytes):
    for opcode, argumento, posicion in pickletools.genops(datos):
        yield {
            "posicion": posicion,
            "opcode": opcode.name,
            "protocolo": opcode.proto,
            "argumento": repr(argumento),
        }

Limita la longitud de repr(argumento) porque strings y blobs pueden ser enormes.

Señalar operaciones sensibles

Una auditoría puede destacar opcodes relacionados con globals y llamadas de reconstrucción.

ALERTAS = {
    "GLOBAL",
    "STACK_GLOBAL",
    "REDUCE",
    "BUILD",
    "OBJ",
    "INST",
    "NEWOBJ",
    "NEWOBJ_EX",
}

for opcode, argumento, posicion in pickletools.genops(datos):
    if opcode.name in ALERTAS:
        print("Atención:", posicion, opcode.name, argumento)

Una allowlist o blocklist de opcodes no hace seguro el unpickle. Combinaciones, extensiones y objetos aparentemente permitidos todavía pueden ser peligrosos.

Protocolos

Pickle posee varias versiones de protocolo. Los protocolos modernos pueden incorporar frames, memoización más eficiente y mejor soporte para objetos grandes.

import pickle

for protocolo in range(pickle.HIGHEST_PROTOCOL + 1):
    datos = pickle.dumps({"x": 1}, protocol=protocolo)
    nombres = [op.name for op, _, _ in pickletools.genops(datos)]
    print(protocolo, nombres)

El protocolo no equivale a la versión de Python, aunque el intérprete determina cuáles están disponibles.

PROTO y protocolo más alto

El opcode PROTO declara una versión, pero la desmontagem también calcula el protocolo más alto exigido por las operaciones. Los flujos antiguos pueden no comenzar con PROTO.

Las herramientas deben analizar la secuencia completa.

Frames

Los protocolos modernos pueden dividir el flujo con FRAME. Los frames ayudan al unpickler a procesar bloques y reducir llamadas de lectura.

Un tamaño declarado no debe provocar una asignación ilimitada. Procesa entradas desconocidas con límites de memoria y tamaño.

El memo

El memo evita serializar repetidamente el mismo objeto y conserva referencias compartidas.

lista = []
objeto = [lista, lista]
datos = pickle.dumps(objeto, protocol=4)

pickletools.dis(datos)

La salida muestra almacenamiento y recuperación de la referencia, explicando por qué ambos elementos apuntan a la misma lista tras el unpickle.

Optimizar con optimize()

pickletools.optimize() elimina opcodes PUT no utilizados y devuelve un flujo equivalente.

optimizado = pickletools.optimize(datos)

print(len(datos), len(optimizado))

Puede ocupar menos espacio, transmitirse más rápido y cargarse con mayor eficiencia. Optimize no es un sanitizador.

Verificar solamente datos confiables

original = pickle.loads(datos)
objeto_optimizado = pickle.loads(optimizado)
assert original == objeto_optimizado

Este test ejecuta pickle y debe hacerse únicamente con datos generados o autenticados por el propio sistema.

Autenticar pickles internos

Cuando una aplicación realmente necesita pickle, protege integridad y autenticidad con HMAC o firma y conserva la clave por separado.

Una firma detecta alteraciones, pero no vuelve confiable contenido de terceros. Solo prueba que un poseedor de la clave aprobó el flujo.

Preferir formatos de datos en fronteras

Para entrada externa e interoperabilidad, elige JSON, MessagePack, Protocol Buffers o un esquema de base de datos. Estos formatos representan datos y no instrucciones arbitrarias de reconstrucción Python.

Pickle resulta más adecuado para comunicación y persistencia internas entre componentes confiables y compatibles.

Límites de recursos

Incluso sin ejecutar opcodes, el análisis puede consumir recursos mediante archivos enormes, argumentos extensos o secuencias artificiales.

Define tamaño máximo, timeout, cantidad de opcodes y salida truncada. Analiza uploads en un subprocess con CPU y memoria limitadas.

Scanner aislado

from pathlib import Path


def analizar(ruta: Path, limite=10_000_000):
    if ruta.stat().st_size > limite:
        raise ValueError("Archivo demasiado grande")

    with ruta.open("rb") as archivo:
        for indice, (op, arg, pos) in enumerate(
            pickletools.genops(archivo)
        ):
            if indice > 100_000:
                raise ValueError("Demasiados opcodes")
            yield pos, op.name, arg

Acepta solo archivos regulares dentro de una raíz autorizada y evita symlinks inesperados.

pickletools no certifica seguridad

La desmontagem ayuda a comprender un flujo, pero no prueba que sea inofensivo. El comportamiento final depende de objetos importados, reducers, extensiones y código disponible.

La regla permanece: nunca hagas unpickle de datos no confiables.

Errores frecuentes

  • Usar python -m pickle con un archivo desconocido.
  • Cargar el objeto después de desmontar para confirmar contenido.
  • Confiar en una pequeña blocklist.
  • Compartir memo entre archivos independientes.
  • Generar salida sin limitar argumentos.
  • Tratar optimize como sanitización.
  • Ignorar límites de tamaño y opcodes.
  • Suponer compatibilidad permanente entre versiones.

Buenas prácticas

  • Usa pickletools para inspección sin ejecución.
  • Mantén archivos desconocidos lejos de pickle.load.
  • Limita tamaño, tiempo, opcodes y salida.
  • Analiza archivos hostiles en subprocess aislado.
  • Usa formatos declarativos en interfaces externas.
  • Autentica solo pickles generados internamente.
  • Registra protocolo y versión.
  • Prueba optimize solo con datos confiables.

Conclusión

El módulo pickletools en Python revela las instrucciones de un pickle sin ejecutar su bytecode. La terminal y dis() generan desmontajes legibles, genops() permite análisis estructurado y optimize() elimina memoizaciones innecesarias.

Esta visibilidad ayuda a aprender el formato e investigar archivos, pero no hace seguro pickle. La protección real consiste en no cargar entradas desconocidas, imponer límites y preferir formatos declarativos en las fronteras del sistema.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Código fuente en pantalla que representa análisis con tokenize en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    tokenize en Python: analiza código fuente

    Aprende tokenize en Python para inspeccionar tokens, comentarios, indentación, codificación, posiciones y reconstruir código de forma segura.

    Ler mais

    Tempo de leitura: 7 minutos
    05/08/2026
    Desarrollador trabajando en automatización de build y compilación de directorios con compileall en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    compileall en Python: compila directorios

    Aprende compileall en Python para compilar directorios, generar pyc en paralelo, filtrar rutas y controlar optimización e invalidación.

    Ler mais

    Tempo de leitura: 6 minutos
    04/08/2026
    Monitor con código binario que representa generación de archivos pyc con py_compile en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    py_compile en Python: genera archivos pyc

    Aprende py_compile en Python para generar archivos pyc, validar sintaxis y controlar optimización e invalidación por timestamp o hash.

    Ler mais

    Tempo de leitura: 6 minutos
    04/08/2026
    Editor de código que representa corrección de tabs y espacios con tabnanny en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    tabnanny en Python: corrige la indentación

    Aprende tabnanny en Python para detectar tabs y espacios ambiguos, revisar proyectos y evitar TabError e IndentationError.

    Ler mais

    Tempo de leitura: 7 minutos
    04/08/2026
    Código fuente y sintaxis que representan análisis léxico con tokenize en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    tokenize en Python: analiza código fuente

    Aprende tokenize en Python para analizar tokens, comentarios, encoding, posiciones y reconstruir código fuente con seguridad.

    Ler mais

    Tempo de leitura: 6 minutos
    04/08/2026
    Terminal de programación que representa compilación de entradas interactivas con codeop en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    codeop en Python: compila entradas interactivas

    Aprende codeop en Python para detectar entradas completas, compilar comandos de REPL y conservar __future__ con seguridad.

    Ler mais

    Tempo de leitura: 7 minutos
    04/08/2026