Spaces:
Running
Running
File size: 3,020 Bytes
0da308d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | """
Utility functions for DFIR iOS Enhancement Application
"""
from datetime import datetime, timedelta, timezone
from pathlib import Path
from typing import Any, Optional
import hashlib
import sqlite3
# Apple epoch (January 1, 2001)
APPLE_EPOCH = datetime(2001, 1, 1, tzinfo=timezone.utc)
def apple_time_to_datetime(ts: Any) -> Optional[datetime]:
"""
Convert Apple timestamp to Python datetime.
Apple uses a custom epoch starting from January 1, 2001.
Args:
ts: Apple timestamp (can be seconds or nanoseconds)
Returns:
datetime object or None if conversion fails
"""
if ts is None:
return None
try:
ts = float(ts)
# Handle nanoseconds
if ts > 1e12:
ts = ts / 1e9
return APPLE_EPOCH + timedelta(seconds=ts)
except Exception:
return None
def normalize_phone(phone: Optional[str]) -> Optional[str]:
"""
Normalize phone number by removing common formatting characters.
Args:
phone: Raw phone number string
Returns:
Normalized phone number or None
"""
if not phone:
return phone
value = str(phone).replace("+1", "").replace(" ", "").replace("-", "")
value = value.replace("(", "").replace(")", "")
return value.strip() or None
def sha256_file(path: Path, chunk_size: int = 1024 * 1024) -> str:
"""
Calculate SHA256 hash of a file.
Args:
path: Path to the file
chunk_size: Size of chunks to read (default 1MB)
Returns:
Hexadecimal SHA256 hash string
"""
h = hashlib.sha256()
with path.open("rb") as f:
while True:
chunk = f.read(chunk_size)
if not chunk:
break
h.update(chunk)
return h.hexdigest()
def get_manifest_files(manifest_db: Path) -> list:
"""
Query Manifest.db for file entries.
Args:
manifest_db: Path to Manifest.db
Returns:
List of file entries from manifest
"""
if not manifest_db.exists():
return []
conn = sqlite3.connect(str(manifest_db))
conn.row_factory = sqlite3.Row
cursor = conn.cursor()
try:
cursor.execute("""
SELECT fileID, domain, relativePath, flags, file
FROM Files
WHERE relativePath IS NOT NULL
""")
return [dict(row) for row in cursor.fetchall()]
except Exception:
return []
finally:
conn.close()
def find_artifact_in_manifest(manifest_files: list, artifact_name: str) -> Optional[dict]:
"""
Find a specific artifact in manifest files.
Args:
manifest_files: List of manifest file entries
artifact_name: Name of artifact to find
Returns:
File entry dict or None
"""
for file_entry in manifest_files:
if artifact_name in (file_entry.get('relativePath') or ''):
return file_entry
return None |