README
¶
alosgarble
Built on top of garble by the Garble Authors. We do not take full credit for this project. The original garble is an incredible tool and the foundation of everything here. Our work adds new obfuscation layers, a code guard system, a live build progress display, and performance improvements on top of that base.
alosgarble is a Go binary obfuscator that wraps the Go toolchain — just like the original garble — but with additional protection layers designed for high-security production binaries. You use it the same way you use go build, just prefix it with alosgarble.
Installation
One-liner (Linux / macOS)
Latest version:
curl -fsSL https://raw.githubusercontent.com/guno1928/alosgarble/main/install.sh | bash
Specific commit hash, branch, or tag:
curl -fsSL https://raw.githubusercontent.com/guno1928/alosgarble/main/install.sh | bash -s -- <hash>
Replace <hash> with any full or short commit hash, branch name, or tag — e.g. bash -s -- abc1234.
This will install alosgarble via go install, auto-detect your shell (bash/zsh/fish), add GOPATH/bin to your PATH, and verify the command works — all automatically.
Manual
# latest
go install github.com/guno1928/alosgarble@latest
# specific commit / tag
go install github.com/guno1928/alosgarble@<hash>
If alosgarble says "command not found" after install, add Go's bin directory to your PATH:
echo 'export PATH="$(go env GOPATH)/bin:$PATH"' >> ~/.bashrc && source ~/.bashrc
Windows
go install github.com/guno1928/alosgarble@latest
Go automatically adds %GOPATH%\bin to PATH on Windows.
Requirements:
- Go 1.26.2 or later (required by the patched linker)
- Windows, Linux, or macOS
Quick Start
# Build your project (replaces: go build)
alosgarble build .
# Build with a fixed seed (reproducible output)
alosgarble -seed=o9WDTZ4CN4w build .
# Build with a random seed each time
alosgarble -seed=random build .
# Cross-compile
GOOS=linux GOARCH=amd64 alosgarble build -o myapp-linux .
# Build a specific package path
alosgarble build ./cmd/myapp
What It Does
alosgarble applies multiple independent obfuscation passes to your compiled binary. Each pass is always-on by default — there are no flags to forget.
1. Symbol Renaming
All exported and unexported symbols (function names, type names, variable names, package paths) are replaced with random-looking identifiers derived from a hash of the build seed. The binary contains no readable symbol table.
main.processRequest → fv3kXqZ9
crypto/tls.(*Conn).Read → bR7mNw2p
2. Literal Obfuscation (Always On)
String literals, integer constants, byte slices, and other constant values in your source are never stored plaintext in the binary. They are encrypted at compile time and decrypted at runtime using the WideXOR cipher.
WideXOR is our custom literal obfuscator. Each literal gets:
- A randomly sized keystream split into K independent byte-slice fragments (K varies per literal)
- Four independent permutations controlling which fragment is declared where, which alias it gets, and what order the
copy()calls appear in - Per-fragment alias variables — each real fragment gets a one-hop alias so an analyst must trace a three-way mapping (offset → alias → fragment → bytes) before reconstructing the key
- Two decoy fragments with identical random content that participate in a self-cancelling checksum XOR — they look real to static analysis but contribute nothing to decryption
len()-based offset arithmetic incopy()calls instead of integer constants, forcing the analyst to trace alias→fragment lengths before computing copy offsets- A rolling XOR checksum over decrypted bytes with tamper detection — if the check fails, the data is corrupted in-place so the program silently misbehaves rather than crashing cleanly
// Source
var token = "prod-secret-key-XKZETA2847"
// Binary sees: fragmented encrypted bytes + 4 permutations + 2 decoys
// No string scanner will find "prod-secret-key-XKZETA2847" anywhere
3. Control Flow Obfuscation (Always On)
Functions annotated with //garble:controlflow have their control flow graph destroyed and rebuilt using control flow flattening. The original structure (loops, branches, returns) is replaced by a dispatcher switch that routes execution via an opaque state integer.
You can tune the flattening per function:
//garble:controlflow block_splits=2 junk_jumps=4 flatten_passes=2 trash_blocks=8
func sensitiveFunction() {
// ...
}
| Parameter | Default | Max | Description |
|---|---|---|---|
block_splits |
0 | unlimited | Split basic blocks into more pieces |
junk_jumps |
0 | 256 | Insert dead branches that never execute |
flatten_passes |
1 | 4 | How many times to apply flattening |
trash_blocks |
0 | 1024 | Insert unreachable junk basic blocks |
Dispatcher hardening options:
//garble:controlflow flatten_passes=2 hardening=xor
//garble:controlflow flatten_passes=2 hardening=delegate_table
//garble:controlflow flatten_passes=2 hardening=xor,delegate_table
4. Guard Code Injection
This is the major addition over upstream garble. Every package in your build gets a guard file injected at compile time. The guard file serves two purposes:
a) Binary integrity check — the guard reads your own executable path via os.Executable(), hashes its content, and verifies it against a magic value baked in at build time. If the binary has been patched or tampered with, the guard sets _gsecActive = false, which makes all literal decryption silently produce garbage. The program keeps running but outputs corrupted data — no obvious crash for a reverse engineer to trigger.
b) Anti-analysis ballast — the guard generates 36–54 lookup tables (each ~5–8 KB) and tens of thousands of chained computation functions of varying complexity. The function counts per package are randomized per build:
| Class | Base | Variance | Ops per function | What it does |
|---|---|---|---|---|
| Heavy | 55 | +0–54 | 120–240 ops | Complex XOR/rotate/multiply chains across 3 tables |
| Medium | 55 | +0–54 | 30–50 ops | Moderate math, can call Heavy functions |
| Light | 35,000 | +0–34,999 | 4–10 ops | Tiny wrappers, delegates to helpers |
These are real, live code paths that execute at init() time. They:
- Dramatically increase the binary's apparent complexity
- Fill decompiler views with thousands of functions that look like they do something
- Are seeded per-build so every binary looks different even from the same source
Packages that cannot import "os" (e.g. pure utility packages with no OS dependency) get a ballast-only guard — same lookup tables and function chains, but no file-integrity check, so the import graph stays clean.
The _gsecActive boolean is the bridge between the guard and the obfuscated literals. String decryption is gated on it:
guard.init() runs → file hash verified → _gsecActive = true → literals decrypt correctly
binary tampered → hash mismatch → _gsecActive stays false → literals corrupt silently
5. Linker-Level Symbol Stripping
The Go linker is patched to remove unexported function names from the binary's function name table. Combined with symbol renaming this means there is no readable name anywhere in the binary for unexported code — not in the symbol table, not in stack traces, not in pclntab.
6. Decoy Literals
Every obfuscated package gets 2–4 package-level var declarations injected that look like real secrets — API keys, JWTs, connection strings, bearer tokens. They are encrypted identically to real string literals using WideXOR, so an analyst who decrypts them cannot tell which strings are real and which are bait. Every decrypted value must be individually validated.
// injected automatically — analyst sees this after decryption:
var _dc3a7f = "postgresql://svc_user:Kp8!nR3zQw@db-prod-07.internal:5432/coredb"
var _dc91be = "ghp_16C7e42F292c6912E169D1a7dB4e59b1D2c5"
// these are bait — they decrypt fine but mean nothing
7. Live Build Progress Display
While alosgarble builds your project, a live animated box is drawn to the terminal showing:
- Package count and percentage complete
- Elapsed time and ETA
- Current package being compiled
- Active pipeline phases
┌──────────────────────────────────────────────────────────────────┐
│ A L O S G A R B L E // Binary Obfuscator │
├──────────────────────────────────────────────────────────────────┤
│ ██████████████████████████████ 100% · 105 / 105 pkgs │
│ Elapsed 4:46 ETA 00:00 │
│ Package complete │
│ Phase Obfuscation · Guard Inject · Literal Encrypt │
└──────────────────────────────────────────────────────────────────┘
The display only activates when stderr is a real terminal (not redirected or piped), so CI logs stay clean.
Examples
Basic Application
cd myapp/
alosgarble build -o myapp.exe .
./myapp.exe
With a Reproducible Seed
Using the same seed produces the same obfuscation every time. Useful for CI.
alosgarble -seed=o9WDTZ4CN4w build -o myapp.exe .
Force Full Rebuild
alosgarble build -a -o myapp.exe .
The -a flag forces every package to be rebuilt and re-obfuscated rather than reusing cached packages from a previous build.
Control Flow on a Sensitive Function
package main
import "fmt"
//garble:controlflow flatten_passes=2 junk_jumps=8 trash_blocks=16 hardening=xor,delegate_table
func verifyLicense(key string) bool {
// complex logic here
return key == "valid"
}
func main() {
fmt.Println(verifyLicense("valid"))
}
alosgarble build -o app.exe .
Anti-Debug Integration
alosgarble works with github.com/guno1928/antidebug for runtime debugger detection:
package main
import (
"fmt"
"os"
antidebug "github.com/guno1928/antidebug/core"
)
func main() {
cfg := antidebug.DefaultConfig()
cfg.OnDetect = func(reason string) {
os.Exit(1)
}
antidebug.Start(cfg)
fmt.Println("Running securely.")
}
cd myapp/
alosgarble build -a -o secure.exe .
The obfuscated binary will:
- Run the anti-debug checks at startup
- Verify binary integrity via the guard
- Decrypt all strings only if both checks pass
Multi-Package Project
Guard injection works across every package in your module automatically. No changes to your code are needed.
# All packages in the module get guard code injected
alosgarble build -a ./...
Flags
| Flag | Default | Description |
|---|---|---|
-seed |
random per build | Base64 seed for obfuscation. Use -seed=random for a fresh random seed. |
-debug |
off | Inject a debug runtime into the built binary. On startup the binary prints a banner to stderr and writes all debug events to a .garbledebug_<pid>.log file next to the executable. |
-debugpassword <pass> |
off | Requires -debug. Encrypt all debug log messages with ChaCha20 using <pass> as the key. No output appears on stderr — everything goes to the encrypted log file only. The password constant is itself obfuscated in the binary by WideXOR. |
-debugdir |
off | Write pre/post obfuscation source trees to a directory |
-literals and -tiny are always forced on and cannot be disabled.
Debug Mode
Plain debug (logs to stderr + file)
alosgarble -debug build -o myapp.exe .
./myapp.exe
# stderr shows: [GARBLE-DEBUG] ====== PROCESS STARTED ======
# also written to: myapp.exe.garbledebug_<pid>.log
The injected debug runtime captures:
- Process startup (PID, executable path, Go version, OS/arch, GOMAXPROCS)
- Unhandled panics in
main()with full goroutine stack dump - Panics in any
init()function os.Exit()calls with exit code and call stack
Encrypted debug (log file only, no terminal output)
alosgarble -debug -debugpassword mySecretPass build -o myapp.exe .
./myapp.exe
# nothing on stderr
# encrypted log written to: myapp.exe.garbledebug_<pid>.log
Decrypt the log later:
alosgarble decrypt -password mySecretPass myapp.exe.garbledebug_12345.log
Output:
[GARBLE-DEBUG] ====== PROCESS STARTED ======
[GARBLE-DEBUG] PID : 12345
[GARBLE-DEBUG] Executable : C:\path\to\myapp.exe
[GARBLE-DEBUG] Go version : go1.26.2
[GARBLE-DEBUG] OS/Arch : windows/amd64
[GARBLE-DEBUG] GOMAXPROCS : 8
[GARBLE-DEBUG] Log file : myapp.exe.garbledebug_12345.log (encrypted)
[GARBLE-DEBUG] Mode : password-encrypted — no terminal output
...
If you supply the wrong password, decryption fails with a clear error:
decryption failed at message 0 — wrong password or corrupted log file
Encrypted log format:
| Offset | Size | Content |
|---|---|---|
| 0 | 8 bytes | Magic: ALOSDBG\x01 |
| 8 | 16 bytes | PID-derived salt |
| 24+ | repeated | msgLen[4LE] + nonce[12] + ChaCha20 ciphertext |
Each plaintext block has ALOS prepended as a validation token so wrong-password attempts are detected immediately.
The ChaCha20 implementation and 100,000-round key derivation function are fully inlined into the built binary with no extra imports — there is no golang.org/x/crypto dependency in your final binary.
Debugging a Build (Source Inspection)
If you want to inspect the generated source before and after obfuscation:
alosgarble -debugdir=./debug_out build .
This writes the original and transformed Go AST for every package into ./debug_out/.
How It Differs from Upstream Garble
| Feature | Upstream garble | alosgarble |
|---|---|---|
| Symbol renaming | ✅ | ✅ |
| Literal obfuscation | ✅ (multiple strategies) | ✅ WideXOR (custom, always on) |
| Control flow flattening | ✅ | ✅ |
| Linker symbol stripping | ✅ | ✅ (patched) |
| Binary integrity guard | ❌ | ✅ |
| Anti-analysis ballast | ❌ | ✅ (~35,000–70,000 light functions + 55–109 heavy + 55–109 medium per package) |
| Decoy literals | ❌ | ✅ (2–4 fake secrets per package, WideXOR encrypted) |
| Per-fragment alias permutations | ❌ | ✅ (4 independent permutations) |
| Silent corruption on tamper | ❌ | ✅ |
| Live build progress display | ❌ | ✅ (animated terminal UI with ETA) |
| ASLR-aware guard | ❌ | ✅ (table base address mixed into verification) |
| Cross-validation chain | ❌ | ✅ (adjacent lookup tables check each other) |
| Multi-sentinel guard activation | ❌ | ✅ (3 independent booleans + monotonic counter) |
| Debug runtime injection | ❌ | ✅ (panic/exit capture, startup banner, log file) |
| Encrypted debug logs | ❌ | ✅ (ChaCha20, inline KDF, no extra imports) |
decrypt subcommand |
❌ | ✅ (alosgarble decrypt -password <pass> <logfile>) |
Credits
This project is a fork of garble by the Garble Authors, licensed under the BSD 3-Clause License. The core obfuscation engine, toolchain wrapping, AST transformation, and SSA-based control flow work are all from the original project. We do not take credit for that work.
Our additions:
- Guard code injection system — binary integrity check + multi-sentinel activation + ASLR-aware verification + cross-validation chain between lookup tables
- WideXOR literal cipher — per-fragment aliases, 4 independent permutations, rolling checksum, decoy fragments, and silent tamper corruption
- Anti-analysis ballast generator — 36–54 lookup tables, 55–109 heavy functions, 55–109 medium functions, 35,000–70,000 light functions per package, content-dependent sizing per build
- Zero-cost dead-code obfuscation — injected
if false,for false, dead variables, and harmless wrapper syntax that the Go compiler eliminates entirely, bloating source confusion without runtime cost - Decoy literal injection — 2–4 fake secrets per package, obfuscated identically to real strings
- Live build progress display — animated terminal UI with ETA, package count, and current package name
- Performance optimizations — lookup table encoding switched from composite literals to string ballast, reducing go/types type-check work by ~10–19% on large builds
- Debug runtime injection (
-debug) — captures panics,os.Exitcalls, andinit()failures; writes a startup banner to stderr and a persistent log file next to the executable - Encrypted debug logs (
-debugpassword) — ChaCha20-encrypted log-file-only mode; no terminal output; password constant is itself obfuscated by WideXOR in the binary; fully inline crypto with no extra binary imports decryptsubcommand —alosgarble decrypt -password <pass> <logfile>streams all decrypted debug messages; wrong-password detection via per-block validation token
License
BSD 3-Clause — same as upstream garble. See LICENSE.
Documentation
¶
There is no documentation for this package.