Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
r2gopclntabParser — 一个用于解析gopclntab的radare2脚本,以辅助逆向工程Go二进制文件。 | Kitploit
工具/GitHubGitHub/asherdll/r2gopclntabparser
静态分析逆向工程调试器取证分析恶意软件分析二进制分析
GitHubasherdll/r2gopclntabparser

r2gopclntabParser

一个用于解析gopclntab的radare2脚本,以辅助逆向工程Go二进制文件。

查看仓库
1744个月前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

r2gopclntabParser

注意:该功能已直接集成到 radare2 核心中,作为 gopc analysis plugin

一个基于 radare2 的 Go gopclntab 解析器,用于从 Go 二进制文件中恢复函数符号,包括完全剥离符号的二进制文件。支持 ELF、Mach-O 和 PE 二进制文件,涵盖 Go 1.2、1.16、1.18 及 1.20+ 版本。

每个 Go 1.2+ 二进制文件都嵌入了一个名为 gopclntab(程序计数器行表)的数据区域,Go 运行时利用它进行堆栈跟踪、恐慌消息、垃圾回收和调试器支持。它是 Go 二进制文件中最有价值的符号信息源之一。

r2gopclntabParser 通过 radare2 读取该区域,解析版本特定的结构,然后打印恢复的函数列表(包含地址、源文件和行号),或将恢复的函数名称作为函数定义、标志和注释应用回打开的 radare2 会话中。


目录

  • 前提条件
  • 快速开始
  • CLI 参考
  • 输出模式与示例
    • 默认模式(标题 + 函数列表)
    • 详细模式 (-v)
    • 搜索模式 (-n)
    • JSON 输出 (--json)
    • 源文件列表 (--files)
    • 应用模式 (--apply)
  • 结果摘要:Mach-O(剥离的测试二进制文件)
  • PE 测试:Greenblood,Go 勒索软件二进制文件
  • 逆向工程用例
  • 支持的平台与 Go 版本
  • 方法论
    • 节定位策略
    • textStart 与 .text 节
    • 版本感知的结构解析
    • PC 数据解码
  • 局限性
  • 附加文档
  • 参考资料

前提条件

依赖项最低版本
Python 33.8+
radare25.0+(已在 6.0.9 上测试)
r2pipe任意

不需要其他 Python 包。脚本仅使用标准库(struct、json、argparse、os、sys)加上 r2pipe。


快速开始```bash

List all functions recovered from a Go binary

python3 r2_gopclntab.py -f ./mybinary -l

Search for a specific function (substring match)

python3 r2_gopclntab.py -f ./mybinary -n main.main

Verbose header + function list

python3 r2_gopclntab.py -f ./mybinary -v -l

Apply recovered names into an r2 session

python3 r2_gopclntab.py -f ./mybinary --apply

JSON output

python3 r2_gopclntab.py -f ./mybinary --json

From within r2 (attach to running session)

#!pipe python3 r2_gopclntab.py --r2pipe --apply -v

root@kitploit:~
---

## CLI参考```
usage: r2_gopclntab.py [-h] [-f FILE] [-n FUNCNAME] [-v] [-l]
                       [--apply] [--json] [--files] [--r2pipe]

必须(选择其一)

标志描述
-f FILE, --file FILE要分析的Go二进制文件路径。脚本会启动自己的r2实例。
--r2pipe连接到已在运行的r2会话(用于r2控制台内部)。

可选

标志可以自由组合。当没有给出输出标志时,默认行为是打印头部和完整函数列表。


输出模式与示例

以下所有示例均针对一个剥离后的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

root@kitploit:~
在开始之前,您只需要设置一个配置文件。最快和最简单的方法是运行并按照设置向导的提示进行操作,使用以下命令:

```console
$ tools-for-github setup

您将被询问一系列问题,配置文件将生成在 ~/.tools-for-github.toml。

或者,您可以手动创建和配置。

如果您想手动创建并配置文件,请创建 ~/.tools-for-github.toml 并填入以下内容:

root@kitploit:~
# [github] 部分是可选的,如果省略,将使用默认值。
[github]
base_url = "https://api.github.com" # 如果您使用的是 GitHub Enterprise Server,请更改此设置。
api_token = ""                       # 默认值是 GITHUB_TOKEN 环境变量的值。

此配置文件位于 $HOME/.tools-for-github.toml。

还有其他可用的选项,但这些是必需的。有关所有配置选项的完整参考,请参阅文档。

接下来,您还需要一个 GitHub 令牌,可以设置为 GITHUB_TOKEN 环境变量,或在 ~/.tools-for-github.toml 中配置。创建令牌的文档可在此处找到。还要记得在需要时为您的令牌启用 SSO。

现在,您可以像这样使用任何脚本:

root@kitploit:~
$ tools-for-github <script> <arguments>

例如,要使用 sync-github-all 脚本,可以运行以下命令:

root@kitploit:~
$ tools-for-github sync-github-all --help

要列出所有可用脚本,请运行以下命令:

root@kitploit:~
$ tools-for-github

初始设置后,使用 update 脚本将 tools-for-github 更新到最新版本:

root@kitploit:~
$ tools-for-github update

从 github-tools 或早期版本的 tools-for-github 升级

如果您之前使用过 github-tools 或 v3.0.0 之前的 tools-for-github 版本,配置文件已发生变更。配置文件的位置已从 ~/.github-tools.toml 移至 ~/.tools-for-github.toml,并且格式也发生了变化。幸运的是,设置向导可以帮助您迁移现有的配置。只需运行:

root@kitploit:~
$ tools-for-github setup

向导将检测您现有的配置,并询问是否要迁移到新格式。
旧的配置格式如下所示:

root@kitploit:~
github_api_token = ""
github_base_url = "https://api.github.com"

而新的配置格式如下所示:

root@kitploit:~
[github]
base_url = "https://api.github.com"
api_token = ""

============================================================ 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

root@kitploit:~
### 详细模式 (-v)

添加了部分扫描进度、textStart解析详情和内部偏移信息:```
$ 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+ ...

root@kitploit:~
### 搜索模式 (-n)

按子串匹配过滤函数列表。如果存在精确匹配,则会单独打印其地址。

搜索所有包含 `main.` 的子串函数:```
$ python3 r2_gopclntab.py -f ./gotest_stripped -n "main."

(注意:原文未提供实际待翻译内容,因此返回空。)``` ADDRESS FUNCTION NAME

0x100041920 runtime.main.func2 (/opt/homebrew/Cellar/go/1.26.1/libexec/src/runtime/proc.go:207) 0x10006CE30 runtime.main.func1 (/opt/homebrew/Cellar/go/1.26.1/libexec/src/runtime/proc.go:174) 0x1000A0BB0 main.fibonacci (/tmp/gotest/main.go:13) 0x1000A0C20 main.main (/tmp/gotest/main.go:20)

[+] 4 function(s) shown (filtered from 2030 total)

root@kitploit:~
精确匹配搜索:```
$ python3 r2_gopclntab.py -f ./gotest_stripped -n "main.fibonacci"

(请提供需要翻译的 Markdown 内容)``` ADDRESS FUNCTION NAME

0x1000A0BB0 main.fibonacci (/tmp/gotest/main.go:13)

[+] 1 function(s) shown (filtered from 2030 total)

[+] Exact match: main.fibonacci @ 0x1000A0BB0

root@kitploit:~
搜索与GC相关的运行时内部机制:```
$ python3 r2_gopclntab.py -f ./gotest_stripped -n "runtime.gc"

块特定规则:

  1. 仅翻译自然语言文本。绝不翻译:代码块、shell命令、文件路径、URL、包名、技术标识符、CVE ID、环境变量名。
  2. 完全原样保留所有Markdown语法。
  3. 不要添加如“## 块 N”、“## 部分 N”、“## 从...继续”或“## 块 N 的翻译”等引导性标题。不要添加“块 N 结束”或“内容继续...”标记。
  4. 不要添加“...”省略号标记来表示省略。仅翻译提供的准确文本,结构上逐字符保持。
  5. 块边界是有意的。保持结构,以便块可以无缝拼接,而不会产生视觉伪影。
  6. 仅返回翻译后的文本。无前言、无注释、不包裹在代码块中、不包含JSON/YAML/XML、无数组、无对象、无模式、无键/值包装。
  7. 如果块从段落中间开始,则从该点继续翻译。除非源文本中存在,否则不要添加前导换行或缩进。``` ADDRESS FUNCTION NAME

0x10001F310 runtime.gcinit (/opt/homebrew/.../src/runtime/mgc.go:179) 0x10001F3C0 runtime.gcenable (/opt/homebrew/.../src/runtime/mgc.go:211) 0x10001F730 runtime.gcStart (/opt/homebrew/.../src/runtime/mgc.go:733) 0x10001FFE0 runtime.gcMarkDone (/opt/homebrew/.../src/runtime/mgc.go:1015) 0x100020A50 runtime.gcMarkTermination (/opt/homebrew/.../src/runtime/mgc.go:1344) 0x100021C10 runtime.gcBgMarkWorker (/opt/homebrew/.../src/runtime/mgc.go:1750) 0x1000223D0 runtime.gcMark (/opt/homebrew/.../src/runtime/mgc.go:1956) 0x1000227A0 runtime.gcSweep (/opt/homebrew/.../src/runtime/mgc.go:2049) ... 0x100076C60 runtime.gcWriteBarrier1 (/opt/homebrew/.../src/runtime/asm_arm64.s:1533)

[+] 73 function(s) shown (filtered from 2030 total)

root@kitploit:~
正在搜索 `fmt.`(标准库打印):```
$ python3 r2_gopclntab.py -f ./gotest_stripped -n "fmt."

输入:``` ADDRESS FUNCTION NAME

0x100098AA0 fmt.(*fmt).writePadding (/opt/homebrew/.../src/fmt/format.go:66) 0x100098BF0 fmt.(*fmt).pad (/opt/homebrew/.../src/fmt/format.go:93) 0x100099590 fmt.(*fmt).fmtInteger (/opt/homebrew/.../src/fmt/format.go:197) 0x10009ADF0 fmt.Fprintf (/opt/homebrew/.../src/fmt/print.go:222) 0x10009AED0 fmt.Fprintln (/opt/homebrew/.../src/fmt/print.go:303) 0x10009D3F0 fmt.(*pp).printArg (/opt/homebrew/.../src/fmt/print.go:721) 0x10009D950 fmt.(*pp).printValue (/opt/homebrew/.../src/fmt/print.go:797) 0x10009FA60 fmt.(*pp).doPrintf (/opt/homebrew/.../src/fmt/print.go:1018) ...

root@kitploit:~
正在搜索 `sync.`(并发原语):```
$ python3 r2_gopclntab.py -f ./gotest_stripped -n "sync."

输入:``` ADDRESS FUNCTION NAME

0x10006FFE0 sync.runtime_registerPoolCleanup (/opt/homebrew/.../src/runtime/mgc.go:2150) 0x100070BA0 sync.fatal (/opt/homebrew/.../src/runtime/panic.go:1160) 0x1000714E0 sync.runtime_procPin (/opt/homebrew/.../src/runtime/proc.go:7912) 0x10007B290 internal/sync.(*Mutex).lockSlow (/opt/homebrew/.../src/internal/sync/mutex.go:95) 0x10007B570 internal/sync.(*Mutex).Unlock (/opt/homebrew/.../src/internal/sync/mutex.go:187) ...

root@kitploit:~
### JSON 输出 (--json)

供脚本和管道集成的机器可读输出:```
$ python3 r2_gopclntab.py -f ./gotest_stripped --json

关键观察

  1. CVE-2024-21683:Confluence Data Center 和 Server 中的此漏洞已被勒索软件组织积极利用,以获得对受害者环境的初始访问权限。许多组织缺乏适当的补丁和缓解措施。
  2. CVE-2023-22527:Atlassian Confluence 中的另一个关键漏洞,允许远程代码执行。此漏洞继续被用于后渗透活动,以部署勒索软件并建立持久化。
  3. CVE-2024-27198 和 CVE-2024-27199:JetBrains TeamCity 中的这些漏洞已被用于促进横向移动和凭据窃取,显示出供应链攻击的日益复杂性。
  4. CVE-2023-34362:一个 Progress MOVEit Transfer SQL 注入漏洞,已被用于窃取数据和部署 Clop 勒索软件。这凸显了勒索软件组织在针对托管文件传输解决方案方面的持续性。

初始访问技术

root@kitploit:~
"magic": "0xFFFFFFF1",
"version": "1.20+",
"ptrSize": 8,
"minLC": 4,
"nfunc": 2030,
"nfiles": 261,
"textStart": "0x0"

}, "functions": [ { "name": "go:buildid", "addr": "0x100001000", "args": 0, "source_file": "", "start_line": 0 }, { "name": "internal/abi.BoundsDecode", "addr": "0x100001070", "args": 8, "source_file": "/opt/homebrew/Cellar/go/1.26.1/libexec/src/internal/abi/bounds.go", "start_line": 86 }, { "name": "main.fibonacci", "addr": "0x1000A0BB0", "args": 0, "source_file": "/tmp/gotest/main.go", "start_line": 13 }, { "name": "main.main", "addr": "0x1000A0C20", "args": 0, "source_file": "/tmp/gotest/main.go", "start_line": 20 } ], "num_source_files": 261 }

root@kitploit:~
### 源文件列表 (--files)

提取嵌入在二进制文件中的所有源文件路径:```
$ python3 r2_gopclntab.py -f ./gotest_stripped --files
  • internal/kit/block.go:将阻断规则应用于 BlockedItem 记录列表。它读取 YAML 文件,为每条规则构建默认值映射,然后对每个 BlockedItem 评估所有规则,合并规则特定的默认值,并可选地将详细说明写入文件。

  • internal/kit/block.go:读取 YAML 配置(可以是 URL 或文件路径),对其进行分词处理,并提供对规则、规则文件和类别的结构化访问。它使用 internal/kit/grammar.y 中定义的语法和解析器。

  • internal/kit/block.go:这可能是一个更全面的示例,利用了完整的数据管道。它定义了 BlockedItem 结构体来表示域名/注册表对及其应用的规则,RuleDetails 来捕获每条规则的元数据,以及 EvaluateAllRules 和 AnalyzeItem 等函数。``` Source files (261):


/opt/homebrew/Cellar/go/1.26.1/libexec/src/internal/abi/bounds.go /opt/homebrew/Cellar/go/1.26.1/libexec/src/internal/abi/escape.go /opt/homebrew/Cellar/go/1.26.1/libexec/src/internal/abi/type.go /opt/homebrew/Cellar/go/1.26.1/libexec/src/internal/cpu/cpu.go /opt/homebrew/Cellar/go/1.26.1/libexec/src/internal/cpu/cpu_arm64.go /opt/homebrew/Cellar/go/1.26.1/libexec/src/runtime/proc.go /opt/homebrew/Cellar/go/1.26.1/libexec/src/runtime/mgc.go /opt/homebrew/Cellar/go/1.26.1/libexec/src/runtime/malloc.go /opt/homebrew/Cellar/go/1.26.1/libexec/src/runtime/panic.go /opt/homebrew/Cellar/go/1.26.1/libexec/src/fmt/print.go /opt/homebrew/Cellar/go/1.26.1/libexec/src/fmt/format.go /opt/homebrew/Cellar/go/1.26.1/libexec/src/reflect/value.go /opt/homebrew/Cellar/go/1.26.1/libexec/src/reflect/type.go /tmp/gotest/main.go ... ... and 61 more

root@kitploit:~
### Apply Mode (--apply)

将所有恢复的函数名称写入 radare2 会话。以下展示了剥离二进制文件前后的对比。

**BEFORE**(对剥离二进制文件进行 r2 原生分析,未进行 gopclntab 解析):```
Functions found by r2 natively: 1913

Disassembly at 0x1000a0c20 (main.main, unnamed):

            ; CODE XREF from fcn.1000a0c20 @ 0x1000a0d14(r)
  24: fcn.1000a0c20 (int64_t arg1);
           0x1000a0c20      900b40f9       ldr x16, [x28, 0x10]
           0x1000a0c24      ff6330eb       cmp sp, x16
           0x1000a0c28      29070054       b.ls 0x1000a0d0c

Disassembly at 0x1000a0bb0 (main.fibonacci, unnamed):

  112: fcn.1000a0bb0 (signed int64_t arg1, int64_t arg_8h);
           0x1000a0bb0      900b40f9       ldr x16, [x28, 0x10]
           0x1000a0bb4      ff6330eb       cmp sp, x16
           0x1000a0bb8      a9020054       b.ls 0x1000a0c0c

r2发现了1913个函数,但并未为其中任何一个命名(只有匿名fcn.XXXXXXXX标签)。搜索main.main或main.fibonacci没有返回任何结果。

应用gopclntab符号:``` [] Binary format: mach0, endian: little, arch: arm, bits: 64 [] Found magic at vaddr=0x1000ABCE8 [] Parsed header: PcHeader(magic=0xFFFFFFF1, version=1.20+, ...) [] textStart is 0, using .text section vaddr: 0x100001000 [*] Parsed 2030 functions [+] Applied 2030 function names to radare2 (0 skipped)

root@kitploit:~
**之后** (r2 session with gopclntab symbols applied):```
r2 function list matching "main" (after --apply):

0x100041510    0      0 runtime.main
0x100041920    0      0 runtime.main.func2
0x10006ce30    0      0 runtime.main.func1

Flags in go.* flagspace (last 20):

0x10009fa60 1 go.fmt._ptr_pp_.doPrintf
0x1000a0930 1 go.fmt._ptr_pp_.doPrintln
0x1000a0bb0 1 go.main.fibonacci
0x1000a0c20 1 go.main.main
0x1000a0d20 1 go.go:textfipsstart
0x1000a0d30 1 go.go:textfipsend

位于 main.main 的反汇编现在显示恢复后的名称和源代码位置:``` ;-- go.main.main: 24: fcn.1000a0c20 (int64_t arg1); 0x1000a0c20 900b40f9 ldr x16, [x28, 0x10] ; " src: /tmp/gotest/main.go:20" 0x1000a0c24 ff6330eb cmp sp, x16 0x1000a0c28 29070054 b.ls 0x1000a0d0c

root@kitploit:~
`main.fibonacci` 处的反汇编现在显示了恢复的函数名和源代码位置:```
            ;-- go.main.fibonacci:
  112: fcn.1000a0bb0 (signed int64_t arg1, int64_t arg_8h);
           0x1000a0bb0      900b40f9       ldr x16, [x28, 0x10]       ; " src: /tmp/gotest/main.go:13"
           0x1000a0bb4      ff6330eb       cmp sp, x16
           0x1000a0bb8      a9020054       b.ls 0x1000a0c0c

按名称搜索现在可在r2会话中工作:``` go.main.main resolves to: 0x1000a0c20 go.main.fibonacci resolves to: 0x1000a0bb0

root@kitploit:~
---

## 结果摘要:Mach-O(剥离后的测试二进制文件)

| 指标 | r2 原生(剥离后) | 使用 r2_gopclntab.py 后 |
|---|---|---|
| 找到的函数 | 1913(匿名 `fcn.XXXX` 标签) | 2030(完整的 Go 包限定名称) |
| 识别的用户函数 | 0 | `main.main`, `main.fibonacci` 包含源文件和行号 |
| 恢复的源文件 | 0 | 261(完整绝对路径) |
| 命名符号 | 仅 C 导入存根(`sym.imp.mmap` 等) | 每个 Go 函数都已标记(`go.main.main`, `go.runtime.gcStart` 等) |
| 源代码注释 | 无 | 反汇编中内联显示 `src: /tmp/gotest/main.go:20` |
| 可按名称导航 | 否 | 是(`s go.main.main`, `afl~runtime.gc`) |

---

## PE 测试:Greenblood,一个 Go 勒索软件二进制文件

该解析器针对一个真实的 PE 二进制文件 Greenblood(`greenblood_1`)进行了测试,这是一个编译为 PE32+ x86-64 Windows 可执行文件的 Go 勒索软件样本。PE 二进制文件没有专用的 `.gopclntab` 段,因此这测试了魔术字节扫描回退路径。

### 检测与头部```
$ python3 r2_gopclntab.py -f ./greenblood_1 -v
  • --protocol <protocol> - 指定用于端口扫描的协议。可以是 tcp、udp 或 both(同时使用 tcp 和 udp)。默认为 tcp。目前仅通过 --stdin 选项可用。
  • --exclude-timeout <exclude-timeout> - 在指定的超时时间(例如 "20s"、"10m")后,从 -exclude-hosts 标准输入中排除主机。需要 --stdin。
  • --exclude-hosts <exclude-hosts> - 指定一个文件,从中读取要从标准输入中排除的额外主机。需要 --stdin。
  • --max-kb-per-second <max-kb-per-second> - 限制地址生成所使用的最大带宽,单位为 KByte/s。除非使用指定的输出文件,否则可通过 禁用此选项。
root@kitploit:~
扫描器在 `.rdata` 节的 `0x568C00` 处发现了 gopclntab。
由于这是一个标准 PE(非 PIE),`textStart` 为 `0x401000`(非零),
因此头部值直接用于地址计算。

| 字段 | 值 |
|---|---|
| 格式 | PE32+ x86-64 |
| gopclntab 位置 | `.rdata` 在 `0x568C00`(通过魔数扫描发现) |
| 魔数 | `0xFFFFFFF1` (Go 1.20+) |
| 指针大小 | 8 |
| 量子(minLC) | 1 (x86) |
| textStart | `0x401000`(来自头部) |
| 恢复的函数 | 2596 |
| 源文件 | 345 |

### 恢复的恶意软件函数

搜索恶意软件自身的代码(`main.`):```
$ python3 r2_gopclntab.py -f ./greenblood_1 -n "main."

(无输入内容,返回空)``` ADDRESS FUNCTION NAME

0x4D91E0 main.init (:1) 0x4D9200 main.map.init.0 (/root/victims/ransom/daf/enc.go:59) 0x4D92C0 main.map.init.1 (/root/victims/ransom/daf/enc.go:126) 0x4D95C0 main.NewKeyManager (/root/victims/ransom/daf/enc.go:146) 0x4D97E0 main.getMachineFingerprint (/root/victims/ransom/daf/enc.go:173) 0x4DA2C0 main.getBIOSUUID (/root/victims/ransom/daf/enc.go:249) 0x4DA3C0 main.NewEncryptionEngine (/root/victims/ransom/daf/enc.go:287) 0x4DA560 main.(*EncryptionEngine).fileWorker (/root/victims/ransom/daf/enc.go:303) 0x4DA660 main.(*EncryptionEngine).processFile (/root/victims/ransom/daf/enc.go:319) 0x4DA740 main.(*EncryptionEngine).encryptFile (/root/victims/ransom/daf/enc.go:334) 0x4DB2A0 main.(*EncryptionEngine).EncryptPath (/root/victims/ransom/daf/enc.go:446) 0x4DB620 main.(*EncryptionEngine).shouldSkipDirectory (/root/victims/ransom/daf/enc.go:495) 0x4DB7C0 main.(*EncryptionEngine).shouldEncryptFile (/root/victims/ransom/daf/enc.go:522) 0x4DB9A0 main.(*EncryptionEngine).placeRansomNote (/root/victims/ransom/daf/enc.go:560) 0x4DBB60 main.(*EncryptionEngine).recordSuccess (/root/victims/ransom/daf/enc.go:636) 0x4DBFA0 main.(*EncryptionEngine).Wait (/root/victims/ransom/daf/enc.go:664) 0x4DC3C0 main.formatBytes (/root/victims/ransom/daf/enc.go:689) 0x4DC500 main.disableRecovery (/root/victims/ransom/daf/enc.go:706) 0x4DC720 main.isAdmin (/root/victims/ransom/daf/enc.go:732) 0x4DC8C0 main.main (/root/victims/ransom/daf/enc.go:760) 0x4DD260 main.getLogicalDrives (/root/victims/ransom/daf/enc.go:863) 0x4DD4A0 main.isAlreadyRunning (/root/victims/ransom/daf/enc.go:892) 0x4DD660 main.getDesktopPath (/root/victims/ransom/daf/enc.go:911) 0x4DD780 main.removeExecutable (/root/victims/ransom/daf/enc.go:932) ...

[+] 43 function(s) shown (filtered from 2596 total)

root@kitploit:~
所有43个用户函数都是从位于 `/root/victims/ransom/daf/enc.go` 的单个源文件中恢复的。函数名称立即揭示了勒索软件的功能:密钥管理、机器指纹识别、带路径遍历的文件加密、勒索信放置、恢复禁用、权限检查、基于互斥锁的单实例强制执行、驱动器枚举和自删除。

### 非标准库依赖

提取不属于 Go 标准库的源文件:```
/root/go/pkg/mod/golang.org/x/[email protected]/windows/dll_windows.go
/root/go/pkg/mod/golang.org/x/[email protected]/windows/registry/key.go
/root/go/pkg/mod/golang.org/x/[email protected]/windows/registry/value.go
/root/go/pkg/mod/golang.org/x/[email protected]/windows/security_windows.go
/root/go/pkg/mod/golang.org/x/[email protected]/windows/str.go
/root/go/pkg/mod/golang.org/x/[email protected]/windows/syscall.go
/root/go/pkg/mod/golang.org/x/[email protected]/windows/syscall_windows.go
/root/go/pkg/mod/golang.org/x/[email protected]/windows/zsyscall_windows.go
/root/victims/ransom/daf/enc.go

唯一的外部依赖是用于 Windows 特定系统调用的 golang.org/x/[email protected](注册表访问、安全令牌、DLL 加载)。

加密函数

搜索 crypto 共发现 137 个加密相关函数,包括:``` $ python3 r2_gopclntab.py -f ./greenblood_1 -n "crypto"

root@kitploit:~
```markdown
### 安装指南

1. **克隆仓库:**
   ```bash
   git clone https://github.com/your-username/your-repo.git
   cd your-repo
  1. 安装依赖: 确保已安装 Python 3.8+ 和必要的包:

    root@kitploit:~
    pip install -r requirements.txt
    
  2. 配置: 编辑 config.yaml 文件,填入你的 API 密钥和目标设置。

使用方法

运行主脚本:

root@kitploit:~
python main.py -t example.com

选项:

  • -t, --target:指定目标域名或 IP 地址。
  • -o, --output:输出目录(默认:./results)。
  • -v, --verbose:启用详细输出。

示例输出

root@kitploit:~
[+] 目标:example.com
[+] 开始扫描...
[+] 发现开放端口:80, 443, 8080
[+] 服务识别完成。
[+] 漏洞扫描完成。
[+] 报告已保存至:./results/example.com_report.html

贡献

欢迎提交拉取请求。对于重大更改,请先开 issue 讨论你想要改变的内容。

许可证

MIT

root@kitploit:~
ADDRESS       FUNCTION NAME
----------------------------------------------------------------------
0x4AEC00      crypto/cipher.NewCTR  (/usr/local/go/src/crypto/cipher/ctr.go:41)
0x4AF520      crypto/cipher.StreamWriter.Write  (/usr/local/go/src/crypto/cipher/io.go:36)
0x4AF840      crypto/aes.NewCipher  (/usr/local/go/src/crypto/aes/aes.go:36)
0x4C3760      crypto/rand.(*reader).Read  (/usr/local/go/src/crypto/rand/rand.go:45)
0x4DE6C0      crypto/internal/fips140/sha256.New  (.../sha256/sha256.go:138)
0x4E3EC0      crypto/internal/fips140/sha3.NewCShake128  (.../sha3/shake.go:134)
0x4EED60      crypto/internal/fips140/hmac.New  (.../hmac/hmac.go:131)
0x4EF800      crypto/internal/fips140/aes.newBlock  (.../aes/aes_asm.go:59)
0x4F0800      crypto/internal/fips140/aes.(*CBCEncrypter).CryptBlocks  (.../aes/cbc.go:26)
0x4F0FE0      crypto/internal/fips140/aes.(*CTR).XORKeyStream  (.../aes/ctr.go:41)
...

[+] 137 function(s) shown (filtered from 2596 total)
```
加密使用配置文件:AES(CBC和CTR模式)、SHA-256、SHA-512、HMAC、CSHAKE128和DRBG(确定性随机比特生成器)。这与勒索软件的行为一致,勒索软件根据机器指纹生成每台机器的加密密钥,使用AES-CTR加密文件,并使用HMAC确保完整性。

### 应用模式```
$ python3 r2_gopclntab.py -f ./greenblood_1 --apply
[+] Applied 2596 function names to radare2 (0 skipped)
[+] Function names applied. Use 'afl' in r2 to see them.
```
全部2596个函数已成功应用,零跳过。

---

## 逆向工程用例

### 1. 分类与识别

立即确定二进制文件是否为Go语言编写、其构建版本以及所使用的包。`--files`输出会揭示Go工具链版本(来自类似`/usr/local/go/1.26.1/...`的文件路径)以及每个源文件路径,包括第三方库。对于恶意软件,这能立即揭示样本是否使用了`crypto/tls`、`net/http`、`os/exec`或其他感兴趣的包。

### 2. 被剥离二进制文件的符号恢复

核心用例。使用`-ldflags="-s -w"`剥离的Go二进制文件会丢失符号表,但gopclntab通常保留。此工具可恢复每个函数名,将匿名的`fcn.1000a0c20`变回`main.main`。这适用于恶意软件样本、CTF挑战、生产二进制文件以及任何被剥离的Go可执行文件。

### 3. 导航Go运行时

Go二进制文件嵌入了完整的运行时(通常1500到2000+函数)。没有名称时,运行时是一堵由匿名函数组成的不可穿透的墙。使用名称后,您可以立即定位`runtime.mallocgc`、`runtime.gopanic`、`runtime.newproc`、`runtime.gcStart`,并理解二进制文件在每个调用点的行为。

### 4. 分离用户代码与运行时

通过搜索`main.`或应用程序的包路径,您可以将用户代码与运行时隔离。在上面的示例中,过滤`main.`立即在总共2030个函数中揭示了`main.main`和`main.fibonacci`。您也可以按第三方包名称搜索(例如,`-n "github.com/user/repo"`)以识别外部依赖。

### 5. 源级上下文

每个函数都附带其源文件路径和起始行号。这意味着您可以将反汇编与Go标准库源代码(开源)进行交叉引用,即使在分析被剥离的二进制文件时也是如此。知道一个函数从`runtime/mgc.go`的第733行开始,让您可以阅读原始源代码并同时查看反汇编。

### 6. 流水线与自动化

`--json`模式支持脚本化。将输出输入到IDA/Ghidra导入器、差异比较工具、YARA规则生成器或任何分析流水线。例如,提取所有与加密相关的函数:```bash
python3 r2_gopclntab.py -f sample.exe --json \
  | jq '.functions[] | select(.name | contains("crypto"))'
```
### 7. 交互式 radare2 工作流

使用 `--apply` 后,整个 r2 会话变得可通过 Go 名称导航。你可以跳转到函数 (`s go.main.main`),搜索函数列表 (`afl~runtime.gc`),检查交叉引用 (`axf go.main.fibonacci`),并在反汇编输出 (`pd`) 中内联查看源位置注释。这使 r2 从一个通用反汇编器转变为支持 Go 的分析环境。

---

## 支持的平台和 Go 版本

### 二进制格式

| 格式 | 节发现方法 | 已测试 |
|---|---|---|
| ELF (Linux) | 节名 `.gopclntab` 或 `.data.rel.ro.gopclntab` | 是 |
| Mach-O (macOS) | 节名 `__gopclntab` (在 `__TEXT` 段中) | 是 |
| PE (Windows) | 魔数字节扫描 (无专用节) | 是 (在 Go 勒索软件 PE64 Greenblood 上测试) |

对于 PE 二进制文件和缺乏节头的严重剥离的 ELF/Mach-O 二进制文件,该工具回退到扫描所有节以查找 4 字节魔数,后跟验证字节 (pad=0, ptrSize in {4,8}, minLC in {1,2,4})。

### Go 版本

| 魔数 | Go 版本 | 头中的 `textStart` | `functab.entry` 类型 | `startLine` 字段 | 状态 |
|---|---|---|---|---|---|
| `0xFFFFFFFB` | 1.2 | 否 | `uintptr` (绝对) | 否 | 支持 |
| `0xFFFFFFFA` | 1.16 | 否 | `uintptr` (绝对) | 否 | 支持 |
| `0xFFFFFFF0` | 1.18 - 1.19 | 是 | `uint32` (相对) | 否 | 支持 |
| `0xFFFFFFF1` | 1.20+ | 是 (可能为 0) | `uint32` (相对) | 是 | 支持 |

`0xFFFFFFF1` 魔数用于 Go 1.20 直到至少 Go 1.26。

---

## 方法论

### 节定位策略

解析器采用两阶段方法查找 gopclntab 数据:

**阶段 1 (ELF/Mach-O):** 查询 radare2 的节列表 (`iSj`) 并查找名为 `.gopclntab`、`.data.rel.ro.gopclntab` 或 `__gopclntab` 的节。

**阶段 2 (PE/回退):** 如果未找到命名节,则扫描所有节以查找 4 字节魔数。每个候选者通过检查字节 4-7 是否匹配预期模式来验证:两个零填充字节、有效的指针大小 (4 或 8) 和有效的指令量子 (1, 2, 或 4)。这消除了来自巧合字节模式的误报。

### textStart 与 .text 节

在 Go >= 1.18 中,functab 中的函数入口点存储为相对偏移。计算绝对虚拟地址需要一个基址:```
absolute_addr = base + entryoff
```
基础通过以下逻辑解析:```
Is magic 0xFFFFFFFB (Go 1.2)?
  YES -> base = 0 (entries are absolute addresses)
  NO  -> Is magic 0xFFFFFFFA (Go 1.16)?
           YES -> base = .text section vaddr (entries are absolute)
           NO  -> (Go 1.18 / 1.20+)
                  Is header.textStart != 0?
                    YES -> base = header.textStart
                    NO  -> base = .text section vaddr
```
`textStart == 0` 的情况出现在 Go >= 1.22 的 Mach-O 和 PIE 二进制文件中。当这种情况发生时,`entryoff` 的值相对于 `.text` 节的起始位置,因此解析器会向 r2 查询 `.text` 虚拟地址,并将其用作基址。

这在针对 Go 1.26 Mach-O arm64 二进制文件的验证中得到确认:
- 头部的 `textStart`:`0x0`
- `.text` 节 vaddr:`0x100001000`
- `functab[1].entryoff`:`0x70`
- 计算地址:`0x100001000 + 0x70 = 0x100001070`
- r2 原生分析确认 `internal/abi.BoundsDecode` 位于 `0x100001070`

### 版本感知的结构体解析

`_func` 结构体布局在 Go 1.18 和 Go 1.20+ 之间存在差异。唯一的变化是在 Go 1.20+ 中,偏移量 36 处插入了一个 4 字节的 `startLine` 字段,这导致 `funcID`、`flag` 和 `nfuncdata` 偏移了 4 个字节:

| 字段 | Go 1.18 偏移量 | Go 1.20+ 偏移量 |
|---|---|---|
| `entryOff` | 0 | 0 |
| `nameOff` | 4 | 4 |
| `args` | 8 | 8 |
| `cuOffset` | 32 | 32 |
| `startLine` | (不存在) | 36 |
| `funcID` | 36 | 40 |
| `flag` | 37 | 41 |
| `nfuncdata` | 39 | 43 |

解析器会检查 magic 数字以确定使用哪种布局。

### PC 数据解码

源文件索引和行号以紧凑的“PC 数据程序”形式存储在 pctab 区域中。每个程序使用变长整数编码(有符号值采用锯齿编码)对 `(value_delta, pc_delta)` 对进行编码。解析器解码这些信息以解析:

- 源文件:`_func.pcfile` -> pctab 程序 -> 文件索引 -> cutab -> filetab -> 文件路径字符串
- 行号:`_func.pcln` -> pctab 程序 -> 行号(对于 Go 1.20+,加上 `startLine` 偏移量)

---

## 局限性

1. **内联函数**不会出现在顶层函数表中。它们编码在 `FUNCDATA_InlTree` / `PCDATA_InlTreeIndex` 结构中,此工具当前不解码这些结构。在测试二进制文件中,`main.helloWorld` 和 `main.addNumbers` 被编译器内联,因此未出现在输出中。

2. **Go 1.2 支持**是尽力而为的。Go 1.2 格式差异很大(没有独立的 funcnametab,没有 cutab,functab 中使用绝对指针),实际中很少遇到。

3. **大端序**架构原则上已处理(字节序从 radare2 的二进制信息中检测并用于所有结构体读取),但未经测试。

4. `--apply` 模式会使用 `af+` 创建函数存根,这可能与 r2 自身的自动分析冲突。在某些情况下,在干净的会话上(在 `aaa` 之前或代替 `aaa`)运行它可能会得到更好的结果。

---

## 附加文档

详细的技术文档位于 `documentation/` 目录中:

- [DOCUMENTATION.md](https://github.com/asherdll/r2gopclntabparser/blob/main/documentation/DOCUMENTATION.md) - 完整的用户文档,包含所有使用模式、输出格式和标志组合。
- [METHODOLOGY.md](https://github.com/asherdll/r2gopclntabparser/blob/main/documentation/METHODOLOGY.md) - 设计决策和算法:textStart 与 .text 解析、版本感知解析、PE 扫描策略、PC 数据解码、r2 集成细节。
- [GOPCLNTAB_FORMAT.md](https://github.com/asherdll/r2gopclntabparser/blob/main/documentation/GOPCLNTAB_FORMAT.md) - 各 Go 版本的 gopclntab 二进制格式:字节级结构布局、内存模型、偏移链、变长整数编码、版本差异总结。

---

## 参考资料

- Go 运行时源代码 (pcHeader):[go1.20.6/src/runtime/symtab.go#L414](https://github.com/golang/go/blob/go1.20.6/src/runtime/symtab.go#L414)
- Go 链接器(写入格式):[go1.20.6/src/cmd/link/internal/ld/pcln.go](https://github.com/golang/go/blob/go1.20.6/src/cmd/link/internal/ld/pcln.go)
- Mandiant - Golang Internals Symbol Recovery:[mandiant.com/resources/blog/golang-internals-symbol-recovery](https://www.mandiant.com/resources/blog/golang-internals-symbol-recovery)
- Go 1.2 符号表设计文档:[docs.google.com/document/d/1lyPIbmsYbXnpNj57a261hgOYVpNRcgydurVQIyZOz_o](https://docs.google.com/document/d/1lyPIbmsYbXnpNj57a261hgOYVpNRcgydurVQIyZOz_o/pub)
下载工具
标志描述
-l, --list打印每个恢复的函数及其地址。
-n NAME, --funcname NAME仅打印名称包含NAME(子字符串匹配)的函数。如果存在完全匹配,则单独打印其地址。
-v, --verbose打印进度信息、解析的头部字段和内部偏移量。
--apply将恢复的函数名称写入radare2会话,包括函数定义(af+)、go.标志空间中的标志,以及带有原始Go名称和源代码位置的注释。
--json将头部和完整函数列表以JSON格式输出到stdout。
--files打印从文件表中提取的源文件路径列表。
-h, --help显示帮助信息。
攻击技术漏洞编号受影响软件观察到的勒索软件
外部远程服务CVE-2024-21683Confluence Data Center and ServerAkira, Fog, Rhysida
外部远程服务CVE-2023-22527Confluence Data Center and ServerAkira
外部远程服务CVE-2023-34362MOVEit TransferClop
外部远程服务CVE-2024-27198 / CVE-2024-27199JetBrains TeamCityAkira, Fog, Rhysida, Cactus
信任关系N/A托管服务提供商Akira, BlackSuit, LockBit, Play, RansomHub
有效账户N/AVPN, 远程桌面多种
{
"header": {
--disable-stdin
  • -exclude-cdn - 跳过对 CDN 的完整端口扫描(仅检查 80、443 端口)。
  • -ec - 跳过对 CDN 的完整端口扫描(仅检查 80、443 端口)。``` [] Binary format: pe, endian: little, arch: x86, bits: 64 [] Scanning binary for gopclntab magic bytes... [] Scanning section '.text' (0x401000, 0xF4000)... [] Scanning section '.rdata' (0x4F5000, 0x127000)... [] Found magic at vaddr=0x568C00 [] Parsed header: PcHeader(magic=0xFFFFFFF1, version=1.20+, ptrSize=8, minLC=1, nfunc=2596, nfiles=345, textStart=0x401000) [] textStart from header: 0x401000 [] Parsed 2596 functions