Skip to content

Releases: riven-labs/unstrip

unstrip v1.2.0

Choose a tag to compare

@github-actions github-actions released this 01 Jul 12:28

unstrip 1.2.0 recovers pre-1.18 Go binaries. The reader parses the Go 1.16 and 1.17 pclntab, recovers types and itabs from the older moduledata layout, and decodes the pre-1.17 type-name length form. Functions, types, and code-signature identification now work on binaries that before reported nothing. A Go 1.2 through 1.15 binary is named by its version rather than read as a non-Go container, and a file whose header cannot be parsed yields a best-effort probe instead of a bare error.

Older Go is where a lot of shipped software and older samples live, so a triage pass that used to stop at "no pclntab found" now has names, types, and interfaces to work from.

cargo install unstrip

Every change is listed in CHANGELOG.md.

unstrip 1.1.0

Choose a tag to compare

@DEMON1A DEMON1A released this 26 Jun 20:07

Broadens what unstrip parses, adds garble reflected-name recovery, and hardens the parsers against crafted input.

Install with cargo install unstrip, or download a prebuilt binary below.

Added

  • --reflect-names recovers garble's obfuscated-to-original reflected-name dictionary from the data the runtime keeps for reflection. --degarble relabels the hashed identifiers in the function, type, and itab output back to those names where they are known.
  • Parses the Go 1.26 moduledata layout. Recovery is now exercised across Go 1.18 through 1.26, including PIE address rebasing and arm64 type recovery.
  • Recovers itabs and reads moduledata on 32-bit and big-endian binaries. Pointer size and byte order come from the container header instead of an assumed 64-bit little-endian. Type-graph recovery still requires 64-bit little-endian and reports a clear error rather than returning a wrong answer on anything else.
  • Selects the matching slice from a Mach-O universal (fat) binary, and locates moduledata through the Mach-O go_module section.
  • Recovers struct field tags into the type catalog.
  • Recovers the ELF load base from the program headers when an ELF has had its section table stripped, so addresses still resolve.
  • Finds the pclntab structurally when its magic has been rewritten, validating each candidate by layout instead of trusting the magic bytes.
  • A container probe names packed and non-Go inputs by reason, so a file that is not a Go binary fails with an explanation instead of a misparse.
  • --fingerprint emits a stable structural hash for clustering recompiles of the same source. --behavioral narrows it to the stdlib-interface method-set vector, which stays stable under garble.
  • libfuzzer targets covering the parser entry points.

Changed

  • --diff matches functions by identity (name, then code signature) instead of by address, so a rebuild that moves code still lines the two binaries up.
  • The export generator records which tool produced the output, in both the Ghidra metadata and the stats lines, so an export driven by another tool is not labeled as unstrip.
  • --buildinfo degrades when the modinfo blob is stripped, reporting what it can recover instead of failing.
  • Pclntab derives Clone.

Fixed

  • Bounds and overflow checks across the parsers for attacker-crafted headers: cutab index arithmetic, moduledata section offsets, PE section address arithmetic, the Mach-O fat-arch count, section file ranges, and moduledata slice extents are all verified or saturated before use.
  • --data-at reads NOBITS sections without panicking and rejects implausible string headers.
  • The pcHeader scan accepts a zero textStart.
  • Pre-1.18 pclntab layouts fail with an honest version error instead of a partial misread.

unstrip v1.0.0

Choose a tag to compare

@github-actions github-actions released this 01 Jun 19:38

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.