digital forensics

Performing Windows Artifact Analysis with Eric Zimmerman Tools

Perform comprehensive Windows forensic artifact analysis using Eric Zimmerman's open-source EZ Tools suite including KAPE, MFTECmd, PECmd, LECmd, JLECmd, and Timeline Explorer for parsing registry hives, prefetch files, event logs, and file system metadata.

artifact-analysisdfireric-zimmermanez-toolsjlecmdkapelecmdmftecmd
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

Overview

Eric Zimmerman's EZ Tools suite is a collection of open-source forensic utilities that have become the global standard for Windows digital forensics investigations. Originally developed by a former FBI agent and current SANS instructor, these tools parse and analyze critical Windows artifacts including the Master File Table ($MFT), registry hives, prefetch files, event logs, shortcut (LNK) files, and jump lists. The suite integrates with KAPE (Kroll Artifact Parser and Extractor) for automated artifact collection and processing, producing structured CSV output that can be ingested into Timeline Explorer for visual analysis. EZ Tools are widely used by law enforcement, corporate incident responders, and forensic consultants worldwide.

When to Use

  • When conducting security assessments that involve performing windows artifact analysis with eric zimmerman tools
  • When following incident response procedures for related security events
  • When performing scheduled security testing or auditing activities
  • When validating security controls through hands-on testing

Prerequisites

  • Windows 10/11 or Windows Server 2016+ analysis workstation
  • .NET 6 Runtime installed (required for EZ Tools v2.x+)
  • Administrative privileges on the analysis workstation
  • Forensic disk image or triage collection from target system
  • At least 8 GB RAM (16 GB recommended for large datasets)
  • Familiarity with NTFS file system structures and Windows internals

Tool Suite Components

KAPE (Kroll Artifact Parser and Extractor)

KAPE is the primary orchestration tool that automates artifact collection (Targets) and processing (Modules). It uses configuration files (.tkape and .mkape) to define what artifacts to collect and which EZ Tools to run against them.

Installation and Setup:

# Download KAPE from https://www.kroll.com/en/services/cyber-risk/incident-response-litigation-support/kroll-artifact-parser-extractor-kape
# Extract to C:\Tools\KAPE
 
# Update KAPE targets and modules
C:\Tools\KAPE\gkape.exe  # GUI version
C:\Tools\KAPE\kape.exe   # CLI version
 
# Sync latest EZ Tools binaries
C:\Tools\KAPE\Get-KAPEUpdate.ps1

Running KAPE Collection and Processing:

# Collect artifacts from E: drive (mounted forensic image) and process with EZ Tools
kape.exe --tsource E: --tdest C:\Cases\Case001\Collection --target KapeTriage --mdest C:\Cases\Case001\Processed --module !EZParser
 
# Collect specific artifact categories
kape.exe --tsource E: --tdest C:\Cases\Case001\Collection --target FileSystem,RegistryHives,EventLogs --mdest C:\Cases\Case001\Processed --module MFTECmd,RECmd,EvtxECmd
 
# Live system triage collection (run as administrator)
kape.exe --tsource C: --tdest D:\LiveTriage\Collection --target KapeTriage --mdest D:\LiveTriage\Processed --module !EZParser --vhdx LiveTriageImage

MFTECmd - Master File Table Parser

MFTECmd parses the NTFS $MFT, $J (USN Journal), $Boot, $SDS, and $LogFile into human-readable CSV format.

# Parse the $MFT file
MFTECmd.exe -f "C:\Cases\Evidence\$MFT" --csv C:\Cases\Output --csvf MFT_output.csv
 
# Parse the USN Journal ($J)
MFTECmd.exe -f "C:\Cases\Evidence\$J" --csv C:\Cases\Output --csvf USNJournal_output.csv
 
# Parse $Boot for volume information
MFTECmd.exe -f "C:\Cases\Evidence\$Boot" --csv C:\Cases\Output --csvf Boot_output.csv
 
# Parse $SDS for security descriptors
MFTECmd.exe -f "C:\Cases\Evidence\$SDS" --csv C:\Cases\Output --csvf SDS_output.csv

Key Fields in MFT Output:

Field Description
EntryNumber MFT record number
ParentEntryNumber Parent directory MFT record
InUse Whether the record is active or deleted
FileName Name of the file or directory
Created0x10 $STANDARD_INFORMATION creation timestamp
Created0x30 $FILE_NAME creation timestamp
LastModified0x10 $STANDARD_INFORMATION modification timestamp
IsDirectory Boolean indicating directory or file
FileSize Logical file size in bytes
Extension File extension

PECmd - Prefetch File Parser

PECmd parses Windows Prefetch files (.pf) to provide evidence of program execution, including run counts and timestamps.

# Parse all prefetch files from a directory
PECmd.exe -d "C:\Cases\Evidence\Windows\Prefetch" --csv C:\Cases\Output --csvf Prefetch_output.csv
 
# Parse a single prefetch file with verbose output
PECmd.exe -f "C:\Cases\Evidence\Windows\Prefetch\CMD.EXE-4A81B364.pf" --json C:\Cases\Output
 
# Parse prefetch with keyword filtering
PECmd.exe -d "C:\Cases\Evidence\Windows\Prefetch" -k "powershell,cmd,wscript,cscript,mshta" --csv C:\Cases\Output --csvf SuspiciousExec.csv

RECmd - Registry Explorer Command Line

RECmd processes Windows registry hives using batch files that define which keys and values to extract.

# Process all registry hives with the default batch file
RECmd.exe --bn C:\Tools\KAPE\Modules\bin\RECmd\BatchExamples\RECmd_Batch_MC.reb -d "C:\Cases\Evidence\Registry" --csv C:\Cases\Output --csvf Registry_output.csv
 
# Process a single NTUSER.DAT hive
RECmd.exe -f "C:\Cases\Evidence\Users\suspect\NTUSER.DAT" --bn C:\Tools\KAPE\Modules\bin\RECmd\BatchExamples\RECmd_Batch_MC.reb --csv C:\Cases\Output
 
# Process SYSTEM hive for USB device history
RECmd.exe -f "C:\Cases\Evidence\Registry\SYSTEM" --bn C:\Tools\KAPE\Modules\bin\RECmd\BatchExamples\RECmd_Batch_MC.reb --csv C:\Cases\Output

EvtxECmd - Windows Event Log Parser

EvtxECmd parses Windows Event Log (.evtx) files into structured CSV format with customizable event ID maps.

# Parse all event logs from a directory
EvtxECmd.exe -d "C:\Cases\Evidence\Windows\System32\winevt\Logs" --csv C:\Cases\Output --csvf EventLogs_output.csv
 
# Parse a single event log
EvtxECmd.exe -f "C:\Cases\Evidence\Security.evtx" --csv C:\Cases\Output --csvf Security_output.csv
 
# Parse with custom maps for enhanced field extraction
EvtxECmd.exe -d "C:\Cases\Evidence\Logs" --csv C:\Cases\Output --maps C:\Tools\KAPE\Modules\bin\EvtxECmd\Maps

LECmd and JLECmd - Shortcut and Jump List Parsers

# Parse LNK files from Recent directory
LECmd.exe -d "C:\Cases\Evidence\Users\suspect\AppData\Roaming\Microsoft\Windows\Recent" --csv C:\Cases\Output --csvf LNK_output.csv
 
# Parse Jump Lists (automatic destinations)
JLECmd.exe -d "C:\Cases\Evidence\Users\suspect\AppData\Roaming\Microsoft\Windows\Recent\AutomaticDestinations" --csv C:\Cases\Output --csvf JumpLists_auto.csv
 
# Parse Jump Lists (custom destinations)
JLECmd.exe -d "C:\Cases\Evidence\Users\suspect\AppData\Roaming\Microsoft\Windows\Recent\CustomDestinations" --csv C:\Cases\Output --csvf JumpLists_custom.csv

SBECmd - Shellbag Explorer Command Line

# Parse shellbags from a directory of registry hives
SBECmd.exe -d "C:\Cases\Evidence\Registry" --csv C:\Cases\Output --csvf Shellbags_output.csv
 
# Parse shellbags from a live system (requires admin)
SBECmd.exe --live --csv C:\Cases\Output --csvf LiveShellbags_output.csv

Timeline Explorer - Visual Analysis

Timeline Explorer is the GUI tool for analyzing CSV output from all EZ Tools. It supports filtering, sorting, column grouping, and conditional formatting.

# Launch Timeline Explorer and open CSV output
TimelineExplorer.exe "C:\Cases\Output\MFT_output.csv"

Key Timeline Explorer Features:

  • Column-level filtering with regular expressions
  • Conditional formatting for timestamp anomalies
  • Multi-column sorting for chronological analysis
  • Export filtered results to new CSV files
  • Bookmarking rows of interest

Investigation Workflow

Step 1: Artifact Collection with KAPE

# Full triage collection from forensic image mounted at E:
kape.exe --tsource E: --tdest C:\Cases\Case001\Collected --target KapeTriage --vhdx TriageImage --zv false

Step 2: Artifact Processing with EZ Tools

# Process all collected artifacts
kape.exe --msource C:\Cases\Case001\Collected --mdest C:\Cases\Case001\Processed --module !EZParser

Step 3: Timeline Analysis

  1. Open processed CSV files in Timeline Explorer
  2. Sort by timestamp columns to establish chronological order
  3. Filter for specific file extensions, paths, or event IDs
  4. Cross-reference MFT timestamps with event log entries
  5. Identify timestomping by comparing $SI and $FN timestamps
  6. Document findings with bookmarks and exported filtered views

Step 4: Timestomping Detection

# In Timeline Explorer, compare these columns:
# Created0x10 ($STANDARD_INFORMATION) vs Created0x30 ($FILE_NAME)
# If Created0x10 < Created0x30, timestomping is indicated
# $FILE_NAME timestamps are harder to manipulate than $STANDARD_INFORMATION

Forensic Artifacts Reference

Tool Artifact Location
MFTECmd $MFT Root of NTFS volume
MFTECmd $J (USN Journal) $Extend$UsnJrnl:$J
PECmd Prefetch files C:\Windows\Prefetch*.pf
RECmd NTUSER.DAT C:\Users{user}\NTUSER.DAT
RECmd SYSTEM hive C:\Windows\System32\config\SYSTEM
RECmd SAM hive C:\Windows\System32\config\SAM
RECmd SOFTWARE hive C:\Windows\System32\config\SOFTWARE
EvtxECmd Event logs C:\Windows\System32\winevt\Logs*.evtx
LECmd LNK files C:\Users{user}\AppData\Roaming\Microsoft\Windows\Recent\
JLECmd Jump lists C:\Users{user}\AppData\Roaming\Microsoft\Windows\Recent\AutomaticDestinations\
SBECmd Shellbags NTUSER.DAT and UsrClass.dat registry hives

Common Investigation Scenarios

Malware Execution Evidence

  1. Parse Prefetch with PECmd to identify executed binaries
  2. Cross-reference with MFT for file creation timestamps
  3. Check Amcache.hve with RECmd for SHA1 hashes of executables
  4. Correlate with Event Log entries for process creation (Event ID 4688)

Data Exfiltration Investigation

  1. Parse USN Journal with MFTECmd for file rename/delete operations
  2. Analyze LNK files with LECmd for recently accessed documents
  3. Review Shellbags with SBECmd for directory browsing activity
  4. Check for USB device connections in SYSTEM registry with RECmd

Lateral Movement Detection

  1. Parse Security.evtx with EvtxECmd for logon events (4624, 4625)
  2. Analyze RDP-related event logs (Microsoft-Windows-TerminalServices)
  3. Cross-reference with network share access from SMB logs
  4. Review scheduled tasks and services for persistence mechanisms

Output Format and Integration

All EZ Tools produce CSV output that can be:

  • Analyzed in Timeline Explorer for visual investigation
  • Imported into Splunk, Elastic, or other SIEM platforms
  • Processed by Python/PowerShell scripts for automated analysis
  • Combined into super timelines using log2timeline/Plaso

References

Source materials

References and resources

Everything below is rendered for inspection. Script files are read-only and never run.

References 3

api-reference.md2.0 KB

API Reference: Windows Artifact Analysis with Eric Zimmerman Tools

EZ Tools Suite

Tool Artifact Description
MFTECmd.exe $MFT Parse Master File Table
PECmd.exe Prefetch Parse prefetch files for execution history
LECmd.exe LNK Parse shortcut files
JLECmd.exe Jump Lists Parse automatic/custom jump lists
SBECmd.exe ShellBags Parse folder access history from registry
AmcacheParser.exe Amcache Parse application execution evidence
AppCompatCacheParser.exe Shimcache Parse application compatibility cache
EvtxECmd.exe EVTX Parse Windows event logs
RECmd.exe Registry Parse registry hives

Common CLI Flags

Flag Description
-f <file> Input file path
-d <directory> Input directory
--csv <dir> Output directory for CSV
--csvf <file> CSV output filename
--json <dir> Output directory for JSON
--body <dir> Output bodyfile for timeline

Key Artifacts and Locations

Artifact Path Evidence
$MFT C:\$MFT File creation/modification/access
Prefetch C:\Windows\Prefetch\ Program execution with timestamps
LNK Files %APPDATA%\Microsoft\Windows\Recent\ Recently accessed files
Jump Lists %APPDATA%\Microsoft\Windows\Recent\AutomaticDestinations\ Per-app recent files
ShellBags NTUSER.DAT, UsrClass.dat Folder browsing history
Amcache C:\Windows\AppCompat\Programs\Amcache.hve Application execution

Python Libraries

Library Version Purpose
subprocess stdlib Execute EZ tools
csv stdlib Parse CSV output
json stdlib Report generation

References

standards.md2.6 KB

Standards and References - EZ Tools Windows Forensics

Industry Standards

NIST SP 800-86 - Guide to Integrating Forensic Techniques

  • Framework for collecting, examining, and analyzing digital evidence
  • Defines procedures for forensic acquisition and chain of custody
  • EZ Tools align with NIST evidence handling and analysis guidelines

ISO/IEC 27037 - Digital Evidence Collection

  • International standard for identification, collection, acquisition, and preservation
  • KAPE collection follows ISO 27037 acquisition methodology
  • Timeline Explorer output supports ISO-compliant reporting

SWGDE Best Practices for Computer Forensics

  • Scientific Working Group on Digital Evidence guidelines
  • Defines validation requirements for forensic tools
  • EZ Tools undergo community-driven validation testing

Tool References

EZ Tools Suite Components

Tool Version Purpose
KAPE 1.3+ Artifact collection and processing orchestration
MFTECmd 1.2+ NTFS Master File Table parser
PECmd 1.5+ Windows Prefetch file parser
RECmd 2.0+ Registry hive parser with batch processing
EvtxECmd 1.5+ Windows Event Log parser with maps
LECmd 1.5+ LNK shortcut file parser
JLECmd 1.5+ Jump List parser
SBECmd 2.0+ Shellbag parser
Timeline Explorer 2.0+ CSV analysis and visualization
Registry Explorer 2.0+ GUI registry hive viewer
ShellBags Explorer 2.0+ GUI shellbag viewer
AmcacheParser 1.5+ Amcache.hve parser
AppCompatCacheParser 1.5+ ShimCache parser
WxTCmd 1.0+ Windows Timeline database parser
RBCmd 1.5+ Recycle Bin artifact parser
bstrings 1.5+ Binary string extraction

SANS Training Courses

  • FOR500: Windows Forensic Analysis
  • FOR508: Advanced Incident Response, Threat Hunting, and Digital Forensics
  • FOR498: Battlefield Forensics & Data Acquisition
  • FOR610: Reverse-Engineering Malware

MITRE ATT&CK Relevance

  • T1070 - Indicator Removal: Timestomping detection via MFT analysis
  • T1547 - Boot or Logon Autostart Execution: Registry persistence detection
  • T1053 - Scheduled Task/Job: Task scheduler artifact analysis
  • T1059 - Command and Scripting Interpreter: Prefetch execution evidence
  • T1021 - Remote Services: Lateral movement via event log analysis

Official Resources

workflows.md2.9 KB

Workflows - EZ Tools Windows Forensic Analysis

Workflow 1: Full Triage Collection and Processing

Step 1: Mount forensic image as read-only drive (E:)
    |
Step 2: Run KAPE with KapeTriage target
    |-- Collects: $MFT, $J, Registry, Event Logs, Prefetch, LNK, Jump Lists
    |
Step 3: Run KAPE with !EZParser module
    |-- Processes all collected artifacts with appropriate EZ Tools
    |
Step 4: Open CSV outputs in Timeline Explorer
    |-- Sort by timestamp, filter by artifact type
    |
Step 5: Build investigation timeline
    |-- Cross-reference MFT, Event Logs, Prefetch, Registry
    |
Step 6: Document findings and export filtered results

Workflow 2: Timestomping Detection

Step 1: Parse $MFT with MFTECmd
    |
Step 2: Open MFT CSV in Timeline Explorer
    |
Step 3: Compare $SI timestamps (Created0x10) vs $FN timestamps (Created0x30)
    |-- Flag entries where Created0x10 < Created0x30
    |-- Flag entries where all four $SI timestamps are identical
    |
Step 4: Cross-reference flagged files with USN Journal entries
    |
Step 5: Verify with event log correlation (process creation, file access)

Workflow 3: Program Execution Timeline

Step 1: Parse Prefetch with PECmd
    |
Step 2: Parse Amcache.hve with AmcacheParser
    |
Step 3: Parse ShimCache with AppCompatCacheParser
    |
Step 4: Parse UserAssist from NTUSER.DAT with RECmd
    |
Step 5: Merge and correlate execution timestamps
    |-- Prefetch: Run count + last 8 execution times
    |-- Amcache: First execution + SHA1 hash
    |-- ShimCache: Last modified time (execution indicator)
    |-- UserAssist: GUI program execution tracking
    |
Step 6: Build execution timeline in Timeline Explorer

Workflow 4: Lateral Movement Investigation

Step 1: Parse Security.evtx with EvtxECmd
    |-- Filter Event ID 4624 (Logon Type 3, 10)
    |-- Filter Event ID 4625 (Failed logons)
    |
Step 2: Parse TerminalServices event logs
    |-- Microsoft-Windows-TerminalServices-LocalSessionManager
    |-- Microsoft-Windows-TerminalServices-RDPClient
    |
Step 3: Correlate with registry RDP MRU entries
    |-- NTUSER.DAT\Software\Microsoft\Terminal Server Client\Servers
    |
Step 4: Analyze LNK files for network path access
    |
Step 5: Review scheduled task creation from event logs
    |
Step 6: Map lateral movement path across systems

Workflow 5: USB Device Investigation

Step 1: Parse SYSTEM hive with RECmd
    |-- SYSTEM\CurrentControlSet\Enum\USBSTOR
    |-- SYSTEM\CurrentControlSet\Enum\USB
    |-- SYSTEM\MountedDevices
    |
Step 2: Parse NTUSER.DAT with RECmd
    |-- MountPoints2 for user-level device associations
    |
Step 3: Parse setupapi.dev.log for device installation timestamps
    |
Step 4: Analyze LNK files referencing removable drive letters
    |
Step 5: Check Shellbags for folder browsing on USB devices
    |
Step 6: Correlate with Event Logs (Microsoft-Windows-Partition/Diagnostic)

Scripts 2

agent.py6.7 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Agent for Windows artifact analysis with Eric Zimmerman tools.

Runs EZ tools (MFTECmd, PECmd, LECmd, JLECmd, ShellBags Explorer CLI)
via subprocess, parses CSV output, and builds a forensic timeline
from Windows filesystem and registry artifacts.
"""

import subprocess
import json
import sys
import csv
import io
from datetime import datetime
from pathlib import Path


class EZToolsAgent:
    """Analyzes Windows forensic artifacts using Eric Zimmerman tools."""

    def __init__(self, output_dir="./ez_analysis"):
        self.output_dir = Path(output_dir)
        self.output_dir.mkdir(parents=True, exist_ok=True)
        self.timeline = []

    def _run_tool(self, tool, args, timeout=300):
        cmd = [tool] + args
        try:
            result = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)
            return {"return_code": result.returncode,
                    "stdout_lines": len(result.stdout.splitlines()),
                    "stderr": result.stderr[:300] if result.stderr else ""}
        except FileNotFoundError:
            return {"error": f"{tool} not found. Download from https://ericzimmerman.github.io/"}
        except subprocess.TimeoutExpired:
            return {"error": f"{tool} timed out"}

    def parse_mft(self, mft_path):
        """Parse $MFT using MFTECmd and extract file creation/modification."""
        csv_out = self.output_dir / "mft_output.csv"
        result = self._run_tool("MFTECmd.exe", ["-f", mft_path,
                                                  "--csv", str(self.output_dir),
                                                  "--csvf", "mft_output.csv"])
        if "error" in result:
            return result
        return self._parse_csv(csv_out, "MFT",
                               time_cols=["Created0x10", "LastModified0x10"])

    def parse_prefetch(self, prefetch_dir):
        """Parse Prefetch files using PECmd for program execution evidence."""
        csv_out = self.output_dir / "prefetch_output.csv"
        result = self._run_tool("PECmd.exe", ["-d", prefetch_dir,
                                               "--csv", str(self.output_dir),
                                               "--csvf", "prefetch_output.csv"])
        if "error" in result:
            return result
        return self._parse_csv(csv_out, "Prefetch",
                               time_cols=["LastRun", "PreviousRun0"])

    def parse_lnk_files(self, lnk_dir):
        """Parse LNK shortcut files using LECmd."""
        csv_out = self.output_dir / "lnk_output.csv"
        result = self._run_tool("LECmd.exe", ["-d", lnk_dir,
                                               "--csv", str(self.output_dir),
                                               "--csvf", "lnk_output.csv"])
        if "error" in result:
            return result
        return self._parse_csv(csv_out, "LNK",
                               time_cols=["TargetCreated", "TargetModified"])

    def parse_jump_lists(self, jl_dir):
        """Parse Jump Lists using JLECmd for recent file access."""
        csv_out = self.output_dir / "jumplist_output.csv"
        result = self._run_tool("JLECmd.exe", ["-d", jl_dir,
                                                "--csv", str(self.output_dir),
                                                "--csvf", "jumplist_output.csv"])
        if "error" in result:
            return result
        return self._parse_csv(csv_out, "JumpList",
                               time_cols=["TargetCreated", "TargetModified"])

    def parse_shellbags(self, registry_hive):
        """Parse ShellBags from NTUSER.DAT/UsrClass.dat."""
        csv_out = self.output_dir / "shellbags_output.csv"
        result = self._run_tool("SBECmd.exe", ["-d", registry_hive,
                                                "--csv", str(self.output_dir),
                                                "--csvf", "shellbags_output.csv"])
        if "error" in result:
            return result
        return self._parse_csv(csv_out, "ShellBag",
                               time_cols=["LastWriteTime", "FirstExplored"])

    def _parse_csv(self, csv_path, artifact_type, time_cols=None):
        """Parse EZ tool CSV output into timeline entries."""
        if not csv_path.exists():
            return {"error": f"CSV not found: {csv_path}"}
        entries = []
        try:
            with open(csv_path, "r", encoding="utf-8-sig") as f:
                reader = csv.DictReader(f)
                for row in reader:
                    entry = {"artifact_type": artifact_type}
                    for key, val in row.items():
                        if val:
                            entry[key] = val
                    if time_cols:
                        for tc in time_cols:
                            ts = row.get(tc, "")
                            if ts:
                                entry["timestamp"] = ts
                                self.timeline.append({
                                    "timestamp": ts,
                                    "artifact": artifact_type,
                                    "source": csv_path.name,
                                    "details": {k: v for k, v in row.items()
                                                if v and k != tc}
                                })
                                break
                    entries.append(entry)
        except (csv.Error, UnicodeDecodeError) as exc:
            return {"error": str(exc)}
        return {"artifact": artifact_type, "entries": len(entries)}

    def build_timeline(self):
        """Sort all collected entries into a unified forensic timeline."""
        self.timeline.sort(key=lambda x: x.get("timestamp", ""))
        return self.timeline

    def generate_report(self):
        self.build_timeline()
        report = {
            "report_date": datetime.utcnow().isoformat(),
            "total_timeline_entries": len(self.timeline),
            "timeline_sample": self.timeline[:50],
        }
        report_path = self.output_dir / "ez_tools_report.json"
        with open(report_path, "w") as f:
            json.dump(report, f, indent=2, default=str)
        print(json.dumps(report, indent=2, default=str))
        return report


def main():
    if len(sys.argv) < 3:
        print("Usage: agent.py <artifact_type> <path>")
        print("  artifact_type: mft|prefetch|lnk|jumplist|shellbag")
        sys.exit(1)
    agent = EZToolsAgent()
    atype = sys.argv[1]
    path = sys.argv[2]
    dispatch = {"mft": agent.parse_mft, "prefetch": agent.parse_prefetch,
                "lnk": agent.parse_lnk_files, "jumplist": agent.parse_jump_lists,
                "shellbag": agent.parse_shellbags}
    fn = dispatch.get(atype)
    if fn:
        fn(path)
    agent.generate_report()


if __name__ == "__main__":
    main()
process.py12.2 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""
EZ Tools Forensic Artifact Processor

Automates the execution of Eric Zimmerman's tools against collected
forensic artifacts and generates consolidated analysis reports.
"""

import subprocess
import csv
import os
import sys
import json
import hashlib
from pathlib import Path
from datetime import datetime
from collections import defaultdict


class EZToolsProcessor:
    """Orchestrates EZ Tools processing against forensic artifact collections."""

    def __init__(self, ez_tools_path: str, evidence_path: str, output_path: str):
        self.ez_tools_path = Path(ez_tools_path)
        self.evidence_path = Path(evidence_path)
        self.output_path = Path(output_path)
        self.output_path.mkdir(parents=True, exist_ok=True)
        self.results = {}
        self.tools = {
            "MFTECmd": self.ez_tools_path / "MFTECmd.exe",
            "PECmd": self.ez_tools_path / "PECmd.exe",
            "LECmd": self.ez_tools_path / "LECmd.exe",
            "JLECmd": self.ez_tools_path / "JLECmd.exe",
            "SBECmd": self.ez_tools_path / "SBECmd.exe",
            "EvtxECmd": self.ez_tools_path / "EvtxECmd.exe",
            "RECmd": self.ez_tools_path / "RECmd.exe",
            "RBCmd": self.ez_tools_path / "RBCmd.exe",
            "AmcacheParser": self.ez_tools_path / "AmcacheParser.exe",
            "AppCompatCacheParser": self.ez_tools_path / "AppCompatCacheParser.exe",
        }

    def verify_tools(self) -> dict:
        """Verify which EZ Tools are available on the system."""
        availability = {}
        for name, path in self.tools.items():
            availability[name] = path.exists()
        return availability

    def run_tool(self, tool_name: str, args: list) -> dict:
        """Execute an EZ Tool with given arguments."""
        tool_path = self.tools.get(tool_name)
        if not tool_path or not tool_path.exists():
            return {"status": "error", "message": f"{tool_name} not found at {tool_path}"}

        cmd = [str(tool_path)] + args
        try:
            result = subprocess.run(
                cmd,
                capture_output=True,
                text=True,
                timeout=300
            )
            return {
                "status": "success" if result.returncode == 0 else "error",
                "stdout": result.stdout,
                "stderr": result.stderr,
                "returncode": result.returncode
            }
        except subprocess.TimeoutExpired:
            return {"status": "error", "message": f"{tool_name} timed out after 300 seconds"}
        except Exception as e:
            return {"status": "error", "message": str(e)}

    def process_mft(self, mft_path: str = None) -> dict:
        """Parse the $MFT file with MFTECmd."""
        if mft_path is None:
            mft_candidates = list(self.evidence_path.rglob("$MFT"))
            if not mft_candidates:
                return {"status": "skipped", "message": "$MFT not found"}
            mft_path = str(mft_candidates[0])

        output_file = "MFT_output.csv"
        args = ["-f", mft_path, "--csv", str(self.output_path), "--csvf", output_file]
        result = self.run_tool("MFTECmd", args)
        result["output_file"] = str(self.output_path / output_file)
        self.results["MFT"] = result
        return result

    def process_usn_journal(self, journal_path: str = None) -> dict:
        """Parse the USN Journal ($J) with MFTECmd."""
        if journal_path is None:
            j_candidates = list(self.evidence_path.rglob("$J"))
            if not j_candidates:
                return {"status": "skipped", "message": "$J not found"}
            journal_path = str(j_candidates[0])

        output_file = "USNJournal_output.csv"
        args = ["-f", journal_path, "--csv", str(self.output_path), "--csvf", output_file]
        result = self.run_tool("MFTECmd", args)
        result["output_file"] = str(self.output_path / output_file)
        self.results["USNJournal"] = result
        return result

    def process_prefetch(self, prefetch_dir: str = None) -> dict:
        """Parse Prefetch files with PECmd."""
        if prefetch_dir is None:
            pf_candidates = list(self.evidence_path.rglob("Prefetch"))
            if not pf_candidates:
                return {"status": "skipped", "message": "Prefetch directory not found"}
            prefetch_dir = str(pf_candidates[0])

        output_file = "Prefetch_output.csv"
        args = ["-d", prefetch_dir, "--csv", str(self.output_path), "--csvf", output_file]
        result = self.run_tool("PECmd", args)
        result["output_file"] = str(self.output_path / output_file)
        self.results["Prefetch"] = result
        return result

    def process_lnk_files(self, lnk_dir: str = None) -> dict:
        """Parse LNK shortcut files with LECmd."""
        if lnk_dir is None:
            recent_candidates = list(self.evidence_path.rglob("Recent"))
            if not recent_candidates:
                return {"status": "skipped", "message": "Recent directory not found"}
            lnk_dir = str(recent_candidates[0])

        output_file = "LNK_output.csv"
        args = ["-d", lnk_dir, "--csv", str(self.output_path), "--csvf", output_file]
        result = self.run_tool("LECmd", args)
        result["output_file"] = str(self.output_path / output_file)
        self.results["LNK"] = result
        return result

    def process_jump_lists(self, jl_dir: str = None) -> dict:
        """Parse Jump List files with JLECmd."""
        if jl_dir is None:
            auto_dest = list(self.evidence_path.rglob("AutomaticDestinations"))
            if not auto_dest:
                return {"status": "skipped", "message": "AutomaticDestinations not found"}
            jl_dir = str(auto_dest[0])

        output_file = "JumpLists_output.csv"
        args = ["-d", jl_dir, "--csv", str(self.output_path), "--csvf", output_file]
        result = self.run_tool("JLECmd", args)
        result["output_file"] = str(self.output_path / output_file)
        self.results["JumpLists"] = result
        return result

    def process_event_logs(self, evtx_dir: str = None) -> dict:
        """Parse Windows Event Logs with EvtxECmd."""
        if evtx_dir is None:
            logs_candidates = list(self.evidence_path.rglob("winevt"))
            if logs_candidates:
                evtx_dir = str(logs_candidates[0] / "Logs")
            else:
                evtx_files = list(self.evidence_path.rglob("*.evtx"))
                if not evtx_files:
                    return {"status": "skipped", "message": "Event logs not found"}
                evtx_dir = str(evtx_files[0].parent)

        output_file = "EventLogs_output.csv"
        args = ["-d", evtx_dir, "--csv", str(self.output_path), "--csvf", output_file]
        result = self.run_tool("EvtxECmd", args)
        result["output_file"] = str(self.output_path / output_file)
        self.results["EventLogs"] = result
        return result

    def process_shellbags(self, registry_dir: str = None) -> dict:
        """Parse Shellbag artifacts with SBECmd."""
        if registry_dir is None:
            registry_dir = str(self.evidence_path)

        output_file = "Shellbags_output.csv"
        args = ["-d", registry_dir, "--csv", str(self.output_path), "--csvf", output_file]
        result = self.run_tool("SBECmd", args)
        result["output_file"] = str(self.output_path / output_file)
        self.results["Shellbags"] = result
        return result

    def process_recycle_bin(self, recycle_dir: str = None) -> dict:
        """Parse Recycle Bin artifacts with RBCmd."""
        if recycle_dir is None:
            rb_candidates = list(self.evidence_path.rglob("$Recycle.Bin"))
            if not rb_candidates:
                return {"status": "skipped", "message": "$Recycle.Bin not found"}
            recycle_dir = str(rb_candidates[0])

        output_file = "RecycleBin_output.csv"
        args = ["-d", recycle_dir, "--csv", str(self.output_path), "--csvf", output_file]
        result = self.run_tool("RBCmd", args)
        result["output_file"] = str(self.output_path / output_file)
        self.results["RecycleBin"] = result
        return result

    def detect_timestomping(self, mft_csv_path: str) -> list:
        """Analyze MFT CSV output to detect timestomping indicators."""
        timestomped = []
        try:
            with open(mft_csv_path, "r", encoding="utf-8-sig") as f:
                reader = csv.DictReader(f)
                for row in reader:
                    si_created = row.get("Created0x10", "")
                    fn_created = row.get("Created0x30", "")
                    if si_created and fn_created:
                        try:
                            si_dt = datetime.fromisoformat(si_created.replace("Z", "+00:00"))
                            fn_dt = datetime.fromisoformat(fn_created.replace("Z", "+00:00"))
                            if si_dt < fn_dt:
                                timestomped.append({
                                    "file": row.get("FileName", "Unknown"),
                                    "entry_number": row.get("EntryNumber", ""),
                                    "si_created": si_created,
                                    "fn_created": fn_created,
                                    "indicator": "$SI Created before $FN Created"
                                })
                        except (ValueError, TypeError):
                            continue
        except FileNotFoundError:
            return [{"error": f"MFT CSV not found: {mft_csv_path}"}]
        return timestomped

    def process_all(self) -> dict:
        """Run all available EZ Tools processors against evidence."""
        print("[*] Starting comprehensive EZ Tools processing...")
        print(f"[*] Evidence path: {self.evidence_path}")
        print(f"[*] Output path: {self.output_path}")

        available = self.verify_tools()
        print(f"[*] Available tools: {sum(v for v in available.values())}/{len(available)}")

        processors = [
            ("MFT", self.process_mft),
            ("USN Journal", self.process_usn_journal),
            ("Prefetch", self.process_prefetch),
            ("LNK Files", self.process_lnk_files),
            ("Jump Lists", self.process_jump_lists),
            ("Event Logs", self.process_event_logs),
            ("Shellbags", self.process_shellbags),
            ("Recycle Bin", self.process_recycle_bin),
        ]

        for name, processor in processors:
            print(f"[*] Processing {name}...")
            result = processor()
            status = result.get("status", "unknown")
            print(f"    [{status.upper()}] {name}")

        return self.results

    def generate_report(self) -> str:
        """Generate a summary report of all processing results."""
        report = {
            "timestamp": datetime.now().isoformat(),
            "evidence_path": str(self.evidence_path),
            "output_path": str(self.output_path),
            "results": {}
        }

        for artifact, result in self.results.items():
            report["results"][artifact] = {
                "status": result.get("status", "unknown"),
                "output_file": result.get("output_file", ""),
            }
            output_file = result.get("output_file", "")
            if output_file and os.path.exists(output_file):
                file_size = os.path.getsize(output_file)
                report["results"][artifact]["output_size_bytes"] = file_size
                with open(output_file, "rb") as f:
                    report["results"][artifact]["sha256"] = hashlib.sha256(f.read()).hexdigest()

        report_path = self.output_path / "processing_report.json"
        with open(report_path, "w") as f:
            json.dump(report, f, indent=2)

        print(f"\n[*] Report saved to: {report_path}")
        return str(report_path)


def main():
    if len(sys.argv) < 4:
        print("Usage: python process.py <ez_tools_path> <evidence_path> <output_path>")
        print("Example: python process.py C:\\Tools\\EZTools C:\\Cases\\Evidence C:\\Cases\\Output")
        sys.exit(1)

    ez_tools_path = sys.argv[1]
    evidence_path = sys.argv[2]
    output_path = sys.argv[3]

    processor = EZToolsProcessor(ez_tools_path, evidence_path, output_path)
    processor.process_all()
    processor.generate_report()


if __name__ == "__main__":
    main()

Assets 1

template.mdtext/markdown · 1.7 KB
Keep exploring