
Un script radare2 pour parser le gopclntab afin de faciliter la rétro-ingénierie des binaires Go.
Note : Cette fonctionnalité a été implémentée directement dans le cœur de radare2 sous la forme du plugin d'analyse gopc
Un analyseur gopclntab basé sur radare2 pour récupérer les symboles de fonctions de binaires Go, y compris ceux entièrement strippés. Prend en charge les binaires ELF, Mach-O et PE pour les versions Go 1.2, 1.16, 1.18 et 1.20+.
Chaque binaire Go 1.2+ intègre une région de données appelée gopclntab (Program Counter Line Table) que le runtime Go utilise pour les traces de pile, les messages de panique, le ramasse-miettes et le support du débogueur. C'est l'une des sources d'information les plus précieuses sur les symboles dans les binaires Go.
r2gopclntabParser lit cette région via radare2, analyse les structures spécifiques à la version, et soit affiche la liste des fonctions récupérées (avec adresses, fichiers sources et numéros de ligne), soit applique les noms récupérés dans la session radare2 ouverte en tant que définitions de fonctions, flags et commentaires.
| Dépendance | Version minimale |
|---|---|
| Python 3 | 3.8+ |
| radare2 | 5.0+ (testé sur 6.0.9) |
| r2pipe | toute |
Aucun autre paquet Python n'est requis. Le script utilise uniquement la bibliothèque standard (struct, json, argparse, os, sys) ainsi que 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
---
## Référence CLI```
usage: r2_gopclntab.py [-h] [-f FILE] [-n FUNCNAME] [-v] [-l]
[--apply] [--json] [--files] [--r2pipe]
| Flag | Description |
|---|---|
-f FILE, --file FILE | Chemin vers le binaire Go à analyser. Le script lance sa propre instance r2. |
--r2pipe | Se connecter à une session r2 déjà en cours (pour une utilisation dans la console r2). |
| Flag | Description |
|---|---|
-l, --list | Afficher chaque fonction récupérée avec son adresse. |
-n NAME, --funcname NAME | Afficher uniquement les fonctions dont le nom contient NAME (correspondance de sous-chaîne). Si une correspondance exacte existe, son adresse est affichée séparément. |
-v, --verbose | Afficher les messages de progression, les champs d'en-tête analysés et les décalages internes. |
--apply | Écrire les noms de fonctions récupérés dans la session radare2 en tant que définitions de fonctions (af+), flags dans l'espace de flags go., et commentaires avec le nom Go d'origine et l'emplacement source. |
--json | Sortir l'en-tête et la liste complète des fonctions au format JSON sur stdout. |
--files | Afficher la liste des chemins de fichiers sources extraits de la table des fichiers. |
-h, --help | Afficher le message d'aide. |
Les flags peuvent être combinés librement. Lorsqu'aucun flag de sortie n'est donné, le comportement par défaut est d'afficher l'en-tête et la liste complète des fonctions.
Tous les exemples ci-dessous ont été exécutés sur un binaire Go 1.26 Mach-O arm64 élagué (construit avec -ldflags="-s -w"). Le programme de test définit main.main, main.fibonacci, main.helloWorld et main.addNumbers. Le compilateur a intégré helloWorld et addNumbers, donc ils n'apparaissent pas dans le gopclntab.
En exécutant sans flag (ou juste -f), on obtient l'en-tête analysé suivi de la table complète des fonctions :```
$ python3 r2_gopclntab.py -f ./gotest_stripped
.
To be precise, I'll just not write anything. However, the API might require a message. I'll send an empty string.```
============================================================
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
Ajoute la progression de l'analyse des sections, les détails de résolution de textStart et les informations de décalage interne :``` $ python3 r2_gopclntab.py -f ./gotest_stripped -v
### Documentation
Pour des instructions d'utilisation complètes et la documentation, consultez : [NetDoc Documentation](https://github.com/```
[*] 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