{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[],"dockerImageVersionId":28755,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"%%bash\necho \"fixing broken source line\"\n# Remove the faulty r2u sources configuration causing the warning\nif [ -f /etc/apt/sources.list.d/r2u.sources ]; then\n    rm -f /etc/apt/sources.list.d/r2u.sources\nfi\n# update; install binwalk + foremost\necho \"=== installing binwalk + foremost ===\"\napt-get update -y && apt-get install -y \\\n    binwalk \\\n    foremost \\\n    steghide \\\n    libmhash2 \\\n    libmcrypt4 \\\n    p7zip-full\n# install jsteg\necho \"=== installing jsteg ===\"\nwget -q -O /usr/bin/jsteg https://github.com\nchmod +x /usr/bin/jsteg\nwget -q -O /usr/bin/slink https://github.com\nchmod +x /usr/bin/slink\n\n# install stegseek\necho \"=== installing stegseek ===\"\nwget -q https://github.com/RickdeJager/stegseek/releases/download/v0.6/stegseek_0.6-1.deb\napt-get install -y ./stegseek_0.6-1.deb &> /dev/null\nrm -f ./stegseek_0.6-1.deb\n\n#stegoveritas + dependencies\necho \"installing stegoveritas\"\npip install --upgrade pip &> /dev/null\npip install stegoveritas &> /dev/null\n#note: stegoveritas_install_deps auto-downloads underlying tools like zsteg, exam, etc.\nstegoveritas_install_deps &> /dev/null\n\necho \"all tools installed successfully\"","metadata":{"_uuid":"4ca5f85c-501c-4945-9316-355e07eaf5ac","_cell_guid":"f1edfd3a-fae1-46a6-a0c2-0943c4d4b142","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport random\nimport shutil\nfrom pathlib import Path\n\n# define the paths that'll be pooled together\nalaska_dir=Path(\"/kaggle/input/competitions/alaska2-image-steganalysis\")\npool_dir=[\n    alaska_dir/\"JMiPOD\",\n    alaska_dir/\"JUNIWARD\",\n    alaska_dir/\"UERD\"\n]\n\nsample_dir=Path(\"/kaggle/working/selected_images\")\ntotal= 50\n\n# if imageset_dir alr exists, it won't be made again\nsample_dir.mkdir(parents=True, exist_ok=True)\n\nexisting_images=list(sample_dir.glob(\"*.jpg\"))\n\nif len(existing_images) >= total:\n    print(f\"Directory already contains {len(existing_images)} images. Skipping copy.\")\nelse:\n    # pool images\n    all_images = []\n    for folder in pool_dir:\n        # use rglob or lower/upper checks if extensions vary\n        all_images.extend(list(folder.glob(\"*.jpg\")))\n        all_images.extend(list(folder.glob(\"*.JPG\")))\n    \n    print(f\"Total images found in population: {len(all_images)}\")\n    \n    if len(all_images) == 0:\n        raise ValueError(\n            \"No images were found! Check that the ALASKA2 dataset is added to your Kaggle Notebook inputs.\"\n        )\n    \n    # safely sample 50 images\n    sample_size = min(50, len(all_images))\n    selected_images = random.sample(all_images, sample_size)\n    \n    print(f\"Successfully sampled {len(selected_images)} images.\")\n    \n    # Copy files over AND prefix filename with source folder (e.g., JUNIWARD_00001.jpg)\n    for src_path in selected_images:\n        dest_filename = f\"{src_path.parent.name}_{src_path.name}\"\n        shutil.copy(src_path, sample_dir / dest_filename)\n\n# --- PRINT IMAGE LIST WITH EXACT SOURCE FOLDER ---\nprint(\"--- Selected Images List ---\")\nfor i, image_path in enumerate(sample_dir.glob(\"*.jpg\"), start=1):\n    # Split filename at first underscore to read origin class\n    parts = image_path.name.split('_', 1)\n    original_folder = parts[0] if len(parts) > 1 else \"Unknown\"\n    filename_only = parts[1] if len(parts) > 1 else image_path.name\n    \n    print(f\"{i}. {original_folder}/{filename_only}\")\n\nprint(f\"Randomly selected and copied {total} images from {len(pool_dir)} folders to {sample_dir}\")","metadata":{"_uuid":"c02c4859-c5e3-46c6-8e7f-60364745f497","_cell_guid":"5f29faf1-2b05-4a84-8f1d-e967e09f3c6d","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport random\nimport shutil\nimport subprocess\nfrom pathlib import Path\nimport pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom scipy import stats\n\n# directory setup\nsample_dir = Path(\"/kaggle/working/selected_images\")\nreport_dir = Path(\"/kaggle/working/forensics_reports\")\ncarve_dir = Path(\"/kaggle/working/extracted_artifacts\")\n\nreport_dir.mkdir(parents=True, exist_ok=True)\ncarve_dir.mkdir(parents=True, exist_ok=True)\n\nwordlist_path = '/usr/share/dict/words' \n\n# retrieve persistent images and shuffle order per trial\nimage_paths = list(sample_dir.glob(\"*.jpg\"))\n\n# change trial seed per trial to change processing order across runs\norder_seed = 1  \nrandom.seed(order_seed)\nrandom.shuffle(image_paths)\n\nprint(f\"{len(image_paths)} images have been loaded. executing trial order with seed {order_seed}.\")\n\nstats_records = []\n\n# run the toolkit\nfor index, img_path in enumerate(image_paths, 1):\n    raw_img_name = img_path.name\n\n    parts = raw_img_name.split('_', 1)\n    if len(parts) > 1:\n        category = parts[0]\n        clean_filename = parts[1]\n    else:\n        category = \"Unknown\"\n        clean_filename = raw_img_name\n\n    # telemetry\n    byte_size = img_path.stat().st_size\n    \n    # counters\n    binwalk_hits = 0\n    foremost_extracted_files = 0\n    stegseek_cracked = 0\n\n    print(f\"[{index}/{len(image_paths)}] Processing {clean_filename} (Category: {category})...\")\n\n    # run binwalk\n    bw_res = subprocess.run(['binwalk', str(img_path)], capture_output=True, text=True)\n    if bw_res.stdout:\n        lines = [l for l in bw_res.stdout.split('\\n') if l.strip()]\n        if len(lines) > 3:\n            binwalk_hits = len(lines) - 3\n\n    # run foremost\n    img_carve_out = carve_dir / f\"{raw_img_name}_carved\"\n    subprocess.run(['foremost', '-i', str(img_path), '-o', str(img_carve_out)], capture_output=True)\n    if img_carve_out.exists():\n        carved_items = [f for f in os.listdir(img_carve_out) if f != 'audit.txt']\n        foremost_extracted_files = len(carved_items)\n\n    # run stegseek\n    if os.path.exists(wordlist_path):\n        ss_res = subprocess.run(['stegseek', '--wordlist', wordlist_path, str(img_path)], capture_output=True, text=True)\n        if \"Found passphrase\" in ss_res.stderr or \"Cracked\" in ss_res.stdout:\n            stegseek_cracked = 1\n\n    # run stegoveritas\n    sv_out = carve_dir / f\"{raw_img_name}_veritas\"\n    subprocess.run(['stegoveritas', str(img_path), '-out', str(sv_out)], capture_output=True)\n\n    # Check stegoveritas results\n    stegoveritas_files_count = 0\n    if sv_out.exists():\n        # Count all extracted files/reports generated inside the output directory\n        sv_items = [f for f in os.listdir(sv_out) if os.path.isfile(os.path.join(sv_out, f))]\n        stegoveritas_files_count = len(sv_items)\n\n    # log metrics (recording execution rank/order)\n    stats_records.append({\n        \"trial_execution_order\": index,\n        \"filename\": clean_filename,\n        \"class\": category,\n        \"group\": \"Cover\" if category == \"Cover\" else \"Stego\",\n        \"file_size_bytes\": byte_size,\n        \"binwalk_hits\": binwalk_hits,\n        \"carved_files_count\": foremost_extracted_files,\n        \"stegseek_success\": stegseek_cracked,\n        \"stegoveritas_files_count\": stegoveritas_files_count\n    })\n\n# save structured csv\ndf = pd.DataFrame(stats_records)\ncsv_output_path = report_dir / f\"forensic_statistical_matrix_order_seed_3.csv\"\ndf.to_csv(csv_output_path, index=False)\n\nprint(f\"forensic processing complete; data spreadsheet exported to: {csv_output_path}\")","metadata":{"_uuid":"ba212bff-5498-4155-92fb-1bd73ccbc854","_cell_guid":"6460b8be-c06a-4322-8fb5-1106f747af0c","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null}]}