
Скрипт для radare2, который парсит gopclntab для облегчения реверс-инжиниринга Go-бинарных файлов.
Примечание: Эта функция была напрямую реализована в ядре radare2 как плагин анализа gopc
Парсер gopclntab на основе radare2 для восстановления символов функций из Go-бинарников, включая полностью лишённые символьной информации. Поддерживает ELF, Mach-O и PE бинарники для версий Go 1.2, 1.16, 1.18 и 1.20+.
Каждый Go-бинарник версии 1.2+ содержит область данных, называемую gopclntab (таблица строк программы и счётчика команд), которую рантайм Go использует для стековых трасс, сообщений паники, сборки мусора и поддержки отладчика. Это один из наиболее ценных источников информации о символах в Go-бинарниках.
r2gopclntabParser считывает эту область через radare2, анализирует версионно-зависимые структуры и либо выводит восстановленный список функций (с адресами, исходными файлами и номерами строк), либо применяет восстановленные имена обратно в открытую сессию radare2 в качестве определений функций, флагов и комментариев.
| Зависимость | Минимальная версия |
|---|---|
| Python 3 | 3.8+ |
| radare2 | 5.0+ (протестировано на 6.0.9) |
| r2pipe | любая |
Других пакетов Python не требуется. Скрипт использует только стандартную библиотеку (struct, json, argparse, os, sys) и 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```
usage: r2_gopclntab.py [-h] [-f FILE] [-n FUNCNAME] [-v] [-l]
[--apply] [--json] [--files] [--r2pipe]
| Flag | Description |
|---|---|
-f FILE, --file FILE | Путь к Go-бинарному файлу для анализа. Скрипт запускает собственный экземпляр r2. |
--r2pipe | Подключиться к уже запущенной сессии r2 (для использования внутри консоли r2). |
| Flag | Description |
|---|---|
-l, --list | Вывести каждую восстановленную функцию с её адресом. |
-n NAME, --funcname NAME | Вывести только функции, имя которых содержит NAME (поиск по подстроке). Если существует точное совпадение, его адрес выводится отдельно. |
-v, --verbose | Выводить сообщения о ходе работы, разобранные поля заголовка и внутренние смещения. |
--apply | Записать восстановленные имена функций в сессию radare2 как определения функций (af+), флаги в пространстве флагов go. и комментарии с исходным именем Go и местоположением в исходном коде. |
--json | Вывести заголовок и полный список функций в формате JSON в stdout. |
--files | Вывести список путей к исходным файлам, извлечённым из таблицы файлов. |
-h, --help | Показать справочное сообщение. |
Флаги можно свободно комбинировать. Если не указан флаг вывода, поведение по умолчанию — вывод заголовка и полного списка функций.
Все примеры ниже были запущены на обезображенном бинарном файле Go 1.26 Mach-O arm64
(собранном с -ldflags="-s -w"). Тестовая программа определяет main.main,
main.fibonacci, main.helloWorld и main.addNumbers. Компилятор
встроил helloWorld и addNumbers, поэтому они не появляются в gopclntab.
Запуск без флагов (или только с -f) выводит разобранный заголовок, а затем
полную таблицу функций:```
$ python3 r2_gopclntab.py -f ./gotest_stripped
[Вики](https://github.com/ainrm/OffensivePipelineCD/wiki) • [Установка](https://github.com/ainrm/OffensivePipelineCD/wiki/Installation) • [Загрузки](https://github.com/ainrm/OffensivePipelineCD/wiki/Downloads) • [Плагины](https://github.com/ainrm/OffensivePipelineCD/wiki/Development)```
============================================================
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
Добавляет ход сканирования разделов, детали разрешения textStart и информацию о внутренних смещениях:``` $ python3 r2_gopclntab.py -f ./gotest_stripped -v
Please provide the Markdown content to translate.```
[*] 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+
...
Фильтрует список функций по совпадению подстроки. Если найдено точное совпадение, его адрес выводится отдельно.