Skip to content
Security
Skill

/analyzing-go-binaries

Reverse engineer Go binaries by recovering function names and types from pclntab and moduledata using GoReSym, redress, and IDA/Ghidra Go plugins, and by reading Go's non-standard calling convention, interface dispatch, and string layout. Use when a binary contains Go runtime

BOOST
From plugin
secskills
17094 skills
Install
$ npx -y skills add trilwu/secskills --skill analyzing-go-binaries --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/analyzing-go-binaries

Context preview

The summary Claude sees to decide when to auto-load this skill.

Reverse engineer Go binaries by recovering function names and types from pclntab and moduledata using GoReSym, redress, and IDA/Ghidra Go plugins, and by reading Go's non-standard calling convention, interface dispatch, and string layout. Use when a binary contains Go runtime

SKILL.md

analyzing-go-binaries.SKILL.md
name: analyzing-go-binaries
description: Reverse engineer Go binaries by recovering function names and types from pclntab and moduledata using GoReSym, redress, and IDA/Ghidra Go plugins, and by reading Go's non-standard calling convention, interface dispatch, and string layout. Use when a binary contains Go runtime strings, when strings show runtime.main or go:buildid, when a stripped binary is unexpectedly large, or when analyzing Go malware or a Go-based service.
verified: 2026-07-27

Analyzing Go Binaries

Go binaries look hostile — statically linked, tens of megabytes, no imports you recognize, and "stripped" in a way that makes tools show thousands of `sub_` functions. They are not. Go ships its own symbol table for runtime reflection and panic traces, and `strip` does not remove it. Recover it and the binary becomes one of the easiest targets there is.

When to Use

  • `strings` shows `runtime.main`, `go:buildid`, `go.buildinfo`, or

`runtime.gopanic`

  • A "stripped" binary is 5–50 MB with almost no dynamic imports
  • The disassembler shows thousands of unnamed functions and unreadable strings
  • Analyzing Go malware, a Go CLI tool, or a compiled Go service

When NOT to Use

  • **Rust binaries** — use `analyzing-rust-binaries`; the symbol recovery is

entirely different

  • **.NET assemblies** — use `analyzing-dotnet-assemblies`
  • **Suspected malware, before containment** — use `analyzing-malware` for the

environment, then come back here

  • **General native RE** — use `analyzing-binaries`

Confirm It Is Go, and Which Version

strings -n 6 target | rg -m5 'go1\.[0-9]+|go:buildid|runtime\.main|GOROOT'
go version target                     # works on unstripped and many stripped builds
go version -m target                  # module list and build settings — free SBOM

`go version -m` is the highest-value first command. It prints the module dependency graph with versions, which gives you the third-party libraries in use before you disassemble anything — often answering the question outright (which HTTP library, which crypto, which C2 framework).

The Go version matters because `pclntab` layout changed at 1.2, 1.16, 1.18, and 1.20. Tooling that fails is usually version mismatch, not a hardened binary.

Recover Symbols

# GoReSym — extracts pclntab, moduledata, types, and build info
GoReSym -t -d -p target > syms.json
#   -t  user type metadata
#   -d  include standard library
#   -p  paths

# redress — Go-aware analysis, works well when GoReSym struggles
redress info target
redress symbols target
redress types target

# Load into the disassembler
#   IDA:    AlphaGolang, golang_loader_assist, or the GoReSym IDA script
#   Ghidra: GolangAnalyzerExtension, or gotools
#   Binja:  the Golang loader plugin

After applying symbols, functions carry their real names — `main.processRequest`, `crypto/tls.(*Conn).Handshake`, `github.com/vendor/pkg.Function`. **Filter to `main.*` and to third-party module paths.** Everything under `runtime.`, `internal/`, and the standard library is stock and is 90%+ of the function count.

jq -r '.UserFunctions[].FunctionName' syms.json | rg -v '^(runtime|internal|reflect|sync)\.' | head -40

Reading Go Code in a Disassembler

Four things make Go listings confusing until you know them:

**Strings have no terminator.** Go strings are a pointer plus a length, so `strings` output runs adjacent literals together and the disassembler shows a pointer load followed by a length constant. Look for the pair — the constant next to the pointer is the length, and that is how you slice the correct substring out of the blob.

**Calling convention.** Before Go 1.17 all arguments and return values went on the stack, not in registers. From 1.17 a register ABI applies on amd64/arm64. A decompiler configured for the C convention will show wrong arguments; Go-aware plugins fix this, and it is the main reason decompiler output looks nonsensical.

**Interface dispatch.** Calls through an interface go via an `itab` — a table holding the concrete type and its method pointers. To resolve a call target, find the `itab` being loaded, then read the concrete type. Type recovery tools name these, which turns an indirect call into a readable one.

**Goroutines and defers.** `go f()` compiles to `runtime.newproc` with `f` as an argument, so concurrent logic does not appear as a direct call. `defer` becomes `runtime.deferproc`/`deferreturn`, which scatters cleanup code away from where it was written. When following control flow, check `newproc` call sites for work you would otherwise miss entirely.

Type Recovery

Go embeds full type descriptors for reflection. That means struct field names and layouts are recoverable — including the JSON tags that map straight to a wire protocol.

redress types target | rg -A10 'type main\.'
# Struct tags like `json:"api_key"` recover the exact protocol field names

This is the fastest route to a Go service's API surface or a Go implant's C2 message format: recover the request and response structs, and you have the protocol without reading a single instruction.

Go-Specific Security Review

If the job is finding bugs rather than understanding behaviour, the Go-specific classes worth targeting:

  • **Ignored errors.** `_ =` on a function returning an error, especially

around auth, crypto, and file operations.

  • **`math/rand` for security values.** Token, session ID, or nonce generation

using the non-crypto RNG.

  • **`InsecureSkipVerify: true`** in a `tls.Config`.
  • **`fmt.Sprintf` building SQL, shell commands, or URLs.**
  • **Data races** on shared maps and structs — often the source of

authorization bugs under load.

  • **`os/exec` with a shell**, or with an argument built from input.

With source available, use `auditing-code-for-vulnerabilities` and `govulncheck`; the above is for when you only have the binary.

Go Malware Notes

Go is common in cross-platform implants, and it leave

Read more
Ships withsecskills

**Security skills for Claude Code that encode the judgment professionals bring, not another wrapper around a scanner.** Offense, defense, and the reverse-engineering and code-audit core both sides share. Every skill is fact-checked against primary sources.

Get the whole plugin

Other skills on secskills.