
pbtk v1.1.3
Um conjunto de ferramentas para engenharia reversa e fuzzing de aplicativos baseados em Protobuf
pbtk - Engenharia reversa de aplicativos Protobuf
Protobuf é um formato de serialização desenvolvido pelo Google e usado em um número crescente de aplicativos Android, web, desktop e outros. Consiste em uma linguagem para declarar estruturas de dados, que é então compilada para código ou outro tipo de estrutura, dependendo da implementação alvo.
pbtk (Protobuf toolkit) é um conjunto completo de scripts, acessível através de uma GUI unificada, que fornece duas funcionalidades principais:
-
Extrair estruturas Protobuf de programas, convertendo-as de volta para arquivos .proto legíveis, suportando várias implementações:
- Todos os principais runtimes Java (base, Lite, Nano, Micro, J2ME), com suporte total ao Proguard (2026: ainda funciona bem, mas principalmente com APKs antigos)
- Binários contendo metadados de reflexão embutidos (tipicamente C++, às vezes Java e a maioria das outras ligações) (2026: ainda funciona bem)
- Aplicativos web usando o runtime JsProtoUrl. (2026: precisa de uma atualização)
-
Editar, repetir e fazer fuzzing de dados enviados para endpoints de rede Protobuf, através de uma interface gráfica prática que permite editar ao vivo os campos de uma mensagem Protobuf e visualizar o resultado.
Instalação
O PBTK requer Python ≥ 3.5, PySide 6, Python-Protobuf 3 e alguns programas executáveis (chromium, jad, dex2jar...) para executar os scripts de extração.
Usuários Ubuntu podem instalá-lo usando snap:
$ sudo snap install pbtk
$ pbtk
Usuários Archlinux podem instalar diretamente através do pacote:
$ yay -S pbtk-git
$ pbtk
Na maioria das outras distribuições, você vai querer executá-lo diretamente:
# Para derivados Ubuntu/Debian em teste:
$ sudo apt install python3-pip git openjdk-8-jre python3-qtpy-pyside6
# Em seguida, usando UV:
$ sudo snap install --classic astral-uv
$ uv tool install pbtk
$ pbtk
# Ou usando pipx:
$ sudo apt install pipx
$ pipx install pbtk
$ pbtk
O Windows também é suportado (com os mesmos módulos necessários). Depois de executar a GUI, ela deve avisar sobre o que está faltando, dependendo do que você tentar fazer.
Uso da linha de comando (instalando via gerenciador de pacotes)
A GUI pode ser iniciada através do script principal:
pbtk
Os seguintes scripts também podem ser usados de forma autônoma, sem GUI:
pbtk-jar-extract [-h] input_file [output_dir]
pbtk-from-binary [-h] input_file [output_dir]
pbtk-web-extract [-h] input_url [output_dir] # Precisa de atualização para funcionar a partir de 2026
Quando instalado via snap, os comandos exatos diferem:
pbtk.jar-extract [-h] input_file [output_dir]
pbtk.from-binary [-h] input_file [output_dir]
pbtk.web-extract [-h] input_url [output_dir]
Uso da linha de comando (local)
A GUI pode ser iniciada através do script principal:
uv sync # Baixar dependências para a pasta .venv
source .venv/bin/activate # Colocar os scripts locais no $PATH para a sessão atual do shell
uv tool install -e . # Colocar os scripts locais no $PATH permanentemente
pbtk
Os seguintes scripts também podem ser usados de forma autônoma, sem GUI:
pbtk-jar-extract [-h] input_file [output_dir]
pbtk-from-binary [-h] input_file [output_dir]
pbtk-web-extract [-h] input_url [output_dir] # Precisa de atualização para funcionar a partir de 2026
Fluxo de trabalho típico
Digamos que você está fazendo engenharia reversa de um aplicativo Android. Você explorou um pouco o aplicativo com seu descompilador favorito e descobriu que ele transporta Protobuf como dados POST via HTTPS de forma típica.
Você abre o PBTK e é recebido de forma significativa:

O primeiro passo é obter seus .protos em formato de texto. Se você está mirando um aplicativo Android, soltar um APK e esperar deve fazer o trabalho mágico! (a menos que seja uma implementação realmente exótica)

Feito isso, você vai para ~/.pbtk/protos/<nome do seu APK> (através da linha de comando ou do botão na parte inferior da tela de boas-vindas para abrir o navegador de arquivos, da forma que preferir). Todos os .protos do aplicativo estão aqui.
De volta ao seu descompilador, você se deparou com a classe que constrói os dados enviados para o endpoint HTTPS que lhe interessa. Ela serializa a mensagem Protobuf chamando uma classe feita de código gerado.

Esta última classe deve ter uma correspondência perfeita dentro do seu diretório .protos (isto é, com.foo.bar.a.b corresponderá a com/foo/bar/a/b.proto). De qualquer forma, pesquisar seu nome deve permitir referenciá-la.
Isso é ótimo: a próxima coisa é ir para a Etapa 2, selecionar o arquivo .proto de entrada desejado e preencher algumas informações sobre seu endpoint.

Você também pode fornecer alguns dados Protobuf brutos de exemplo, que foram enviados para este endpoint, capturados através do mitmproxy ou Wireshark, e que você colará em formato hex.
Etapa 3 é sobre a parte divertida de clicar em botões e ver o que acontece! Você tem uma visualização em árvore representando cada campo na estrutura Protobuf (campos repetidos são sufixados por "+", campos obrigatórios não têm caixas de seleção).

Apenas passe o mouse sobre um campo para focá-lo. Se o campo for do tipo inteiro, use a roda do mouse para incrementar/decrementar. Informações sobre enum aparecem também ao passar o mouse.
Aqui está! Você pode determinar o significado de cada campo com isso. Se você extraiu .protos de código minificado, pode renomear campos de acordo com o que você percebe que eles significam, clicando em seus nomes.
Feliz engenharia reversa! 👌 🎉
Armazenamento local de dados
O PBTK armazena informações de .proto extraídas em ~/.pbtk/protos/ (ou %APPDATA%\pbtk\protos no Windows).
Você pode mover para dentro, mover para fora, renomear, editar ou apagar dados deste diretório diretamente através do seu navegador de arquivos e editor de texto comuns, é a forma esperada de fazer isso e não interferirá com o PBTK.
Endpoints baseados em HTTP são armazenados em ~/.pbtk/endpoints/ como objetos JSON. Esses objetos são arrays de pares de informações de requisição/resposta, que se parecem com isso:
[{
"request": {
"transport": "pburl",
"proto": "www.google.com/VectorTown.proto",
"url": "https://www.google.com/VectorTown",
"pb_param": "pb",
"samples": [{
"pb": "!....",
"hl": "fr"
}]
},
"response": {
"format": "other"
}
}]
Estrutura do código-fonte
O PBTK usa internamente dois tipos de módulos plugáveis: extratores e transportes.
- Um extrator suporta a extração de estruturas .proto de uma implementação ou plataforma Protobuf alvo.
Os extratores são definidos em src/pbtk/extractors/*.py. Eles são definidos como um método precedido por um decorador, assim:
@register_extractor(name = 'my_extractor',
desc = 'Extrair estruturas Protobuf de código Foobar (*.foo, *.bar)',
depends={'binaries': ['foobar-decompiler']})
def my_extractor(path):
# Carregar o conteúdo do arquivo de entrada `path` e fazer seu trabalho...
# Em seguida, gerar os .protos extraídos usando um gerador:
for i in do_your_extraction_work():
yield proto_name + '.proto', proto_contents
# Outros tipos de informação podem ser gerados, como informações de endpoint ou progresso a ser exibido.
- Um transporte suporta uma forma de desserializar, resserializar e enviar dados Protobuf pela rede. Por exemplo, o transporte mais comumente usado são dados POST brutos via HTTP.
Os transportes são definidos em src/pbtk/utils/transports.py. Eles são definidos como uma classe precedida por um decorador, assim:
@register_transport(
name = 'my_transport',
desc = 'Protobuf como dados POST brutos',
ui_data_form = 'hex strings'
)
class MyTransport():
def __init__(self, pb_param, url):
self.url = url
def serialize_sample(self, sample):
# Recebemos um exemplo de dados de entrada do usuário.
# Verificar se é válido na forma descrita através do parâmetro "ui_data_form", falhar com uma exceção ou retornar False caso contrário.
# Opcionalmente modificar esses dados antes de retorná-los.
bytes.fromhex(sample)
return sample
def load_sample(self, sample, pb_msg):
# Analisar os dados de entrada no objeto Protobuf fornecido.
pb_msg.ParseFromString(bytes.fromhex(sample))
def perform_request(self, pb_data, tab_data):
# Realizar uma requisição usando a URL e o objeto Protobuf fornecidos, e opcionalmente outros dados laterais específicos do transporte.
return post(url, pb_data.SerializeToString(), headers=USER_AGENT)
Melhorias futuras
O seguinte pode vir em versões futuras:
- Finalização da parte de fuzzing automático.
- Suporte para extração de extensões de código Java.
- Suporte para o runtime JSPB (JavaScript principal).
- Se houver qualquer outra plataforma que você deseje ver suportada, basta abrir uma issue e darei uma olhada.
Tentei fazer o meu melhor para produzir código minuciosamente legível e comentado (exceto para partes que são praticamente autoexplicativas, como conectar sinais da GUI) para a maioria dos módulos, para que você possa contribuir.
Licenciamento
pbtk é lançado sob a licença GNU GPL (eu, por meio deste, etc.).
Não há regra formalizada para o uso de maiúsculas/minúsculas do nome do projeto, a regra é apenas seguir seu coração ❤