Class Overview
This isn’t just “how to use a computer.” This lab dives into disk images, hash verification, Python automation, and controlled execution. Students learn how real investigators and engineers work with data, verify integrity, and build tools that don’t just “run”—they prove.
Digital Forensics Module
In this unit, students explore:
- Virtual disk images (
Escobar.vdi) – mounting, browsing, and verifying sectors - Compressed evidence sets (
Tim.zip) – extracting, hashing, and cataloging files - MD5 / SHA hashing – why integrity checks matter in investigations
- Basic chain-of-custody concepts for digital artifacts
The goal: treat files like evidence, not just “stuff on a drive.”
Python Script: MD5-Verified Launcher Lab Demo
Students walk through a Python script that:
- Calculates an MD5 hash of a target file
- Checks the hash against a
hash.dbdatabase - Tracks how many times the file has been launched
- Only allows execution if the hash is approved
Example script used in class:
import hashlib
import sqlite3
import subprocess
import os
import sys
from datetime import datetime
HASH_DB = r"C:\Lab\hash.db"
RUNCOUNT_DB = r"C:\Lab\runcount.db"
KILL_SCRIPT = r"C:\Lab\kill.py"
def md5_of_file(path):
h = hashlib.md5()
with open(path, "rb") as f:
for chunk in iter(lambda: f.read(4096), b""):
h.update(chunk)
return h.hexdigest()
def check_hash_in_db(md5_value):
conn = sqlite3.connect(HASH_DB)
cur = conn.cursor()
cur.execute("SELECT COUNT(*) FROM hashes WHERE md5 = ?", (md5_value,))
result = cur.fetchone()[0]
conn.close()
return result > 0
def increment_run_count(file_path):
conn = sqlite3.connect(RUNCOUNT_DB)
cur = conn.cursor()
cur.execute("""
CREATE TABLE IF NOT EXISTS runs (
file TEXT PRIMARY KEY,
count INTEGER,
last_run TEXT
)
""")
cur.execute("SELECT count FROM runs WHERE file = ?", (file_path,))
row = cur.fetchone()
ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
if row:
new_count = row[0] + 1
cur.execute("UPDATE runs SET count = ?, last_run = ? WHERE file = ?",
(new_count, ts, file_path))
else:
cur.execute("INSERT INTO runs (file, count, last_run) VALUES (?, ?, ?)",
(file_path, 1, ts))
conn.commit()
conn.close()
def launch_kill_script():
print("[SECURITY] Hash mismatch — launching kill.py")
subprocess.Popen([sys.executable, KILL_SCRIPT])
def launch_program(path):
print(f"[EXECUTE] Launching: {path}")
subprocess.Popen([path])
def main():
if len(sys.argv) != 2:
print("Usage: launcher.py <program_to_launch>")
return
target = sys.argv[1]
if not os.path.exists(target):
print(f"[ERROR] File not found: {target}")
return
print(f"[INFO] Hashing file: {target}")
md5_value = md5_of_file(target)
print(f"[INFO] MD5: {md5_value}")
print("[INFO] Checking hash database...")
if check_hash_in_db(md5_value):
print("[OK] Hash match — execution allowed.")
increment_run_count(target)
launch_program(target)
else:
print("[FAIL] Hash mismatch — execution denied.")
launch_kill_script()
if __name__ == "__main__":
main()
Students discuss why this kind of launcher is safer than “double‑click and hope.”
Lab Artifacts & Exercises
- Mount
Escobar.vdiand list all files with their MD5 hashes - Unpack
Tim.zipand build a minihash.dbof its contents - Modify the launcher to log to a text file for later review
- Design a simple HTML report summarizing which files were allowed to run