#!/usr/bin/env python3 """Agent for analyzing NTFS slack space and file system artifacts.""" import os import json import struct import argparse import subprocess from datetime import datetime, timedelta from pathlib import Path def parse_mft_with_analyzeMFT(mft_path, output_csv): """Parse MFT using analyzeMFT and return deleted/timestomped files.""" cmd = ["analyzeMFT.py", "-f", mft_path, "-o", output_csv, "-c"] subprocess.run(cmd, check=True, timeout=120) return output_csv def extract_slack_space(image_path, offset, output_path): """Extract slack space from a disk image using blkls from The Sleuth Kit.""" cmd = ["blkls", "-s", "-o", str(offset), image_path] with open(output_path, "wb") as out: subprocess.run(cmd, stdout=out, check=True, timeout=120) return output_path def search_slack_keywords(slack_path, keywords=None): """Search extracted slack space for forensic keywords.""" if keywords is None: keywords = ["password", "secret", "confidential", "credit card", "ssn"] hits = [] with open(slack_path, "rb") as f: data = f.read() for kw in keywords: kw_bytes = kw.encode("utf-8") start = 0 while True: idx = data.find(kw_bytes, start) if idx == -1: break context = data[max(0, idx - 20):idx + len(kw_bytes) + 20] hits.append({ "keyword": kw, "offset": idx, "context": context.decode("utf-8", errors="replace"), }) start = idx + 1 return hits def parse_usn_journal(usn_path): """Parse NTFS USN Change Journal ($UsnJrnl:$J) records.""" REASON_FLAGS = { 0x01: "DATA_OVERWRITE", 0x02: "DATA_EXTEND", 0x04: "DATA_TRUNCATION", 0x100: "FILE_CREATE", 0x200: "FILE_DELETE", 0x400: "EA_CHANGE", 0x800: "SECURITY_CHANGE", 0x1000: "RENAME_OLD_NAME", 0x2000: "RENAME_NEW_NAME", 0x80000000: "CLOSE", } records = [] with open(usn_path, "rb") as f: data = f.read() offset = 0 while offset < len(data) - 8: rec_len = struct.unpack_from(" 65536 or offset + rec_len > len(data): offset += 8 continue major = struct.unpack_from("