Skip to content

maxgio92/resurgo

Repository files navigation

ResurGo

CI Go Reference GitHub Tag

ResurGo is a Go library for static function recovery from stripped executable binaries.

Features

  • Disassembly-based detection: function entry recovery via three complementary signals - prologue pattern matching, call-site analysis, and alignment boundary analysis
  • DWARF CFI-based detection: high-confidence function entries extracted from .eh_frame FDE records - compiler-written, survives strip --strip-all
  • False positive filtering: discards intra-function jump targets and linker-generated PLT stubs from the candidate set
  • Format-agnostic core: works on raw machine code bytes from any binary format
  • ELF convenience wrapper: built-in support for parsing ELF executables and inferring architecture

Supported architectures

  • x86_64 (AMD64)
  • ARM64 (AArch64)

Detection strategies

Disassembly-based

Resurgo disassembles the .text section and runs three independent signals in parallel, then merges the results:

  • Prologue matching - recognizes architecture-specific function entry instruction sequences. See docs/PROLOGUES.md.
  • Call-site analysis - extracts CALL and JMP targets; functions called or jumped to from many sites carry higher confidence. See docs/CALLSITES.md.
  • Alignment boundary analysis - recovers pure-leaf and never-called functions by detecting the alignment gap compilers emit between adjacent functions. See docs/BOUNDARY.md.

Candidates from all three signals are merged and scored. ELF-specific false-positive filters (PLT ranges, intra-function jump anchor check) are applied before the final result is returned.

DWARF CFI-based

When the binary contains an .eh_frame section, resurgo parses its FDE (Frame Description Entry) records and uses their initial_location fields as a high-confidence function entry set. These addresses were written by the compiler - not inferred by heuristics - and are typically present in stripped ELF binaries where .symtab and .debug_* are long gone.

The EhFrameDetector emits these addresses as candidates. The EhFrameFilter then retains only candidates confirmed by an FDE, dropping disassembly noise. See docs/CFI.md.

Usage

Detect functions from a stripped ELF

package main

import (
    "debug/elf"
    "fmt"
    "log"

    "github.com/maxgio92/resurgo"
)

func main() {
    f, err := elf.Open("./myapp")
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()

    candidates, err := resurgo.DetectFunctionsFromELF(f)
    if err != nil {
        log.Fatal(err)
    }

    for _, c := range candidates {
        fmt.Printf("0x%x: %s (confidence: %s)\n",
            c.Address, c.DetectionType, c.Confidence)
    }
}

Example output

0x401000: both (confidence: high)
0x401100: prologue-only (confidence: medium)
0x401200: call-target (confidence: medium)
0x401300: aligned-entry (confidence: low)
0x401400: cfi (confidence: high)

Raw bytes (format-agnostic)

For non-ELF binaries or raw memory dumps, use the lower-level primitives directly:

prologues, err := resurgo.DetectPrologues(data, 0x400000, resurgo.ArchAMD64)
edges, err := resurgo.DetectCallSites(data, 0x400000, resurgo.ArchAMD64)

API Reference

// DetectFunctionsFromELF runs all detectors then all filters against f and
// returns a deduplicated, sorted slice of function candidates.
// Architecture is inferred from the ELF header.
// opts may include WithDetectors or WithFilters to replace either pipeline.
func DetectFunctionsFromELF(f *elf.File, opts ...Option) ([]FunctionCandidate, error)

// WithDetectors replaces the default detector pipeline.
// Detectors run in order; results are merged before filtering.
func WithDetectors(detectors ...CandidateDetector) Option

// WithFilters replaces the default filter pipeline.
// Filters run in order. Pass no arguments to disable all filters.
func WithFilters(filters ...CandidateFilter) Option

// Built-in detectors, enabled by default in the order listed:
var DisasmDetector   CandidateDetector  // prologue, call-site, and alignment-boundary detection
var EhFrameDetector  CandidateDetector  // emits candidates from .eh_frame FDE records

// Built-in filters, enabled by default in the order listed:
var CETFilter     CandidateFilter  // drops non-ENDBR64 aligned entries on CET AMD64 binaries
var EhFrameFilter CandidateFilter  // retains only FDE-confirmed candidates
var PLTFilter     CandidateFilter  // removes PLT-section candidates (always last)

// DetectPrologues scans raw machine code bytes for architecture-specific
// function prologue patterns. Works on any binary format.
func DetectPrologues(code []byte, baseAddr uint64, arch Arch) ([]Prologue, error)

// DetectCallSites scans raw machine code bytes for CALL and JMP instructions
// and returns their resolved target addresses. Works on any binary format.
func DetectCallSites(code []byte, baseAddr uint64, arch Arch) ([]CallSiteEdge, error)

Key types:

type DetectionType string

const (
    DetectionPrologueOnly DetectionType = "prologue-only"
    DetectionCallTarget   DetectionType = "call-target"
    DetectionJumpTarget   DetectionType = "jump-target"
    DetectionPrologueCallSite DetectionType = "prologue-callsite"
    DetectionAlignedEntry DetectionType = "aligned-entry"
    DetectionCFI          DetectionType = "cfi"
)

type FunctionCandidate struct {
    Address       uint64        `json:"address"`
    DetectionType DetectionType `json:"detection_type"`
    PrologueType  PrologueType  `json:"prologue_type,omitempty"`
    CalledFrom    []uint64      `json:"called_from,omitempty"`
    JumpedFrom    []uint64      `json:"jumped_from,omitempty"`
    Confidence    Confidence    `json:"confidence"`
}

Implementation

+------------------+
|   *elf.File      |
+------------------+
         |
         +-------------------------------+
         |                               |
         v                               v
+------------------+           +------------------+
|  DisasmDetector  |           | EhFrameDetector  |
|  (.text bytes)   |           |   (.eh_frame)    |
+---+---------+----+           +--------+---------+
    |         |    |                    |
    v         v    v                    v
+------+ +------+ +--------+  +------------------+
|Prolog| |Call  | |Boundary|  | FDE entry VAs    |
|ues   | |Sites | |Analysis|  | (DetectionCFI)   |
+--+---+ +--+---+ +---+----+  +--------+---------+
   |        |         |                |
   +--------+---------+----------------+
            v
   +------------------+
   | mergeCandidates  |
   | (dedup by addr)  |
   +--------+---------+
            |
            v
   +------------------+
   |   CETFilter      |  drops non-ENDBR64 aligned entries (CET binaries)
   +--------+---------+
            |
            v
   +------------------+
   |  EhFrameFilter   |  retains only FDE-confirmed candidates
   +--------+---------+
            |
            v
   +------------------+
   |   PLTFilter      |  removes PLT-section candidates
   +--------+---------+
            |
            v
   +------------------+
   |[]FunctionCandidate|
   +------------------+

Limitations

  • Reports addresses only - no symbol names on stripped binaries
  • Disassembly signals are heuristic; CRT scaffolding on aligned addresses can still produce false positives when .eh_frame is absent
  • Linear disassembly - indirect jumps and computed addresses are not resolved

Dependencies

  • Go 1.25.7+
  • golang.org/x/arch - x86 and ARM64 disassembler
  • debug/elf (standard library) - ELF parser

References

About

A Go library for function recovery from stripped binaries.

Resources

License

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors