garble

repository·master·Indexed 26 days ago

https://github.com/burrowers/garble

A tool to obfuscate Go binaries by garbling source code and metadata to make reverse engineering more difficult. It acts as a drop-in replacement for standard Go commands like build, test, and run, offering features such as literal obfuscation, experimental control flow obfuscation via //garble:controlflow comments, and a reverse command to de-obfuscate stack traces.

Tokens
3K
Snippets
12
Records
24
Agent score
91%

What's inside garble

  1. Use control flow obfuscation in Go code

    master

    To obfuscate a specific function, add the //garble:controlflow comment directly above the function definition. You can optionally pass parameters to fine-tune the obfuscation intensity.

    Default usage:

    //garble:controlflow
    func main() {
    	println("Hello world!")
    }

    Advanced usage with parameters:

    //garble:controlflow block_splits=max junk_jumps=max flatten_passes=max
    func main() {
        println("Hello world!")
    }
    // Obfuscate with maximum parameters
    //garble:controlflow block_splits=max junk_jumps=max flatten_passes=max
    func main() {
        println("Hello world!")
    }
  2. Use garble to obfuscate Go code

    master

    The garble CLI wraps the Go toolchain to obfuscate Go code. You can use it as a drop-in replacement for standard go commands like build, test, and run.

    Basic Usage Pattern: garble [garble flags] command [go flags] [go arguments]

    Examples:

    Build an obfuscated program:

    garble build ./cmd/foo

    Combine garble flags with Go build flags:

    garble -literals build -tags=purego ./cmd/foo
    garble build ./cmd/foo
  3. De-obfuscate text with `garble reverse`

    master

    The garble reverse command is used to de-obfuscate text, such as panic stack traces or build errors, captured from an obfuscated program. It maps hashed names and obfuscated file paths back to their original, human-readable forms.

    To use it, you must provide the same build flags (like -literals or -tags) that were used during the original obfuscated build, followed by the package path and the target file(s) containing the obfuscated text.

    Usage Pattern: garble [garble flags] reverse [build flags] package [files]

  4. Caveats and limitations of control flow obfuscation

    master

    When using control flow obfuscation, be aware of the following:

    • Map Iteration: Obfuscation breaks lazy iteration over maps.
    • Complexity Scaling: flatten_passes increases control flow complexity non-linearly. It is generally recommended not to exceed a value of 3.
    • Parameter Dependency: block_splits, junk_jumps, and trash_blocks only function if flatten_passes is enabled.
  5. Configure garble via environment variables

    master

    You can control garble behavior and profiling using environment variables:

    • GARBLE_SHARED: Sets the directory for shared cache data.
    • GARBLE_CACHE: Sets the directory for the garble cache (defaults to user cache directory).
    • GARBLE_EXPERIMENTAL_CONTROLFLOW: Set to 1 to enable experimental control flow obfuscation.
    • GARBLE_WRITE_CPUPROFILES: Set to a directory path to write .pprof CPU profiles.
    • GARBLE_WRITE_MEMPROFILES: Set to a directory path to write .pprof heap profiles.
    • GARBLE_WRITE_ALLOCS: Set to a directory path to write allocation statistics text files.
    • GOGARBLE: Set to a value to specify which packages to obfuscate (defaults to * for everything).
  6. Determine obfuscated names with `obfuscatedObjectName`

    master

    The obfuscatedObjectName method is the single source of truth for name obfuscation in garble. It returns the obfuscated name of a types.Object and a boolean indicating if it was actually obfuscated.

    Obfuscation Logic:

    • Exclusions: Certain packages and names are explicitly skipped to prevent breaking the runtime or standard library functionality (e.g., sync/atomic, embed.FS, reflect.Method, main, init, TestMain).
    • Variables: Non-field variables are hashed using the package's action ID.
    • Struct Fields: Fields are hashed using the type of their parent struct (rather than the package ID) to allow for safe cross-package conversions between identical underlying types.
    • Types and Functions: Types and functions (that are not exported methods implementing an interface) are hashed using the package's action ID.
    • Methods: Methods are generally not obfuscated if they are exported and implement an interface.
    func (tf *transformer) obfuscatedObjectName(obj types.Object) (string, bool)
  7. Transform assembly files with transformAsm

    master

    The transformAsm method processes Go assembly files (.s). It performs several tasks:

    • Obfuscates the package name using -p.
    • Modifies -trimpath to prevent leaking temporary directories.
    • Replaces Go assembly constant names (e.g., go_asm.h mappings).
    • Replaces function references in assembly with their obfuscated counterparts.
    • Handles #include directives by creating garbled versions of header files.
    • Writes the resulting obfuscated assembly to a temporary directory.
    func (tf *transformer) transformAsm(args []string) ([]string, error)
  8. Transform Go source files with transformCompile

    master

    The transformCompile method is the core of the Go source obfuscation process. It:

    • Forces the linker to drop DWARF information via -dwarf=false.
    • Seeds a deterministic random number generator for literal and control flow obfuscation.
    • Performs type-checking on the files.
    • Optionally builds SSA (Static Single Assignment) form for control flow obfuscation.
    • Obfuscates literals and transforms Go source files (renaming packages, types, and functions).
    • Rewrites toolchain directives like //go:linkname and //go:cgo_import_dynamic.
    • Generates a new -importcfg to account for obfuscated import paths.
    • Returns the updated compiler flags and paths to the new obfuscated files.
    func (tf *transformer) transformCompile(args []string) ([]string, error)
  9. Process import configuration with processImportCfg

    master
    The processImportCfg method parses an existing -importcfg file and generates a new one. This is necessary because the import paths of dependencies may have been obfuscated. It handles importmap (mapping old paths to new paths) and packagefile (mapping import paths to specific object files) entries.