Wednesday, March 25, 2026

Installing Oh My Zsh & Powerlevel10k on macOS

 

Guide: Installing Oh My Zsh & Powerlevel10k on macOS

If you're running Python scripts, managing Git repos for your tooling, and switching between virtual environments for different parsers, a terminal that gives context can help with your work.

This guide sets up Oh My Zsh with Powerlevel10k on the native macOS Terminal.app, then adds the plugins.


Why This Matters for Forensic Work

The default macOS Zsh prompt tells you almost nothing. It shows your current directory and that's it. When you're:

  • Running Python scripts from inside a case directory three levels deep
  • Working across multiple Git branches in your DFIR tools repo
  • Switching Python virtual environments between different parsers
  • Checking whether your last collection script exited cleanly or threw an error

This helps if you want that information visible in your prompt without running a separate command. 


Step 1: Install Oh My Zsh

Oh My Zsh is the framework that manages your Zsh configuration, themes, and plugins.

sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"

Action: When prompted to change your default shell to Zsh, type Y.


Step 2: Install Powerlevel10k Theme

Clone the theme into your Oh My Zsh custom themes folder:

git clone --depth=1 https://github.com/romkatv/powerlevel10k.git ${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/themes/powerlevel10k

Then activate it. Open your config:

nano ~/.zshrc

Find ZSH_THEME="robbyrussell" and replace it with:

ZSH_THEME="powerlevel10k/powerlevel10k"

Save and exit: Ctrl+O, Enter, Ctrl+X.


Step 3: Fix Missing Icons (Force-Install Nerd Fonts)

If the P10k wizard doesn't offer to install fonts, or you see square boxes instead of symbols, install the MesloLGS Nerd Fonts manually. Run these four commands:

curl -L https://github.com/romkatv/powerlevel10k-media/raw/master/MesloLGS%20NF%20Regular.ttf -o ~/Library/Fonts/"MesloLGS NF Regular.ttf"
curl -L https://github.com/romkatv/powerlevel10k-media/raw/master/MesloLGS%20NF%20Bold.ttf -o ~/Library/Fonts/"MesloLGS NF Bold.ttf"
curl -L https://github.com/romkatv/powerlevel10k-media/raw/master/MesloLGS%20NF%20Italic.ttf -o ~/Library/Fonts/"MesloLGS NF Italic.ttf"
curl -L https://github.com/romkatv/powerlevel10k-media/raw/master/MesloLGS%20NF%20Bold%20Italic.ttf -o ~/Library/Fonts/"MesloLGS NF Bold Italic.ttf"

Then activate the font in Terminal.app:

  1. Quit Terminal completely (Cmd + Q) and reopen it
  2. Press Cmd + , to open Settings
  3. Go to Profiles → Text
  4. Under Font, click Change — search for MesloLGS NF and select it
  5. Close Settings

Step 4: Run the Configuration Wizard

p10k configure

When it asks whether each symbol renders correctly, you should now see actual glyphs instead of boxes. Answer y for each. The Rainbow style gives the best at-a-glance context for multi-component prompts.


Step 5: DFIR-Specific Plugins and Aliases

This is where the setup becomes relevant to forensic work rather than just looking nice.

Recommended Plugins

Open ~/.zshrc and find the plugins=() line. Update it:

plugins=(git python brew colored-man-pages zsh-syntax-highlighting zsh-autosuggestions)

Install the two community plugins that aren't bundled with Oh My Zsh:

git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting
git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions

Why these matter for DFIR:

  • git — Shows branch and dirty/clean status inline. When you're pushing parser updates or tracking changes to triage scripts, this is ambient context with no extra commands.
  • python — Displays the active virtualenv name in your prompt. Running scripts in isolated environments is the right practice; this makes it visible at all times.
  • zsh-syntax-highlighting — Commands turn green when valid, red when not. Catches typos in long forensic commands before you execute them.
  • zsh-autosuggestions — Suggests previous commands as you type. Saves re-typing long script invocations with full case directory paths.
  • colored-man-pages — Colors man page output. Useful when referencing tool flags during an active investigation.

Useful DFIR Aliases

Add these to the bottom of ~/.zshrc. Adjust paths to match your case and tools directories:

# --- DFIR Aliases ---

# Navigate to active case directory quickly
alias case='cd ~/Cases'

# Python virtual environment shortcuts
alias ve='python3 -m venv venv'
alias va='source venv/bin/activate'

# Quick hash verification
alias sha256='shasum -a 256'
alias md5='md5 -r'

# Reload .zshrc after edits
alias reload='source ~/.zshrc'

Apply all changes:

source ~/.zshrc

What You Get

After this setup, your prompt surfaces: current directory, active Git branch with clean/dirty status, Python virtualenv name, exit code of the last command, and execution time for commands over a threshold. For a forensic examiner running custom Python scripts and managing tool repos, this replaces several git status and which python3 commands you'd otherwise run manually throughout a session.

Powerlevel10k terminal styles — Lean, Classic, and Rainbow

Lean, Classic, and Rainbow prompt styles. Rainbow (bottom) is recommended for the most context at a glance.

Tool source and additional DFIR scripts: github.com/dynamicallystatic

Friday, March 20, 2026

iOS Acquisition Landscape

The Shifting iOS Acquisition Landscape


Cellebrite published an article this week noting that iOS 26 effectively ends physical jailbreaking as a viable forensic acquisition path. 

What does it mean for mobile forensics:

  • Full filesystem extractions from modern iOS devices increasingly require either the device's passcode + GrayKey/Cellebrite UFED, or other licensed platforms 
  • Logical and advanced logical acquisitions remain viable for many cases but don't reach the same locations and full filesystem


iOS Battery Artifact Path:

Battery data is one of those iOS artifact categories that could potentially be of use for pattern of life forensics. 

Located at:

/private/var/db/Battery/BDC/

Inside this directory you'll find a collection of CSV files. The ones of forensic interest have the prefix BDC_SBC — these are the only files in the folder that include battery percentages and human-readable fields. 

Key Fields in BDC_SBC CSV Files

FieldDescriptionForensic Significance
TimeStampEvent timestampPrimary timeline anchor for all battery events
Current CapacityRaw battery capacity (%)Tracks charge level across time — useful for device usage timelines
IsChargingBoolean charging statusProves device was plugged in at specific times
TemperaturePhone temperature in C × 1000Abnormal heat can indicate sustained CPU load (e.g., encryption, crypto mining, heavy app use)
AmperageCurrent draw in mA (negative = draining, positive = charging)Differentiates passive standby from active use; identifies fast vs. slow charging
VoltageVoltage in mVCombined with amperage, can indicate charging watt profile
StateOfChargeBattery percentage as shown in iOS UICorroborates screenshots or user claims about charge level
WattsWattage input during chargingIdentifies slow charging (5W) vs. fast charging (20W+)

What You Can Establish From This Data

Charging timeline: IsCharging combined with TimeStamp creates a precise record of when the device was connected to power. 

Active use vs. standby: A device in standby draws minimal current (low negative amperage, stable temperature). Sustained negative amperage combined with elevated temperature indicates active use — CPU-intensive processes running, app activity, or communication. This can challenge an alibi claim of "the phone was just sitting there."

Charging speed as a charger identifier: Fast chargers (20W+) produce a distinct wattage signature vs. standard 5W USB charging. In cases where charger type matters (e.g., travel location, specific hardware correlation), the Watts field may help.

Temperature anomalies: Sustained high temperature periods can indicate background processes running when the user claims the device was idle. Combined with other app activity artifacts, this can paint a fuller picture.


Multiple Files = Extended Timeline Coverage

The BDC directory typically contains multiple CSV files compiled across different date ranges. Correlate the timestamps across files to build a continuous battery timeline for the investigation period.

For manual review: the CSV files can be opened directly in any spreadsheet application. Filter by IsCharging to isolate charging windows, sort by TimeStamp to build a chronological view, and flag rows where Temperature exceeds expected idle thresholds (roughly 25,000–35,000 in the raw C×1000 scale, depending on ambient conditions).



Battery data could potentially be useful evidence it doesn't get deleted by the user, and it records continuously. But requires a full filesystem extraction




Windows 11 - Program Compatibility Assistant (PCA) artifact

Windows 11 shipped a forensic artifact that hasn't been added to most workflows yet.

It's a plain text file sitting in a directory t and it can tell you exactly what executable a user double-clicked, including the full path and a UTC timestamp. 


What Is the PCA Launch Dictionary?

The Program Compatibility Assistant (PCA) service has existed since Vista. Its original purpose is to monitor launched applications, detect compatibility issues, and suggest fixes when old software has issues. 

Starting with Windows 11 22H2, Microsoft added a persistent text-based tracking mechanism to support that service :

Artifact location:

C:\Windows\appcompat\pca\PcaAppLaunchDic.txt

Companion files in the same directory:

C:\Windows\appcompat\pca\PcaGeneralDb0.txt
C:\Windows\appcompat\pca\PcaGeneralDb1.txt

The PcaGeneralDb files alternate as active logs and contain additional detail about compatibility errors and application exits — useful corroborating data alongside the launch dictionary.


File Format

The file is encoded in UTF-16 LE (not UTF-8 — tools that assume ASCII or UTF-8 will fail silently or be unreadable). 

Each line contains one entry: the full executable path, a pipe separator, and a UTC timestamp.

EXAMPLE:

C:\Users\Alice\Downloads\Quarterly_Review.pdf.exe|2026-03-15 09:42:11.000
C:\Temp\tool.exe|2026-03-15 09:43:05.000
D:\AUTORUN\payload.exe|2026-03-15 09:44:22.000

That third entry is immediately significant — D:\ is a removable drive. 


Scope and Limitations

It does not capture execution from:

  • cmd.exe or PowerShell
  • WMI or DCOM
  • PsExec or remote execution
  • Scheduled tasks or services

The artifact also persists after the source file is deleted. 

Quick Triage — PowerShell

During live response, read the file directly (the -Encoding Unicode flag is critical for UTF-16 LE):

Get-Content -Path "C:\Windows\appcompat\pca\PcaAppLaunchDic.txt" -Encoding Unicode

Filter for high-interest paths:

Get-Content -Path "C:\Windows\appcompat\pca\PcaAppLaunchDic.txt" -Encoding Unicode |
  Select-String -Pattern "Temp|Downloads|AppData|\\Users\\"

Copy for offline analysis:

copy "C:\Windows\appcompat\pca\PcaAppLaunchDic.txt" %USERPROFILE%\Desktop\PcaAppLaunchDic.txt

Python Parser for Pipeline Integration

import sys

def parse_pca(filepath):
    results = []
    with open(filepath, encoding="utf-16-le", errors="replace") as f:
        for line in f:
            line = line.strip()
            if "|" in line:
                path, timestamp = line.rsplit("|", 1)
                results.append({"path": path.strip(), "timestamp": timestamp.strip()})
    return results

if __name__ == "__main__":
    entries = parse_pca(sys.argv[1])
    for e in entries:
        print(f"[{e['timestamp']}] {e['path']}")

Run as: python3 parse_pca.py PcaAppLaunchDic.txt


Investigative Value — A Practical Scenario

A workstation is flagged in a phishing incident. The email attachment is gone. The downloaded file is gone. The user insists they only previewed a document. 

You check PcaAppLaunchDic.txt and find:

C:\Users\Alice\Downloads\Quarterly_Review.pdf.exe|2026-03-15 09:42:11.000

Where This Fits in the Execution Artifact Stack

The PCA launch dictionary doesn't replace the standard execution artifact set — it adds to it. Correlation is where the value compounds:

ArtifactLocationCovers CLI execution?Survives file deletion?
PcaAppLaunchDic.txtC:\Windows\appcompat\pca\No (Explorer only)Yes
PrefetchC:\Windows\Prefetch\YesYes
AmcacheC:\Windows\AppCompat\Programs\Amcache.hveYesYes (SHA-1 retained)
BAMSYSTEM hive – bam\State\UserSettingsYes (background)Yes
UserAssistNTUSER.DAT – UserAssist\CountNo (GUI only)Yes

Worth adding to your standard Windows 11 triage checklist. 

Browser Artifacts

Browser forensics shows up in lots of investigations. From  insider threat, data exfiltration, phishing analysis, etc. 

Chrome, Edge, and Firefox all store their artifacts as SQLite databases, which means the analysis methodology is consistent once one knows the schema. 

Here's a breakdown of where the databases live, what tables matter, and how to extract them:


Google Chrome

Profile directory: C:\Users\<username>\AppData\Local\Google\Chrome\User Data\Default\

(Multi-profile installs use Profile 1, Profile 2, etc. instead of Default)

Artifact File Path Key Table / Notes
Browsing HistoryDefault\Historyurls, visits tables; timestamps in WebKit epoch (microseconds since 1601-01-01)
DownloadsDefault\Historydownloads, downloads_url_chains tables; records full local save path
CookiesDefault\Network\Cookiescookies table; values encrypted with DPAPI on Windows
CacheDefault\Cache\Cache_Data\Binary cache blocks; use ChromeCacheView (NirSoft) to parse
BookmarksDefault\BookmarksJSON format; no SQLite needed
Login DataDefault\Login Datalogins table; passwords DPAPI-encrypted
Web DataDefault\Web DataAutofill, form data, credit card metadata (no raw PAN)
FaviconsDefault\Faviconsicon_mapping table; can reveal sites visited even if history was cleared
Sessions / TabsDefault\Sessions\SNSS format; use ChromeSessionParser
ExtensionsDefault\Extensions\Subdirectories per extension ID; check manifests for suspicious permissions

Timestamp note: Chrome uses WebKit time (microseconds since January 1, 1601). To convert: subtract 11644473600000000 microseconds to get Unix epoch. Most tools handle this automatically, but knowing the raw format matters when you're writing custom queries.


Microsoft Edge (Chromium)

Profile directory: C:\Users\<username>\AppData\Local\Microsoft\Edge\User Data\Default\

Edge Chromium uses the same underlying SQLite schema as Chrome — identical table names, identical timestamp format. The only differences are the profile path and some Edge-specific databases:

Artifact File Path Notes
HistoryDefault\HistoryIdentical schema to Chrome
CookiesDefault\Network\CookiesDPAPI encrypted
CollectionsDefault\Collections\collectionsSQLiteEdge-specific "Collections" feature; tracks saved web content
Edge ShoppingDefault\EdgeShopping\Coupons, price comparisons — can reveal purchasing intent

Mozilla Firefox

Profile directory: C:\Users\<username>\AppData\Roaming\Mozilla\Firefox\Profiles\<profile-id>.default-release\

The profile ID is a random alphanumeric string. List all profiles by reading C:\Users\<username>\AppData\Roaming\Mozilla\Firefox\profiles.ini.

Artifact File Path (relative to profile dir) Key Table / Notes
Browsing Historyplaces.sqlitemoz_places, moz_historyvisits; timestamps in microseconds since Unix epoch
Downloadsplaces.sqlitemoz_annos with annotation type downloads/
Bookmarksplaces.sqlitemoz_bookmarks table
Cookiescookies.sqlitemoz_cookies; stored in plaintext (unlike Chrome)
Form History / Autofillformhistory.sqlitemoz_formhistory table
Login Datalogins.json + key4.dbPasswords encrypted with NSS; key4.db holds the decryption key
Cachecache2\entries\Binary format; use MozillaCacheView (NirSoft)
Session Restoresessionstore-backups\JSON files; records open tabs and history at time of last session
Extensionsextensions.jsonJSON; list of installed add-ons with installation date

Analysis Methods

Direct SQLite querying: DB Browser for SQLite lets you open these databases directly and run custom queries. 

Example query for Chrome — URLs visited in a specific time range:

SELECT urls.url, urls.title, datetime((visits.visit_time/1000000)-11644473600, 'unixepoch') AS visit_time
FROM visits
JOIN urls ON visits.url = urls.id
WHERE visits.visit_time BETWEEN 13370000000000000 AND 13380000000000000
ORDER BY visits.visit_time ASC;

Automated parsing tools:

  • Hindsight — Chrome/Chromium-focused; outputs timeline CSV, JSON, or XLSX. Handles Chrome's evolving schema versions well.
  • Browser History Viewer — free GUI tool supporting Chrome, Firefox, Edge, IE, Safari
  • Browser Reviewer — portable tool for analyzing user activity across Firefox and Chrome-based browsers
  • KAPE with the BrowserArtifacts compound target — collects all browser profile directories in one triage pass

Anti-Forensic Considerations

A few things to keep in mind when browser artifacts appear clean or sparse:

  • Incognito/Private mode doesn't write to the History database — but the DNS cache, Windows Event Logs (network connections), and proxy logs may still capture the activity
  • History deletion removes records from urls and visits but the Favicons database often retains entries — favicon records for visited sites persist independently and don't get purged with standard history clearing
  • Cache survives many "clear history" operations depending on what checkboxes the user selected — always check the cache directory even when history is empty
  • SQLite WAL files (History-wal, places.sqlite-wal) may contain recently written but not yet checkpointed records — always grab these alongside the main database


Windows Execution Artifacts

One of the most consistent questions I get is: "Where do I look to prove a program ran on a system?" This is the right question. Execution artifacts are the backbone of any Windows investigation they tell you what ran, when, how many times, and sometimes from where. Below is a practitioner's reference covering the key artifact locations for Windows:

Prefetch

Path: C:\Windows\Prefetch\*.pf

Prefetch files are created by the Windows Superfetch/prefetch service to speed up application launches. Each .pf file records the executable name, run count, last eight run times (Windows 8+), and file system resources referenced during load. This is gold for proving execution — even if the binary has been deleted, the prefetch file may still exist.

  • Enabled by default on workstations; disabled by default on Windows Server — check HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management\PrefetchParameters\EnablePrefetcher
  • Files are named EXECUTABLENAME-HASH.pf where the hash is computed from the full launch path
  • Tool: PECmd (Eric Zimmerman) — parses single files or entire directories and outputs to CSV/JSON for timeline ingestion

Teaching point for students: A deleted executable with a remaining prefetch file is a classic indicator of anti-forensic activity. The prefetch hash also lets you distinguish between two executables with the same name launched from different paths.


Shimcache (AppCompatCache)

Registry key: HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\AppCompatCache

Hive file: C:\Windows\System32\config\SYSTEM

Shimcache records entries for executables that have interacted with the Application Compatibility database. It tracks the file path, last modified timestamp, and (on older systems) a flag indicating whether the file was actually executed. Important caveat: on Windows 10/11, the executed flag is no longer reliably present — presence in shimcache indicates the file was visible to the OS, not necessarily run.

  • Entries are written to the registry at shutdown, not in real time — data in memory may not be captured from a live system unless you dump the hive
  • Tool: AppCompatCacheParser (Eric Zimmerman) — exports to CSV; correlate timestamps with your master timeline

Amcache

Path: C:\Windows\AppCompat\Programs\Amcache.hve

Amcache is a registry hive (not a flat registry key) that stores metadata about installed applications and recently executed programs. Unlike Shimcache, Amcache records SHA-1 hashes of executables — invaluable for malware identification and hash-based threat hunting.

  • Key subkeys: Root\InventoryApplication, Root\InventoryApplicationFile, Root\Programs
  • Records: full file path, SHA-1, compile time, PE metadata, and first execution timestamp
  • Tool: AmcacheParser (Eric Zimmerman)

BAM / DAM (Background Activity Moderator)

Registry key: HKLM\SYSTEM\CurrentControlSet\Services\bam\State\UserSettings\{SID}

Hive file: C:\Windows\System32\config\SYSTEM

BAM is a Windows 10 (build 1709+) kernel driver that throttles background application activity. As a forensic side effect, it records the last execution time of background processes per user SID — one of the few artifacts that gives you user-attributed execution timestamps in a single registry location.

  • Values are stored as binary data; the timestamp is a 64-bit FILETIME value at offset 0
  • Particularly useful for lateral movement investigations — remote execution artifacts (psexec, scheduled tasks) often appear here

LNK Files (Shell Link)

Path: C:\Users\<username>\AppData\Roaming\Microsoft\Windows\Recent\*.lnk

Windows automatically creates LNK shortcut files when a user opens a file or folder. Each LNK records the target file's path, MAC timestamps, file size, volume serial number, and even the target system's NetBIOS name and MAC address if opened over a network share.

  • Network-originated LNK files can reveal attacker infrastructure (UNC paths, internal hostnames)
  • Tool: LECmd (Eric Zimmerman)

Jump Lists

Path (Automatic): C:\Users\<username>\AppData\Roaming\Microsoft\Windows\Recent\AutomaticDestinations\*.automaticDestinations-ms

Path (Custom): C:\Users\<username>\AppData\Roaming\Microsoft\Windows\Recent\CustomDestinations\*.customDestinations-ms

Jump lists are OLE structured storage files that track recently and frequently accessed files per application (identified by AppID). They contain embedded LNK entries — giving you all the same metadata described above, but organized by the application that opened them.

  • AppID mapping resources are publicly available to correlate AppIDs to application names
  • Tool: JLECmd (Eric Zimmerman)

UserAssist

Registry key: HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist\{GUID}\Count

Hive file: C:\Users\<username>\NTUSER.DAT

UserAssist records GUI-launched applications (those opened through Explorer, desktop shortcuts, or the Start Menu). Values are ROT-13 encoded — a trivial obfuscation but one that trips up manual review. Data includes run count and last execution timestamp.

  • Does not capture command-line execution — pair with Prefetch and BAM to get full coverage
  • Tool: RegRipper plugin userassist

Quick Reference Table

Artifact Location Key Data Points Tool
PrefetchC:\Windows\Prefetch\Run count, last 8 timestamps, referenced filesPECmd
ShimcacheSYSTEM hive – AppCompatCacheFile path, last modified timeAppCompatCacheParser
AmcacheC:\Windows\AppCompat\Programs\Amcache.hveSHA-1, first exec time, PE metadataAmcacheParser
BAMSYSTEM hive – bam\State\UserSettingsLast execution time per SIDManual / RegRipper
LNK Files%APPDATA%\Microsoft\Windows\Recent\Target path, MAC times, volume serial, MAC addrLECmd
Jump Lists%APPDATA%\...\AutomaticDestinations\Per-app recent files, embedded LNK dataJLECmd
UserAssistNTUSER.DAT – UserAssist\CountRun count, last exec (GUI only)RegRipper

Workflow Recommendation

TOOLS --> https://ericzimmerman.github.io/#!index.md

For triaging a Windows endpoint I recommend KAPE to pull and parse all of the above. 

The outputs drop into CSV 

Drop any questions in the comments — happy to dig into specific scenarios with any of these artifacts.