unstrip v1.0.0
First public release of unstrip.
unstrip recovers symbols, types, interfaces, method signatures, build
metadata, and the typed call graph from stripped Go binaries, then
either prints what it found or writes the recovered names back into the
binary's symbol table.
What it does
Reads ELF, Mach-O, and PE Go binaries built with Go 1.20 through 1.24.
Walks pclntab, moduledata, typelinks, and itablinks directly
from the bytes the linker emitted, with no DWARF, no debug info, and
no Go toolchain dependency. Every recovery path is bytes-only.
Output is text by default, or JSON when you ask for it. Three
disassembler plugins ship with the binary: a Ghidra PyGhidra script,
an IDA Python script, and a Binary Ninja Python script. Each
generated script reports apply-time success counts and surfaces the
first 20 per-call failure reasons so the operator can tell when
something did not land.
Headline features
Method-signature recovery returns Go-syntax signatures like
(_0 []uint8) (int, error) for every method whose mtyp resolves to a
funcType. Two paths feed it: each type's uncommon().methods table,
and every itab's method table. Argument names are not in the binary
(positional placeholders keep the shape correct).
--xref <SYMBOL> finds every call site that targets a function name
or hex address, grouped by containing caller. Direct CALL rel32 and
indirect CALL [rip+itab+slot] dispatch are both recognized. arm64
binaries get direct-CALL recovery and the canonical LDR Xt, [Xn, #slot]; BLR Xt itab pair.
--data-at <ADDR> interprets bytes at a data address through every
recovery map the binary has. Six modes: bytes, qwords, ptrs,
ifaces (resolves the iface header through the itab table and
inlines the dispatched method body address), slice-header, and
string.
--xref-readers and --xref-writers scan .text for RIP-relative
instructions touching a data address, with a five-kind taxonomy
(lea, mov-load, mov-store, cmp, call-indirect).
unstrip::inline::inline_callgraph reconstructs the inlined-call
graph from FUNCDATA_InlTree. Anonymous-inline nodes are kept as
first-class structural recoveries when callee names have been
stripped (garble -tiny, -ldflags=-s -w).
Disassembler integration
Rewrite mode produces a binary with recovered symbols written back
into the symbol table, so opening the file in IDA, Ghidra, or Binary
Ninja shows real names with zero plugin install. For workflows that
prefer to keep the original bytes untouched, the three plugin scripts
apply the same names through each tool's scripting API.
Install
cargo install unstrip
Or download a prebuilt binary for your platform from the assets
attached to this release. Verify with sha256sum -c SHA256SUMS.
Compatibility
| OS / arch | Recovery | Rewrite | --xref | Prebuilt |
|---|---|---|---|---|
| linux amd64 | yes | yes | yes | yes |
| linux arm64 | yes | yes | yes | yes |
| darwin arm64 | yes | yes | yes | yes |
| darwin amd64 | yes | yes | yes | source |
| windows amd64 | yes | yes | yes | yes |
Intel macOS users: cargo install unstrip.
Go versions: 1.20, 1.21, 1.22, 1.23, 1.24. Older Go produces a
different pclntab magic and is rejected with a clear error.
Safety posture
unstrip parses untrusted binaries by design. Every offset read from
the file is bounds-checked against the section it claims to live in;
out-of-range reads return errors instead of panicking. The parser
does not allocate based on attacker-controlled length fields without
a sanity cap. Untrusted-input fuzzing (structured corpus) and a
Mach-O integration fixture matrix are tracked for the next minor.
Full changelog
See CHANGELOG.md.
Reporting issues
Bug reports and feature requests welcome at
https://github.com/riven-labs/unstrip/issues. Security reports go to
the contact in SECURITY.md.