| name | analyzing-golang-malware-with-ghidra |
| description | Reverse engineer Go-compiled malware in Ghidra by parsing Go buildinfo and pclntab structures, recovering stripped/obfuscated function names (e.g. via GoResolver), and extracting embedded module/dependency strings and types from Go binaries. Use when analyzing a Go-language malware sample, deobfuscating a garble-packed Go binary, or recovering function names and third-party dependencies from a stripped Go executable. |
| domain | cybersecurity |
| subdomain | malware-analysis |
| tags | ["golang","ghidra","reverse-engineering","malware-analysis","binary-analysis","go-malware","disassembly"] |
| version | 1.0 |
| author | mahipal |
| license | Apache-2.0 |
| nist_csf | ["DE.AE-02","RS.AN-03","ID.RA-01","DE.CM-01"] |
| mitre_attack | ["T1027","T1620","T1140","T1059"] |
Analyzing Golang Malware with Ghidra
Overview
Go (Golang) has become a popular language for malware authors due to its cross-compilation capabilities, static linking that produces self-contained binaries, and the complexity it introduces for reverse engineering. Go binaries contain the entire runtime, standard library, and all dependencies statically linked, resulting in large binaries (often 5-15MB) with thousands of functions. Ghidra struggles with Go-specific string formats (non-null-terminated), stripped function names, and goroutine concurrency patterns. Specialized tools like GoResolver (Volexity, 2025) use control-flow graph similarity to automatically deobfuscate and recover function names in stripped or obfuscated Go binaries.
When to Use
- When investigating security incidents that require analyzing golang malware with ghidra
- When building detection rules or threat hunting queries for this domain
- When SOC analysts need structured procedures for this analysis type
- When validating security monitoring coverage for related attack techniques
Prerequisites
- Ghidra 11.0+ with JDK 17+
- GoResolver plugin (for function name recovery)
- Go Reverse Engineering Tool Kit (go-re.tk)
- Python 3.9+ for helper scripts
- Understanding of Go runtime internals (goroutines, channels, interfaces)
- Familiarity with Go binary structure (pclntab, moduledata, itab)
Key Concepts
Go Binary Structure
Go binaries embed rich metadata in the pclntab (PC Line Table) structure, which maps program counters to function names, source files, and line numbers. Even stripped binaries retain this metadata. The moduledata structure contains pointers to type information, itabs (interface tables), and the pclntab itself. Go strings are stored as a pointer-length pair rather than null-terminated C strings.
Function Recovery in Stripped Binaries
Despite stripping symbol tables, Go binaries retain function names within the pclntab. However, obfuscation tools like garble rename functions to random strings. GoResolver addresses this by computing control-flow graph signatures of obfuscated functions and matching them against a database of known Go standard library and third-party package functions.
Crate/Dependency Extraction
Go's dependency management embeds module paths and version strings in the binary. Extracting these reveals the malware's third-party dependencies (HTTP libraries, encryption packages, C2 frameworks), which provides insight into capabilities without full reverse engineering.
Workflow
Step 1: Initial Binary Analysis
struct
sys
re
():
magic =
offset = data.find(magic)
offset == -:
()
go_version = re.search(, data[offset:offset+])
go_version:
()
offset
():
magics = {
: ,
: ,
: ,
: ,
}
magic, version magics.items():
offset = data.find(magic)
offset != -:
()
offset, version
,
():
pclntab_offset :
[]
functions = []
func_pattern = re.(
,
)
func_pattern.finditer(data):
name = .group().decode(, errors=)
(name) > (name) < :
functions.append(name)
((functions))
():
strings = []
ascii_pattern = re.()
ascii_pattern.finditer(data):
s = .group().decode()
interesting = [
, , , , ,
, , , , ,
, , , , ,
, , , , ,
, , , ,
]
(kw s.lower() kw interesting):
strings.append(s)
strings
():
deps = []
dep_pattern = re.(
)
dep_pattern.finditer(data):
dep = .group().decode(, errors=)
deps.append(dep)
unique_deps = ((deps))
unique_deps
():
(filepath, ) f:
data = f.read()
()
()
( * )
find_go_build_info(data)
pclntab_offset, go_version = find_pclntab(data)
functions = extract_function_names(data, pclntab_offset)
()
categories = {
: [], : [], : [],
: [], : [], : [],
}
f functions:
f f.lower():
categories[].append(f)
f:
categories[].append(f)
f f:
categories[].append(f)
f f:
categories[].append(f)
f.startswith():
categories[].append(f)
f f:
categories[].append(f)
cat, funcs categories.items():
funcs:
()
fn funcs[:]:
()
deps = extract_dependencies(data)
()
dep deps[:]:
()
sus_strings = extract_go_strings(data)
()
s sus_strings[:]:
()
__name__ == :
(sys.argv) < :
()
sys.exit()
analyze_go_binary(sys.argv[])