anycoder-a08b208a / index.html
moddux's picture
Upload folder using huggingface_hub
b27c811 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PROJECT: I.O.S. D.F.I.R. // ENHANCEMENT SKELETON</title>
<!-- Import Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link
href="https://fonts.googleapis.com/css2?family=Rajdhani:wght@400;500;600;700&family=Share+Tech+Mono&family=Space+Mono:ital,wght@0,400;0,700;1,400&display=swap"
rel="stylesheet">
<style>
/* --- CSS VARIABLES & RESET --- */
:root {
--bg-dark: #050608;
--bg-panel: #0e1016;
--bg-card: #14161f;
--border-subtle: #2a2f3d;
--text-main: #e0e6ed;
--text-muted: #94a3b8;
--font-body: 'Rajdhani', sans-serif;
--font-mono: 'Share Tech Mono', monospace;
--font-meta: 'Space Mono', monospace;
/* Semantic Colors */
--cyan: #00f0ff;
--green: #00ff9d;
--orange: #ff9d00;
--gold: #ffd700;
--purple: #bd00ff;
--red: #ff0055;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background-color: var(--bg-dark);
color: var(--text-main);
font-family: var(--font-body);
line-height: 1.6;
overflow-x: hidden;
font-size: 16px;
}
/* --- BACKGROUND GRID --- */
.bg-grid {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100vh;
z-index: -1;
background-image:
linear-gradient(rgba(0, 240, 255, 0.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(0, 240, 255, 0.03) 1px, transparent 1px);
background-size: 40px 40px;
pointer-events: none;
}
/* --- TYPOGRAPHY UTILS --- */
h1,
h2,
h3,
h4 {
text-transform: uppercase;
letter-spacing: 1px;
}
.mono {
font-family: var(--font-mono);
}
.meta {
font-family: var(--font-meta);
font-size: 0.85rem;
}
.text-cyan {
color: var(--cyan);
}
.text-green {
color: var(--green);
}
.text-orange {
color: var(--orange);
}
.text-gold {
color: var(--gold);
}
.text-purple {
color: var(--purple);
}
.text-red {
color: var(--red);
}
/* --- HEADER --- */
.header {
padding: 3rem 2rem 2rem;
border-bottom: 1px solid var(--border-subtle);
background: radial-gradient(circle at top, rgba(0, 240, 255, 0.05), transparent 70%);
}
.header-top {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
font-family: var(--font-meta);
font-size: 0.75rem;
color: var(--text-muted);
}
.header-main {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.header-title {
font-size: 3rem;
font-weight: 700;
line-height: 1;
}
.header-title span {
color: var(--cyan);
text-shadow: 0 0 10px rgba(0, 240, 255, 0.4);
}
.header-subtitle {
font-size: 1.2rem;
color: var(--text-muted);
max-width: 800px;
}
.badges {
display: flex;
gap: 0.5rem;
margin-top: 1rem;
flex-wrap: wrap;
}
.badge {
font-family: var(--font-mono);
font-size: 0.7rem;
padding: 2px 8px;
border: 1px solid var(--border-subtle);
border-radius: 2px;
text-transform: uppercase;
}
.badge.cyan {
border-color: var(--cyan);
color: var(--cyan);
}
.badge.gold {
border-color: var(--gold);
color: var(--gold);
}
.badge.green {
border-color: var(--green);
color: var(--green);
}
/* --- STICKY NAV --- */
.nav-container {
position: sticky;
top: 0;
z-index: 100;
background: rgba(5, 6, 8, 0.85);
backdrop-filter: blur(10px);
border-bottom: 1px solid var(--border-subtle);
padding: 0.5rem 2rem;
overflow-x: auto;
white-space: nowrap;
-ms-overflow-style: none;
scrollbar-width: none;
}
.nav-container::-webkit-scrollbar {
display: none;
}
.nav-btn {
background: transparent;
border: none;
color: var(--text-muted);
font-family: var(--font-mono);
font-size: 0.9rem;
padding: 0.8rem 1.2rem;
cursor: pointer;
text-transform: uppercase;
transition: all 0.2s ease;
border-bottom: 2px solid transparent;
}
.nav-btn:hover {
color: var(--text-main);
background: rgba(255, 255, 255, 0.03);
}
.nav-btn.active {
color: var(--cyan);
border-bottom-color: var(--cyan);
text-shadow: 0 0 8px rgba(0, 240, 255, 0.3);
}
/* --- CONTENT LAYOUT --- */
.container {
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
}
.section {
display: none;
animation: fadeIn 0.4s ease-out;
}
.section.active {
display: block;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.section-header {
margin-bottom: 2rem;
border-left: 4px solid var(--cyan);
padding-left: 1rem;
}
.section-title {
font-size: 2rem;
color: var(--text-main);
}
.section-desc {
font-family: var(--font-meta);
color: var(--text-muted);
font-size: 0.9rem;
}
/* --- CARDS --- */
.card {
background: var(--bg-card);
border: 1px solid var(--border-subtle);
padding: 1.5rem;
position: relative;
transition: transform 0.2s;
}
.card:hover {
border-color: rgba(255, 255, 255, 0.1);
}
.card::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 3px;
}
.card.cyan::before {
background: var(--cyan);
}
.card.green::before {
background: var(--green);
}
.card.orange::before {
background: var(--orange);
}
.card.gold::before {
background: var(--gold);
}
.card.purple::before {
background: var(--purple);
}
.card.red::before {
background: var(--red);
}
.card-title {
font-family: var(--font-mono);
font-size: 0.85rem;
text-transform: uppercase;
margin-bottom: 1rem;
display: flex;
justify-content: space-between;
align-items: center;
}
/* --- GRIDS --- */
.grid-2 {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1.5rem;
}
.grid-3 {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1.5rem;
}
.grid-4 {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 1.5rem;
}
@media (max-width: 900px) {
.grid-2,
.grid-3,
.grid-4 {
grid-template-columns: 1fr;
}
.header-title {
font-size: 2.2rem;
}
}
/* --- ROLE CARDS --- */
.role-card {
overflow: hidden;
position: relative;
}
.role-watermark {
position: absolute;
bottom: -10px;
right: -10px;
font-size: 5rem;
font-weight: 700;
opacity: 0.05;
font-family: var(--font-mono);
line-height: 1;
pointer-events: none;
}
.role-tag {
font-family: var(--font-meta);
font-size: 0.7rem;
color: var(--cyan);
margin-bottom: 0.5rem;
display: block;
}
.role-name {
font-size: 1.4rem;
font-weight: 600;
margin-bottom: 0.2rem;
}
.role-handle {
font-family: var(--font-mono);
color: var(--text-muted);
font-size: 0.8rem;
margin-bottom: 1rem;
}
.role-list {
list-style: none;
margin-top: 1rem;
}
.role-list li {
font-size: 0.9rem;
margin-bottom: 0.4rem;
padding-left: 1rem;
position: relative;
}
.role-list li::before {
content: '>';
position: absolute;
left: 0;
color: var(--text-muted);
}
/* --- SIGNALS & TAGS --- */
.signal {
display: inline-block;
font-family: var(--font-mono);
font-size: 0.65rem;
padding: 1px 4px;
margin-right: 4px;
border-radius: 2px;
vertical-align: middle;
}
.sig-ref {
color: var(--cyan);
border: 1px solid var(--cyan);
}
.sig-block {
color: var(--red);
border: 1px solid var(--red);
}
.sig-done {
color: var(--green);
border: 1px solid var(--green);
}
.sig-gate {
color: var(--gold);
border: 1px solid var(--gold);
}
/* --- CODE BLOCKS --- */
.code-block {
background: #000;
border: 1px solid var(--border-subtle);
padding: 1rem;
font-family: 'Space Mono', monospace;
font-size: 0.85rem;
overflow-x: auto;
color: #d4d4d4;
max-height: 600px;
overflow-y: auto;
white-space: pre;
}
/* Python Syntax Highlighting */
.py-kw { color: #569cd6; }
.py-fn { color: #dcdcaa; }
.py-cmt { color: #6a9955; }
.py-type { color: #4ec9b0; }
.py-str { color: #ce9178; }
.py-dec { color: #c586c0; }
/* --- DIAGRAMS (CSS ONLY) --- */
.diagram-box {
display: flex;
align-items: center;
justify-content: center;
gap: 1rem;
padding: 2rem;
background: rgba(0, 0, 0, 0.2);
border: 1px dashed var(--border-subtle);
}
.diag-node {
border: 1px solid var(--cyan);
background: rgba(0, 240, 255, 0.1);
padding: 0.5rem 1rem;
font-family: var(--font-mono);
font-size: 0.8rem;
}
.diag-arrow {
color: var(--cyan);
font-size: 1.2rem;
}
/* --- ANYCODER LINK --- */
.anycoder-link {
font-family: var(--font-mono);
font-size: 0.7rem;
color: var(--text-muted);
text-decoration: none;
border: 1px solid var(--border-subtle);
padding: 2px 6px;
border-radius: 4px;
transition: color 0.2s;
}
.anycoder-link:hover {
color: var(--cyan);
border-color: var(--cyan);
}
</style>
</head>
<body>
<!-- Background Grid -->
<div class="bg-grid"></div>
<!-- Header -->
<header class="header">
<div class="header-top">
<span>SYS_REF: DFIR_IOS_V1.0</span>
<span>
<a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link">Built with anycoder</a>
</span>
</div>
<div class="header-main">
<h1 class="header-title">Project <span>DFIR</span> // iOS ENHANCEMENT</h1>
<p class="header-subtitle">Structured SQLite-backed iOS backup extractor pipeline. Artifact hashing, logging, and extensible parser modules.</p>
<div class="badges">
<span class="badge cyan">STATUS: SKELETON</span>
<span class="badge gold">LANG: PYTHON3</span>
<span class="badge green">LOGGING: ENABLED</span>
<span class="badge">HASHING: SHA256</span>
</div>
</div>
</header>
<!-- Navigation -->
<nav class="nav-container">
<button class="nav-btn active" onclick="showSection('overview')">Manifesto</button>
<button class="nav-btn" onclick="showSection('modules')">Modules</button>
<button class="nav-btn" onclick="showSection('pipeline')">Pipeline</button>
<button class="nav-btn" onclick="showSection('schema')">Data Structs</button>
<button class="nav-btn" onclick="showSection('source')">Source Code</button>
</nav>
<!-- Main Content -->
<div class="container">
<!-- SECTION: OVERVIEW -->
<div id="overview" class="section active">
<div class="section-header">
<h2 class="section-title">System Manifesto</h2>
<span class="section-desc">:: EXEC_SUMMARY :: Purpose and operational goals of the DFIR skeleton.</span>
</div>
<div class="grid-2">
<div class="card cyan">
<div class="card-title">
<span>Primary Objective</span>
<span class="signal sig-gate">CORE</span>
</div>
<p>Upgrade simple SQLite-backed iOS backup extractors into a structured DFIR pipeline. Preserve analyst visibility, logging, and artifact hashing.</p>
</div>
<div class="card gold">
<div class="card-title">
<span>Current Status</span>
<span class="signal sig-ref">DEV</span>
</div>
<p>This file is intentionally verbose. Several modules are scaffolded as placeholders (Stubs) for future parser implementation.</p>
</div>
</div>
<div style="margin-top: 2rem;">
<div class="card purple">
<div class="card-title">
<span>Operational Flow</span>
<span class="signal sig-ref">REF</span>
</div>
<div class="diagram-box">
<div class="diag-node">CLI_ARGS</div>
<div class="diag-arrow"></div>
<div class="diag-node" style="border-color: var(--purple);">MANIFEST_DB</div>
<div class="diag-arrow"></div>
<div class="diag-node" style="border-color: var(--green);">PARSER_MODULES</div>
<div class="diag-arrow"></div>
<div class="diag-node">TIMELINE_CSV</div>
</div>
</div>
</div>
</div>
<!-- SECTION: MODULES -->
<div id="modules" class="section">
<div class="section-header">
<h2 class="section-title">Parser Modules</h2>
<span class="section-desc">:: AGENTS :: Specialized extraction subroutines.</span>
</div>
<div class="grid-3">
<!-- Module 1 -->
<div class="card cyan role-card">
<div class="role-watermark">CORE</div>
<span class="role-tag">CONTACTS</span>
<h3 class="role-name">AddressBook Parser</h3>
<div class="role-handle">export_contacts()</div>
<ul class="role-list">
<li>Modern & Legacy Schema</li>
<li>Phone Normalization</li>
<li>CSV/JSON Export</li>
</ul>
</div>
<!-- Module 2 -->
<div class="card green role-card">
<div class="role-watermark">CORE</div>
<span class="role-tag">MESSAGING</span>
<h3 class="role-name">SMS / iMessage</h3>
<div class="role-handle">extract_sms()</div>
<ul class="role-list">
<li>Handle Resolution</li>
<li>AttributedBody Stub</li>
<li>Timeline Event Gen</li>
</ul>
</div>
<!-- Module 3 -->
<div class="card orange role-card">
<div class="role-watermark">GEO</div>
<span class="role-tag">LOCATION</span>
<h3 class="role-name">LocationD / Cache</h3>
<div class="role-handle">extract_locations()</div>
<ul class="role-list">
<li>RTCL Location MO</li>
<li>Visit MO Parsing</li>
<li>Cell Tower Logs</li>
</ul>
</div>
<!-- Module 4 -->
<div class="card red role-card">
<div class="role-watermark">STUB</div>
<span class="role-tag">FUTURE</span>
<h3 class="role-name">Safari / Notes</h3>
<div class="role-handle">extract_safari_stub()</div>
<ul class="role-list">
<li>History / Downloads</li>
<li>Rich Notes</li>
<li>App Containers</li>
</ul>
</div>
</div>
</div>
<!-- SECTION: PIPELINE -->
<div id="pipeline" class="section">
<div class="section-header">
<h2 class="section-title">Execution Pipeline</h2>
<span class="section-desc">:: LOGIC :: The `run()` orchestration sequence.</span>
</div>
<div class="timeline">
<div class="timeline-item done">
<div class="timeline-marker"></div>
<div class="timeline-date">STEP 1</div>
<div class="timeline-title">Initialization</div>
<div class="card" style="margin-top: 0.5rem; border: none; background: rgba(255,255,255,0.02); padding: 1rem;">
<span class="signal sig-done">DONE</span> Setup Logging. Validate Backup Root. Parse CLI Arguments.
</div>
</div>
<div class="timeline-item active">
<div class="timeline-marker"></div>
<div class="timeline-date">STEP 2</div>
<div class="timeline-title">Manifest Indexing</div>
<div class="card" style="margin-top: 0.5rem; border: none; background: rgba(255,255,255,0.02); padding: 1rem;">
<span class="signal sig-gate">ACTIVE</span> Load `Manifest.db`. Search for `sms.db`, `AddressBook`, and Location artifacts.
</div>
</div>
<div class="timeline-item next">
<div class="timeline-marker"></div>
<div class="timeline-date">STEP 3</div>
<div class="timeline-title">Extraction & Export</div>
<div class="card" style="margin-top: 0.5rem; border: none; background: rgba(255,255,255,0.02); padding: 1rem;">
<span class="signal sig-ref">NEXT</span> Run Parser Modules. Generate CSV/JSON. Build Unified Timeline.
</div>
</div>
</div>
</div>
<!-- SECTION: SCHEMA -->
<div id="schema" class="section">
<div class="section-header">
<h2 class="section-title">Data Structures</h2>
<span class="section-desc">:: STRUCT :: Python Dataclasses used for typing.</span>
</div>
<div class="card cyan">
<div class="card-title">Config & Metadata</div>
<div class="code-block" style="max-height: 400px;">
<span class="py-dec">@dataclass</span>
<span class="py-kw">class</span> <span class="py-type">CaseMetadata</span>:
case_name: <span class="py-type">str</span> = <span class="py-str">"UNSPECIFIED_CASE"</span>
examiner: <span class="py-type">str</span> = <span class="py-str">"UNSPECIFIED_EXAMINER"</span>
evidence_id: <span class="py-type">str</span> = <span class="py-str">"UNSPECIFIED_EVIDENCE"</span>
<span class="py-dec">@dataclass</span>
<span class="py-kw">class</span> <span class="py-type">AppConfig</span>:
backup_root: <span class="py-type">Path</span>
manifest_db: <span class="py-type">Path</span>
output_root: <span class="py-type">Path</span>
query_terms: <span class="py-type">List</span>[<span class="py-type">str</span>] = field(default_factory=<span class="py-type">list</span>)
verbose: <span class="py-type">bool</span> = <span class="py-kw">False</span>
hash_exports: <span class="py-type">bool</span> = <span class="py-kw">True</span>
copy_raw_files: <span class="py-type">bool</span> = <span class="py-kw">True</span>
export_csv: <span class="py-type">bool</span> = <span class="py-kw">True</span>
export_jsonl: <span class="py-type">bool</span> = <span class="py-kw">True</span>
</div>
</div>
<div class="card purple" style="margin-top: 1.5rem;">
<div class="card-title">Timeline Event Model</div>
<div class="code-block" style="max-height: 300px;">
<span class="py-dec">@dataclass</span>
<span class="py-kw">class</span> <span class="py-type">TimelineEvent</span>:
timestamp: <span class="py-type">Optional</span>[<span class="py-type">str</span>]
artifact_type: <span class="py-type">str</span>
source_file: <span class="py-type">str</span>
summary: <span class="py-type">str</span>
attributes: <span class="py-type">Dict</span>[<span class="py-type">str</span>, <span class="py-type">Any</span>] = field(default_factory=<span class="py-type">dict</span>)
</div>
</div>
</div>
<!-- SECTION: SOURCE -->
<div id="source" class="section">
<div class="section-header">
<h2 class="section-title">Source Implementation</h2>
<span class="section-desc">:: CODE :: The complete Python script logic.</span>
</div>
<div class="card gold">
<div class="card-title">
<span>dfir_ios_enhancement.py</span>
<span class="signal sig-done">READY</span>
</div>
<div class="code-block" style="font-size: 0.75rem; line-height: 1.4;">
<span class="py-cmt">#!/usr/bin/env python3</span>
<span class="py-cmt">""" DFIR iOS Backup Enhancement Skeleton """</span>
<span class="py-kw">from</span> __future__ <span class="py-kw">import</span> annotations
<span class="py-kw">import</span> argparse, csv, dataclasses, hashlib, json, logging, os, shutil, sqlite3, sys, traceback
<span class="py-kw">from</span> dataclasses <span class="py-kw">import</span> dataclass, field
<span class="py-kw">from</span> datetime <span class="py-kw">import</span> datetime, timedelta, timezone
<span class="py-kw">from</span> pathlib <span class="py-kw">import</span> Path
<span class="py-kw">from</span> typing <span class="py-kw">import</span> Any, Dict, Iterable, List, Optional, Tuple
<span class="py-type">APPLE_EPOCH</span> = datetime(<span class="py-str">2001</span>, <span class="py-str">1</span>, <span class="py-str">1</span>, tzinfo=timezone.utc)
<span class="py-cmt"># ============================================================</span>
<span class="py-cmt"># CONFIG / DATA MODELS</span>
<span class="py-cmt"># ============================================================</span>
<span class="py-dec">@dataclass</span>
<span class="py-kw">class</span> <span class="py-type">CaseMetadata</span>:
case_name: <span class="py-type">str</span> = <span class="py-str">"UNSPECIFIED_CASE"</span>
examiner: <span class="py-type">str</span> = <span class="py-str">"UNSPECIFIED_EXAMINER"</span>
evidence_id: <span class="py-type">str</span> = <span class="py-str">"UNSPECIFIED_EVIDENCE"</span>
notes: <span class="py-type">str</span> = <span class="py-str">""</span>
<span class="py-dec">@dataclass</span>
<span class="py-kw">class</span> <span class="py-type">AppConfig</span>:
backup_root: <span class="py-type">Path</span>
manifest_db: <span class="py-type">Path</span>
output_root: <span class="py-type">Path</span>
query_terms: <span class="py-type">List</span>[<span class="py-type">str</span>] = field(default_factory=<span class="py-type">list</span>)
verbose: <span class="py-type">bool</span> = <span class="py-kw">False</span>
hash_exports: <span class="py-type">bool</span> = <span class="py-kw">True</span>
copy_raw_files: <span class="py-type">bool</span> = <span class="py-kw">True</span>
export_csv: <span class="py-type">bool</span> = <span class="py-kw">True</span>
export_jsonl: <span class="py-type">bool</span> = <span class="py-kw">True</span>
export_kml: <span class="py-type">bool</span> = <span class="py-kw">False</span>
export_geojson: <span class="py-type">bool</span> = <span class="py-kw">False</span>
case: <span class="py-type">CaseMetadata</span> = field(default_factory=<span class="py-type">CaseMetadata</span>)
<span class="py-dec">@dataclass</span>
<span class="py-kw">class</span> <span class="py-type">LocatedFile</span>:
file_id: <span class="py-type">str</span>
relative_path: <span class="py-type">str</span>
domain: <span class="py-type">Optional</span>[<span class="py-type">str</span>]
source_path: <span class="py-type">Path</span>
<span class="py-dec">@dataclass</span>
<span class="py-kw">class</span> <span class="py-type">TimelineEvent</span>:
timestamp: <span class="py-type">Optional</span>[<span class="py-type">str</span>]
artifact_type: <span class="py-type">str</span>
source_file: <span class="py-type">str</span>
summary: <span class="py-type">str</span>
attributes: <span class="py-type">Dict</span>[<span class="py-type">str</span>, <span class="py-type">Any</span>] = field(default_factory=<span class="py-type">dict</span>)
<span class="py-cmt"># ============================================================</span>
<span class="py-cmt"># LOGGING</span>
<span class="py-cmt"># ============================================================</span>
<span class="py-kw">def</span> <span class="py-fn">setup_logging</span>(output_root: <span class="py-type">Path</span>, verbose: <span class="py-type">bool</span> = <span class="py-kw">False</span>) -> logging.<span class="py-type">Logger</span>:
output_root.mkdir(parents=<span class="py-kw">True</span>, exist_ok=<span class="py-kw">True</span>)
log_path = output_root / <span class="py-str">"dfir_run.log"</span>
logger = logging.getLogger(<span class="py-str">"dfir_ios"</span>)
logger.setLevel(logging.DEBUG <span class="py-kw">if</span> verbose <span class="py-kw">else</span> logging.INFO)
logger.handlers.clear()
fmt = logging.Formatter(<span class="py-str">"%(asctime)s | %(levelname)s | %(message)s"</span>)
fh = logging.FileHandler(log_path, encoding=<span class="py-str">"utf-8"</span>)
fh.setLevel(logging.DEBUG)
fh.setFormatter(fmt)
logger.addHandler(fh)
sh = logging.StreamHandler(sys.stdout)
sh.setLevel(logging.DEBUG <span class="py-kw">if</span> verbose <span class="py-kw">else</span> logging.INFO)
sh.setFormatter(fmt)
logger.addHandler(sh)
logger.debug(<span class="py-str">"Logging initialized"</span>)
<span class="py-kw">return</span> logger
<span class="py-cmt"># ============================================================</span>
<span class="py-cmt"># UTILS</span>
<span class="py-cmt"># ============================================================</span>
<span class="py-kw">def</span> <span class="py-fn">apple_time_to_datetime</span>(ts: <span class="py-type">Any</span>) -> <span class="py-type">Optional</span>[datetime]:
<span class="py-kw">if</span> ts <span class="py-kw">is</span> <span class="py-kw">None</span>: <span class="py-kw">return</span> <span class="py-kw">None</span>
<span class="py-kw">try</span>:
ts = <span class="py-type">float</span>(ts)
<span class="py-kw">if</span> ts > <span class="py-str">1e12</span>: ts = ts / <span class="py-str">1e9</span>
<span class="py-kw">return</span> <span class="py-type">APPLE_EPOCH</span> + timedelta(seconds=ts)
<span class="py-kw">except</span> <span class="py-type">Exception</span>: <span class="py-kw">return</span> <span class="py-kw">None</span>
<span class="py-kw">def</span> <span class="py-fn">normalize_phone</span>(phone: <span class="py-type">Optional</span>[<span class="py-type">str</span>]) -> <span class="py-type">Optional</span>[<span class="py-type">str</span>]:
<span class="py-kw">if</span> <span class="py-kw">not</span> phone: <span class="py-kw">return</span> phone
value = <span class="py-type">str</span>(phone).replace(<span class="py-str">"+1"</span>, <span class="py-str">""</span>).replace(<span class="py-str">" "</span>, <span class="py-str">""</span>).replace(<span class="py-str">"-"</span>, <span class="py-str">""</span>).replace(<span class="py-str">"("</span>, <span class="py-str">""</span>).replace(<span class="py-str">")"</span>, <span class="py-str">""</span>)
<span class="py-kw">return</span> value.strip() <span class="py-kw">or</span> <span class="py-kw">None</span>
<span class="py-kw">def</span> <span class="py-fn">sha256_file</span>(path: <span class="py-type">Path</span>, chunk_size: <span class="py-type">int</span> = <span class="py-str">1024</span> * <span class="py-str">1024</span>) -> <span class="py-type">str</span>:
h = hashlib.sha256()
<span class="py-kw">with</span> path.open(<span class="py-str">"rb"</span>) <span class="py-kw">as</span> f:
<span class="py-kw">while</span> <span class="py-kw">True</span>:
chunk = f.read(chunk_size)
<span class="py-kw">if</span> <span class="py-kw">not</span> chunk: <span class="py-kw">break</span>
h.update(chunk)
<span class="py-kw">return</span> h.hexdigest()
<span class="py-cmt"># ... [Manifest Access Layer and Extraction Logic Truncated for Brevity in View] ...</span>
<span class="py-cmt"># ============================================================</span>
<span class="py-cmt"># MAIN ORCHESTRATION</span>
<span class="py-cmt"># ============================================================</span>
<span class="py-kw">def</span> <span class="py-fn">run</span>(cfg: <span class="py-type">AppConfig</span>) -> <span class="py-type">int</span>:
logger = setup_logging(cfg.output_root, cfg.verbose)
logger.info(<span class="py-str">"Starting DFIR iOS backup extraction"</span>)