{"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":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\n#import numpy as np # linear algebra\n#import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\n#import os\n#for dirname, _, filenames in os.walk('/kaggle/input'):\n#    for filename in filenames:\n#        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session\n\n# Use the kagglehub client library to attach Kaggle resources like competitions, datasets, and models to your session\n# Learn more about kagglehub: https://github.com/Kaggle/kagglehub/blob/main/README.md\n\n#import kagglehub\n# kagglehub.dataset_download('<owner>/<dataset-slug>')","metadata":{"_uuid":"16f52e53-9ffb-4969-a110-c1557c8e3729","_cell_guid":"5f5a9cbd-b862-4be8-be1d-e60d1eff0437","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"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":"0af0715a-3377-4fd5-ba4b-0b037c4d7ca3","_cell_guid":"e5cf26d1-0091-45a7-91ad-5a81c4c8ffb5","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    all_images=[]\n    for folder in pool_dir:\n        all_images.extend(list(folder.glob(\"*.jpg\")))\n    \n    # copy them over to image_set.dir\n    for src_path in selected_images:\n        shutil.copy(src_path, sample_dir / src_path.name)\n\n    # list the 50 images that were randomly selected\n    for i, image_path in enumerate(sample_dir.glob(\"*.jpg\"), start=1):\n        folder_name=image_path.parent.name\n        print(f\"{i}. {folder_name}/{image_path.name}\")\n        \n    print(f\"Randomly selected and copied {total} images from {len(pool_dir)} folders to {sample_dir}\")","metadata":{"_uuid":"bab80e7d-b2ef-4376-a0bc-3447000b54f8","_cell_guid":"b0db664c-f614-4274-a512-7feed1927358","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# 2. 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    img_name = img_path.name\n    \n    # infer category (checks prefix or filename structure)\n    category = \"Unknown\"\n    for cat in ['Cover', 'JMiPOD', 'JUNIWARD', 'UERD']:\n        if cat.lower() in img_name.lower():\n            category = cat\n            break\n\n    # telemetry\n    byte_size = img_path.stat().st_size\n    \n    # counters\n    binwalk_hits = 0\n    foremost_extracted_files = 0\n    jsteg_anomaly = 0\n    stegseek_cracked = 0\n\n    print(f\"[{index}/{len(image_paths)}] Processing {img_name} (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\"{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 jsteg\n    jsteg_out_txt = carve_dir / f\"{img_name}_jsteg.txt\"\n    js_res = subprocess.run(['jsteg', 'reveal', str(img_path), str(jsteg_out_txt)], capture_output=True, text=True)\n    if jsteg_out_txt.exists() and jsteg_out_txt.stat().st_size > 0:\n        jsteg_anomaly = 1\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\"{img_name}_veritas\"\n    subprocess.run(['stegoveritas', str(img_path), '-out', str(sv_out)], capture_output=True)\n\n    # log metrics (recording execution rank/order)\n    stats_records.append({\n        \"trial_execution_order\": index,\n        \"filename\": img_name,\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        \"jsteg_anomaly\": jsteg_anomaly,\n        \"stegseek_success\": stegseek_cracked\n    })\n\n# save structured csv\ndf = pd.DataFrame(stats_records)\ncsv_output_path = report_dir / f\"forensic_statistical_matrix_order_seed_{order_seed}.csv\"\ndf.to_csv(csv_output_path, index=False)\n\nprint(f\"Forensic processing complete. Data matrix exported to: {csv_output_path}\")","metadata":{"_uuid":"dd647e19-d5c0-4516-be99-7547d4dcee2d","_cell_guid":"fb164162-8a87-4710-9485-0871e2cf6c70","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null}]}