
Um script radare2 para analisar o gopclntab para facilitar a Engenharia Reversa de binários Go.
Nota: Esta funcionalidade foi implementada diretamente no núcleo do radare2 como plugin de análise gopc
Um analisador gopclntab baseado em radare2 para recuperar símbolos de funções de binários Go, incluindo os totalmente despojados (stripped). Suporta binários ELF, Mach-O e PE nas versões Go 1.2, 1.16, 1.18 e 1.20+.
Todo binário Go 1.2+ incorpora uma região de dados chamada gopclntab (Tabela de Linhas de Contadores de Programa) que o runtime Go usa para stack traces, mensagens de pânico, coleta de lixo e suporte ao depurador. É uma das fontes mais valiosas de informação de símbolos em binários Go.
r2gopclntabParser lê essa região através do radare2, analisa as estruturas específicas da versão e imprime a lista de funções recuperadas (com endereços, arquivos fonte e números de linha) ou aplica os nomes recuperados de volta na sessão aberta do radare2 como definições de funções, flags e comentários.
| Dependência | Versão Mínima |
|---|---|
| Python 3 | 3.8+ |
| radare2 | 5.0+ (testado na 6.0.9) |
| r2pipe | qualquer |
Nenhum outro pacote Python é necessário. O script usa apenas a biblioteca padrão (struct, json, argparse, os, sys) além do r2pipe.
python3 r2_gopclntab.py -f ./mybinary -l
python3 r2_gopclntab.py -f ./mybinary -n main.main
python3 r2_gopclntab.py -f ./mybinary -v -l
python3 r2_gopclntab.py -f ./mybinary --apply
python3 r2_gopclntab.py -f ./mybinary --json
#!pipe python3 r2_gopclntab.py --r2pipe --apply -v
---
## CLI Referência```
usage: r2_gopclntab.py [-h] [-f FILE] [-n FUNCNAME] [-v] [-l]
[--apply] [--json] [--files] [--r2pipe]
| Flag | Descrição |
|---|---|
-f FILE, --file FILE | Caminho para o binário Go a ser analisado. O script cria sua própria instância r2. |
--r2pipe | Anexar a uma sessão r2 já em execução (para uso dentro do console r2). |
| Flag | Descrição |
|---|---|
-l, --list | Imprime todas as funções recuperadas com seus endereços. |
-n NAME, --funcname NAME | Imprime apenas funções cujo nome contenha NAME (correspondência de substring). Se houver uma correspondência exata, seu endereço é impresso separadamente. |
-v, --verbose | Imprime mensagens de progresso, campos de cabeçalho analisados e offsets internos. |
--apply | Escreve os nomes de funções recuperados na sessão radare2 como definições de função (af+), flags no espaço de flags go. e comentários com o nome Go original e localização do código-fonte. |
--json | Produz o cabeçalho e a lista completa de funções como JSON na saída padrão. |
--files | Imprime a lista de caminhos de arquivos fonte extraídos da tabela de arquivos. |
-h, --help | Exibe a mensagem de ajuda. |
As flags podem ser combinadas livremente. Quando nenhuma flag de saída é fornecida, o comportamento padrão é imprimir o cabeçalho e a lista completa de funções.
Todos os exemplos abaixo foram executados contra um binário Go 1.26 Mach-O arm64 stripado (compilado com -ldflags="-s -w"). O programa de teste define main.main, main.fibonacci, main.helloWorld e main.addNumbers. O compilador inlineou helloWorld e addNumbers, então eles não aparecem no gopclntab.
Executar sem flags (ou apenas -f) imprime o cabeçalho analisado seguido pela tabela completa de funções:```
$ python3 r2_gopclntab.py -f ./gotest_stripped
Please provide the Markdown content to translate.```
============================================================
Go pclntab Header
============================================================
Magic: 0xFFFFFFF1
Go version: 1.20+
Pointer size: 8
Min LC (quantum):4
Num functions: 2030
Num files: 261
funcnameOffset: 0x48
cuOffset: 0x14D20
filetabOffset: 0x158D8
pctabOffset: 0x19A98
pclnOffset: 0x55E40
============================================================
ADDRESS FUNCTION NAME
----------------------------------------------------------------------
0x100001000 go:buildid
0x100001070 internal/abi.BoundsDecode (/opt/homebrew/Cellar/go/1.26.1/libexec/src/internal/abi/bounds.go:86)
0x100001150 internal/abi.NoEscape (/opt/homebrew/Cellar/go/1.26.1/libexec/src/internal/abi/escape.go:19)
0x100001160 internal/abi.Kind.String (/opt/homebrew/Cellar/go/1.26.1/libexec/src/internal/abi/type.go:143)
0x1000011E0 internal/abi.TypeOf (/opt/homebrew/Cellar/go/1.26.1/libexec/src/internal/abi/type.go:181)
0x1000011F0 internal/abi.(*Type).ExportedMethods (/opt/homebrew/Cellar/go/1.26.1/libexec/src/internal/abi/type.go:453)
...
0x1000A0BB0 main.fibonacci (/tmp/gotest/main.go:13)
0x1000A0C20 main.main (/tmp/gotest/main.go:20)
0x1000A0D20 go:textfipsstart
0x1000A0D30 go:textfipsend
[+] 2030 function(s) shown
Adiciona progresso da varredura de seção, detalhes de resolução de textStart e informações de offset internas:``` $ python3 r2_gopclntab.py -f ./gotest_stripped -v
```
[*] Running radare2 analysis...
[*] Binary format: mach0, endian: little, arch: arm, bits: 64
[*] Scanning binary for gopclntab magic bytes...
[*] Scanning section '0.__TEXT.__text' (0x100001000, 0x9FD44)...
[*] Scanning section '1.__TEXT.__symbol_stub1' (0x1000A0D60, 0x2B8)...
[*] Scanning section '2.__TEXT.__rodata' (0x1000A1020, 0xACC2)...
[*] Scanning section '3.__TEXT.__gopclntab' (0x1000ABCE8, 0xA48AE)...
[*] Found magic at vaddr=0x1000ABCE8
[*] Parsed header: PcHeader(magic=0xFFFFFFF1, version=1.20+, ptrSize=8, minLC=4,
nfunc=2030, nfiles=261, textStart=0x0)
[*] textStart is 0, using .text section vaddr: 0x100001000
[*] Parsed 2030 functions
============================================================
Go pclntab Header
============================================================
Magic: 0xFFFFFFF1
Go version: 1.20+
...
Filtra a lista de funções por correspondência de substring. Se uma correspondência exata existir, seu endereço é impresso separadamente.