# binsnoop A pure-Go binary analysis library for detecting and extracting embedded files, firmware images, compressed data, filesystems, and more from binary blobs. Ported from the [binwalk](https://github.com/ReFirmLabs/binwalk) Rust codebase with like-for-like scanning logic. **Zero external dependencies** — everything is self-contained, including the Aho-Corasick multi-pattern matcher, CRC32 validation, and binary structure parsing. ## Features - **111 built-in signatures** covering archives, compression, filesystems, images, executables, firmware headers, cryptographic constructs, and more - **Aho-Corasick** multi-pattern scanning with overlapping match support - **Signature validation** — every magic byte match is verified by a dedicated parser that checks headers, checksums, and structural integrity - **Confidence scoring** — results are ranked Low / Medium / High so callers can filter noise - **Extraction support** — carve identified objects out of a binary, or invoke external tools (7z, unsquashfs, etc.) to fully extract them - **Filtering** — include/exclude signatures by name, restrict short signatures to offset 0, or add custom signatures - **JSON-friendly** — all result types carry `json` struct tags for easy serialisation ## Installation ```bash go get binsnoop ``` Or add to your `go.mod`: ``` require binsnoop v0.0.0 ``` ## Quick Start ```go package main import ( "fmt" "binsnoop" ) func main() { scanner := binsnoop.New() // Scan a file results, err := scanner.ScanFile("firmware.bin") if err != nil { panic(err) } for _, r := range results { fmt.Printf("[0x%08X] %s (confidence: %d, size: %d)\n", r.Offset, r.Description, r.Confidence, r.Size) } } ``` ## API Reference ### Creating a Scanner ```go // Default scanner with all 111 signatures scanner := binsnoop.New() // Scanner with options scanner, err := binsnoop.NewWithOptions(binsnoop.Options{ Include: []string{"gzip", "squashfs", "uimage"}, // only these signatures Exclude: []string{"copyright"}, // remove these signatures FullSearch: true, // match short sigs at any offset AdditionalSignatures: []binsnoop.Signature{...}, // add custom signatures }) ``` ### Scanning ```go // Scan raw bytes results := scanner.ScanBytes(data) // Scan a file on disk results, err := scanner.ScanFile("/path/to/firmware.bin") ``` Both return `[]SignatureResult` sorted by offset. ### Analysis (Scan + Extraction) ```go // Analyse raw bytes (with optional extraction) analysis := scanner.AnalyzeBytes(data, "firmware.bin", true) // Analyse a file on disk analysis, err := scanner.AnalyzeFile("/path/to/firmware.bin", true) ``` Returns `*AnalysisResults` containing the file map and extraction outcomes. ### Carving Extract a single identified object from raw data: ```go err := binsnoop.CarveResult(data, results[0], "/tmp/output") ``` This writes the bytes at the result's offset/size to a file in the output directory. ### Listing Signatures ```go names := binsnoop.ListSignatures() // []string of all registered signature names ``` ### Scanner Info ```go scanner.SignatureCount() // number of registered signatures (111) scanner.PatternCount() // total magic byte patterns across all signatures ``` ## Types ### `SignatureResult` ```go type SignatureResult struct { Offset int `json:"offset"` // byte offset in the input data ID string `json:"id"` // unique identifier for this match Size int `json:"size"` // size in bytes (0 = unknown) Name string `json:"name"` // signature name (e.g. "gzip") Confidence uint8 `json:"confidence"` // 0=Low, 128=Medium, 250=High Description string `json:"description"` // human-readable description AlwaysDisplay bool `json:"always_display"` // show even if low confidence ExtractionDeclined bool `json:"extraction_declined"` // parser advises against extraction PreferredExtractor *Extractor `json:"-"` // extractor to use for this result } ``` ### `Options` ```go type Options struct { Include []string // restrict to these signature names Exclude []string // remove these signature names FullSearch bool // short sigs match at any offset AdditionalSignatures []Signature // user-defined signatures } ``` ### `AnalysisResults` ```go type AnalysisResults struct { FilePath string `json:"file_path"` FileMap []SignatureResult `json:"file_map"` Extractions map[string]*ExtractionResult `json:"extractions"` } ``` ### Confidence Levels | Constant | Value | Meaning | |----------|-------|---------| | `ConfidenceLow` | 0 | Magic matched but weak validation | | `ConfidenceMedium` | 128 | Header parsed and mostly valid | | `ConfidenceHigh` | 250 | Full structural validation passed | ## Custom Signatures You can add your own signatures at scanner creation: ```go mySignature := binsnoop.Signature{ Name: "my_format", Magic: [][]byte{[]byte("MYMAGIC")}, Parser: func(data []byte, offset int) (*binsnoop.SignatureResult, error) { // Validate the match and return a result if offset+16 > len(data) { return nil, fmt.Errorf("insufficient data") } return &binsnoop.SignatureResult{ Offset: offset, Size: 1024, Description: "My custom format, size: 1024 bytes", Confidence: binsnoop.ConfidenceHigh, }, nil }, Description: "My custom file format", } scanner, _ := binsnoop.NewWithOptions(binsnoop.Options{ AdditionalSignatures: []binsnoop.Signature{mySignature}, }) ``` ## Supported Signatures ### Archives (12) gzip, zip, tarball, 7zip, rar, cab, deb, cpio, arj, iso9660, dahua\_zip ### Compression (10) lzma, bzip2, xz, zstd, lz4, lzop, lzfse, zlib, compress'd ### Filesystems (13) squashfs, cramfs, jffs2, yaffs, romfs, ext, fat, ntfs, apfs, btrfs, logfs, ubi, ubifs ### Images & Media (7) png, jpeg, bmp, gif, svg, riff, pdf ### Executables (2) elf, pe ### Firmware (32) uimage, uboot, trx, tplink, tplink\_rtos, dlob, arcadyan, chk, seama, cfe, jboot\_arm, jboot\_stag, jboot\_sch2, packimg, binhdr, autel, android\_sparse, android\_bootimg, dtb, dlink\_tlv, dlke, shrs, dkbs, dms, encfw, encrpted\_img, wince, mh01, csman, dxbc, matter\_ota, rtk ### Cryptographic (17) aes\_sbox, aes\_forward\_table, aes\_reverse\_table, aes\_rcon, aes\_acceleration\_table, rsa, openssl, gpg\_signed, pem\_certificate, pem\_public\_key, pem\_private\_key, pkcs\_der\_hash, luks, crc32, sha256, md5 ### Miscellaneous (18) copyright, wind\_kernel, vxworks\_symtab, ecos, linux\_kernel, linux\_boot\_image, linux\_arm\_zimage, linux\_arm64\_boot\_image, efigpt, mbr, uefi\_pi\_volume, uefi\_capsule, pchrom, pcapng, pjl, srecord, dmg, qnx\_ifs, dpapi, qcow ## Extraction Many signatures have associated extractors that invoke external command-line tools. When you call `AnalyzeFile` or `AnalyzeBytes` with `extract: true`, binsnoop will: 1. Carve the identified data to a temporary file 2. Run the configured external tool (e.g. `7z x`, `unsquashfs`, `jefferson`) 3. Return the extraction results including output directory and success/failure status **Common external tools used:** | Tool | Signatures | |------|-----------| | `7z` | zip, cpio, arj, iso9660, xz, lzma, squashfs, dmg, efigpt | | `gzip` | gzip | | `bzip2` | bzip2 | | `zstd` | zstd | | `lz4` | lz4 | | `unsquashfs` | squashfs | | `jefferson` | jffs2 | | `unyaffs` | yaffs | | `tsk_recover` | ext, fat, ntfs | | `uefi-firmware-parser` | uefi\_pi\_volume, uefi\_capsule, pchrom | | `simg2img` | android\_sparse | | `dtc` | dtb | ## How It Works ### Scanning Algorithm 1. **Short-signature pass** — signatures marked `Short` (e.g. zlib, MBR) are checked only at offset 0 (unless `FullSearch` is enabled) 2. **Aho-Corasick scan** — all magic byte patterns are compiled into an Aho-Corasick automaton for efficient multi-pattern matching. When a high-confidence sized match is found, scanning restarts from the end of that match 3. **Validation** — every magic match is passed to its signature parser which validates headers, checksums, and structure 4. **Post-processing** — results are sorted by offset, overlapping/duplicate matches are resolved (higher confidence wins), and unknown sizes are estimated based on the next significant match or EOF ### Architecture ``` binsnoop.go Public API (New, ScanBytes, ScanFile, AnalyzeFile, CarveResult, etc.) scanner.go Core scan engine (Aho-Corasick integration, post-processing) match.go Self-contained Aho-Corasick trie with overlapping match support parse.go Generic binary structure parser (u8/u16/u24/u32/u64, big/little endian) types.go Core types (SignatureResult, Signature, Extractor, Options, etc.) common.go Utility functions (CRC32, endian helpers, string helpers) extract.go Carving and external tool execution magic.go Signature registry — wires all 111 signatures together sig_archives.go Archive signature parsers sig_compression.go Compression signature parsers sig_filesystems.go Filesystem signature parsers sig_images.go Image/media signature parsers sig_executables.go Executable signature parsers sig_firmware.go Firmware header signature parsers sig_crypto.go Cryptographic signature parsers sig_misc.go Miscellaneous signature parsers ``` ## License This project is a Go port of [binwalk](https://github.com/ReFirmLabs/binwalk) by ReFirmLabs. See the original project for licensing details.