pickletools no Python: analise pickles

Publicado em: 05/08/2026
Tempo de leitura: 7 minutos
Monitor com código binário representando análise de opcodes pickle com pickletools no Python

O formato pickle representa objetos Python como uma sequência de instruções. Carregar um arquivo com pickle.load() executa essas instruções e pode importar funções ou chamar construtores, por isso dados não confiáveis representam risco de execução de código. O módulo pickletools no Python desmonta o fluxo em opcodes legíveis sem executar o pickle, ajudando a estudar protocolos, investigar arquivos e otimizar serializações.

Neste guia, você aprenderá a usar a interface de terminal, dis(), genops() e optimize(), além de reconhecer limites de segurança. O conteúdo complementa nossos artigos sobre pickle no Python, bytecode com dis, shelve, traceback e comparação de arquivos.

Por que pickle exige cuidado

Pickle não é apenas um contêiner passivo de dados. O fluxo contém instruções para reconstruir objetos e pode referenciar globals, callables e mecanismos de redução.

import pickle

with open("dados.pickle", "rb") as arquivo:
    objeto = pickle.load(arquivo)

Esse código é seguro somente quando o arquivo vem de uma fonte totalmente confiável e sua integridade está protegida. Nunca carregue um upload, anexo ou arquivo baixado aleatoriamente.

O papel de pickletools

Pickletools interpreta a estrutura do formato e exibe seus opcodes sem executar as operações de reconstrução.

A documentação oficial de pickletools informa que o módulo é voltado principalmente a desenvolvedores do pickle e ferramentas de baixo nível, mas suas funções também são úteis em auditoria e aprendizado.

Criar um pickle de teste

import pickle

conteudo = {
    "nome": "Ana",
    "pontos": [10, 20, 30],
}

dados = pickle.dumps(
    conteudo,
    protocol=pickle.HIGHEST_PROTOCOL,
)

with open("exemplo.pickle", "wb") as arquivo:
    arquivo.write(dados)

Use dados criados localmente para aprender. Não execute pickle.loads() apenas para descobrir o conteúdo de um arquivo desconhecido.

Desmontar pela linha de comando

python -m pickletools exemplo.pickle

A saída mostra posição, byte do opcode, nome, argumento e informações adicionais. No final, aparece o maior protocolo exigido pelas instruções encontradas.

pickletools versus python -m pickle

python -m pickle carrega o objeto para mostrar sua representação. Isso executa o fluxo e não deve ser usado em arquivos não confiáveis.

python -m pickletools desmonta o formato e é a opção mais segura para inspeção estrutural. Ainda assim, limite tamanho e tempo ao processar entradas hostis.

Anotar cada opcode

A opção -a adiciona descrições curtas.

python -m pickletools -a exemplo.pickle

A anotação ajuda a entender o efeito de operações como PROTO, FRAME, MARK, MEMOIZE, BINUNICODE e STOP.

Salvar a desmontagem

python -m pickletools \
  -o relatorio.txt \
  exemplo.pickle

Não confunda o relatório textual com uma versão sanitizada do objeto. Ele ainda pode conter strings, nomes e outros dados sensíveis presentes no arquivo.

Controlar indentação

-l define quantos espaços são usados a cada nível iniciado por MARK.

python -m pickletools -l 2 exemplo.pickle

A indentação é apenas visual e não altera o arquivo original.

Vários arquivos e preâmbulo

A opção -p imprime um texto antes de cada arquivo.

python -m pickletools \
  -p '=== próximo pickle ===' \
  a.pickle b.pickle

Isso ajuda a separar relatórios concatenados.

Preservar memo entre arquivos

-m mantém o memo ao desmontar vários fluxos.

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

Use somente quando os pickles foram produzidos por um processo que compartilha memo de forma compatível. Para arquivos independentes, o padrão separado é mais claro.

Ler de stdin

cat exemplo.pickle | python -m pickletools -

Em pipelines, evite misturar dados binários e logs no mesmo fluxo. Defina limites de tamanho antes de encaminhar uploads.

Usar dis() programaticamente

pickletools.dis() escreve uma desmontagem simbólica em um objeto de arquivo.

import io
import pickletools

saida = io.StringIO()
pickletools.dis(
    dados,
    out=saida,
    annotate=40,
)

print(saida.getvalue())

pickle pode ser bytes ou um objeto de arquivo. out usa sys.stdout por padrão.

Memo programático

O parâmetro memo recebe um dicionário compartilhado entre desmontagens.

memo = {}

pickletools.dis(dados_a, memo=memo)
pickletools.dis(dados_b, memo=memo)

O memo representa objetos já armazenados no protocolo. Compartilhá-lo sem que os fluxos tenham relação pode produzir interpretações incorretas.

Iterar opcodes com genops()

genops() fornece triplas (opcode, argumento, posição).

for opcode, argumento, posicao in pickletools.genops(dados):
    print(
        posicao,
        opcode.name,
        argumento,
    )

O objeto OpcodeInfo contém nome, código, documentação, efeito de pilha, protocolo mínimo e outras informações internas.

Criar um relatório estruturado

def relatorio_pickle(dados: bytes):
    for opcode, argumento, posicao in pickletools.genops(dados):
        yield {
            "posicao": posicao,
            "opcode": opcode.name,
            "protocolo": opcode.proto,
            "argumento": repr(argumento),
        }

Limite o tamanho de repr(argumento), pois strings e blobs podem ser enormes.

Identificar operações sensíveis

Uma auditoria pode sinalizar opcodes relacionados à reconstrução de globals e chamadas.

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

for opcode, argumento, posicao in pickletools.genops(dados):
    if opcode.name in ALERTAS:
        print("Atenção:", posicao, opcode.name, argumento)

Uma allowlist ou blocklist de opcodes não torna o unpickle seguro. Combinações, extensões e objetos permitidos ainda podem criar comportamentos perigosos.

Protocolos

Pickle possui várias versões de protocolo. Protocolos mais novos podem usar frames, memoização eficiente e suporte melhor a objetos grandes.

import pickle

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

O protocolo não é equivalente à versão do Python, embora versões do interpretador definam quais protocolos são suportados.

PROTO e maior protocolo usado

O opcode PROTO declara uma versão, mas a desmontagem também calcula o maior protocolo exigido pelas operações. Fluxos antigos podem não começar com PROTO.

Ferramentas devem observar a sequência completa, não apenas o primeiro byte.

Frames

Protocolos modernos podem dividir o fluxo com FRAME. Frames ajudam o unpickler a processar blocos e reduzir chamadas de leitura.

Um tamanho de frame declarado não deve levar uma ferramenta a alocar memória ilimitada. Leia entradas em ambiente controlado e imponha tamanho máximo ao arquivo.

Memo

O memo evita serializar repetidamente o mesmo objeto e preserva referências compartilhadas.

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

pickletools.dis(dados)

A desmontagem mostra armazenamento e recuperação da referência. Isso explica por que os dois itens após unpickle apontam para a mesma lista.

Otimizar com optimize()

pickletools.optimize() remove opcodes PUT não utilizados e devolve um pickle equivalente.

otimizado = pickletools.optimize(dados)

print(len(dados), len(otimizado))

O resultado pode ocupar menos espaço e ser carregado mais rapidamente. Não aplique a função a dados não confiáveis sem limites, e não carregue o resultado para “validar” segurança.

Verificar equivalência em dados confiáveis

original = pickle.loads(dados)
otimizado_objeto = pickle.loads(otimizado)
assert original == otimizado_objeto

Esse teste executa pickle e deve ser feito apenas com dados criados ou autenticados pelo próprio sistema.

Assinar pickles confiáveis

Se uma aplicação realmente precisa armazenar pickle, proteja integridade e autenticidade com HMAC ou assinatura e mantenha a chave separada.

A assinatura impede adulteração detectável, mas não transforma arquivos de terceiros em dados confiáveis. Ela só confirma que o conteúdo foi produzido por uma entidade que possui a chave.

Preferir formatos de dados

Para interoperabilidade ou entrada externa, prefira JSON, MessagePack, Protocol Buffers ou um esquema de banco. Esses formatos representam dados, não instruções arbitrárias de reconstrução Python.

Pickle é apropriado principalmente para comunicação e persistência internas entre componentes confiáveis e compatíveis.

Limites de recurso

Mesmo sem executar opcodes, uma análise pode consumir recursos com arquivos grandes, argumentos extensos ou sequências artificiais.

Imponha tamanho máximo, timeout, limite de opcodes e saída truncada. Para uploads, execute a desmontagem em subprocesso com memória e CPU limitadas.

Exemplo de scanner isolado

from pathlib import Path


def analisar(caminho: Path, limite=10_000_000):
    if caminho.stat().st_size > limite:
        raise ValueError("Arquivo grande demais")

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

Abra somente arquivos regulares dentro de uma raiz autorizada e evite seguir symlinks inesperados.

pickletools não valida segurança

A desmontagem ajuda analistas a compreender o fluxo, mas não prova que ele é inofensivo. A semântica final depende dos objetos importados, reducers e código disponível no ambiente.

A regra permanece: nunca faça unpickle de uma fonte não confiável.

Erros frequentes

  • Usar python -m pickle para arquivo desconhecido.
  • Carregar o objeto após desmontar para confirmar conteúdo.
  • Confiar em uma blocklist pequena de opcodes.
  • Compartilhar memo entre arquivos independentes.
  • Gerar relatórios sem truncar argumentos.
  • Tratar optimize como sanitização.
  • Ignorar tamanho e quantidade de operações.
  • Presumir compatibilidade eterna entre versões.

Boas práticas

  • Use pickletools para inspeção não executável.
  • Mantenha arquivos desconhecidos longe de pickle.load.
  • Limite tamanho, tempo, opcodes e saída.
  • Execute análises hostis em subprocesso isolado.
  • Use formatos de dados para interfaces externas.
  • Assine apenas pickles produzidos internamente.
  • Registre protocolo e versão do Python.
  • Teste otimização somente com dados confiáveis.

Conclusão

O módulo pickletools no Python revela as instruções que formam um pickle sem executar seu bytecode. A interface de terminal e dis() produzem desmontagens legíveis, genops() permite análise estruturada e optimize() remove memoizações desnecessárias.

Essa visibilidade ajuda a aprender o formato e investigar arquivos, mas não torna pickle seguro. A proteção real vem de não carregar entradas desconhecidas, impor limites e preferir formatos puramente declarativos nas fronteiras do sistema.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Programador analisando código para identificar tipos MIME de arquivos no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    mimetypes.guess_file_type: detecte tipos MIME

    Aprenda mimetypes.guess_file_type no Python para detectar tipos MIME em caminhos, URLs, uploads e respostas HTTP com fallbacks seguros.

    Ler mais

    Tempo de leitura: 6 minutos
    13/09/2026
    Componentes de servidor representando interpretadores Python executando em paralelo
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    InterpreterPoolExecutor: paralelismo real no Python

    Aprenda InterpreterPoolExecutor no Python para executar tarefas CPU-bound em interpretadores isolados com paralelismo real.

    Ler mais

    Tempo de leitura: 6 minutos
    13/09/2026
    Microprocessador representando CPUs disponíveis para um processo Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    os.process_cpu_count: conte CPUs disponíveis

    Aprenda os.process_cpu_count no Python para dimensionar workers conforme as CPUs realmente disponíveis ao processo.

    Ler mais

    Tempo de leitura: 4 minutos
    12/09/2026
    Laptop com código digital representando dados BLOB no SQLite
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    sqlite3.Blob: leia BLOBs sem carregar tudo na memória

    Aprenda sqlite3.Blob no Python para ler e gravar BLOBs em partes, reduzir memória e trabalhar com dados binários no SQLite.

    Ler mais

    Tempo de leitura: 5 minutos
    12/09/2026
    Análise estatística para random.binomialvariate no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    random.binomialvariate: simule distribuições binomiais

    Aprenda random.binomialvariate no Python para simular sucessos, validar probabilidades e analisar cenários binomiais com clareza.

    Ler mais

    Tempo de leitura: 6 minutos
    11/09/2026
    Código Python analisado com inspect.signature.bind
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    inspect.signature.bind: valide argumentos de funções

    Aprenda inspect.signature.bind no Python para validar argumentos, aplicar padrões e criar decorators e APIs dinâmicas com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    11/09/2026