Getting Started
exhume_grep searches the contents of regular files selected from an
Exhume Indexer SQLite database. It opens
the index and evidence read-only and streams matches with their indexed paths,
file identifiers, and original byte offsets.
It supports extracted folders and disk-image filesystems through exhume_filesystem. Files are read directly from their source; exporting them before searching is unnecessary. Rust applications can use the streaming library API.
Install​
Requires Rust 1.92 or newer. See the toolkit installation instructions if Rust is not installed.
cargo install exhume_grep --locked
From the Exhume workspace, build the module and inspect its options:
cargo build --release -p exhume_grep
cargo run -p exhume_grep -- --help
The release binary is written to target/release/. The examples below use
the installed exhume_grep command.
Prepare an index​
Index the evidence before searching. For an extracted folder:
exhume_indexer --body /evidence/extracted-folder \
--database /cases/case.sqlite --evidence-id 1
Keep the database outside the evidence folder. For disk images, follow the Indexer instructions to supply the filesystem offset in bytes and its length in sectors.
Grep reads the evidence location and partition geometry from the database. The original source must remain accessible: an index contains metadata, not the file contents needed for this search. Signature identification and artefact extraction are optional and are not required by Grep.
Use the evidence ID assigned during indexing. If necessary, inspect IDs with the SQLite CLI:
sqlite3 /cases/case.sqlite 'SELECT id, path, type FROM evidence;'
sqlite3 /cases/case.sqlite 'SELECT id, evidence_id, kind FROM partitions;'
Search examples​
Find a literal string​
exhume_grep -d /cases/case.sqlite --evidence-id 1 -e 'password'
The defaults are case-sensitive literal matching in UTF-8 with one
worker. Repeat -e to search for multiple patterns in one pass:
exhume_grep -d /cases/case.sqlite --evidence-id 1 \
-e 'password' -e 'secret'
Alternatively, use --pattern-file indicators.txt with one nonempty UTF-8
pattern per line. Patterns from the file are appended after command-line
patterns. The CLI assigns IDs 1, 2, and so on in that order. At least one
pattern is required; empty patterns are rejected. The limit is 4,096 patterns
and 1 MiB of combined pattern text; the pattern file itself must also fit in 1 MiB.
Show matching lines​
exhume_grep -d /cases/case.sqlite --evidence-id 1 \
-e 'password' --show-line
Matching lines include their line number and source byte offset. Matched spans
are highlighted in red when terminal color is enabled. Use --color always
to force color or --color never for plain text; automatic color respects
NO_COLOR and terminal capabilities.
Long lines are shown as bounded excerpts, with truncation markers where
necessary. --line-bytes 8192 changes the display limit from its default of
4,096 source bytes. This display limit is independent of the regex search limit.
Control characters and invalid text are escaped for display.
Search a regex in UTF-8 and UTF-16​
exhume_grep -d /cases/case.sqlite --evidence-id 1 \
--mode regex -e '(?i)password|secret' --encoding all \
--output ndjson > matches.ndjson
(?i) enables case-insensitive regex matching. --encoding all searches
UTF-8, UTF-16 little-endian, and UTF-16 big-endian. Offsets and lengths always
refer to bytes in the original logical file, including UTF-16 matches.
Regex matching is line-based. Use ^ and $ for line anchors. Empty-match
expressions, file anchors \A and \z, and disabling multiline anchors are
unsupported. Byte-mode regexes that can split UTF-8 characters cannot be used
with UTF-16 searching.
Restrict the search​
Search beneath an indexed directory and list files containing at least one hit:
exhume_grep -d /cases/case.sqlite --evidence-id 1 \
-e 'password' --directory /Users --files-with-matches
Directory and path filters refer to indexed paths, not paths on the
examiner's workstation. Paths use normalized / separators. --include and
--exclude take regular expressions, not shell globs. Repeated include filters
accept any matching path; any matching exclude filter removes a path.
exhume_grep -d /cases/case.sqlite --evidence-id 1 \
-e 'password' --include '\.(txt|log)$' --exclude '/cache/' \
--min-size 1 --max-size 10485760 --workers 4
Use --partition-id <ID> to restrict the search to an indexed partition;
repeat the option to select more than one. All selected partitions must belong
to the chosen evidence ID.
--files-with-matches stops at the first hit in each matching file. It cannot
be combined with --show-line, and its summary does not count every occurrence.
Multiple workers can emit results in a different order between runs.
Open relocated evidence​
exhume_grep -d /cases/case.sqlite --evidence-id 1 \
--body /new/evidence/path -e 'password'
The override must identify the same evidence source. Grep checks file identity and metadata against the index; moving the source does not bypass those checks. Changed files may require a new index.
Output and coverage​
By default, text output prints the quoted path, byte offset, match length,
pattern ID, encoding, and indexed file ID. Offsets are relative to the logical
file, not absolute positions inside the disk image. --preview-bytes 64 adds
a bounded hexadecimal preview of original bytes around a hit.
--output ndjson emits one JSON event per line to stdout. Events include
matches, file errors, skipped ranges, progress, and a final summary. With
--show-line, the stream adds matching_line events while retaining the
individual match events.
Text mode writes diagnostics and the final summary to stderr. Check the summary and exit code before interpreting an absence of hits:
| Exit code | Meaning |
|---|---|
0 | Search completed with matches. |
1 | Search completed without matches. |
2 | Invalid request, failure, incomplete coverage, or a reached hit limit. |
130 | Search cancelled. |
The summary distinguishes complete, incomplete, limited, and cancelled
and reports scanned files, hits, failed files, skipped ranges, and limited files.
Search coverage is restricted to indexed regular files. Grep does not extract text from archives or documents, search deleted files or unallocated space, or enumerate alternate streams. It searches the logical bytes provided by the filesystem reader.
Regex lines exceeding 8 MiB are skipped by default and reported as incomplete coverage. Adjust --max-line-bytes when appropriate; the maximum is 256 MiB. This is separate from truncating a displayed line with --line-bytes. --max-hits and --max-hits-per-file bound the results, but reaching a limit
means coverage is limited. Ctrl+C requests cooperative cancellation. A search with coverage gaps, a hit limit, or cancellation cannot establish that the remaining content contains no matches.
CLI reference​
| Option | Meaning |
|---|---|
-d, --database <PATH> | Required existing Exhume SQLite index; opened read-only. |
--evidence-id <ID> | Required evidence ID in the index. |
-b, --body <PATH> | Override the indexed evidence path after relocation. |
--partition-id <ID> | Search a selected indexed partition; repeatable. Default: all partitions for the evidence. |
-e, --pattern <TEXT> | Literal or regex pattern; repeatable. |
--pattern-file <PATH> | UTF-8 file containing one nonempty pattern per line. |
--mode <MODE> | literal (default) or regex. |
--encoding <ENCODING> | utf8 (default), utf16le, utf16be, or all. |
--directory <PATH> | Restrict to an indexed directory and its descendants. |
--include <REGEX> | Include matching normalized indexed paths; repeatable. |
--exclude <REGEX> | Exclude matching normalized indexed paths; repeatable. |
--min-size <BYTES> | Minimum indexed file size, inclusive. |
--max-size <BYTES> | Maximum indexed file size, inclusive. |
-j, --workers <COUNT> | Worker count, 1–64; default: 1. |
-l, --files-with-matches | List matching files, stopping at the first hit per file. Conflicts with --show-line. |
--max-hits <COUNT> | Positive global hit limit; unlimited by default. |
--max-hits-per-file <COUNT> | Positive per-file hit limit; unlimited by default. |
--max-line-bytes <BYTES> | Regex line limit, 1–268435456; default: 8388608. |
--preview-bytes <BYTES> | Maximum original-byte preview length, 0–4096; default: 0 (disabled). |
--show-line | Display grouped matching lines, or add them to NDJSON output. |
--line-bytes <BYTES> | Source bytes per display excerpt, 16–65536; default: 4096. Requires --show-line. |
--color <MODE> | Text color: auto (default), always, or never. |
--output <FORMAT> | text (default) or ndjson. |
-h, --help | Print help. |
-V, --version | Print version. |