Skip to main content

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.

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 codeMeaning
0Search completed with matches.
1Search completed without matches.
2Invalid request, failure, incomplete coverage, or a reached hit limit.
130Search 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​

OptionMeaning
-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-matchesList 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-lineDisplay 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, --helpPrint help.
-V, --versionPrint version.