bbe: sed for Binary Files (When Hex-Editor-Through-a-Pipe Just Won't Do)

2026-05-27

Every few years someone hands me a corrupt firmware blob, a leaked savefile, or a multi-gigabyte mmap'd database with one wrong byte at offset 0x4A2E, and asks me to "just sed it." I used to mumble something about xxd | sed | xxd -r and quietly hate my life. Then in 2005 I discovered bbe — the Binary Block Editor — and never looked back.

bbe is sed's binary-aware cousin. It understands the concept of a block (fixed-length, delimiter-bounded, or pattern-anchored), and runs a tiny command language against each block. It handles NUL bytes natively, takes hex escapes everywhere a pattern is expected, and streams gigabytes without buffering the whole file. On Debian/Ubuntu it's a one-liner: apt install bbe.

The trivial case — substitute a binary pattern:

$ bbe -e 's/\x89PNG\r\n\x1a\n/\x89BAD\r\n\x1a\n/' good.png > broken.png

Try doing that with GNU sed without it choking on the embedded NUL or eating your \r. You can't — sed is line-oriented and POSIX sed barfs on binary data entirely.

Patch a single byte at an exact offset (the firmware-modder's bread and butter):

# Replace byte at offset 0x4A2E with 0xAA, leave everything else alone
$ bbe -b '#0:1' -e 'r 0x4A2E \xAA' firmware.bin > patched.bin

The -b flag defines the block — here, the whole file as one block starting at byte 0. r OFFSET STRING replaces bytes at a position. There's also i (insert), d (delete), j (join), y/abc/xyz/ (transliterate), and >FILE / <FILE to redirect a block to or from disk.

Operate per fixed-size record — e.g. strip trailing NUL padding from every 512-byte tar block:

$ bbe -b ':512' -e 's/\x00\x00\x00\x00$//' archive.tar > trimmed.tar

Block defined by delimiters — extract every JPEG embedded inside a memory dump or PCAP, one file per match:

$ bbe -b '/\xFF\xD8\xFF/\xFF\xD9/' -e '>jpeg_%n.jpg' memdump.bin
# %n auto-increments, so you get jpeg_1.jpg, jpeg_2.jpg, ...

That one's the killer use case for me. Carving files out of unknown binary containers usually means firing up a 600 MB forensics suite. Three flags of bbe and you're done.

Translate bytes wholesale — useful for trivial obfuscation or fixing endianness in fixed-width records:

$ bbe -e 'y/\x00\xFF/\xFF\x00/' image.raw > inverted.raw

Chain multiple commands on each block like sed:

$ bbe -b '/HEADER/FOOTER/' \
       -e 'd 0 8; s/\xDE\xAD\xBE\xEF/\xCA\xFE\xBA\xBE/; r 16 \x01\x02' \
       blob.dat

Why not just use a Python script with open(..., 'rb') and .replace()? Because Python loads the file into memory, and your "quick fix" for the 40 GB VM image becomes a 40 GB malloc. bbe streams. It's also the right call inside shell pipelines where dropping into a real language feels like overkill — dd if=/dev/sdb | bbe -e 's/\xDE\xAD/\xBE\xEF/' | dd of=/dev/sdc just works.

The man page is short. Read it once and you'll keep bbe in your toolbox forever — right next to xxd and the dog-eared printout of ASCII codes you keep meaning to throw out.

Key Takeaway: When you need to grep-and-replace inside binary data without round-tripping through hex, bbe gives you sed's ergonomics with native support for NUL bytes, hex escapes, fixed-size blocks, and pattern-bounded extraction — perfect for firmware patches, file carving, and surgical edits on multi-gigabyte streams.

All newsletters