Skip to content

borzacchiello/bhex

Repository files navigation

BHex

Tests action Ask DeepWiki

Autogenerated documentation with DeepWiki

Minimalistic and lightweight shell-based hex editor. It’s designed to have a low memory footprint, making it usable on very low-end devices.

Features

  • Print file content in various formats.
  • Write/overwrite data into the file.
  • Undo writes until committed.
  • Enumerate (ASCII) strings.
  • Search strings or binary data.
  • Calculate hashes, checksums, and CRCs.
  • Execute template files using a custom language (bhe), see examples in the templates/ subdirectory.
  • Disassemble opcodes (using Capstone).
  • Assemble opcodes (using Keystone).

Just run bhex <file> to start the shell.

Supported flags:

Usage:  bhex [ options ] inputfile
  -h  --help        Print help
  -w  --write       Open the file in write mode
  -b  --backup      Backup original file in "filename.bk"
  -2  --no_warning  Disable warnings
  -n  --no_history  Do not save command history
  -c  "c1; c2; ..." Execute the commands given as argument and exit
  -s  --script      Script mode (commands from raw stdin)

command history is saved in "$HOME/.bhex_history", it can be changed by setting the BHEX_HISTORY_FILE environment variable

Compilation

The project can be compiled using cmake. Without Capstone and Keystone, it has no runtime dependencies (apart from libc), so it should be quite straightforward:

$ mkdir build
$ cd build
$ cmake ..
$ make

To enable the disassembler command, use "-DENABLE_CAPSTONE=on".

To enable the assembler command, use "-DENABLE_KEYSTONE=on".

To enable an ASAN build, use "-DASAN=on -DCMAKE_BUILD_TYPE=Debug".

To enable tests, use "-DENABLE_TESTS=on".

Command Format

Every command has the following structure:

$ command_name/mod1/mod2/mod3/... arg1 arg2 ...

where the modifiers (e.g., mod1) are optional parameters of the command.

The documentation for each command can be accessed by typing "?" after the name of the command.

Commands

If you type "help" (or "h"), you get the list of commands:

[0x0000000] $ h

Available commands:
    help [h]
    info [i]
    interactive [int]
    entropy [e]
    search [src]
    hash [hh]
    checksum [cs]
    crc [cr]
    strings [str]
    template [t]
    seek [s]
    print [p]
    diff [df]
    export [ex]
    import [im]
    assemble [as]
    disas [ds]
    write [w]
    delete [d]
    undo [u]
    commit [c]

Info

[0x0000000] $ i?

info: prints information about the opened binary

Entropy

[0x0000000] $ e?

entropy: display an entropy graph

  e [<rows> <len>]

  rows: number of points in the graph (if omitted or '-', auto mode)
  len:  number of bytes to include starting from the current offset (if omitted, use the whole file)

[0x0000000] $ e - 8
[ 00000000 - 000277c8 ] (5.980) ---------------------------------+
[ 000277c8 - 0004ef90 ] (6.398) -----------------------------------+
[ 0004ef90 - 00076758 ] (6.492) ------------------------------------+
[ 00076758 - 0009df20 ] (4.491) -------------------------+
[ 0009df20 - 000c56e8 ] (6.441) ------------------------------------+
[ 000c56e8 - 000eceb0 ] (6.477) ------------------------------------+
[ 000eceb0 - 00114678 ] (6.495) ------------------------------------+
[ 00114678 - 0013be40 ] (4.388) ------------------------+

Interactive

Start an interactive session.

Search

[0x0000000] $ src?

search: search a string or a sequence of bytes in the file

  src[/{x, s}/sk/p] <what>
     x:  data is a hex string
     s:  data is a string (default)
     sk: seek to first match
     c:  print context

  what: either a string or an hex string

Strings

[0x0000000] $ str?

enumerate the strings in the file (i.e., sequences of printable ascii characters with 8 or 16 bits)

  str[/n/{a,w}] [ <pattern> <num> ]
     n: look for null-terminated strings
     a: 8-bit only
     w: 16-bit only

  pattern: print only strings that contain the pattern as substring (use * for any character)
  num:     minimum length (default: 3)

Hash

[0x0000000] $ hh?

hash: calculate the hash of <size> bytes at current offset + <off>

  hash /l <algorithm> [ <size> <off> ]
     l:  list the supported hashing algorithms

  algorithm: hashing algorithm (or '*' to use all supported algorithms)
  size: number of bytes to include in the hash (if omitted or zero, hash the whole file starting from current offset)
  off:  starting offset wrt to current offset (default 0)

[0x0000000] $ hh/l

  md2
  md4
  md5
  md6-128
  md6-256
  md6-384
  md6-512
  sm3
  sha1
  sha224
  sha256
  sha384
  sha512
  sha3-128
  sha3-224
  sha3-256
  sha3-384
  sha3-512
  RipeMD-128
  RipeMD-160
  RipeMD-256
  RipeMD-320
  blake2s
  blake2b
  ghost

Template

[0x0000000] $ t?

template: parse the file at current offset using a 'bhe' template file

  t[/l/i/x] <name or file>
     l: list available templates and structs
     x: output in XML
     i: interpret inline code

  arg: its meaning depends on the mode. It could be
       - the name of the pre-loaded template/struct/proc to use
       - a path to a template file
       - a filter (if in list mode)
       - inline bhex code (if in interpret mode)

[0x0000000] $ t/l

Available templates:
  squashfs
  zip
  tar
  jpeg
  elf
  gzip
  lzo
  png
  pe
  rpm

...

Seek

[0x0000000] $ s?

seek: change current offset
  s[/{+,-}] <off>
    +: sum 'off' to current offset (wrap if greater than filesize)
    -: subtract 'off' from current offset (wrap if lower than zero)

  off: can be either a number or the character '-'.
       In the latter case seek to the offset before the last seek.

  NOTE: if called without arguments, print current offset

Crc

[0x0000000] $ crc?

import: calculate the CRC <name> at current offset + <off>

  crc[/l] <name> [<size> <off>]
     l:   list the supported crc names

  name:   name of the CRC (or a partial name, or '*')
  size:   number of bytes to include in the crc (if omitted or zero, import the whole file starting from current offset)
  offset: starting offset of the imported file (if omitted, import from current offset)

Checksum

[0x0000000] $ cs?

checksum: calculate a checksum at current offset + <off>

  checksum [/l] <name> [<size> <off>]
     l:   list the supported checksum names

  name:   name of the checksum (or a partial name, or '*')
  size:   number of bytes to include (if omitted or zero, use the whole file starting from current offset)
  offset: starting offset (if omitted, use current offset)

Assemble

[0x0000000] $ as?

assemble: assemble code and write it at current offset

  as[/l/i/s] <arch> "<code>"
     l:  list supported architectures
     i:  insert instead of overwrite
     s:  seek to the end of the write

  arch: the architecture to use
  code: assembly code string (e.g., "inc eax; inc ecx; ret")

Disassemble

[0x0000000] $ ds?

disas: disassemble code at current offset

  ds[/l/i] [<arch>] [<nbytes>]
     l:  list supported architectures
     i:  identify architecture; optional nbytes limits the scan to
         that many bytes from the current offset (default: whole file)

  arch:   the architecture to use
  nbytes: number of opcodes to disassemble (default: 8)

Print

[0x0000000] $ p?

print: display the data at current offset in various formats

  p[/{x,w,d,q,a,C}/{le,be}/r/W/{+,-}] <nelements>
     x:  hex output (default)
     w:  words
     d:  dwords
     q:  qwords
     a:  as ascii
     C:  as C buffer
     le: little-endian (default)
     be: big-endian
     r:  raw mode (no ascii, no header and no addresses)
     W:  wide mode (print 32 bytes per line)
     +:  seek forward after printing
     -:  seek backwards after printing

  nelements: the number of elements to display
  (default: enough to display 256 bytes, if '-' the whole file)

Diff

[0x0000000] $ df?

diff: prints the differences with another file

  df[/p/w] <file>
     p:  print different bytes
     w:  wide print (rows are 16 bytes)

  file: path to the file to compare

Export

[0x0000000] $ ex?

export: write <size> bytes of the file starting from current offset to <ofile>

  ex <ofile> [<size>]

  ofile: output file
  size:  number of bytes to export (if omitted, all the remaining bytes)

Import

[0x0000000] $ im?

import: import the content of <file> at current offset

  im[/{ovw,i}] <file> [<size> <offset>]
     i:   insert in current file (default)
     ovw: overwrite current file

  file:   input file
  size:   number of bytes to import (if omitted or zero, import the whole file)
  offset: starting offset of the imported file (if omitted, import from offset 0)

Write

[0x0000000] $ w?

write: write data at current offset

  w[{s,x,b,w,d,q}/{le,be}/u/i] <data>
     s:   string input (default)
     x:   hex input
     b:   byte
     w:   word
     d:   dword
     q:   qword
     le:  little-endian (default)
     be:  big-endian
     u:   unsigned
     i:   insert

  data: the data to write. The format depends on the type of
        write. Here are some examples:
            w/x "00 01 02 03"
            w/s "a string"
            w/q/be 0x1234

Delete

[0x0000000] $ d?

delete: delete bytes at current offset (all remaining bytes if the argument is omitted)

  d  [<nbytes>]

Undo

[0x0000000] $ u?

undo: undo the last write

  u[/a]
     a: undo all

Commit

[0x0000000] $ c?

commit: commit all writes to file

  c[/l]
     l: list uncommitted changes

About

A lightweight and portable shell-based binary hex editor

Topics

Resources

License

Stars

Watchers

Forks

Packages