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

    Archivadores organizados que representan aplicaciones empaquetadas en archivos .pyz con zipapp en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    zipapp en Python: crea ejecutables .pyz

    Aprende zipapp en Python para empaquetar aplicaciones en archivos .pyz, definir entry points, incluir dependencias y distribuir con seguridad.

    Ler mais

    Tempo de leitura: 6 minutos
    13/08/2026
    Editor de código que representa autocompletado de REPL con rlcompleter en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    rlcompleter en Python: autocompletar REPL

    Aprende rlcompleter en Python para añadir autocompletado a REPLs, consolas y editores, controlar namespaces y evitar efectos secundarios.

    Ler mais

    Tempo de leitura: 6 minutos
    13/08/2026
    Ventana de terminal que representa una consola interactiva creada con cmd en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    cmd en Python: crea consolas interactivas

    Aprende cmd en Python para crear consolas interactivas con comandos, ayuda, historial, autocompletado, pruebas y control seguro de acciones.

    Ler mais

    Tempo de leitura: 5 minutos
    12/08/2026
    Terminal interactivo que representa un REPL personalizado creado con el módulo code en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    code en Python: crea un REPL personalizado

    Aprende el módulo code en Python para crear REPLs personalizados, controlar namespaces, prompts, salida, bloques incompletos y cierre local.

    Ler mais

    Tempo de leitura: 6 minutos
    12/08/2026
    Código de aplicación web que representa WSGI con wsgiref en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    wsgiref en Python: aplicaciones WSGI

    Aprende wsgiref en Python para crear y validar aplicaciones WSGI, probar environ y headers, enrutar solicitudes y ejecutar un servidor

    Ler mais

    Tempo de leitura: 4 minutos
    12/08/2026
    Protocolo seguro de Internet que representa preparación Unicode con stringprep en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    stringprep en Python: prepara Unicode

    Aprende stringprep en Python para aplicar tablas RFC 3454, mapear Unicode, rechazar caracteres prohibidos y validar reglas bidireccionales.

    Ler mais

    Tempo de leitura: 5 minutos
    12/08/2026