Skip to main content

Getting Started

Exhume Artefacts dependencies and current support

exhume_artefacts is Exhume's registry of forensic artefact parsers. It accepts standalone files, in-memory data, seekable filesystem streams, or compound evidence with companion files and streams normalized JSON objects to the caller.

Use the CLI for focused examination of one file. Use the library directly—or through exhume_indexer—when provenance, image-backed access, SQLite sidecars, timeline events, and persistent investigation results are required.

Install​

Install the command-line tool:

cargo install exhume_artefacts

From the Exhume workspace, replace exhume_artefacts in the examples below with:

cargo run -p exhume_artefacts --

Discover available parsers​

The registry is the authoritative parser list for the installed build:

exhume_artefacts --list-parsers

Version 0.3.1 currently registers:

ParserPurpose
macos_chromiumChrome, Chromium, Brave, and Edge history, visits, and downloads.
macos_firefoxFirefox Places history and visits.
macos_imessageMessages chats, messages, and attachment references.
macos_keychainLegacy CSSM and modern SQLite keychain item metadata without decrypting secrets.
macos_launchdLaunchAgent and LaunchDaemon persistence definitions and triggers.
macos_loginwindowSystem, user, and ByHost loginwindow configuration, relaunch items, and hooks.
macos_networkNetwork services, interfaces, known Wi-Fi networks, and DHCP leases.
macos_quarantineLaunchServices quarantine download-provenance events.
macos_safariSafari history, visits, and downloads.
macos_sharedfilelistSFL/SFL2/SFL3 recent-item bookmark archives.
macos_spotlightSpotlight Store-V2 metadata, external dbStr maps, and reconstructed item paths.
macos_whatsappWhatsApp desktop chats, messages, and media references.
mobile_android_smsAndroid SMS/MMS conversations and message records.
mobile_ios_calendarCalendar event records, locations, timestamps, and attendees.
mobile_ios_callhistoryCellular, FaceTime, and third-party CallKit call records.
mobile_ios_contactsAddress book records and multi-value phone, email, and address data.
mobile_ios_datausagePer-process cellular and Wi-Fi usage.
mobile_ios_imessagesms.db chats, SMS/iMessage messages, and attachment references.
mobile_ios_interactioncCoreDuet communication interaction records.
mobile_ios_knowledgecCoreDuet app, web, lock-state, backlight, and notification events.
mobile_ios_mailApple Mail Envelope Index, with optional Protected Index data.
mobile_ios_notesApple Notes NoteStore.sqlite metadata.
mobile_ios_photosPhotos ZASSET inventory, capture dates, GPS, and flags.
mobile_ios_routinedRoutined significant-location cache GPS fixes.
mobile_ios_safariSafari History.db sites and visits.
mobile_ios_tccTCC privacy permission decisions.
mobile_ios_whatsappWhatsApp ChatStorage.sqlite chats, messages, and media references.
windows_evtxWindows Event Log records.
windows_pePE metadata and hashes for malware analysis.
windows_pmlProcess Monitor PML v9 events.

Parser names are stable integration identifiers. Always use --list-parsers to confirm what is compiled into a particular binary.

CLI usage​

Parse a standalone file​

exhume_artefacts \
--file /evidence/Windows/System32/winevt/Logs/Security.evtx \
--parser windows_evtx > security.jsonl

The CLI writes one parser JSON object per line to stdout and writes the final object count to stderr. This separation makes redirection and streaming tools safe:

exhume_artefacts \
--log-level error \
--file /evidence/sample.exe \
--parser windows_pe \
| jq .

Log levels are error, warn, info, debug, and trace.

Parse a file inside an image​

The parse_from_fs example opens an image, detects its filesystem, fetches a file by native record identifier, and presents it to the parser as a seekable stream:

cargo run -p exhume_artefacts --example parse_from_fs -- \
--body /evidence/disk.raw \
--format auto \
--offset 0x100000 \
--size 0x400000 \
--record 42 \
--parser windows_evtx > security.jsonl

For this example, --offset is in bytes and --size is in sectors. --record is the native filesystem identifier, not the row ID from an Exhume SQLite index.

The example supplies only the selected file. Prefer exhume_indexer for parsers that need WAL/SHM files, named sibling databases, source-path metadata, or attachment resolution from the surrounding filesystem.

Use case: reconstruct a WhatsApp conversation​

The iOS and macOS WhatsApp parsers read the Core Data ChatStorage.sqlite schema and emit separate chat, message, and attachment objects. When parsing a standalone database, keep ChatStorage.sqlite-wal and ChatStorage.sqlite-shm beside the primary file. The SQLite input layer discovers and copies those sidecars automatically.

Parse an iOS store:

exhume_artefacts \
--log-level error \
--file /evidence/ios/ChatStorage.sqlite \
--parser mobile_ios_whatsapp > whatsapp.jsonl

For a macOS desktop store, use the same workflow with --parser macos_whatsapp.

Count the emitted record types:

jq -r '.record_type' whatsapp.jsonl | sort | uniq -c

List conversations and their message counts without displaying message text:

jq -r '
select(.record_type == "message")
| [.conversation.id, (.conversation.display_name // "<unnamed>")]
| @tsv
' whatsapp.jsonl \
| sort \
| uniq -c \
| sort -nr

After choosing a conversation ID, render a chronological, redacted-friendly conversation view:

jq -c --arg conversation '<conversation-id>' '
select(
.record_type == "message"
and .conversation.id == $conversation
)
| {
timestamp: .timestamps.message.rfc3339,
direction,
sender: (.sender.display_name // .sender.id // "<unknown>"),
body,
has_attachments
}
' whatsapp.jsonl \
| jq -s 'sort_by(.timestamp)[]'

Representative normalized output, with identifying values redacted:

{
"timestamp": "2025-01-15T08:42:17+00:00",
"direction": "incoming",
"sender": "<contact>",
"body": "<message text redacted>",
"has_attachments": false
}

To inspect media records for the same conversation:

jq -c --arg conversation '<conversation-id>' '
select(
.record_type == "attachment"
and ((.chat.rowid | tostring) == $conversation or .chat.jid == $conversation)
)
| {
timestamp: .timestamps.message.rfc3339,
kind: .attachment.kind,
file_name: .attachment.file_name,
local_path: .attachment.local_path,
mime: .attachment.mime
}
' whatsapp.jsonl

This workflow was smoke-tested against an already indexed Thanatology iPhone case. The current parser emitted 82,395 objects: 305 chats, 74,015 messages, and 8,075 attachments. A macOS case in the same test set produced 2 chats, 14 messages, and 7 attachments. These counts identify the fixtures, not an expected result for other evidence.

Output model​

Every library result is an ObjectParsed:

pub struct ObjectParsed {
pub parser: &'static str,
pub kind: &'static str,
pub text: String,
pub json: serde_json::Value,
}
  • parser identifies the implementation that produced the object.
  • kind is a stable domain type such as mobile.communication.message.
  • text is the best concise/searchable representation of the object.
  • json preserves normalized fields, native details, and provenance.

The CLI emits the json value because it is the portable external format. exhume_indexer also persists parser, kind, and text in dedicated columns.

Normalized communication records​

Mobile and desktop communication parsers share canonical kinds:

  • mobile.communication.chat for conversation metadata;
  • mobile.communication.message for a message envelope;
  • mobile.communication.attachment for a separately addressable media record.

Message objects use the chat.v1 schema and consistently expose:

  • application and platform;
  • stable per-store conversation identity and participants;
  • incoming, outgoing, or unknown direction;
  • sender and self-identification;
  • canonical message time as Unix milliseconds and RFC 3339;
  • body, attachment state, delivery/read/deletion state where available;
  • source table, row identity, source files, and schema variant;
  • app-specific values under details, so normalization does not discard evidence.

An absent state remains null; parsers do not invent false when the source application has no such concept.

Timeline events​

A parser can implement extract_timeline_events for each parsed object. A TimelineEvent contains:

  • ts_unix_ms: normalized Unix time in milliseconds;
  • a dot-namespaced event type;
  • an optional concise description;
  • an optional actor such as a user, process, or correspondent.

The standalone CLI does not emit timeline events separately. exhume_indexer calls this hook and stores the results in timeline_events with a link back to the parsed object.