Skip to content

Instantly share code, notes, and snippets.

@unrooted
Created June 2, 2026 05:33
Show Gist options
  • Select an option

  • Save unrooted/2fcfed3e8718e7be560f77974b39a364 to your computer and use it in GitHub Desktop.

Select an option

Save unrooted/2fcfed3e8718e7be560f77974b39a364 to your computer and use it in GitHub Desktop.
Apple Silicon Forensics - Cheatsheet

Apple Silicon Forensics — Cheatsheet

Companion to Terra Incognita — Apple Silicon Forensics · v1.0 · Konrad (@unrooted) Klawikowski · 02/06/2026


Mental model

The wall is also a witness. The same lockdown that denies you the old acquisition options — Target Disk Mode, block-level imaging, chip-off, memory dumps, SEP introspection — is the architecture that signs, caches, logs, and SIP-protects more persistent evidence than Intel ever did.

Three buckets — keep them straight:

  • Logical collection of the trail → solved. Glob the files today, with tools that exist. This is the easy half.
  • Deep acquisition (memory, SEP internals, physical) → still the frontier. Largely unsolved; that's the research list.
  • Interpretation of the AS-native artifacts → the real terra incognita. Parsers exist (you can read the bytes), but the forensic meaning — cross-version behaviour, normal-vs-anomalous baselines, how artifacts corroborate each other — is still being charted. A parser is a camera, not a map.

Collection is commodity. Understanding what you collected is the open problem.


Apple-Silicon-native vs inherited — only claim bucket 1 as AS evidence

Bucket Artifacts Why
Genuinely AS-native Rosetta AOT cache (/var/db/oah) · aot_shared_cache · PAC crash artifacts (ARMv8.3) · LocalPolicy as a user-configurable on-disk policy object + its 4CCs · the NVRAM 1TR-gated boot-security model · the iSCPreboot/Preboot two-volume layout New because the chip + boot architecture are new
Format older than AS / shared with iOS or T2 IMG4 (Image4 since ~iOS 7 / 2013; on Intel via the T2 chip, 2017+, for secure boot) — only the LocalPolicy usage is AS-era · SEP (T2 Intel Macs had one too) The container/coprocessor predates Apple Silicon; the application is what changed
macOS-wide — not AS at all (the anti-list) Unified Logs / tracev3 (macOS Sierra 10.12, 2016) · Biome (Ventura 13) · KnowledgeC · TCC · Quarantine DB · APFS snapshots · FSEvents · ExecPolicy / KextPolicy (notarisation era) Shipped across the macOS line, Intel included

Rule of thumb: tracev3 and Biome are modern macOS; IMG4/SEP are Apple, pre-AS; AOT, PAC, and LocalPolicy-on-disk are the genuinely new terrain.


Boot chain → where each stage drops evidence

Each stage cryptographically verifies the next (the chain of trust); each leaves a different artifact.

Stage Drops
Boot ROM (immutable, on-die) nothing on disk — root of trust
LLB → iBoot (reads LocalPolicy, checks nonce) LocalPolicy IMG4 (iSCPreboot) · NVRAM boot-args/boot-nonces (1TR-gated)
Kernel + AuxKC (SEP co-signs) single kernelcache (root:wheel) in Preboot · AuxKC = approved third-party kexts, hash tied to LocalPolicy
macOS userspace (Cryptex · Rosetta · execution) Preboot restore/update history + CryptexRegistry · Rosetta AOT first-execution timestamps
Runtime (PAC enforced) EXC_BAD_ACCESS w/ high-bit address on auth failure → exploitation-attempt signal

iSCPreboot = the policy (LocalPolicy img4). Preboot = the history (BORD logs, RestoreLog, CryptexRegistry, kernelcache, firmware). Same volume-group UUID — collect both.


Red flags — if you see X, think Y

Observation Interpretation
LocalPolicy at Permissive Security on a non-dev machine Physical access + admin credentials, via 1TR. Timestamped.
Anomalous boot-args in NVRAM Deliberate weakening — userspace can't write these; required 1TR.
coih set in LocalPolicy Non-Apple kernel hash installed via kmutil configure-boot. Asahi, dev, or attacker.
Cluster of PAC failure crashes against one process Exploitation attempt — attacker iterating.
RestoreLog.plist shows rollback to older macOS Possible reach for a vulnerable kernel or to defeat newer EDR.
AOT files for ping, chmod, sudo, id, cat clustered in a tight window x86_64 process invoked native tools — POOLRAT-style tradecraft. Clustering/timing is the tell — individual AOTs may predate the intrusion.
/var/db/oah empty when x86_64 binaries were present Either no Rosetta execution or SIP was disabled (itself a 1TR event).
Cryptex/RSR versions mismatched against claimed macOS build "macOS 14.x.y" isn't precise enough — actual patch level differs.
Touch ID enrolled count drops between snapshots User or actor tidied biometric state. Note: no userspace timestamps.
_oahd no longer owns an .aot, or AOT mtime > binary's last run Possible AOT tampering (see tradecraft). SIP-off is the precondition.

Key artifact paths

Path / Command What it tells you AS-only? Parse with
/var/db/oah/<UUID>/<binary>.aot First-execution evidence for x86_64 binaries (birth-time); _oahd-owned stat -f %SB; binary tools
aot_shared_cache (with dyld_shared_cache) Pre-translated system frameworks; tampering surface Project Champollion
/System/Volumes/iSCPreboot/<UUID>/LocalPolicy/*.img4 Security mode, SIP bits, AuxKC, MDM, custom kernel ✓ (usage) bputil -e · img4lib · pyimg4
…/Preboot/<UUID>/var/log/BORD/ Boot progress logs log
…/Preboot/<UUID>/RestoreLog.plist OS reinstall/restore history with timestamps plutil
…/Preboot/<UUID>/var/db/CryptexRegistry/ RSR + cryptex versions, install times plutil
…/Preboot/<UUID>/boot/<hash>/…/com.apple.kernelcaches/kernelcache Single Boot Kernel Collection (root:wheel, ~30 MB); chip ID not in filename; AuxKC separate binary tools (Mach-O)
~/Library/Logs/DiagnosticReports/ PAC failure crashes (exploit attempts) grep
/var/db/spindump/ · ASL DB Backup locations for PAC crashes log
/var/db/lockdown/ DFU / Configurator pairing records partial binary
/var/MobileAsset/AssetsV2/ Downloaded-but-unapplied updates / RSRs partial filesystem
NVRAM boot-args, boot-nonces Boot security state — userspace can't modify nvram -p
bioutil -r -s / -c -s Touch ID config + enrolled count (current state only) direct output
/var/db/SystemPolicyConfiguration/ExecPolicy Execution provenance, signing identity ✗ Intel too sqlite3
/var/db/SystemPolicyConfiguration/KextPolicy Approved third-party kext history ✗ Intel too sqlite3

Notes: /var/db/oah and /var/db/lockdown are SIP-protected — even sudo find returns Operation not permitted on a live box, so read them from the image or with your acquisition tool's privileges. The kernelcache <hash> dir and exact MobileAsset layout vary by build — verify paths on your own machine before quoting them in reports.


LocalPolicy 4CCs

4CC Meaning
smb0, smb1 Security downgrade bits: Reduced (smb0) / Permissive (smb1)
sip0 SIP policy bits (moved off NVRAM)
sip3 Disables iBoot's boot-args allow-list enforcement
auxp, auxr AuxKC policy hash + receipt (third-party kexts)
coih Custom kernel hash — kmutil configure-boot from 1TR
smb4 MDM enrolment via ABM/ASM

SoC ID cheat sheet

SoC ID Chip
t8103 M1
t8112 M2
t8122 M3
t8132 M4

Read these from ioreg, panic logs, system_profiler SPHardwareDataType, or IPSW/restore filenames — not from the on-disk Preboot kernelcache (that file is just kernelcache, root:wheel; no chip ID in the name).


PAC crash signature

Apple Silicon-exclusive (ARMv8.3 hardware; Intel literally cannot generate these).

Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x<high-bit-set value>

High bits set because the chip stripped the auth code and found a mismatch. One = curiosity. Cluster against same process = strong exploit-attempt evidence.


Attacker tradecraft to know

  • x86_64 / Rosetta loophole — ARM64 requires strict code signing + notarisation; x86_64 under Rosetta has relaxed requirements. State-sponsored actors ship x86_64 builds by choice (not for lack of an ARM64 toolchain). POOLRAT is the named example.
  • Forced Rosetta — any x86-only tool drags the chain into the relaxed environment. Classic illustration: Python 2 (no ARM64-native macOS build), but it's just one instance — pick any x86-only binary and you've downgraded signing for free and generated AOT cache.
  • Universal binaries default to the x86_64 slice under Rosetta — any universal or x86 tool invoked by an x86_64 process is translated and leaves an AOT entry, even on an M-series Mac.
  • AOT poisoning — theoretical, not yet in the wild. SIP off → rewrite a .aot so the clean, correctly-signed source binary silently executes injected ARM64. Detect: regenerate a known-good AOT from the original binary (pinned OS + Rosetta) and hash-diff vs the on-disk entry; at scale, glob the oah tree and re-translate offline, alerting where the source hash is unchanged but the AOT differs. Cheap signals first: broken _oahd ownership · AOT mtime newer than the binary's last legit run · SIP-off precondition (no SIP-off, no poisoning). Caveat: re-translation isn't bit-deterministic across versions — pin OS + Rosetta, or diff behaviourally.
  • AuxKC abuse — only possible with SIP disabled, which itself is a noisy 1TR event.
  • EDR / Endpoint Security visibility — ES sees the x86 exec event, but the _oahd AOT write is post-hoc — the cache is evidence after the fact, not a real-time alert.
  • Prevention (blue team) — MDM-enforce Full Security so a LocalPolicy downgrade is impossible-or-visible; alert on LocalPolicy downgrades and _oahd anomalies. Turns these artifacts into tripwires, not just post-hoc evidence.
  • Catalog of macOS LOTL binaries: LOOBins — by Brendan Chamberlain (@infosecB). Each entry has MITRE ATT&CK mappings, example malicious uses, and detection signatures. Cited by CISA and Australia's ASD as the canonical macOS LOTL reference. PyLOOBins SDK + JSON API for programmatic consumption.

Anti-list — NOT Apple Silicon-only

Modern-macOS artifacts that get mistaken for AS-specific. Collect them, but don't claim them as Apple Silicon evidence (see the native-vs-inherited table above for why):

Biome · Unified Logs (tracev3) · KnowledgeC · TCC · Quarantine DB · APFS snapshots · FSEvents · ExecPolicy / KextPolicy


Collector coverage — Velociraptor & SQLiteHunter

What the two big collection frameworks grab out of the box, and — critically — what they don't (the AS-specific artifacts you still collect by hand).

Velociraptor — built-in macOS artifacts (MacOS.* / Generic.*):

Artifact Covers
MacOS.System.TCC TCC.db — privacy/permission grants
MacOS.System.QuarantineEvents LSQuarantine — download provenance
MacOS.System.Plist arbitrary plist parsing
MacOS.System.Users / .Dock / .Wifi / .TimeMachine accounts, dock, Wi-Fi, TM config
MacOS.Applications.KnowledgeC KnowledgeC.db — app usage/activity
MacOS.Applications.Chrome.History / .MRU browser + recent items
MacOS.Detection.Autoruns / .InstallHistory persistence + install history
MacOS.UnifiedLogHunter live log predicate hunting
Exchange.MacOS.UnifiedLogParser offline tracev3 (wraps Mandiant lib)
Exchange.MacOS.Collection.Aftermath runs Aftermath, ships result to server
Generic.Forensic.SQLiteHunter see below

SQLiteHunter (Generic.Forensic.SQLiteHunter, bundled since Velociraptor 0.7.0) — auto-discovers and parses any known SQLite DB on the host: browser history/cookies/downloads (Chrome, Firefox, Edge, Safari, Opera), TCC, KnowledgeC, Quarantine, plus many app DBs. One artifact, identify-then-parse, self-contained. The right tool when you don't know in advance which DBs are present.

What neither parses natively today — hand-collection list (the AS-specific gap):

/var/db/oah (Rosetta AOT) · LocalPolicy img4 (bputil -e / iSCPreboot) · NVRAM boot-args/boot-nonces · Preboot BORD logs · RestoreLog.plist · CryptexRegistry · PAC crash reports · /var/db/lockdown pairing · SEP/firmware version · bioutil Touch ID state

This is the seam the talk is about: the general macOS frameworks have mature coverage of the charted (modern-macOS) artifacts and no native parser for the AS-specific ones — you bolt on the standalone tools below by hand. Getting the bytes is solved; turning them into meaning is the frontier.


Tools

Acquisition (Apple Silicon-capable): Fuji · LLIMAGER · RECON ITR · Cellebrite Digital Collector

Collection frameworks: Velociraptor (MacOS.* artifacts + offline collector) · SQLiteHunter (Generic.Forensic.SQLiteHunter)

Live IR / MDM-driven: Aftermath (Jamf Threat Labs) — Swift, deploys via MDM, --analyze mode produces chronological storyline

Image artifact parser: mac_apt (Yogesh Khatri) — Python, cross-platform, 30+ plugins, works on E01/VMDK/AFF4/DD/DMG/Velociraptor/UAC collections

Unified Logs parser (offline): mandiant/macos-UnifiedLogs — Rust, parses .logarchive / tracev3 to CSV or JSON, cross-platform binaries

AOT shared cache RE: Project Champollion (FFRI) — format research; no turnkey forensic parser yet

Native / raw parsing: bputil -e (LocalPolicy) · img4lib / pyimg4 (raw IMG4) · sqlite3 (ExecPolicy/KextPolicy) · plutil (plists, RestoreLog) · nvram -p · log (live unified logs)


Monday-morning triage

Order matches priority. All commands need an unlocked, logged-in session and sudo.

# 1. Logical image — pick one
#    Fuji · LLIMAGER · RECON ITR · Cellebrite Digital Collector

# 2. Unified logs
sudo log collect --last 30d

# 3. LocalPolicy
sudo bputil -e
#    + raw img4 files from:
#      /System/Volumes/iSCPreboot/<UUID>/LocalPolicy/

# 4. SIP-protected dirs — even root gets "Operation not permitted" live;
#    read them from the IMAGE, or with your acquisition tool's privileges
#    /var/db/oah                          # Rosetta AOT cache
#    /var/db/lockdown                     # DFU/Configurator pairing
#    /var/db/SystemPolicyConfiguration    # ExecPolicy, KextPolicy (Intel too)
#    /System/Volumes/Preboot/<UUID>/      # BORD logs, RestoreLog, CryptexRegistry

# 4b. AOT first-execution timeline (run on the IMAGE — live find is denied)
#     sudo find /var/db/oah -name '*.aot' \
#       -exec stat -f '%SB  %Su  %N' -t '%Y-%m-%d %H:%M:%S' {} \; | sort

# 4c. Verify the real kernelcache path on THIS build (the <hash> dir varies)
#     sudo find /System/Volumes/Preboot -type d -name com.apple.kernelcaches
#     # -> contains ONE file named `kernelcache` (root:wheel, ~30 MB)

# 5. Single-shot commands
sudo nvram -p
system_profiler SPHardwareDataType
sudo bioutil -c -s
sudo sysdiagnose

Drive removal = ciphertext, always. On a live machine, FileVault decides: on → password-gated; off → the Secure Enclave releases the volume key at boot (data accessible without the password). Check FileVault state first.


Sources

  • Goddard, J. — Not Lost in Translation, Mandiant / Google Cloud, 2025
  • Apple — Apple Platform Security guide
  • Howard Oakley — The Eclectic Light Company (LocalPolicy, boot internals)
  • Aftermath — Jamf Threat Labs · github.com/jamf/aftermath
  • mac_apt — Y. Khatri (@SwiftForensics) · github.com/ydkhatri/mac_apt
  • macos-UnifiedLogs — Mandiant · github.com/mandiant/macos-UnifiedLogs
  • Velociraptor (Rapid7 / Velocidex) · SQLiteHunter — github.com/Velocidex/SQLiteHunter
  • FFRI — Project Champollion (AOT shared cache reverse-engineering)
  • LOOBins — B. Chamberlain (@infosecB) · loobins.io
  • Fuji (A. Lazzarotto) · RECON ITR (SUMURI) · LLIMAGER (e-Forensics) · Digital Collector (Cellebrite)
  • Prior work: Sarah Edwards (mac4n6) · Csaba Fitzl · Patrick Wardle / Objective-See · the Mandiant FLARE team
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment