crack

module
v0.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 9, 2026 License: MIT

README

CRACK - Compiler Hardening Checker

Note: This is a v0 release, API may change.

A tool to analyze ELF binaries for security hardening features. Supports binaries compiled with gcc, clang, and rustc (stable).

Based on recommendations from:

Installation

go install github.com/mkacmar/crack/cmd/crack@latest

Or download pre-built binaries from releases.

Usage

crack analyze [options] [<path>...]
Input Options
  • <path>... - Files or directories to analyze (supports glob patterns)
  • --recursive - Recursively scan directories
  • --input <file> - Read paths from file, one per line (use - for stdin)
  • --parallel <n> - Number of files to analyze in parallel
Rule Selection

See rules reference for available rules.

  • --rules <ids> - Comma-separated list of rule IDs to run
  • --target-compiler <spec> - Only run rules available for these compilers (e.g., gcc, clang:15)
  • --target-platform <spec> - Only run rules available for these platforms (e.g., arm64, amd64)

The --target-compiler and --target-platform flags filter which rules are loaded based on their applicability. At runtime, the tool also detects the actual compiler from binary metadata and skips rules that don't apply to the detected compiler. For stripped binaries where detection fails, all loaded rules run.

Output Options
  • --include-passed - Include passing checks in output
  • --include-skipped - Include skipped checks in output
  • --sarif <file> - Save detailed SARIF report to file
  • --aggregate - Aggregate findings into actionable recommendations
  • --exit-zero - Exit with 0 even when findings are detected

The --include-passed and --include-skipped flags affect both text and SARIF output.

For programmatic access to results, use SARIF output (--sarif). SARIF (Static Analysis Results Interchange Format) is a standardized JSON format. We support SARIF version 2.1.0.

Logging Options
  • --log <file> - Write logs to file
  • --log-level <level> - Log level: none, debug, info, warn, error
Debuginfod Options

Fetch debug symbols from debuginfod servers.

  • --debuginfod - Enable debuginfod integration
  • --debuginfod-servers <urls> - Comma-separated server URLs
  • --debuginfod-cache <dir> - Cache directory for downloaded symbols
  • --debuginfod-timeout <duration> - HTTP timeout
  • --debuginfod-retries <n> - Max retries per server
Exit Codes
  • 0 - Success (no findings, or --exit-zero specified)
  • 1 - Error (invalid arguments, file errors, etc.)
  • 2 - Findings detected

Programmatic Usage

The public packages can be used as a library to integrate binary analysis into your own tools, write custom rules, or build an alternative frontend.

Import the relevant packages:

import (
    "github.com/mkacmar/crack/analyzer"
    "github.com/mkacmar/crack/binary"
    "github.com/mkacmar/crack/rule"
    "github.com/mkacmar/crack/rule/elf"
    "github.com/mkacmar/crack/toolchain"
)

Parse a binary using binary.ParseELF:

f, err := os.Open("/usr/bin/ls")
if err != nil {
    log.Fatal(err)
}
defer f.Close()

bin, err := binary.ParseELF(f)
if err != nil {
    log.Fatal(err)
}

The ELFBinary struct provides access to ELF metadata (see debug/elf for types), symbol tables, and detected toolchain info. Helper methods simplify common checks:

Create an analyzer with the rules you want to run (see rules reference for available rules):

rules := []rule.ELFRule{
    elf.PIERule{},
    elf.StackCanaryRule{},
    elf.FullRELRORule{},
}

a := analyzer.NewAnalyzer(rules, opts)

findings := a.Analyze(bin)
for _, f := range findings {
    fmt.Printf("%s: %s - %s\n", f.RuleID, f.Status, f.Message)
}

The Options struct controls which findings are returned:

  • IncludePassed - include rules that passed
  • IncludeSkipped - include rules that were skipped

By default, only failed rules are returned. Pass nil for options to use defaults.

Applicability

Each rule declares its applicability - which platforms and compilers it supports. For example, a rule might require GCC 10+ or only apply to ARM64 architecture. When analyzing a binary, rules that don't apply are automatically skipped.

Rules specify a MinVersion - the minimum compiler version required for the feature. The Platform field specifies which architectures the rule applies to. For example, the ARM PAC rule:

rule.Applicability{
    Platform: binary.PlatformARM64v83,
    Compilers: map[toolchain.Compiler]rule.CompilerRequirement{
        toolchain.GCC:   {MinVersion: toolchain.Version{Major: 10, Minor: 1}, Flag: "-mbranch-protection=pac-ret"},
        toolchain.Clang: {MinVersion: toolchain.Version{Major: 12}, Flag: "-mbranch-protection=pac-ret"},
    },
}

If your binaries are built with an internal compiler, register it via a custom detector so rules can determine whether they apply.

Use analyzer.CheckApplicability() to manually check if a rule applies:

result := analyzer.CheckApplicability(myRule.Applicability(), bin)
if result == analyzer.Applicable {
	// ...
}

Use rule.FilterRules() to pre-filter rules based on your target environment. The filter uses MaxVersion to exclude rules that require a newer compiler than you have:

filter := &rule.TargetFilter{
    Compilers: []rule.CompilerTarget{
        {Compiler: toolchain.GCC, MaxVersion: &toolchain.Version{Major: 12}},
    },
}
filtered := rule.FilterRules(rules, filter)
Custom Rules

To create a custom rule, implement the rule.ELFRule interface. For example, a rule that checks for a minimum stack size:

type MinStackSizeRule struct {
    MinBytes uint64
}

func (r MinStackSizeRule) ID() string          { return "min-stack-size" }
func (r MinStackSizeRule) Name() string        { return "Minimum Stack Size" }
func (r MinStackSizeRule) Description() string { return "Ensures stack size meets minimum requirements" }

func (r MinStackSizeRule) Applicability() rule.Applicability {
    return rule.Applicability{
        Platform: binary.PlatformAll,
    }
}

func (r MinStackSizeRule) Execute(bin *binary.ELFBinary) rule.Result {
    for _, prog := range bin.Progs {
        if prog.Type == elf.PT_GNU_STACK && prog.Memsz >= r.MinBytes {
            return rule.Result{Status: rule.StatusPassed, Message: fmt.Sprintf("Stack size %d bytes", prog.Memsz)}
        }
    }
    return rule.Result{Status: rule.StatusFailed, Message: "Stack size below minimum or not set"}
}
Custom Compiler Detection

To detect custom compilers, implement toolchain.ELFDetector and pass it to binary.ParseELFWithDetector(). This enables applicability checks for binaries built with internal or proprietary compilers:

// AcmeDetector detects Acme Corp's internal compiler, falling back to standard detection.
type AcmeDetector struct {
    fallback toolchain.ELFCommentDetector
}

func (d AcmeDetector) Detect(comment string) (toolchain.Compiler, toolchain.Version) {
    // Acme compiler writes "ACME C Compiler 2.3.1" in .comment section
    if strings.Contains(comment, "ACME C Compiler") {
        parts := strings.Fields(comment)
        if len(parts) >= 4 {
            if v, err := toolchain.ParseVersion(parts[3]); err == nil {
                return toolchain.Compiler("acme-cc"), v
            }
        }
        return toolchain.Compiler("acme-cc"), toolchain.Version{}
    }
    return d.fallback.Detect(comment)
}

bin, err := binary.ParseELFWithDetector(f, AcmeDetector{})

For complete API documentation, see pkg.go.dev.

License

MIT License - see LICENSE for details.

Directories

Path Synopsis
Package analyzer provides binary security analysis.
Package analyzer provides binary security analysis.
Package binary provides types for parsing and representing executable binaries.
Package binary provides types for parsing and representing executable binaries.
cmd
crack command
internal
cli
tools/doc command
Package rule defines rules and their results.
Package rule defines rules and their results.
elf
Package elf provides built-in ELF security hardening rules.
Package elf provides built-in ELF security hardening rules.
test
e2e
Package toolchain provides compiler and version detection for binaries.
Package toolchain provides compiler and version detection for binaries.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL