Analyzing Go programs with GoReSym

   

GoReSym is a tool that parses executables compiled by Go and extracts metadata, such embedded types, file paths, compilation flags, etc.

As a reminder, here is the help text:

Usage of GoReSym:
  -d    Print Default Packages
  -human
        Human view, print information flat rather than json, some information is omitted for clarity
  -m int
        Manually parse the RTYPE at the provided virtual address, disables automated enumeration of moduledata typelinks itablinks
  -p    Print File Paths
  -t    Print types automatically, enumerate typelinks and itablinks
  -v string
        Override the automated version detection, ex: 1.17. If this is wrong, parsing may fail or produce nonsense

I like to use the following flags:

$  GoReSym -d -p -t /path/to/sample  >  goresym.json

Often I’ll output to a temporary JSON file so that I can more quickly run queries against the data. Then I use jless to interactively explore the results:

▽ {Version: "1.19.5", BuildId: "Gftn3y7Me3ljLt7lDk7Y/_fkIctizzhovOx…", …}
    Version: "1.19.5"
    BuildId: "Gftn3y7Me3ljLt7lDk7Y/_fkIctizzhovOxyBwvYX/11PCBjPQbF43WKD…"
    Arch: "amd64"
    OS: "freebsd"
  ▽ TabMeta: {VA: 7262656, Version: "1.18", Endianess: "LittleEndian", …}
      VA: 7262656
      Version: "1.18"
      Endianess: "LittleEndian"
      CpuQuantum: 1
      CpuQuantumStr: "x86/x64/wasm"
      PointerSize: 8
  ▷ ModuleMeta: {VA: 8504992, TextVA: 4198400, Types: 6381568, ET…: …, …}
  ▷ Types: [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, …]
  ▷ Interfaces: [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, …]
  ▽ BuildInfo: {GoVersion: "go1.19.5", Path: "command-line-arguments", …}
      GoVersion: "go1.19.5"
      Path: "command-line-arguments"
    ▽ Main: {Path: "", Version: "", Sum: "", Replace: null}
        Path: ""
        Version: ""
        Sum: ""
        Replace: null
    ▽ Deps: [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
      ▽ [0]: {Path: "github.com/go-ping/ping", Version: "v1.1.0", …}
          Path: "github.com/go-ping/ping"
          Version: "v1.1.0"
          Sum: "h1:3MCGhVX4fyEUuhsfwPrsEdQw6xspHkv5zHsiSoDFZYw="
...

Here are some jq invocations that are useful for exploring the results:

show Go compiler version:

❯ cat goresym.json | jq -r ".BuildInfo.GoVersion"
go1.19.5

show compiler settings:

❯ cat goresym.json | jq -r '.BuildInfo.Settings[] | "\(.Key): \(.Value)"' | sort
-compiler: gc
-ldflags: "-s -w"
-tags: release
CGO_ENABLED: 0
GOAMD64: v1
GOARCH: amd64
GOOS: windows

show dependencies and their versions:

❯ cat goresym.json | jq -r '.BuildInfo.Deps[] | "\(.Path) \(.Version)"' | sort
github.com/go-ping/ping v1.1.0
github.com/google/uuid v1.3.0
golang.org/x/sys v0.2.0
...

list packages:

❯ cat goresym.json | jq -r '.UserFunctions[].PackageName' | sort | uniq
github.com/go-ping/ping
github.com/google/uuid
golang.org/x/sync/errgroup
main
...

list main package functions:

❯ cat goresym.json | jq -r '.UserFunctions[] | select(.PackageName == "main") | .FullName' | sort | uniq
main.connect
main.spin
main.main
...

list source file paths:

❯ cat goresym.json | jq -r ".Files[]" | sort
...
/usr/lib/go-1.19/src/crypto/ecdsa/ecdsa.go
/usr/lib/go-1.19/src/crypto/ecdsa/ecdsa_noasm.go
/usr/lib/go-1.19/src/crypto/ed25519/ed25519.go
/usr/lib/go-1.19/src/crypto/elliptic/elliptic.go
/usr/lib/go-1.19/src/crypto/elliptic/nistec.go
/usr/lib/go-1.19/src/crypto/elliptic/nistec_p256.go
...

list type names:

❯ cat goresym.json | jq -r ".Types[].Str" | sort | uniq
**big.Int
**gob.decEngine
*[]*runtime.bmap
*[]runtime.ancestorInfo
*[]syscall.Iovec
*[]uint8
*abi.IntArgRegBitmap
...