
Ein radare2-Skript zum Parsen der gopclntab, um das Reverse Engineering von Go-Binärdateien zu erleichtern.
Hinweis: Diese Funktion wurde direkt in den Kern von radare2 als gopc analysis plugin implementiert.
Ein radare2-basierter Go gopclntab-Parser zum Wiederherstellen von Funktionssymbolen aus Go-Binaries, einschließlich vollständig gestrippter. Unterstützt ELF-, Mach-O- und PE-Binaries für Go-Versionen 1.2, 1.16, 1.18 und 1.20+.
Jedes Go 1.2+ Binary enthält einen Datenbereich namens gopclntab (Program Counter Line Table), den die Go-Laufzeit für Stack Traces, Panic-Meldungen, Garbage Collection und Debugger-Unterstützung verwendet. Es ist eine der wertvollsten Quellen für Symbolinformationen in Go-Binaries.
r2gopclntabParser liest diesen Bereich über radare2, parst die versionsspezifischen Strukturen und gibt entweder die wiederhergestellte Funktionsliste (mit Adressen, Quelldateien und Zeilennummern) aus oder wendet die wiederhergestellten Namen wieder in die offene radare2-Sitzung als Funktionsdefinitionen, Flags und Kommentare an.
| Abhängigkeit | Mindestversion |
|---|---|
| Python 3 | 3.8+ |
| radare2 | 5.0+ (getestet mit 6.0.9) |
| r2pipe | beliebig |
Es werden keine weiteren Python-Pakete benötigt. Das Skript verwendet nur die Standardbibliothek (struct, json, argparse, os, sys) sowie 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-Referenz```
usage: r2_gopclntab.py [-h] [-f FILE] [-n FUNCNAME] [-v] [-l]
[--apply] [--json] [--files] [--r2pipe]
| Flag | Beschreibung |
|---|---|
-f FILE, --file FILE | Pfad zur zu analysierenden Go-Binärdatei. Das Skript startet eine eigene r2-Instanz. |
--r2pipe | An eine bereits laufende r2-Sitzung anhängen (zur Verwendung innerhalb der r2-Konsole). |
| Flag | Beschreibung |
|---|---|
-l, --list | Jede wiederhergestellte Funktion mit ihrer Adresse ausgeben. |
-n NAME, --funcname NAME | Nur Funktionen ausgeben, deren Name NAME enthält (Teilstring-Übereinstimmung). Wenn eine exakte Übereinstimmung existiert, wird ihre Adresse separat ausgegeben. |
-v, --verbose | Fortschrittsmeldungen, geparste Header-Felder und interne Offsets ausgeben. |
--apply | Wiederhergestellte Funktionsnamen in die radare2-Sitzung als Funktionsdefinitionen (af+), Flags im go.-Flagspace und Kommentare mit dem ursprünglichen Go-Namen und Quellort schreiben. |
--json | Den Header und die vollständige Funktionsliste als JSON nach stdout ausgeben. |
--files | Die Liste der aus der Dateitabelle extrahierten Quellcodedateipfade ausgeben. |
-h, --help | Hilfemeldung anzeigen. |
Flags können frei kombiniert werden. Wenn kein Ausgabe-Flag angegeben ist, werden standardmäßig der Header und die vollständige Funktionsliste ausgegeben.
Alle untenstehenden Beispiele wurden gegen eine gestrippte Go 1.26 Mach-O arm64-Binärdatei ausgeführt (kompiliert mit -ldflags="-s -w"). Das Testprogramm definiert main.main, main.fibonacci, main.helloWorld und main.addNumbers. Der Compiler hat helloWorld und addNumbers inline eingefügt, sodass sie nicht im gopclntab erscheinen.
Bei Ausführung ohne Flags (oder nur -f) wird der geparste Header gefolgt von der vollständigen Funktionstabelle ausgegeben:```
$ python3 r2_gopclntab.py -f ./gotest_stripped
### Projektzuordnung
[COBALT](https://github.com/MobSF/mobsfscan/tree/main/mobsfscan/rules/semgrep) enthält einen robusten Satz an Regeln für Android-Sicherheit und Datenschutz.```
============================================================
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
Fügt Abschnitts-Scan-Fortschritt, textStart-Auflösungsdetails und interne Offset-Informationen hinzu:``` $ python3 r2_gopclntab.py -f ./gotest_stripped -v
[No content provided after INPUT:]```
[*] 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+
...