8 min read

Disk Forensics

Analysis

Disk forensics involves analyzing file systems to recover deleted files, examine artifacts, and reconstruct user activity. Understanding NTFS, ext4, and other file systems is essential.

File System Analysis

Every Sleuth Kit command takes -o as a sector offset to the start of the file system, and that number comes from the partition table — never from a tutorial. Read the table first, then carry the value forward in a variable.

disk-forensics.sh
# 1. Read the partition table. Units are 512-byte sectors.
mmls disk.raw

#    Slot      Start        End          Length       Description
#    004:  000  0000002048   0001026047   0001024000   EFI System Partition
#    005:  001  0001026048   1953523711   1952497664   Microsoft basic data (NTFS)
#
# On this GPT disk the NTFS volume starts at sector 1026048, NOT at 2048.
# 2048 is where an MBR disk's first partition usually starts.

# 2. Take the Start value from the NTFS row
OFFSET=1026048

# 3. Use that same offset for every Sleuth Kit command
fsstat -o "$OFFSET" disk.raw
fls -r -o "$OFFSET" disk.raw
icat -o "$OFFSET" disk.raw 12345 > recovered_file.doc
fls -r -m "/" -o "$OFFSET" disk.raw > bodyfile.txt
mactime -b bodyfile.txt > timeline.csv

# Deleted file recovery with Scalpel
scalpel -c /etc/scalpel/scalpel.conf -o /output disk.raw

# PhotoRec for file carving
photorec disk.raw

# Foremost data carving
foremost -t all -i disk.raw -o /carved_files

A wrong offset produces "Cannot determine file system type" from fsstat, which reads like a corrupt image and is almost always arithmetic. To convert to bytes for any tool that wants them, multiply by the logical sector size — $(( OFFSET * 512 )) here. Carvers such as Scalpel, PhotoRec and Foremost work at the image level and need a byte offset (-o means something different — an output directory — to those tools).

NTFS Artifacts

Artifact Location Evidence Value
$MFT Root of NTFS All file metadata, timestamps, deleted entries
$UsnJrnl $Extend folder File system change journal
$LogFile Root of NTFS Transaction log for recovery

macOS: APFS Snapshots and On-Disk Artefacts

macOS gives an investigator one gift and several locked doors. The gift is that APFS takes copy-on-write snapshots continuously, so a point-in-time view of the data volume usually already exists on the disk. The doors are FileVault, System Integrity Protection and the privacy database, each of which decides whether you can read what the snapshot contains. Two rules follow: create or locate a snapshot before you change anything, and settle the volume-key question before anyone shuts the machine down.

APFS Local Snapshots

A local snapshot is a read-only, copy-on-write view of the Data volume at a moment in time. macOS creates them hourly when Time Machine is enabled and keeps them while space allows, which on a busy laptop means roughly a day of history and sometimes none at all. Take one yourself as the first action: it is a write to the disk, it belongs in the notes, and it is the cheapest insurance in the whole procedure.

apfs-snapshot-mount.sh
tmutil listlocalsnapshots /
sudo tmutil localsnapshot

# Find the Data volume's device identifier, then mount a snapshot read-only
diskutil apfs list
mkdir -p /tmp/ir-snap
sudo mount_apfs -o ro,nobrowse -s com.apple.TimeMachine.2026-05-26-141503.local /dev/disk3s5 /tmp/ir-snap

# The snapshot is immutable, so hashing the mounted tree is stable
cd /tmp/ir-snap && find . -type f -print0 | sort -z | xargs -0 sha256sum > /evidence/snapshot-SHA256SUMS.txt

The snapshot covers the Data volume only: no unallocated space, no other volumes, no memory, and not the sealed System volume, which is identical across installations anyway. It answers "what did the files and databases look like at 14:15", not "what was deleted last week" — for that you need a physical image or a lab.

Unified Logging

Unified logging is the system's central event stream, stored as .tracev3 files under /var/db/diagnostics. Collect it into a log archive rather than querying the live store, because the store rotates and the archive keeps the metadata a later query needs.

unified-log-collect.sh
sudo log collect --last 6h --output /evidence/host01.logarchive

sudo log show --archive /evidence/host01.logarchive \
  --predicate 'process == "sudo" OR eventMessage CONTAINS[c] "Gatekeeper"' \
  --info --debug > sudo-and-gatekeeper.txt

The limitation is privacy, not volume. Dynamic strings in log messages are redacted as <private> unless the subsystem opts into private-data logging, so a sudo entry may give you the timestamp, the user and the PID without the command that ran. That makes the unified log an excellent corroborator and a poor primary source: use it to place an event in time, and get the content from shell history, TCC, FSEvents or the application's own files.

FSEvents

Each volume keeps a file-system event journal in /.fseventsd that records path, flags and timestamp for changes, and it survives the deletion of the files it describes. It has two honest weaknesses: no process attribution at all, and coalescing — a single record can stand for several operations, so sub-second ordering from FSEvents alone is a guess. Parse it with mac_apt or a dedicated FSEvents parser, and use it to widen a window rather than to prove a sequence.

TCC: The Permission Record

The Transparency, Consent and Control database is the record of which applications were granted which privacy permissions. There are two: a per-user database under the user's Library and a system database under /Library/Application Support/com.apple.TCC/. The access table is what matters — service, client, auth_value (2 for allowed, 0 for denied) and last_modified.

tcc-query.sh
sqlite3 -readonly "/tmp/ir-snap/Users/alice/Library/Application Support/com.apple.TCC/TCC.db" \
  "SELECT service, client, auth_value, datetime(last_modified,'unixepoch') AS modified
     FROM access ORDER BY modified DESC LIMIT 40;"

This answers a question no other artefact does: could this binary have read Mail, recorded the screen, or driven the accessibility APIs? It proves the grant, not the use. It also degrades quietly — tccutil reset and application removal delete rows, and reading the file at all requires Full Disk Access for your tool, which is why the query above runs against the mounted snapshot rather than the live volume.

Persistence, Quarantine and Gatekeeper

Launch agents and daemons are the classic persistence mechanism, and the modern additions are background task items registered through the system's Background Task Management store. Enumerate what is loaded, not just what is on disk: a plist in /Library/LaunchDaemons can be present and disabled, and the disabled state is recorded separately.

macos-persistence-and-trust.sh
ls -la ~/Library/LaunchAgents /Library/LaunchAgents /Library/LaunchDaemons
sudo launchctl print system | head -60
sudo launchctl print-disabled system
sudo sfltool dumpbtm > /evidence/btm.txt

# Download provenance and code trust
xattr -l ~/Downloads/invoice.pdf
spctl --assess --type execute -vv /Applications/Invoice.app
codesign -dvvv --entitlements :- /Applications/Invoice.app

The com.apple.quarantine extended attribute records a flag set, a timestamp, the application that wrote the file and a UUID. It is the single best answer to "how did this get here", and its absence proves nothing: a file fetched with curl, written by a script or produced by an app that does not set the attribute carries no flag at all. Gatekeeper's assessment and the code signature answer a different question — who signed this and has it been modified since — and a valid signature from a developer you have never heard of is a valid signature. Note that codesign writes the signature details to stderr and the entitlements to stdout, so redirect both when you capture the output.

sysdiagnose, MDM and the Doors That Stay Shut

sudo sysdiagnose -f /evidence builds a diagnostic archive in five to ten minutes: system profile, process list, network and launchd state, installed configuration profiles, recent log extracts and hardware detail. It is the fastest broad snapshot of a healthy-looking machine, and it is a live collection that changes the host, so record it as one. Configuration profiles are readable with sudo profiles show -type configuration, but the authoritative record of what an MDM told the device to do — and the FileVault recovery key it escrowed — lives on the MDM server, not on the Mac.

SIP explains why several of these paths need care. With csrutil status reporting enabled, the System volume is sealed and read-only even for root, and the system TCC database sits on it. You read those files through a tool holding Full Disk Access, or from a recovery-environment mount of the Data volume — and you record which, because the two produce different access to the same bytes.

Powering Off a FileVault Mac Can End the Case

With FileVault enabled, the Data volume is encrypted with a key wrapped by the user's password and, when the machine is enrolled, escrowed to MDM. A locked, powered-off laptop with no user password and no institutional key is not recoverable by any tool, and a shutdown can convert a live acquisition into an encryption problem. Resolve the key — user password, institutional recovery key, or MDM escrow — before the machine loses power, and record who authorised the attempt.