From fea7a0e2e0a07209c2e28b24fdd117e4f89620ce Mon Sep 17 00:00:00 2001 From: Samuele95 Date: Wed, 21 Jan 2026 08:08:09 +0100 Subject: [PATCH] Improve TUI rendering and add forensic analysis features MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Refactored TUI with helper functions (tui_line, tui_section, tui_item) - Added TUI constants for box-drawing borders - Improved key reading with faster response times - Disabled mouse tracking to prevent input issues - Added forensic analysis options (persistence, execution, file anomalies, RE triage, MFT) - Updated configuration file with forensic analysis settings - Enhanced documentation in README and WIKI 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- README.md | 37 + WIKI.md | 379 ++++ malscan.conf | 34 + malware_scan.sh | 4466 ++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 4681 insertions(+), 235 deletions(-) diff --git a/README.md b/README.md index 6e23afb..80af3d7 100644 --- a/README.md +++ b/README.md @@ -121,6 +121,15 @@ sudo ./malware_scan.sh -i ║ [ ] Rootkit detection (requires mount) ║ ║ [ ] Generate file timeline ║ ╟──────────────────────────────────────────────────────────────────────╢ +║ FORENSIC ANALYSIS (Windows artifacts) ║ +╟──────────────────────────────────────────────────────────────────────╢ +║ [ ] Full forensic analysis (enables all below) ║ +║ [ ] Persistence artifacts (registry, tasks, services) ║ +║ [ ] Execution artifacts (prefetch, amcache, shimcache) ║ +║ [ ] File anomalies (timestomping, ADS, suspicious paths) ║ +║ [ ] RE triage (capa, imports, entropy) ║ +║ [ ] MFT/filesystem forensics (deleted files, USN journal) ║ +╟──────────────────────────────────────────────────────────────────────╢ ║ OUTPUT ║ ╟──────────────────────────────────────────────────────────────────────╢ ║ [✓] Generate HTML report ║ @@ -306,6 +315,28 @@ sudo dms --interactive | :floppy_disk: | **Slack Space Recovery** | Extract and analyze unallocated disk space for hidden threats | | :globe_with_meridians: | **VirusTotal Integration** | Automatic hash lookup via VT API for threat intelligence | | :arrows_counterclockwise: | **Checkpoint/Resume** | Resume interrupted scans without losing progress | +| :mag_right: | **Forensic Analysis** | Persistence, execution artifacts, RE triage, MFT analysis | + +### Behavioral & Forensic Analysis (NEW) + +DMS now includes comprehensive **behavioral and forensic analysis** for Windows artifacts: + +| Module | Artifacts Analyzed | MITRE ATT&CK | +|--------|-------------------|--------------| +| **Persistence Scan** | Registry Run keys, Services, Scheduled Tasks, Startup folders, WMI subscriptions | T1547, T1543, T1053, T1546 | +| **Execution Scan** | Prefetch, Amcache, Shimcache, UserAssist, SRUM, BAM/DAM | T1059, T1204 | +| **File Anomalies** | Magic/extension mismatch, ADS, timestomping, suspicious paths, packed executables | T1036, T1070, T1564 | +| **RE Triage** | capa analysis, suspicious imports (process injection/hollowing), similarity hashes, shellcode detection | T1055, T1055.012 | +| **MFT Forensics** | Deleted file recovery, $MFT parsing, $UsnJrnl analysis, timestamp anomalies | T1070, T1485 | + +```bash +# Enable all forensic modules +sudo ./malware_scan.sh evidence.E01 --forensic-analysis + +# Or enable specific modules +sudo ./malware_scan.sh /dev/sdb1 --persistence-scan --execution-scan +sudo ./malware_scan.sh evidence.E01 --re-triage --mft-analysis +``` --- @@ -698,6 +729,12 @@ All standard scans PLUS: | `--log-file FILE` | | Write logs to file | | `--output FILE` | `-o` | Custom output path | | `--dry-run` | | Preview without executing | +| `--forensic-analysis` | | **Enable all forensic modules** | +| `--persistence-scan` | | Scan for persistence mechanisms | +| `--execution-scan` | | Analyze execution artifacts | +| `--file-anomalies` | | Detect file anomalies/timestomping | +| `--re-triage` | | RE triage on suspicious files | +| `--mft-analysis` | | MFT/filesystem forensics | --- diff --git a/WIKI.md b/WIKI.md index 2104c0b..9ab02b0 100644 --- a/WIKI.md +++ b/WIKI.md @@ -18,6 +18,7 @@ 10. [Security & Forensic Integrity](#10-security--forensic-integrity) 11. [Dependencies](#11-dependencies) 12. [Troubleshooting](#12-troubleshooting) +13. [Forensic Analysis Modules](#13-forensic-analysis-modules) **(NEW)** --- @@ -254,6 +255,11 @@ When an EWF image is detected: | Bulk Extractor | `scan_bulk_extractor()` | Yes | No | bulk_extractor | | Hashes | `scan_hashes()` | Yes | No | md5deep | | Slack Space | `extract_slack_space()` | Slack Mode | No | blkls | +| **Persistence** | `scan_persistence_artifacts()` | Forensic | No | RegRipper | +| **Execution** | `scan_execution_artifacts()` | Forensic | No | Python parsers | +| **File Anomalies** | `scan_file_anomalies()` | Forensic | No | file, Python | +| **RE Triage** | `scan_re_triage()` | Forensic | No | capa, pefile | +| **MFT Forensics** | `scan_filesystem_forensics()` | Forensic | No | analyzeMFT | ### 4.2 ClamAV Integration @@ -660,6 +666,17 @@ malware_scan.sh -i | `--log-file FILE` | Write logs to file | | `--keep-output` | Keep temporary output directory | +#### Forensic Analysis (NEW) + +| Option | Description | +|--------|-------------| +| `--forensic-analysis` | Enable all forensic analysis modules | +| `--persistence-scan` | Scan for persistence mechanisms (registry, tasks, services) | +| `--execution-scan` | Analyze execution artifacts (prefetch, amcache, shimcache) | +| `--file-anomalies` | Detect file anomalies (timestomping, ADS, suspicious paths) | +| `--re-triage` | Run RE triage on suspicious files (capa, imports, entropy) | +| `--mft-analysis` | Parse MFT for deleted files and filesystem anomalies | + ### 7.3 Exit Codes | Code | Name | Description | @@ -1106,4 +1123,366 @@ sudo ./malware_scan.sh /dev/sdb1 --verbose --log-file /tmp/dms-debug.log --- +## 13. Forensic Analysis Modules + +### 13.1 Overview + +DMS includes comprehensive behavioral and forensic analysis capabilities for Windows artifacts, enabling detection of advanced threats through artifact correlation and MITRE ATT&CK technique mapping. + +**Enable all forensic modules**: +```bash +sudo ./malware_scan.sh evidence.E01 --forensic-analysis +``` + +**Or enable specific modules**: +```bash +sudo ./malware_scan.sh /dev/sdb1 --persistence-scan --execution-scan +``` + +### 13.2 Module Summary + +| Module | CLI Flag | Artifacts | MITRE ATT&CK | +|--------|----------|-----------|--------------| +| Persistence | `--persistence-scan` | Registry Run keys, Services, Tasks, Startup, WMI | T1547, T1543, T1053, T1546 | +| Execution | `--execution-scan` | Prefetch, Amcache, Shimcache, UserAssist, SRUM, BAM | T1059, T1204 | +| File Anomalies | `--file-anomalies` | Magic mismatch, ADS, timestomping, packed files | T1036, T1070, T1564 | +| RE Triage | `--re-triage` | capa analysis, suspicious imports, shellcode | T1055, T1055.012 | +| MFT Forensics | `--mft-analysis` | $MFT parsing, $UsnJrnl, deleted files | T1070, T1485 | + +### 13.3 Persistence Artifact Analysis + +**Function**: `scan_persistence_artifacts()` + +Detects Windows persistence mechanisms used by malware to survive reboots. + +| Artifact | Location | Tool | ATT&CK ID | +|----------|----------|------|-----------| +| Registry Run Keys | NTUSER.DAT, SOFTWARE hives | RegRipper | T1547.001 | +| Services | SYSTEM hive | RegRipper `services` plugin | T1543.003 | +| Scheduled Tasks | Windows/System32/Tasks/* | XML parsing | T1053.005 | +| Startup Folders | Start Menu/Programs/Startup | File enumeration | T1547.001 | +| WMI Subscriptions | OBJECTS.DATA | Python WMI parser | T1546.003 | +| DLL Hijacking | Known vulnerable paths | Path validation | T1574.001 | + +**Statistics Tracked**: +- `persistence_run_keys`: Registry Run key entries +- `persistence_services`: Suspicious services +- `persistence_tasks`: Scheduled tasks +- `persistence_startup`: Startup folder items +- `persistence_wmi`: WMI subscriptions +- `persistence_dll_hijack`: DLL hijack paths + +### 13.4 Execution Artifact Analysis + +**Function**: `scan_execution_artifacts()` + +Analyzes Windows forensic artifacts that prove program execution. + +| Artifact | What It Proves | Tool | Key Value | +|----------|----------------|------|-----------| +| Prefetch | Program executed, timestamps | Python prefetch parser | Last 8 execution times | +| Amcache | Executables that ran | Python Amcache parser | SHA1 hashes | +| Shimcache | Programs that existed | AppCompatCache parser | Chronological order | +| UserAssist | GUI programs launched | RegRipper | Execution count/time | +| SRUM | Network/energy per app | Python SRUM parser (pyesedb) | Resource usage | +| BAM/DAM | Background activity | RegRipper | Last execution | + +**Execution Anomaly Detection**: +- Execution from `%TEMP%`, `%PUBLIC%`, Downloads +- High entropy executable names (obfuscated) +- Mismatched PE internal name vs filename +- Execution outside business hours (configurable) + +**Statistics Tracked**: +- `execution_prefetch`: Prefetch files analyzed +- `execution_amcache`: Amcache entries +- `execution_shimcache`: Shimcache entries +- `execution_userassist`: UserAssist records +- `execution_srum`: SRUM entries +- `execution_bam`: BAM/DAM entries +- `execution_anomalies`: Execution anomalies detected + +### 13.5 File Anomaly Detection + +**Function**: `scan_file_anomalies()` + +Detects file-level indicators of compromise and anti-forensic techniques. + +| Check | Description | ATT&CK ID | +|-------|-------------|-----------| +| Magic vs Extension | Magic bytes don't match file extension | T1036.005 | +| Alternate Data Streams | Hidden data in NTFS ADS | T1564.004 | +| Suspicious Paths | Executables in temp, recycle bin, etc. | T1036 | +| Packed Executables | UPX, Themida, VMProtect signatures | T1027.002 | +| Attribute Anomalies | Deep paths, Unicode tricks, RTL override | T1036.002 | +| Timestomping | $SI vs $FN timestamp discrepancy | T1070.006 | + +**Packer Detection Signatures**: +``` +UPX: "UPX0", "UPX1", "UPX!" +Themida: ".themida", ".winlicense" +VMProtect: ".vmp0", ".vmp1" +ASPack: ".aspack" +PECompact: "PEC2" +``` + +**Statistics Tracked**: +- `file_magic_mismatch`: Magic/extension mismatches +- `file_ads`: Alternate Data Streams +- `file_suspicious_location`: Suspicious paths +- `file_packed`: Packed executables +- `file_attribute_anomalies`: Attribute anomalies +- `timestomping_detected`: Timestomping instances + +### 13.6 Reverse Engineering Triage + +**Function**: `scan_re_triage()` + +Automated reverse engineering analysis for carved/suspicious executables. + +#### 13.6.1 capa Integration + +Uses Mandiant capa for ATT&CK capability mapping: +```bash +capa -j suspicious_file.exe +``` + +**Output Example**: +```json +{ + "capabilities": { + "persistence": ["create scheduled task", "modify registry run key"], + "defense_evasion": ["check for debugger", "disable security tools"], + "collection": ["capture screenshot", "keylogging"] + }, + "attack_techniques": ["T1547.001", "T1082", "T1055"] +} +``` + +#### 13.6.2 Suspicious Import Analysis + +**Process Injection APIs** (T1055): +``` +CreateRemoteThread, WriteProcessMemory, VirtualAllocEx, +NtMapViewOfSection, NtWriteVirtualMemory +``` + +**Process Hollowing APIs** (T1055.012): +``` +NtUnmapViewOfSection, ZwUnmapViewOfSection, SetThreadContext, +NtResumeThread, ResumeThread, NtSuspendThread +``` + +**Anti-Debug/Evasion APIs**: +``` +IsDebuggerPresent, CheckRemoteDebuggerPresent, +NtQueryInformationProcess (ProcessDebugPort) +``` + +**Credential Access APIs** (T1003): +``` +CredEnumerate, LsaRetrievePrivateData, CryptUnprotectData +``` + +#### 13.6.3 Similarity Hashing + +| Hash Type | Purpose | Tool | +|-----------|---------|------| +| imphash | Import table hash (malware family clustering) | pefile | +| ssdeep | Fuzzy hash (similarity detection) | ssdeep | +| TLSH | Locality-sensitive hash | tlsh | + +#### 13.6.4 Shellcode Detection + +**Patterns Detected**: +- GetPC techniques (call/pop sequences) +- API hashing (CRC32, ROR13) +- Metasploit/Cobalt Strike signatures +- Position-independent code indicators + +**Statistics Tracked**: +- `re_triaged_files`: Files analyzed +- `re_suspicious_imports`: Suspicious API imports +- `re_suspicious_strings`: Suspicious strings (URLs, IPs, commands) +- `capa_capabilities`: capa capabilities found +- `attack_techniques_mapped`: ATT&CK techniques identified + +### 13.7 MFT/Filesystem Forensics + +**Function**: `scan_filesystem_forensics()` + +NTFS filesystem forensic analysis for deleted files and anti-forensic detection. + +#### 13.7.1 MFT Analysis + +| Analysis | Description | Tool | +|----------|-------------|------| +| Deleted Records | Files with deleted flag set | analyzeMFT | +| $DATA Streams | All data streams including ADS | analyzeMFT | +| Resident Data | Small files stored in MFT record | analyzeMFT | +| Parent Reconstruction | Full path from parent references | analyzeMFT | + +#### 13.7.2 USN Journal Analysis + +| Event Type | Forensic Value | +|------------|----------------| +| File Create | Program installation, malware drop | +| File Delete | Evidence destruction attempts | +| File Rename | Evasion technique | +| Data Overwrite | Anti-forensic wiping | + +**Anti-Forensic Detection**: +- Mass deletion patterns (wiping tools) +- Timestamp manipulation sequences +- USN journal clearing attempts + +#### 13.7.3 Timestomping Detection + +Compares `$STANDARD_INFORMATION` vs `$FILE_NAME` timestamps: + +| Indicator | Description | +|-----------|-------------| +| $SI < $FN | $SI modified to appear older | +| $SI nanoseconds = 0 | Some tools zero nanoseconds | +| Future timestamps | Impossible dates | +| Pre-OS timestamps | Dates before Windows installation | + +**Statistics Tracked**: +- `mft_records_parsed`: MFT records analyzed +- `mft_deleted_recovered`: Deleted files found +- `usn_entries_parsed`: USN journal entries +- `timestomping_detected`: Timestomping instances + +### 13.8 MITRE ATT&CK Mapping + +All forensic findings are mapped to ATT&CK techniques: + +| Technique ID | Name | Detection Module | +|--------------|------|------------------| +| T1547.001 | Registry Run Keys | Persistence | +| T1543.003 | Windows Service | Persistence | +| T1053.005 | Scheduled Task | Persistence | +| T1546.003 | WMI Event Subscription | Persistence | +| T1574.001 | DLL Search Order Hijacking | Persistence | +| T1059 | Command and Scripting Interpreter | Execution | +| T1204.002 | Malicious File | Execution | +| T1036.005 | Match Legitimate Name | File Anomalies | +| T1036.007 | Double File Extension | File Anomalies | +| T1070.006 | Timestomping | File Anomalies | +| T1564.004 | NTFS File Attributes (ADS) | File Anomalies | +| T1027.002 | Software Packing | File Anomalies | +| T1055 | Process Injection | RE Triage | +| T1055.012 | Process Hollowing | RE Triage | +| T1003 | Credential Dumping | RE Triage | +| T1070 | Indicator Removal | MFT Forensics | + +### 13.9 Configuration Options + +Add to `~/.malscan.conf`: + +```bash +# ============================================ +# Forensic Analysis Settings +# ============================================ + +# Enable full forensic artifact analysis (all modules below) +FORENSIC_ANALYSIS=false + +# Individual module control +PERSISTENCE_SCAN=false +EXECUTION_SCAN=false +FILE_ANOMALIES=false +RE_TRIAGE=false +MFT_ANALYSIS=false + +# Tool paths +REGRIPPER_PATH=/opt/regripper +CAPA_PATH=/opt/capa/capa +``` + +### 13.10 Report Output + +Forensic findings appear in both HTML and JSON reports: + +**HTML Report Section**: +``` +╔══════════════════════════════════════════════════════════════════╗ +║ BEHAVIORAL & FORENSIC ANALYSIS ║ +╠══════════════════════════════════════════════════════════════════╣ +║ Persistence Artifacts ║ +║ Registry Run Keys: 3 T1547.001 ║ +║ Services: 1 T1543.003 ║ +║ Scheduled Tasks: 2 T1053.005 ║ +╟──────────────────────────────────────────────────────────────────╢ +║ MITRE ATT&CK Techniques Detected ║ +║ [T1547.001] [T1543.003] [T1053.005] [T1055.012] ║ +╚══════════════════════════════════════════════════════════════════╝ +``` + +**JSON Report Extension**: +```json +{ + "behavioral_findings": { + "enabled": true, + "persistence": { + "run_keys": 3, + "services": 1, + "scheduled_tasks": 2 + }, + "execution_evidence": { + "prefetch_files": 45, + "amcache_entries": 128 + }, + "file_anomalies": { + "magic_mismatch": 2, + "timestomping_detected": 1 + }, + "re_triage": { + "files_analyzed": 15, + "suspicious_imports": 3 + }, + "attack_techniques": { + "total_mapped": 6, + "techniques": ["T1547.001", "T1543.003", "T1053.005", "T1055.012"] + } + } +} +``` + +--- + +## Appendix C: Forensic Statistics Keys + +| Key | Description | +|-----|-------------| +| `persistence_run_keys` | Registry Run key entries | +| `persistence_services` | Suspicious services found | +| `persistence_tasks` | Scheduled tasks found | +| `persistence_startup` | Startup folder items | +| `persistence_wmi` | WMI subscriptions | +| `persistence_dll_hijack` | DLL hijack paths | +| `execution_prefetch` | Prefetch files analyzed | +| `execution_amcache` | Amcache entries | +| `execution_shimcache` | Shimcache entries | +| `execution_userassist` | UserAssist records | +| `execution_srum` | SRUM database entries | +| `execution_bam` | BAM/DAM entries | +| `execution_anomalies` | Execution anomalies | +| `file_magic_mismatch` | Magic/extension mismatches | +| `file_ads` | Alternate Data Streams | +| `file_suspicious_location` | Files in suspicious paths | +| `file_packed` | Packed executables | +| `file_attribute_anomalies` | Attribute anomalies | +| `timestomping_detected` | Timestomping instances | +| `re_triaged_files` | Files RE analyzed | +| `re_suspicious_imports` | Suspicious API imports | +| `re_suspicious_strings` | Suspicious strings | +| `capa_capabilities` | capa capabilities found | +| `attack_techniques_mapped` | ATT&CK techniques mapped | +| `mft_records_parsed` | MFT records analyzed | +| `mft_deleted_recovered` | Deleted files found | +| `usn_entries_parsed` | USN journal entries | + +--- + *DMS Wiki v2.1 - Complete Technical Reference* diff --git a/malscan.conf b/malscan.conf index d2d1023..849182b 100644 --- a/malscan.conf +++ b/malscan.conf @@ -106,6 +106,34 @@ MAX_CARVED_FILES=1000 # Note: photorec requires manual interaction, so only foremost is enabled by default CARVING_TOOLS=foremost +# ============================================ +# Forensic Analysis Settings +# ============================================ + +# Enable full forensic artifact analysis (all modules below) +FORENSIC_ANALYSIS=false + +# Persistence mechanism scanning (registry, tasks, services) +PERSISTENCE_SCAN=false + +# Execution artifact analysis (prefetch, amcache, shimcache) +EXECUTION_SCAN=false + +# File anomaly detection (timestomping, ADS, suspicious paths) +FILE_ANOMALIES=false + +# Reverse engineering triage (capa, imports, entropy) +RE_TRIAGE=false + +# MFT and filesystem forensics +MFT_ANALYSIS=false + +# RegRipper path (for persistence/execution analysis) +# REGRIPPER_PATH=/opt/regripper + +# capa binary path (for RE triage) +# CAPA_PATH=/opt/capa/capa + # ============================================ # Display Options # ============================================ @@ -135,3 +163,9 @@ HIGH_CONTRAST=false # ./malware_scan.sh /dev/sdb1 --slack # Slack space only scan # ./malware_scan.sh evidence.E01 --scan-mode slack # EWF slack space scan # +# Forensic analysis examples: +# ./malware_scan.sh evidence.E01 --forensic-analysis # Full forensic suite +# ./malware_scan.sh /dev/sdb1 --persistence-scan # Persistence only +# ./malware_scan.sh evidence.E01 --re-triage # RE triage on carved files +# ./malware_scan.sh /dev/sdb1 --mft-analysis # MFT/filesystem forensics +# diff --git a/malware_scan.sh b/malware_scan.sh index 556612b..22c4dd7 100755 --- a/malware_scan.sh +++ b/malware_scan.sh @@ -75,6 +75,24 @@ SLACK_MIN_SIZE_MB=10 # Skip if slack < this size MAX_CARVED_FILES=1000 # Limit recovered files CARVING_TOOLS="foremost" # Tools to use: foremost,photorec,scalpel +# Forensic analysis settings +FORENSIC_ANALYSIS=false # --forensic-analysis flag (enables all) +DO_PERSISTENCE_SCAN=false # --persistence-scan flag +DO_EXECUTION_SCAN=false # --execution-scan flag +DO_FILE_ANOMALIES=false # --file-anomalies flag +DO_RE_TRIAGE=false # --re-triage flag +DO_MFT_ANALYSIS=false # --mft-analysis flag +ATTACK_MAPPING=true # --attack-mapping flag (include ATT&CK IDs) + +# Forensic tools paths (can be overridden in config) +REGRIPPER_PATH="" # Path to rip.pl +CAPA_PATH="" # Path to capa binary +RADARE2_PATH="" # Path to radare2/rizin +PYTHON_FORENSIC_TOOLS="" # Path to Python forensic tools + +# Mounted filesystem path for artifact analysis +MOUNTED_FS_PATH="" # Path where image is mounted for file-level access + # Enhanced color scheme SUCCESS_COLOR='\033[1;32m' # Bright green ERROR_COLOR='\033[1;31m' # Bright red @@ -152,6 +170,54 @@ declare -gA STATS=( [bulk_emails]=0 [bulk_urls]=0 [bulk_ccn]=0 + + # Forensic analysis - Persistence artifacts + [persistence_findings]=0 + [persistence_registry_run]=0 + [persistence_services]=0 + [persistence_tasks]=0 + [persistence_startup]=0 + [persistence_wmi]=0 + [persistence_details]="" + + # Forensic analysis - Execution artifacts + [execution_findings]=0 + [execution_prefetch]=0 + [execution_amcache]=0 + [execution_shimcache]=0 + [execution_userassist]=0 + [execution_srum]=0 + [execution_bam]=0 + [execution_anomalies]=0 + [execution_suspicious_paths]=0 + [execution_details]="" + + # Forensic analysis - File anomalies + [file_anomalies]=0 + [file_timestomping]=0 + [file_ads]=0 + [file_extension_mismatch]=0 + [file_suspicious_paths]=0 + [file_packed]=0 + [file_attribute_anomalies]=0 + [file_anomaly_details]="" + + # Forensic analysis - RE triage + [re_triaged_files]=0 + [re_packed_files]=0 + [re_suspicious_imports]=0 + [re_capa_matches]=0 + [re_shellcode_detected]=0 + [re_suspicious_strings]=0 + [re_attack_techniques]="" + [re_triage_details]="" + + # Forensic analysis - Filesystem + [mft_deleted_recovered]=0 + [mft_timestomping]=0 + [usn_entries]=0 + [filesystem_anomalies]=0 + [filesystem_details]="" ) # Guidance recommendations array @@ -769,6 +835,15 @@ load_config() { # Display options NO_COLOR) [ "$value" = "true" ] && NO_COLOR=true ;; HIGH_CONTRAST) [ "$value" = "true" ] && HIGH_CONTRAST=true ;; + # Forensic analysis options + FORENSIC_ANALYSIS) [ "$value" = "true" ] && FORENSIC_ANALYSIS=true ;; + PERSISTENCE_SCAN) [ "$value" = "true" ] && DO_PERSISTENCE_SCAN=true ;; + EXECUTION_SCAN) [ "$value" = "true" ] && DO_EXECUTION_SCAN=true ;; + FILE_ANOMALIES) [ "$value" = "true" ] && DO_FILE_ANOMALIES=true ;; + RE_TRIAGE) [ "$value" = "true" ] && DO_RE_TRIAGE=true ;; + MFT_ANALYSIS) [ "$value" = "true" ] && DO_MFT_ANALYSIS=true ;; + REGRIPPER_PATH) REGRIPPER_PATH="$value" ;; + CAPA_PATH) CAPA_PATH="$value" ;; esac done < "$config_file" @@ -1280,6 +1355,314 @@ install_bulk_extractor_portable() { return 1 } +# ========================================== +# FORENSIC TOOLS PORTABLE INSTALLERS +# ========================================== + +install_regripper_portable() { + print_status "Installing RegRipper (portable)..." + + local regripper_dir="$PORTABLE_TOOLS_DIR/regripper" + mkdir -p "$regripper_dir" + + # Check if Perl is available (required for RegRipper) + if ! command -v perl &> /dev/null; then + print_error "Perl is required for RegRipper but not installed" + print_status "Install Perl with: sudo apt install perl libparse-win32registry-perl" + return 1 + fi + + # Clone RegRipper from GitHub + local regripper_url="https://github.com/keydet89/RegRipper3.0/archive/refs/heads/master.tar.gz" + local tarball="$PORTABLE_TOOLS_DIR/regripper.tar.gz" + + if download_file "$regripper_url" "$tarball" "RegRipper 3.0"; then + if tar -xzf "$tarball" -C "$regripper_dir" --strip-components=1 2>/dev/null; then + rm -f "$tarball" + + # Create wrapper script + cat > "$PORTABLE_TOOLS_DIR/bin/rip.pl" << 'RIPWRAPPER' +#!/bin/bash +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../regripper" && pwd)" +perl "$SCRIPT_DIR/rip.pl" "$@" +RIPWRAPPER + chmod +x "$PORTABLE_TOOLS_DIR/bin/rip.pl" + + # Verify installation + if [ -f "$regripper_dir/rip.pl" ]; then + REGRIPPER_PATH="$regripper_dir/rip.pl" + print_success "RegRipper installed successfully" + + # Check for required Perl modules + if ! perl -MParse::Win32Registry -e 1 2>/dev/null; then + print_warning "Perl module Parse::Win32Registry not found" + print_status "Install with: sudo cpan Parse::Win32Registry" + print_status "Or: sudo apt install libparse-win32registry-perl" + fi + + return 0 + fi + fi + fi + + print_warning "Could not install RegRipper" + return 1 +} + +install_capa_portable() { + print_status "Installing capa (portable)..." + + local arch=$(detect_architecture) + local capa_url="" + local capa_version="7.1.0" # Update as needed + + case "$arch" in + x86_64) + capa_url="https://github.com/mandiant/capa/releases/download/v${capa_version}/capa-v${capa_version}-linux.zip" + ;; + aarch64) + print_warning "capa does not have official ARM64 builds, skipping" + return 1 + ;; + *) + print_warning "No pre-built capa for $arch" + return 1 + ;; + esac + + local zipfile="$PORTABLE_TOOLS_DIR/capa.zip" + local capa_dir="$PORTABLE_TOOLS_DIR/capa" + + mkdir -p "$capa_dir" + + if download_file "$capa_url" "$zipfile" "capa ${capa_version}"; then + if command -v unzip &> /dev/null; then + if unzip -q -o "$zipfile" -d "$capa_dir" 2>/dev/null; then + # Find and copy the binary + find "$capa_dir" -name "capa" -type f -exec cp {} "$PORTABLE_TOOLS_DIR/bin/" \; 2>/dev/null + chmod +x "$PORTABLE_TOOLS_DIR/bin/capa" 2>/dev/null + + rm -f "$zipfile" + + if [ -x "$PORTABLE_TOOLS_DIR/bin/capa" ]; then + CAPA_PATH="$PORTABLE_TOOLS_DIR/bin/capa" + print_success "capa installed successfully" + + # Download capa rules + print_status "Downloading capa rules..." + local rules_url="https://github.com/mandiant/capa-rules/archive/refs/heads/master.tar.gz" + local rules_tarball="$PORTABLE_TOOLS_DIR/capa-rules.tar.gz" + + if download_file "$rules_url" "$rules_tarball" "capa rules"; then + mkdir -p "$capa_dir/rules" + tar -xzf "$rules_tarball" -C "$capa_dir/rules" --strip-components=1 2>/dev/null + rm -f "$rules_tarball" + print_success "capa rules downloaded" + fi + + return 0 + fi + fi + else + print_warning "unzip required for capa installation" + fi + fi + + print_warning "Could not install capa (optional for RE triage)" + return 1 +} + +install_radare2_portable() { + print_status "Installing radare2 (portable)..." + + local arch=$(detect_architecture) + local r2_version="5.9.0" # Update as needed + + # radare2 provides portable tarballs + local r2_url="" + + case "$arch" in + x86_64) + r2_url="https://github.com/radareorg/radare2/releases/download/${r2_version}/radare2-${r2_version}-linux-x86_64.tar.gz" + ;; + aarch64) + r2_url="https://github.com/radareorg/radare2/releases/download/${r2_version}/radare2-${r2_version}-linux-arm64.tar.gz" + ;; + *) + print_warning "No pre-built radare2 for $arch" + return 1 + ;; + esac + + local tarball="$PORTABLE_TOOLS_DIR/radare2.tar.gz" + local r2_dir="$PORTABLE_TOOLS_DIR/radare2" + + mkdir -p "$r2_dir" + + if download_file "$r2_url" "$tarball" "radare2 ${r2_version}"; then + if tar -xzf "$tarball" -C "$r2_dir" --strip-components=1 2>/dev/null; then + # Copy binaries + if [ -d "$r2_dir/bin" ]; then + cp "$r2_dir/bin/"* "$PORTABLE_TOOLS_DIR/bin/" 2>/dev/null || true + else + find "$r2_dir" -name "r2" -type f -exec cp {} "$PORTABLE_TOOLS_DIR/bin/" \; 2>/dev/null + find "$r2_dir" -name "rabin2" -type f -exec cp {} "$PORTABLE_TOOLS_DIR/bin/" \; 2>/dev/null + find "$r2_dir" -name "rahash2" -type f -exec cp {} "$PORTABLE_TOOLS_DIR/bin/" \; 2>/dev/null + fi + + chmod +x "$PORTABLE_TOOLS_DIR/bin/r2" "$PORTABLE_TOOLS_DIR/bin/rabin2" 2>/dev/null + + rm -f "$tarball" + + if [ -x "$PORTABLE_TOOLS_DIR/bin/rabin2" ]; then + RADARE2_PATH="$PORTABLE_TOOLS_DIR/bin" + print_success "radare2 installed successfully" + return 0 + fi + fi + fi + + print_warning "Could not install radare2 (optional for RE triage)" + return 1 +} + +install_python_forensic_tools() { + print_status "Installing Python forensic tools..." + + # Check if python3 and pip are available + if ! command -v python3 &> /dev/null; then + print_error "Python3 required for forensic tools" + return 1 + fi + + local pip_cmd="" + if command -v pip3 &> /dev/null; then + pip_cmd="pip3" + elif python3 -m pip --version &> /dev/null; then + pip_cmd="python3 -m pip" + else + print_warning "pip not available for Python forensic tools" + return 1 + fi + + # Install forensic Python packages + local packages=("pefile" "python-registry" "prefetch-parser" "yara-python") + local failed=0 + + for pkg in "${packages[@]}"; do + print_status "Installing $pkg..." + if $pip_cmd install --user --quiet "$pkg" 2>/dev/null; then + log "DEBUG" "Installed $pkg successfully" + else + log "DEBUG" "Failed to install $pkg" + ((failed++)) + fi + done + + if [ $failed -lt ${#packages[@]} ]; then + print_success "Python forensic tools installed (${failed} packages failed)" + return 0 + fi + + print_warning "Could not install Python forensic tools" + return 1 +} + +install_behavioral_yara_rules() { + print_status "Downloading behavioral YARA rules..." + + local rules_dir="$PORTABLE_TOOLS_DIR/yara-rules" + mkdir -p "$rules_dir" + + # Download signature-base rules (Neo23x0) + local sig_base_url="https://github.com/Neo23x0/signature-base/archive/refs/heads/master.tar.gz" + local sig_tarball="$PORTABLE_TOOLS_DIR/signature-base.tar.gz" + + if download_file "$sig_base_url" "$sig_tarball" "signature-base YARA rules"; then + mkdir -p "$rules_dir/signature-base" + tar -xzf "$sig_tarball" -C "$rules_dir/signature-base" --strip-components=1 2>/dev/null + rm -f "$sig_tarball" + print_success "signature-base rules downloaded" + fi + + # Download YARA-Rules community rules + local community_url="https://github.com/Yara-Rules/rules/archive/refs/heads/master.tar.gz" + local community_tarball="$PORTABLE_TOOLS_DIR/yara-rules-community.tar.gz" + + if download_file "$community_url" "$community_tarball" "YARA community rules"; then + mkdir -p "$rules_dir/community" + tar -xzf "$community_tarball" -C "$rules_dir/community" --strip-components=1 2>/dev/null + rm -f "$community_tarball" + print_success "YARA community rules downloaded" + fi + + return 0 +} + +setup_forensic_tools() { + # Check if forensic analysis is enabled + if [ "$FORENSIC_ANALYSIS" != true ] && [ "$DO_PERSISTENCE_SCAN" != true ] && \ + [ "$DO_EXECUTION_SCAN" != true ] && [ "$DO_FILE_ANOMALIES" != true ] && \ + [ "$DO_RE_TRIAGE" != true ] && [ "$DO_MFT_ANALYSIS" != true ]; then + return 0 + fi + + print_section "Setting Up Forensic Analysis Tools" + + local -a missing_forensic=() + + # Check for required forensic tools based on enabled scans + if [ "$FORENSIC_ANALYSIS" = true ] || [ "$DO_PERSISTENCE_SCAN" = true ]; then + if [ -z "$REGRIPPER_PATH" ] && ! command -v rip.pl &> /dev/null; then + missing_forensic+=("regripper") + fi + fi + + if [ "$FORENSIC_ANALYSIS" = true ] || [ "$DO_RE_TRIAGE" = true ]; then + if [ -z "$CAPA_PATH" ] && ! command -v capa &> /dev/null; then + missing_forensic+=("capa") + fi + if [ -z "$RADARE2_PATH" ] && ! command -v rabin2 &> /dev/null; then + missing_forensic+=("radare2") + fi + fi + + if [ ${#missing_forensic[@]} -eq 0 ]; then + print_success "All required forensic tools are available" + return 0 + fi + + print_status "Missing forensic tools: ${missing_forensic[*]}" + + if [ "$PORTABLE_MODE" != true ]; then + print_warning "Run with --portable to auto-download forensic tools" + return 1 + fi + + # Install missing forensic tools + for tool in "${missing_forensic[@]}"; do + case "$tool" in + regripper) + install_regripper_portable || true + ;; + capa) + install_capa_portable || true + ;; + radare2) + install_radare2_portable || true + ;; + esac + done + + # Install Python forensic tools + install_python_forensic_tools || true + + # Download behavioral YARA rules + install_behavioral_yara_rules || true + + return 0 +} + setup_portable_tools() { local -a missing_basic=() local -a missing_deep=() @@ -2053,6 +2436,16 @@ Feature Options: --timeline Generate file timeline using fls/mactime --resume FILE Resume from a checkpoint file +Forensic Analysis: + --forensic-analysis Enable all forensic artifact analysis + --persistence-scan Scan for persistence mechanisms (registry, tasks, services) + --execution-scan Analyze execution artifacts (prefetch, amcache, shimcache) + --file-anomalies Detect file anomalies (timestomping, ADS, suspicious paths) + --re-triage Run RE triage on carved executables (capa, imports, entropy) + --mft-analysis Parse MFT for deleted files and filesystem anomalies + --attack-mapping Include MITRE ATT&CK technique IDs (default: enabled) + --no-attack-mapping Disable ATT&CK mapping in output + Output Options: --html Generate HTML report --json Generate JSON report @@ -2093,6 +2486,9 @@ Examples: ./malware_scan.sh /dev/sdb1 --resume .checkpoint # Resume interrupted scan ./malware_scan.sh /dev/sdb1 --portable # Auto-download missing tools ./malware_scan.sh /dev/sdb1 -i # Interactive mode + ./malware_scan.sh /dev/sdb1 --forensic-analysis # Full forensic artifact analysis + ./malware_scan.sh /dev/sdb1 --persistence-scan # Persistence mechanism detection + ./malware_scan.sh evidence.E01 --forensic-analysis --portable # Forensic analysis on EWF Supported Image Formats: - EWF/E01: Expert Witness Format (.E01, .E02, ... .Ex01, .L01, .Lx01) @@ -3235,113 +3631,3435 @@ scan_slack_space() { } # ========================================== -# DEEP SCAN FUNCTIONS +# FORENSIC ANALYSIS FUNCTIONS # ========================================== -scan_entropy() { - print_section "Entropy Analysis" +# ATT&CK technique mapping for findings +declare -gA ATTACK_TECHNIQUES=( + # Persistence + ["registry_run"]="T1547.001" + ["registry_services"]="T1543.003" + ["scheduled_task"]="T1053.005" + ["startup_folder"]="T1547.001" + ["wmi_subscription"]="T1546.003" + ["dll_hijack"]="T1574.001" + + # Execution Evidence + ["prefetch_execution"]="T1059" + ["suspicious_execution_path"]="T1204.002" + ["lolbin_execution"]="T1218" + + # Defense Evasion / Masquerading + ["double_extension"]="T1036.007" + ["name_mismatch"]="T1036.005" + ["masquerading"]="T1036" + ["timestomping"]="T1070.006" + + # Process Injection + ["process_hollowing"]="T1055.012" + ["process_injection"]="T1055" + + # Credential Access + ["credential_access"]="T1003" + ["credential_dumping"]="T1003.001" +) - print_status "Analyzing disk entropy by region (detecting encryption/packing)..." +scan_persistence_artifacts() { + print_section "Persistence Artifact Analysis" - local high_entropy_regions=0 - local device_path="$DEVICE" + # Check if we have access to filesystem (mounted or extracted) + local fs_path="" + if [ -n "$MOUNTED_FS_PATH" ] && [ -d "$MOUNTED_FS_PATH" ]; then + fs_path="$MOUNTED_FS_PATH" + elif [ -n "$MOUNT_POINT" ] && [ -d "$MOUNT_POINT" ]; then + fs_path="$MOUNT_POINT" + fi - python3 << ENTROPY_EOF -import subprocess -import math -import sys -import os + if [ -z "$fs_path" ]; then + print_warning "Filesystem not mounted - attempting to locate registry hives in carved files" + # Try to find registry hives in carved/recovered files + fs_path="$SCAN_OUTPUT_DIR" + fi -chunk_size_mb = 50 -num_chunks = 20 -device = os.environ.get('SCAN_DEVICE', '$device_path') + print_status "Analyzing persistence mechanisms..." -print(f"Scanning {num_chunks} regions of {chunk_size_mb}MB each...") -print("-" * 70) + local findings_file="$SCAN_OUTPUT_DIR/persistence_findings.txt" + local total_findings=0 -suspicious = 0 -for i in range(num_chunks): - offset = i * chunk_size_mb - try: - proc = subprocess.Popen( - ['dd', 'if=' + device, 'bs=1M', f'count={chunk_size_mb}', f'skip={offset}'], - stdout=subprocess.PIPE, - stderr=subprocess.DEVNULL - ) - data, _ = proc.communicate(timeout=60) - except Exception as e: - print(f"Error reading region {offset}MB: {e}") - continue + # Initialize findings file + cat > "$findings_file" << 'EOF' +================================================================================ +PERSISTENCE ARTIFACT ANALYSIS REPORT +================================================================================ +Generated: $(date) - if len(data) == 0: - continue +EOF - freq = [0]*256 - for b in data: - freq[b] += 1 + # 1. Registry Analysis with RegRipper + scan_registry_persistence "$fs_path" "$findings_file" - entropy = 0 - for f in freq: - if f > 0: - p = f / len(data) - entropy -= p * math.log2(p) + # 2. Scheduled Tasks Analysis + scan_scheduled_tasks "$fs_path" "$findings_file" - zero_pct = (freq[0] / len(data)) * 100 + # 3. Startup Folder Analysis + scan_startup_folders "$fs_path" "$findings_file" - status = "" - if entropy > 7.5: - status = "HIGH ENTROPY" - suspicious += 1 - elif entropy < 0.5: - status = "Empty" - elif entropy > 6.0: - status = "Data present" - else: - status = "Low entropy" + # 4. WMI Subscription Analysis + scan_wmi_persistence "$fs_path" "$findings_file" - if entropy > 7.5 or (entropy > 5.0 and entropy < 7.5): - print(f"Region {offset:5d}MB: Entropy={entropy:.2f}/8.0, Zeros={zero_pct:5.1f}% - {status}") + # 5. Service Analysis (from registry) + scan_service_persistence "$fs_path" "$findings_file" -print("-" * 70) -if suspicious > 0: - print(f"Found {suspicious} high-entropy regions (possible encryption/compression)") - sys.exit(1) -else: - print("No suspicious high-entropy regions detected") - sys.exit(0) -ENTROPY_EOF + # Calculate total findings + total_findings=$((${STATS[persistence_registry_run]} + ${STATS[persistence_services]} + \ + ${STATS[persistence_tasks]} + ${STATS[persistence_startup]} + \ + ${STATS[persistence_wmi]})) - if [ $? -eq 1 ]; then - RESULTS["entropy"]=1 + STATS[persistence_findings]=$total_findings + RESULTS["persistence"]=$total_findings + + if [ $total_findings -gt 0 ]; then + print_warning "Found $total_findings persistence artifacts - review recommended" + + # Add to guidance + GUIDANCE_RECOMMENDATIONS+=("Review persistence artifacts in $findings_file") + + if [ "$ATTACK_MAPPING" = true ]; then + print_status "ATT&CK Techniques detected:" + [ "${STATS[persistence_registry_run]}" -gt 0 ] && echo " - ${ATTACK_TECHNIQUES[registry_run]}: Registry Run Keys/Startup Folder" + [ "${STATS[persistence_services]}" -gt 0 ] && echo " - ${ATTACK_TECHNIQUES[registry_services]}: Windows Service" + [ "${STATS[persistence_tasks]}" -gt 0 ] && echo " - ${ATTACK_TECHNIQUES[scheduled_task]}: Scheduled Task/Job" + [ "${STATS[persistence_wmi]}" -gt 0 ] && echo " - ${ATTACK_TECHNIQUES[wmi_subscription]}: WMI Event Subscription" + fi else - RESULTS["entropy"]=0 + print_success "No suspicious persistence artifacts detected" + GUIDANCE_NO_ACTION+=("Persistence artifact scan: No suspicious findings") fi return 0 } -scan_file_carving() { - print_section "File Carving (Deleted File Recovery)" +scan_registry_persistence() { + local fs_path="$1" + local findings_file="$2" - local carve_dir="$SCAN_OUTPUT_DIR/carved" + print_status "Analyzing registry for persistence mechanisms..." - # Check disk space before carving - local estimated_space=$((DEVICE_SIZE_MB / 10)) # Rough estimate - if ! check_disk_space $estimated_space "/tmp"; then - print_warning "Skipping file carving due to insufficient disk space" - RESULTS["carved_files"]=0 - RESULTS["carved_malware"]=0 - return 0 - fi + # Common registry hive locations + local -a hive_paths=( + "Windows/System32/config/SOFTWARE" + "Windows/System32/config/SYSTEM" + "Windows/System32/config/SAM" + "Windows/System32/config/SECURITY" + "Users/*/NTUSER.DAT" + "Users/*/AppData/Local/Microsoft/Windows/UsrClass.dat" + ) - mkdir -p "$carve_dir" + local hives_found=0 + local registry_findings=0 + + echo "" >> "$findings_file" + echo "=== REGISTRY PERSISTENCE ANALYSIS ===" >> "$findings_file" + echo "" >> "$findings_file" + + # Find registry hives + for hive_pattern in "${hive_paths[@]}"; do + while IFS= read -r -d '' hive; do + [ -f "$hive" ] || continue + ((hives_found++)) + + local hive_name=$(basename "$hive") + log "DEBUG" "Found registry hive: $hive" + + # Use RegRipper if available + if [ -n "$REGRIPPER_PATH" ] || command -v rip.pl &> /dev/null; then + local rip_cmd="${REGRIPPER_PATH:-rip.pl}" + local rip_output="$SCAN_OUTPUT_DIR/regripper_${hive_name}.txt" + + # Run relevant plugins based on hive type + case "$hive_name" in + SOFTWARE) + # Run persistence-related plugins + local plugins=("run" "soft_run" "appinitdlls" "clsid" "shellext") + for plugin in "${plugins[@]}"; do + perl "$rip_cmd" -r "$hive" -p "$plugin" >> "$rip_output" 2>/dev/null || true + done + ;; + SYSTEM) + local plugins=("services" "svc" "devclass") + for plugin in "${plugins[@]}"; do + perl "$rip_cmd" -r "$hive" -p "$plugin" >> "$rip_output" 2>/dev/null || true + done + ;; + NTUSER.DAT) + local plugins=("run" "runmru" "userassist" "muicache") + for plugin in "${plugins[@]}"; do + perl "$rip_cmd" -r "$hive" -p "$plugin" >> "$rip_output" 2>/dev/null || true + done + ;; + esac - print_status "Running foremost to recover deleted files..." + # Parse output for suspicious entries + if [ -f "$rip_output" ] && [ -s "$rip_output" ]; then + # Look for suspicious patterns in Run keys + local suspicious=$(grep -iE "(powershell|cmd\.exe|wscript|cscript|mshta|regsvr32|rundll32|certutil)" "$rip_output" 2>/dev/null | wc -l) - foremost -t all -i "$DEVICE" -o "$carve_dir" 2>&1 | tail -10 || true + if [ "$suspicious" -gt 0 ]; then + echo "[HIGH] Suspicious entries in $hive_name:" >> "$findings_file" + grep -iE "(powershell|cmd\.exe|wscript|cscript|mshta|regsvr32|rundll32|certutil)" "$rip_output" >> "$findings_file" 2>/dev/null || true + echo "" >> "$findings_file" + registry_findings=$((registry_findings + suspicious)) + fi - # Check results - local carved_count=$(find "$carve_dir" -type f ! -name "audit.txt" 2>/dev/null | wc -l) + # Look for suspicious paths + local temp_paths=$(grep -iE "(\\\\temp\\\\|\\\\appdata\\\\local\\\\temp|\\\\public\\\\|\\\\downloads\\\\)" "$rip_output" 2>/dev/null | wc -l) + if [ "$temp_paths" -gt 0 ]; then + echo "[MED] Executables in suspicious paths:" >> "$findings_file" + grep -iE "(\\\\temp\\\\|\\\\appdata\\\\local\\\\temp|\\\\public\\\\|\\\\downloads\\\\)" "$rip_output" >> "$findings_file" 2>/dev/null || true + echo "" >> "$findings_file" + registry_findings=$((registry_findings + temp_paths)) + fi + fi + else + # Fallback: Use python-registry if available + if python3 -c "import Registry" 2>/dev/null; then + analyze_registry_python "$hive" "$findings_file" + else + log "DEBUG" "No registry parsing tool available for $hive" + fi + fi + + done < <(find "$fs_path" -ipath "*$hive_pattern" -type f -print0 2>/dev/null) + done + + if [ $hives_found -eq 0 ]; then + echo "No registry hives found in filesystem" >> "$findings_file" + print_warning "No registry hives found" + else + print_status "Analyzed $hives_found registry hives" + fi + + STATS[persistence_registry_run]=$registry_findings + + return 0 +} + +analyze_registry_python() { + local hive_path="$1" + local findings_file="$2" + + python3 << REGPY_EOF +import sys +try: + from Registry import Registry +except ImportError: + sys.exit(0) + +hive_path = "$hive_path" +findings_file = "$findings_file" + +# Common persistence keys to check +run_keys = [ + "Microsoft\\Windows\\CurrentVersion\\Run", + "Microsoft\\Windows\\CurrentVersion\\RunOnce", + "Microsoft\\Windows\\CurrentVersion\\RunServices", + "Microsoft\\Windows\\CurrentVersion\\RunServicesOnce", + "Microsoft\\Windows NT\\CurrentVersion\\Winlogon", +] + +suspicious_patterns = [ + "powershell", "cmd.exe", "wscript", "cscript", "mshta", + "regsvr32", "rundll32", "certutil", "bitsadmin", + "\\\\temp\\\\", "\\\\public\\\\", "\\\\downloads\\\\", + "-enc", "-encodedcommand", "-e ", "frombase64", +] + +try: + reg = Registry.Registry(hive_path) + + with open(findings_file, 'a') as f: + for key_path in run_keys: + try: + key = reg.open(key_path) + for value in key.values(): + val_data = str(value.value()).lower() + for pattern in suspicious_patterns: + if pattern.lower() in val_data: + f.write(f"[HIGH] Suspicious Run entry found:\\n") + f.write(f" Key: {key_path}\\n") + f.write(f" Name: {value.name()}\\n") + f.write(f" Value: {value.value()}\\n") + f.write(f" Pattern: {pattern}\\n\\n") + break + except Registry.RegistryKeyNotFoundException: + pass + except Exception as e: + pass + +except Exception as e: + pass +REGPY_EOF +} + +scan_scheduled_tasks() { + local fs_path="$1" + local findings_file="$2" + + print_status "Analyzing scheduled tasks..." + + echo "" >> "$findings_file" + echo "=== SCHEDULED TASKS ANALYSIS ===" >> "$findings_file" + echo "" >> "$findings_file" + + local tasks_dir="$fs_path/Windows/System32/Tasks" + local task_count=0 + local suspicious_tasks=0 + + if [ ! -d "$tasks_dir" ]; then + echo "Scheduled tasks directory not found" >> "$findings_file" + return 0 + fi + + # Analyze each task XML file + while IFS= read -r -d '' task_file; do + ((task_count++)) + local task_name=$(basename "$task_file") + + # Parse XML for suspicious content + if command -v xmllint &> /dev/null; then + local command=$(xmllint --xpath "//Command/text()" "$task_file" 2>/dev/null || true) + local arguments=$(xmllint --xpath "//Arguments/text()" "$task_file" 2>/dev/null || true) + else + # Fallback: grep-based analysis + local command=$(grep -oP '(?<=)[^<]+' "$task_file" 2>/dev/null || true) + local arguments=$(grep -oP '(?<=)[^<]+' "$task_file" 2>/dev/null || true) + fi + + # Check for suspicious patterns + local combined="$command $arguments" + local is_suspicious=false + + if echo "$combined" | grep -qiE "(powershell|cmd\.exe.*\/c|wscript|cscript|mshta|certutil|bitsadmin)" 2>/dev/null; then + is_suspicious=true + fi + + if echo "$combined" | grep -qiE "(\\\\temp\\\\|\\\\public\\\\|\\\\appdata\\\\|base64|encodedcommand|-enc )" 2>/dev/null; then + is_suspicious=true + fi + + if [ "$is_suspicious" = true ]; then + ((suspicious_tasks++)) + echo "[HIGH] Suspicious scheduled task: $task_name" >> "$findings_file" + echo " Command: $command" >> "$findings_file" + echo " Arguments: $arguments" >> "$findings_file" + echo " ATT&CK: ${ATTACK_TECHNIQUES[scheduled_task]}" >> "$findings_file" + echo "" >> "$findings_file" + fi + + done < <(find "$tasks_dir" -type f -print0 2>/dev/null) + + print_status "Analyzed $task_count scheduled tasks, $suspicious_tasks suspicious" + STATS[persistence_tasks]=$suspicious_tasks + + return 0 +} + +scan_startup_folders() { + local fs_path="$1" + local findings_file="$2" + + print_status "Analyzing startup folders..." + + echo "" >> "$findings_file" + echo "=== STARTUP FOLDER ANALYSIS ===" >> "$findings_file" + echo "" >> "$findings_file" + + local -a startup_paths=( + "ProgramData/Microsoft/Windows/Start Menu/Programs/StartUp" + "Users/*/AppData/Roaming/Microsoft/Windows/Start Menu/Programs/Startup" + ) + + local startup_items=0 + local suspicious_items=0 + + for startup_pattern in "${startup_paths[@]}"; do + while IFS= read -r -d '' startup_dir; do + [ -d "$startup_dir" ] || continue + + while IFS= read -r -d '' item; do + ((startup_items++)) + local item_name=$(basename "$item") + local item_ext="${item_name##*.}" + + # Suspicious extensions in startup + if echo "$item_ext" | grep -qiE "^(exe|bat|cmd|ps1|vbs|js|wsf|hta|scr|pif)$"; then + ((suspicious_items++)) + echo "[MED] Executable in startup folder: $item_name" >> "$findings_file" + echo " Path: $item" >> "$findings_file" + + # Get file hash if possible + if command -v md5sum &> /dev/null; then + local hash=$(md5sum "$item" 2>/dev/null | awk '{print $1}') + [ -n "$hash" ] && echo " MD5: $hash" >> "$findings_file" + fi + + echo " ATT&CK: ${ATTACK_TECHNIQUES[startup_folder]}" >> "$findings_file" + echo "" >> "$findings_file" + fi + + # Check for LNK files pointing to suspicious locations + if [ "$item_ext" = "lnk" ]; then + # LNK parsing would require pylnk or similar + echo "[INFO] Shortcut in startup: $item_name" >> "$findings_file" + fi + + done < <(find "$startup_dir" -type f -print0 2>/dev/null) + + done < <(find "$fs_path" -ipath "*$startup_pattern" -type d -print0 2>/dev/null) + done + + print_status "Found $startup_items startup items, $suspicious_items suspicious" + STATS[persistence_startup]=$suspicious_items + + return 0 +} + +scan_wmi_persistence() { + local fs_path="$1" + local findings_file="$2" + + print_status "Analyzing WMI persistence..." + + echo "" >> "$findings_file" + echo "=== WMI PERSISTENCE ANALYSIS ===" >> "$findings_file" + echo "" >> "$findings_file" + + # WMI repository location + local wmi_repo="$fs_path/Windows/System32/wbem/Repository/OBJECTS.DATA" + local wmi_findings=0 + + if [ ! -f "$wmi_repo" ]; then + echo "WMI repository not found" >> "$findings_file" + return 0 + fi + + # Extract strings from WMI repository and look for event subscriptions + if command -v strings &> /dev/null; then + local wmi_strings=$(strings -n 10 "$wmi_repo" 2>/dev/null) + + # Look for CommandLineEventConsumer (common persistence method) + if echo "$wmi_strings" | grep -qi "CommandLineEventConsumer"; then + ((wmi_findings++)) + echo "[HIGH] CommandLineEventConsumer found in WMI repository" >> "$findings_file" + echo " This is commonly used for WMI persistence" >> "$findings_file" + echo " ATT&CK: ${ATTACK_TECHNIQUES[wmi_subscription]}" >> "$findings_file" + echo "" >> "$findings_file" + + # Extract potential command lines + echo "Potential commands in WMI:" >> "$findings_file" + echo "$wmi_strings" | grep -iE "(powershell|cmd\.exe|wscript|cscript)" | head -10 >> "$findings_file" + echo "" >> "$findings_file" + fi + + # Look for ActiveScriptEventConsumer + if echo "$wmi_strings" | grep -qi "ActiveScriptEventConsumer"; then + ((wmi_findings++)) + echo "[HIGH] ActiveScriptEventConsumer found in WMI repository" >> "$findings_file" + echo " ATT&CK: ${ATTACK_TECHNIQUES[wmi_subscription]}" >> "$findings_file" + echo "" >> "$findings_file" + fi + + # Look for __EventFilter bindings + if echo "$wmi_strings" | grep -qi "__EventFilter"; then + echo "[INFO] WMI Event Filters detected (may be legitimate)" >> "$findings_file" + fi + fi + + print_status "WMI analysis complete, $wmi_findings suspicious findings" + STATS[persistence_wmi]=$wmi_findings + + return 0 +} + +scan_service_persistence() { + local fs_path="$1" + local findings_file="$2" + + print_status "Analyzing services for persistence..." + + echo "" >> "$findings_file" + echo "=== SERVICE PERSISTENCE ANALYSIS ===" >> "$findings_file" + echo "" >> "$findings_file" + + local system_hive="$fs_path/Windows/System32/config/SYSTEM" + local service_findings=0 + + if [ ! -f "$system_hive" ]; then + echo "SYSTEM hive not found" >> "$findings_file" + return 0 + fi + + # Use RegRipper services plugin if available + if [ -n "$REGRIPPER_PATH" ] || command -v rip.pl &> /dev/null; then + local rip_cmd="${REGRIPPER_PATH:-rip.pl}" + local services_output="$SCAN_OUTPUT_DIR/services_analysis.txt" + + perl "$rip_cmd" -r "$system_hive" -p services > "$services_output" 2>/dev/null || true + + if [ -f "$services_output" ] && [ -s "$services_output" ]; then + # Look for services with suspicious paths + while IFS= read -r line; do + if echo "$line" | grep -qiE "(\\\\temp\\\\|\\\\public\\\\|\\\\appdata\\\\|\\\\users\\\\.*\\\\)"; then + ((service_findings++)) + echo "[HIGH] Service with suspicious path:" >> "$findings_file" + echo " $line" >> "$findings_file" + echo " ATT&CK: ${ATTACK_TECHNIQUES[registry_services]}" >> "$findings_file" + echo "" >> "$findings_file" + fi + + # Services running PowerShell or cmd + if echo "$line" | grep -qiE "(powershell|cmd\.exe.*\/c)"; then + ((service_findings++)) + echo "[HIGH] Service executing scripts:" >> "$findings_file" + echo " $line" >> "$findings_file" + echo "" >> "$findings_file" + fi + done < "$services_output" + fi + fi + + print_status "Service analysis complete, $service_findings suspicious findings" + STATS[persistence_services]=$service_findings + + return 0 +} + +# ========================================== +# EXECUTION ARTIFACT ANALYSIS +# ========================================== + +scan_execution_artifacts() { + print_section "Execution Artifact Analysis" + + # Check if we have access to filesystem (mounted or extracted) + local fs_path="" + if [ -n "$MOUNTED_FS_PATH" ] && [ -d "$MOUNTED_FS_PATH" ]; then + fs_path="$MOUNTED_FS_PATH" + elif [ -n "$MOUNT_POINT" ] && [ -d "$MOUNT_POINT" ]; then + fs_path="$MOUNT_POINT" + fi + + if [ -z "$fs_path" ]; then + print_warning "Filesystem not mounted - attempting to locate artifacts in carved files" + fs_path="$SCAN_OUTPUT_DIR" + fi + + print_status "Analyzing program execution evidence..." + + local findings_file="$SCAN_OUTPUT_DIR/execution_findings.txt" + local total_findings=0 + + # Initialize findings file + cat > "$findings_file" << 'EOF' +================================================================================ +EXECUTION ARTIFACT ANALYSIS REPORT +================================================================================ +Generated: $(date) + +This report identifies evidence of program execution on the system. +High-confidence execution artifacts indicate programs that definitely ran. + +EOF + + # 1. Prefetch Analysis (Windows XP+) + scan_prefetch_artifacts "$fs_path" "$findings_file" + + # 2. Amcache Analysis (Windows 8+) + scan_amcache_artifacts "$fs_path" "$findings_file" + + # 3. Shimcache/AppCompatCache Analysis + scan_shimcache_artifacts "$fs_path" "$findings_file" + + # 4. UserAssist Analysis (GUI program execution) + scan_userassist_artifacts "$fs_path" "$findings_file" + + # 5. SRUM Analysis (System Resource Usage Monitor) + scan_srum_artifacts "$fs_path" "$findings_file" + + # 6. BAM/DAM Analysis (Background Activity Moderator) + scan_bam_artifacts "$fs_path" "$findings_file" + + # 7. Execution Anomaly Detection + detect_execution_anomalies "$fs_path" "$findings_file" + + # Calculate total findings + total_findings=$((${STATS[execution_prefetch]} + ${STATS[execution_amcache]} + \ + ${STATS[execution_shimcache]} + ${STATS[execution_userassist]} + \ + ${STATS[execution_srum]} + ${STATS[execution_bam]} + \ + ${STATS[execution_anomalies]:-0})) + + STATS[execution_findings]=$total_findings + RESULTS["execution"]=$total_findings + + if [ $total_findings -gt 0 ]; then + print_warning "Found $total_findings execution-related findings - review recommended" + + GUIDANCE_RECOMMENDATIONS+=("Review execution artifacts in $findings_file") + + if [ "$ATTACK_MAPPING" = true ]; then + print_status "Execution evidence summary:" + [ "${STATS[execution_prefetch]}" -gt 0 ] && echo " - Prefetch: ${STATS[execution_prefetch]} suspicious entries" + [ "${STATS[execution_amcache]}" -gt 0 ] && echo " - Amcache: ${STATS[execution_amcache]} suspicious entries" + [ "${STATS[execution_anomalies]:-0}" -gt 0 ] && echo " - Anomalies: ${STATS[execution_anomalies]:-0} detected" + fi + else + print_success "No suspicious execution artifacts detected" + GUIDANCE_NO_ACTION+=("Execution artifact scan: No suspicious findings") + fi + + return 0 +} + +scan_prefetch_artifacts() { + local fs_path="$1" + local findings_file="$2" + + print_status "Analyzing Prefetch files..." + + echo "" >> "$findings_file" + echo "=== PREFETCH ANALYSIS ===" >> "$findings_file" + echo "Prefetch files prove program execution with timestamps" >> "$findings_file" + echo "" >> "$findings_file" + + local prefetch_dir="$fs_path/Windows/Prefetch" + local prefetch_count=0 + local suspicious_prefetch=0 + + if [ ! -d "$prefetch_dir" ]; then + echo "Prefetch directory not found (may be disabled or non-Windows)" >> "$findings_file" + STATS[execution_prefetch]=0 + return 0 + fi + + # Suspicious executable patterns (commonly abused by malware) + local -a suspicious_names=( + "POWERSHELL" "CMD" "WSCRIPT" "CSCRIPT" "MSHTA" + "REGSVR32" "RUNDLL32" "CERTUTIL" "BITSADMIN" + "MSIEXEC" "WMIC" "PSEXEC" "PROCDUMP" + "MIMIKATZ" "LAZAGNE" "BLOODHOUND" "SHARPHOUND" + "COBALTSTRIKE" "BEACON" "RUBEUS" "KERBEROAST" + ) + + # Suspicious path patterns + local -a suspicious_paths=( + "TEMP" "TMP" "PUBLIC" "DOWNLOADS" "APPDATA" + "RECYCLE" "PROGRAMDATA\\\\TEMP" + ) + + # Python-based Prefetch parser + if ! python3 << 'PREFETCH_EOF' 2>/dev/null +import sys +import os +import struct +from datetime import datetime, timedelta + +prefetch_dir = os.environ.get('PREFETCH_DIR', sys.argv[1] if len(sys.argv) > 1 else '') +findings_file = os.environ.get('FINDINGS_FILE', sys.argv[2] if len(sys.argv) > 2 else '') + +suspicious_names = [ + "POWERSHELL", "CMD", "WSCRIPT", "CSCRIPT", "MSHTA", + "REGSVR32", "RUNDLL32", "CERTUTIL", "BITSADMIN", + "MSIEXEC", "WMIC", "PSEXEC", "PROCDUMP", + "MIMIKATZ", "LAZAGNE", "BLOODHOUND", "SHARPHOUND", +] + +suspicious_paths = [ + "\\TEMP\\", "\\TMP\\", "\\PUBLIC\\", "\\DOWNLOADS\\", + "\\APPDATA\\LOCAL\\TEMP", "\\RECYCLE", "\\PROGRAMDATA\\TEMP" +] + +def parse_prefetch_basic(filepath): + """Basic prefetch header parsing""" + try: + with open(filepath, 'rb') as f: + # Read signature + sig = f.read(4) + if sig not in [b'SCCA', b'MAM\x04']: + return None + + # Version detection + f.seek(0) + data = f.read(84) + if len(data) < 84: + return None + + version = struct.unpack('= 0x1E else 0x90) + run_count_data = f.read(4) + run_count = struct.unpack('= 8 and base_name.isalnum(): + alpha_count = sum(1 for c in base_name if c.isalpha()) + digit_count = sum(1 for c in base_name if c.isdigit()) + if digit_count > 2 and alpha_count > 4: + is_suspicious = True + reason = "High-entropy executable name" + + if is_suspicious: + suspicious_count += 1 + f.write(f"[MED] Suspicious prefetch: {filename}\n") + f.write(f" Executable: {pf_info.get('name', 'Unknown')}\n") + f.write(f" Run count: {pf_info.get('run_count', 'Unknown')}\n") + f.write(f" Reason: {reason}\n") + f.write(f" File: {filepath}\n\n") + + f.write(f"\nPrefetch Summary: {total_count} files analyzed, {suspicious_count} suspicious\n\n") + + print(f"PREFETCH_SUSPICIOUS={suspicious_count}") + print(f"PREFETCH_TOTAL={total_count}") + +if __name__ == '__main__': + if os.path.isdir(prefetch_dir): + analyze_prefetch(prefetch_dir, findings_file) +PREFETCH_EOF + then + : # Python succeeded + else + # Fallback: basic filename analysis + while IFS= read -r -d '' pf_file; do + ((prefetch_count++)) + local pf_name=$(basename "$pf_file") + local exe_name="${pf_name%%-*}" + + # Check against suspicious names + for pattern in "${suspicious_names[@]}"; do + if echo "$exe_name" | grep -qi "$pattern"; then + ((suspicious_prefetch++)) + echo "[INFO] Prefetch for potentially abused utility: $pf_name" >> "$findings_file" + if command -v stat &> /dev/null; then + local mtime=$(stat -c '%y' "$pf_file" 2>/dev/null || echo "Unknown") + echo " Last modified: $mtime" >> "$findings_file" + fi + echo "" >> "$findings_file" + break + fi + done + + # High-entropy filenames (random strings often used by malware) + if echo "$exe_name" | grep -qE '^[A-Z0-9]{8,}\.'; then + ((suspicious_prefetch++)) + echo "[MED] High-entropy prefetch name: $pf_name" >> "$findings_file" + echo "" >> "$findings_file" + fi + + done < <(find "$prefetch_dir" -name "*.pf" -type f -print0 2>/dev/null) + fi + + # Capture python output for stats + local python_output + python_output=$(PREFETCH_DIR="$prefetch_dir" FINDINGS_FILE="$findings_file" python3 2>/dev/null << 'PREFETCH_STATS' +import sys, os +prefetch_dir = os.environ.get('PREFETCH_DIR', '') +findings_file = os.environ.get('FINDINGS_FILE', '') +if os.path.isdir(prefetch_dir): + count = len([f for f in os.listdir(prefetch_dir) if f.upper().endswith('.PF')]) + print(f"Analyzed {count} prefetch files") +PREFETCH_STATS + ) || true + + # Count suspicious from findings file + suspicious_prefetch=$(grep -c "^\[MED\]\|^\[HIGH\]" "$findings_file" 2>/dev/null | tail -1 || echo "0") + + print_status "Prefetch analysis complete: $prefetch_count files, $suspicious_prefetch suspicious" + STATS[execution_prefetch]=$suspicious_prefetch + + return 0 +} + +scan_amcache_artifacts() { + local fs_path="$1" + local findings_file="$2" + + print_status "Analyzing Amcache (program execution history)..." + + echo "" >> "$findings_file" + echo "=== AMCACHE ANALYSIS ===" >> "$findings_file" + echo "Amcache tracks executables that ran on the system (Win8+)" >> "$findings_file" + echo "" >> "$findings_file" + + local amcache_path="$fs_path/Windows/AppCompat/Programs/Amcache.hve" + local suspicious_amcache=0 + + if [ ! -f "$amcache_path" ]; then + echo "Amcache.hve not found (pre-Win8 or not available)" >> "$findings_file" + STATS[execution_amcache]=0 + return 0 + fi + + # Use RegRipper amcache plugin if available + if [ -n "$REGRIPPER_PATH" ] || command -v rip.pl &> /dev/null; then + local rip_cmd="${REGRIPPER_PATH:-rip.pl}" + local amcache_output="$SCAN_OUTPUT_DIR/amcache_analysis.txt" + + perl "$rip_cmd" -r "$amcache_path" -p amcache > "$amcache_output" 2>/dev/null || true + + if [ -f "$amcache_output" ] && [ -s "$amcache_output" ]; then + # Look for suspicious patterns + while IFS= read -r line; do + local is_suspicious=false + + # Executables from suspicious paths + if echo "$line" | grep -qiE "(\\\\temp\\\\|\\\\public\\\\|\\\\appdata\\\\local\\\\temp|\\\\downloads\\\\|\\\\users\\\\[^\\\\]+\\\\appdata)"; then + is_suspicious=true + fi + + # Known malware tools + if echo "$line" | grep -qiE "(mimikatz|lazagne|procdump|psexec|cobaltstrike|beacon)"; then + is_suspicious=true + fi + + if [ "$is_suspicious" = true ]; then + ((suspicious_amcache++)) + echo "[HIGH] Suspicious Amcache entry:" >> "$findings_file" + echo " $line" >> "$findings_file" + echo "" >> "$findings_file" + fi + done < "$amcache_output" + fi + else + # Python fallback using python-registry + python3 << AMCACHE_PY 2>/dev/null || echo " Amcache parsing requires RegRipper or python-registry" >> "$findings_file" +import sys +try: + from Registry import Registry +except ImportError: + sys.exit(0) + +amcache_path = "$amcache_path" +findings_file = "$findings_file" + +suspicious_paths = ["\\\\temp\\\\", "\\\\public\\\\", "\\\\downloads\\\\", "\\\\appdata\\\\local\\\\temp"] +suspicious_names = ["mimikatz", "lazagne", "procdump", "psexec", "beacon"] + +suspicious_count = 0 + +try: + reg = Registry.Registry(amcache_path) + + # Try to access InventoryApplicationFile key (Win10+) + try: + inv_key = reg.open("Root\\InventoryApplicationFile") + for subkey in inv_key.subkeys(): + try: + lower_path = "" + for val in subkey.values(): + if val.name().lower() == "lowercaselongpath": + lower_path = str(val.value()).lower() + break + + is_suspicious = False + for susp_path in suspicious_paths: + if susp_path.lower() in lower_path: + is_suspicious = True + break + + for susp_name in suspicious_names: + if susp_name in lower_path: + is_suspicious = True + break + + if is_suspicious: + suspicious_count += 1 + with open(findings_file, 'a') as f: + f.write(f"[HIGH] Suspicious Amcache entry:\\n") + f.write(f" Path: {lower_path}\\n") + f.write(f" Key: {subkey.name()}\\n\\n") + + except Exception: + pass + except Registry.RegistryKeyNotFoundException: + pass + + print(f"AMCACHE_SUSPICIOUS={suspicious_count}") + +except Exception as e: + pass +AMCACHE_PY + fi + + print_status "Amcache analysis complete: $suspicious_amcache suspicious entries" + STATS[execution_amcache]=$suspicious_amcache + + return 0 +} + +scan_shimcache_artifacts() { + local fs_path="$1" + local findings_file="$2" + + print_status "Analyzing Shimcache/AppCompatCache..." + + echo "" >> "$findings_file" + echo "=== SHIMCACHE ANALYSIS ===" >> "$findings_file" + echo "Shimcache tracks file existence and may indicate execution" >> "$findings_file" + echo "" >> "$findings_file" + + local system_hive="$fs_path/Windows/System32/config/SYSTEM" + local suspicious_shimcache=0 + + if [ ! -f "$system_hive" ]; then + echo "SYSTEM hive not found for Shimcache analysis" >> "$findings_file" + STATS[execution_shimcache]=0 + return 0 + fi + + # Use RegRipper appcompatcache plugin + if [ -n "$REGRIPPER_PATH" ] || command -v rip.pl &> /dev/null; then + local rip_cmd="${REGRIPPER_PATH:-rip.pl}" + local shim_output="$SCAN_OUTPUT_DIR/shimcache_analysis.txt" + + perl "$rip_cmd" -r "$system_hive" -p appcompatcache > "$shim_output" 2>/dev/null || \ + perl "$rip_cmd" -r "$system_hive" -p shimcache > "$shim_output" 2>/dev/null || true + + if [ -f "$shim_output" ] && [ -s "$shim_output" ]; then + # Look for suspicious patterns + while IFS= read -r line; do + local is_suspicious=false + + # Executables from temp/suspicious paths + if echo "$line" | grep -qiE "(\\\\temp\\\\|\\\\public\\\\|\\\\appdata\\\\local\\\\temp|\\\\downloads\\\\|\\\\recycle)"; then + is_suspicious=true + fi + + # Common malware names + if echo "$line" | grep -qiE "(mimikatz|psexec|procdump|lazagne|bloodhound|sharphound|rubeus)"; then + is_suspicious=true + fi + + # Suspicious utilities being executed + if echo "$line" | grep -qiE "(certutil\.exe|bitsadmin\.exe|mshta\.exe|regsvr32\.exe).*\\\\(temp|public|appdata|downloads)"; then + is_suspicious=true + fi + + if [ "$is_suspicious" = true ]; then + ((suspicious_shimcache++)) + echo "[MED] Suspicious Shimcache entry:" >> "$findings_file" + echo " $line" >> "$findings_file" + echo "" >> "$findings_file" + fi + done < "$shim_output" + fi + fi + + print_status "Shimcache analysis complete: $suspicious_shimcache suspicious entries" + STATS[execution_shimcache]=$suspicious_shimcache + + return 0 +} + +scan_userassist_artifacts() { + local fs_path="$1" + local findings_file="$2" + + print_status "Analyzing UserAssist (GUI program execution)..." + + echo "" >> "$findings_file" + echo "=== USERASSIST ANALYSIS ===" >> "$findings_file" + echo "UserAssist tracks GUI programs launched by users" >> "$findings_file" + echo "" >> "$findings_file" + + local suspicious_userassist=0 + + # Find all NTUSER.DAT files + while IFS= read -r -d '' ntuser; do + [ -f "$ntuser" ] || continue + + local user_path=$(dirname "$ntuser") + local username=$(basename "$user_path") + + # Use RegRipper userassist plugin + if [ -n "$REGRIPPER_PATH" ] || command -v rip.pl &> /dev/null; then + local rip_cmd="${REGRIPPER_PATH:-rip.pl}" + local ua_output="$SCAN_OUTPUT_DIR/userassist_${username}.txt" + + perl "$rip_cmd" -r "$ntuser" -p userassist > "$ua_output" 2>/dev/null || true + + if [ -f "$ua_output" ] && [ -s "$ua_output" ]; then + # Look for suspicious programs + while IFS= read -r line; do + local is_suspicious=false + + # Programs run from suspicious locations + if echo "$line" | grep -qiE "(\\\\temp\\\\|\\\\public\\\\|\\\\downloads\\\\|\\\\appdata\\\\local\\\\temp)"; then + is_suspicious=true + fi + + # Known malware or hacking tools + if echo "$line" | grep -qiE "(mimikatz|procdump|psexec|bloodhound|lazagne)"; then + is_suspicious=true + fi + + if [ "$is_suspicious" = true ]; then + ((suspicious_userassist++)) + echo "[MED] Suspicious UserAssist entry for $username:" >> "$findings_file" + echo " $line" >> "$findings_file" + echo "" >> "$findings_file" + fi + done < "$ua_output" + fi + fi + + done < <(find "$fs_path" -ipath "*/Users/*/NTUSER.DAT" -type f -print0 2>/dev/null) + + print_status "UserAssist analysis complete: $suspicious_userassist suspicious entries" + STATS[execution_userassist]=$suspicious_userassist + + return 0 +} + +scan_srum_artifacts() { + local fs_path="$1" + local findings_file="$2" + + print_status "Analyzing SRUM (System Resource Usage Monitor)..." + + echo "" >> "$findings_file" + echo "=== SRUM ANALYSIS ===" >> "$findings_file" + echo "SRUM tracks application resource usage (network, CPU, etc.)" >> "$findings_file" + echo "" >> "$findings_file" + + local srum_path="$fs_path/Windows/System32/sru/SRUDB.dat" + local suspicious_srum=0 + + if [ ! -f "$srum_path" ]; then + echo "SRUM database not found (pre-Win8 or not available)" >> "$findings_file" + STATS[execution_srum]=0 + return 0 + fi + + # SRUM requires ESE database parsing - check for tools + if python3 -c "import pyesedb" 2>/dev/null; then + python3 << SRUM_PY 2>/dev/null || echo " SRUM parsing failed" >> "$findings_file" +import sys +import os + +try: + import pyesedb +except ImportError: + sys.exit(0) + +srum_path = "$srum_path" +findings_file = "$findings_file" + +suspicious_apps = [ + "powershell", "cmd.exe", "wscript", "cscript", "mshta", + "certutil", "bitsadmin", "regsvr32", "rundll32", + "mimikatz", "psexec", "procdump", "lazagne" +] + +suspicious_count = 0 + +try: + ese_file = pyesedb.file() + ese_file.open(srum_path) + + # Look for Application Resource Usage table + for i in range(ese_file.number_of_tables): + table = ese_file.get_table(i) + if "Application" in table.name or "NetworkUsage" in table.name: + for record_idx in range(table.number_of_records): + try: + record = table.get_record(record_idx) + for col_idx in range(record.number_of_values): + value = record.get_value_data_as_string(col_idx) + if value: + value_lower = value.lower() + for susp_app in suspicious_apps: + if susp_app in value_lower: + suspicious_count += 1 + with open(findings_file, 'a') as f: + f.write(f"[INFO] SRUM tracked suspicious app: {value}\\n") + break + except Exception: + pass + + ese_file.close() + print(f"SRUM_SUSPICIOUS={suspicious_count}") + +except Exception as e: + pass +SRUM_PY + else + echo " SRUM parsing requires pyesedb Python module" >> "$findings_file" + echo " Install with: pip3 install pyesedb" >> "$findings_file" + fi + + print_status "SRUM analysis complete: $suspicious_srum findings" + STATS[execution_srum]=$suspicious_srum + + return 0 +} + +scan_bam_artifacts() { + local fs_path="$1" + local findings_file="$2" + + print_status "Analyzing BAM/DAM (Background Activity Monitor)..." + + echo "" >> "$findings_file" + echo "=== BAM/DAM ANALYSIS ===" >> "$findings_file" + echo "BAM tracks background application activity (Win10 1709+)" >> "$findings_file" + echo "" >> "$findings_file" + + local system_hive="$fs_path/Windows/System32/config/SYSTEM" + local suspicious_bam=0 + + if [ ! -f "$system_hive" ]; then + echo "SYSTEM hive not found for BAM analysis" >> "$findings_file" + STATS[execution_bam]=0 + return 0 + fi + + # Use RegRipper bam plugin if available + if [ -n "$REGRIPPER_PATH" ] || command -v rip.pl &> /dev/null; then + local rip_cmd="${REGRIPPER_PATH:-rip.pl}" + local bam_output="$SCAN_OUTPUT_DIR/bam_analysis.txt" + + perl "$rip_cmd" -r "$system_hive" -p bam > "$bam_output" 2>/dev/null || true + + if [ -f "$bam_output" ] && [ -s "$bam_output" ]; then + while IFS= read -r line; do + local is_suspicious=false + + # Executables from suspicious paths + if echo "$line" | grep -qiE "(\\\\temp\\\\|\\\\public\\\\|\\\\appdata\\\\local\\\\temp|\\\\downloads\\\\)"; then + is_suspicious=true + fi + + # Known attack tools + if echo "$line" | grep -qiE "(mimikatz|psexec|procdump|lazagne|bloodhound|rubeus)"; then + is_suspicious=true + fi + + if [ "$is_suspicious" = true ]; then + ((suspicious_bam++)) + echo "[MED] Suspicious BAM entry:" >> "$findings_file" + echo " $line" >> "$findings_file" + echo "" >> "$findings_file" + fi + done < "$bam_output" + fi + else + # Python fallback + if python3 -c "from Registry import Registry" 2>/dev/null; then + python3 << BAM_PY 2>/dev/null || true +import sys +try: + from Registry import Registry +except ImportError: + sys.exit(0) + +system_hive = "$system_hive" +findings_file = "$findings_file" + +suspicious_paths = ["\\\\temp\\\\", "\\\\public\\\\", "\\\\downloads\\\\", "\\\\appdata\\\\local\\\\temp"] +suspicious_names = ["mimikatz", "psexec", "procdump", "lazagne", "bloodhound"] + +suspicious_count = 0 + +try: + reg = Registry.Registry(system_hive) + + # BAM key location varies by Windows version + bam_paths = [ + "ControlSet001\\Services\\bam\\State\\UserSettings", + "ControlSet001\\Services\\bam\\UserSettings", + ] + + for bam_path in bam_paths: + try: + bam_key = reg.open(bam_path) + for sid_key in bam_key.subkeys(): + for value in sid_key.values(): + val_name = value.name().lower() + is_suspicious = False + + for susp_path in suspicious_paths: + if susp_path.lower() in val_name: + is_suspicious = True + break + + for susp_name in suspicious_names: + if susp_name in val_name: + is_suspicious = True + break + + if is_suspicious: + suspicious_count += 1 + with open(findings_file, 'a') as f: + f.write(f"[MED] Suspicious BAM entry:\\n") + f.write(f" Path: {value.name()}\\n") + f.write(f" SID: {sid_key.name()}\\n\\n") + + except Registry.RegistryKeyNotFoundException: + pass + + print(f"BAM_SUSPICIOUS={suspicious_count}") + +except Exception: + pass +BAM_PY + fi + fi + + print_status "BAM analysis complete: $suspicious_bam suspicious entries" + STATS[execution_bam]=$suspicious_bam + + return 0 +} + +detect_execution_anomalies() { + local fs_path="$1" + local findings_file="$2" + + print_status "Detecting execution anomalies..." + + echo "" >> "$findings_file" + echo "=== EXECUTION ANOMALY DETECTION ===" >> "$findings_file" + echo "Looking for suspicious execution patterns and file characteristics" >> "$findings_file" + echo "" >> "$findings_file" + + local anomaly_count=0 + + # 1. High-entropy executable names in common execution locations + print_status " Checking for high-entropy filenames..." + + local -a exec_locations=( + "Windows/Temp" + "Users/*/AppData/Local/Temp" + "Users/*/Downloads" + "ProgramData" + "Users/Public" + ) + + for loc_pattern in "${exec_locations[@]}"; do + while IFS= read -r -d '' exec_file; do + [ -f "$exec_file" ] || continue + + local filename=$(basename "$exec_file") + local name_part="${filename%.*}" + + # Check for high-entropy names (random alphanumeric >= 8 chars) + if echo "$name_part" | grep -qE '^[a-zA-Z0-9]{8,}$'; then + local alpha_count=$(echo "$name_part" | tr -cd 'a-zA-Z' | wc -c) + local digit_count=$(echo "$name_part" | tr -cd '0-9' | wc -c) + + # High entropy if mixed alpha/numeric + if [ "$digit_count" -ge 2 ] && [ "$alpha_count" -ge 4 ]; then + ((anomaly_count++)) + echo "[MED] High-entropy executable name:" >> "$findings_file" + echo " File: $exec_file" >> "$findings_file" + echo " Name: $filename" >> "$findings_file" + + # Get file hash + if command -v sha256sum &> /dev/null; then + local hash=$(sha256sum "$exec_file" 2>/dev/null | awk '{print $1}') + [ -n "$hash" ] && echo " SHA256: $hash" >> "$findings_file" + fi + echo "" >> "$findings_file" + fi + fi + + done < <(find "$fs_path" -ipath "*$loc_pattern*" \( -name "*.exe" -o -name "*.dll" -o -name "*.scr" \) -type f -print0 2>/dev/null) + done + + # 2. Double extensions (e.g., document.pdf.exe) + print_status " Checking for double extensions..." + + while IFS= read -r -d '' file; do + local filename=$(basename "$file") + # Check for patterns like .pdf.exe, .doc.exe, etc. + if echo "$filename" | grep -qiE '\.(pdf|doc|docx|xls|xlsx|jpg|png|txt|mp3|mp4)\.(exe|scr|bat|cmd|ps1|vbs|js)$'; then + ((anomaly_count++)) + echo "[HIGH] Double extension detected (potential masquerading):" >> "$findings_file" + echo " File: $file" >> "$findings_file" + echo " ATT&CK: T1036.007 (Masquerading: Double File Extension)" >> "$findings_file" + echo "" >> "$findings_file" + fi + done < <(find "$fs_path" -type f -print0 2>/dev/null | head -c 1000000) + + # 3. Executables with spaces before extension (e.g., "file .exe") + print_status " Checking for space-before-extension tricks..." + + while IFS= read -r -d '' file; do + local filename=$(basename "$file") + if echo "$filename" | grep -qE ' \.(exe|scr|bat|cmd|ps1|vbs|js)$'; then + ((anomaly_count++)) + echo "[HIGH] Space-before-extension trick detected:" >> "$findings_file" + echo " File: $file" >> "$findings_file" + echo " ATT&CK: T1036 (Masquerading)" >> "$findings_file" + echo "" >> "$findings_file" + fi + done < <(find "$fs_path" -type f -name "* .*" -print0 2>/dev/null) + + # 4. PE files with mismatched internal name vs filename (using pefile) + print_status " Checking for PE name mismatches..." + + if python3 -c "import pefile" 2>/dev/null; then + python3 << PE_MISMATCH_PY 2>/dev/null || true +import sys +import os + +try: + import pefile +except ImportError: + sys.exit(0) + +fs_path = "$fs_path" +findings_file = "$findings_file" + +suspicious_count = 0 +checked = 0 + +temp_dirs = ["Temp", "Downloads", "Public", "AppData"] + +for root, dirs, files in os.walk(fs_path): + for filename in files: + if not filename.lower().endswith(('.exe', '.dll')): + continue + + # Only check files in suspicious locations + if not any(td.lower() in root.lower() for td in temp_dirs): + continue + + filepath = os.path.join(root, filename) + checked += 1 + + if checked > 500: # Limit to prevent slowdown + break + + try: + pe = pefile.PE(filepath, fast_load=True) + pe.parse_data_directories(directories=[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_RESOURCE']]) + + internal_name = None + original_filename = None + + if hasattr(pe, 'VS_VERSIONINFO'): + for entry in pe.FileInfo: + if hasattr(entry, 'StringTable'): + for st in entry.StringTable: + for k, v in st.entries.items(): + k_str = k.decode('utf-8', errors='ignore').lower() + v_str = v.decode('utf-8', errors='ignore') + if k_str == 'internalname': + internal_name = v_str + elif k_str == 'originalfilename': + original_filename = v_str + + # Check for mismatch + if internal_name and original_filename: + file_base = os.path.splitext(filename)[0].lower() + internal_base = os.path.splitext(internal_name)[0].lower() + + # Significant mismatch (not just case or minor variation) + if internal_base and file_base and internal_base != file_base: + if not (internal_base in file_base or file_base in internal_base): + suspicious_count += 1 + with open(findings_file, 'a') as f: + f.write(f"[MED] PE internal name mismatch:\\n") + f.write(f" File: {filepath}\\n") + f.write(f" Filename: {filename}\\n") + f.write(f" Internal name: {internal_name}\\n") + f.write(f" Original filename: {original_filename}\\n") + f.write(f" ATT&CK: T1036.005 (Masquerading: Match Name)\\n\\n") + + pe.close() + + except Exception: + pass + + if checked > 500: + break + +print(f"PE_ANOMALIES={suspicious_count}") +PE_MISMATCH_PY + fi + + # Store total anomalies + STATS[execution_anomalies]=$anomaly_count + + print_status "Anomaly detection complete: $anomaly_count anomalies found" + + return 0 +} + +# ========================================== +# FILE ANOMALY DETECTION +# ========================================== + +scan_file_anomalies() { + print_section "File Anomaly Detection" + + # Check if we have access to filesystem + local fs_path="" + if [ -n "$MOUNTED_FS_PATH" ] && [ -d "$MOUNTED_FS_PATH" ]; then + fs_path="$MOUNTED_FS_PATH" + elif [ -n "$MOUNT_POINT" ] && [ -d "$MOUNT_POINT" ]; then + fs_path="$MOUNT_POINT" + fi + + if [ -z "$fs_path" ]; then + print_warning "Filesystem not mounted - scanning carved files directory" + fs_path="$SCAN_OUTPUT_DIR" + fi + + print_status "Detecting file anomalies and masquerading techniques..." + + local findings_file="$SCAN_OUTPUT_DIR/file_anomalies.txt" + local total_anomalies=0 + + # Initialize findings file + cat > "$findings_file" << 'EOF' +================================================================================ +FILE ANOMALY DETECTION REPORT +================================================================================ +Generated: $(date) + +This report identifies file-level anomalies that may indicate malware or +anti-forensic techniques. + +EOF + + # 1. Magic byte vs extension mismatch + detect_magic_mismatch "$fs_path" "$findings_file" + + # 2. Alternate Data Streams (NTFS ADS) + detect_alternate_data_streams "$fs_path" "$findings_file" + + # 3. Suspicious file locations + detect_suspicious_file_locations "$fs_path" "$findings_file" + + # 4. Packer/crypter detection via section entropy + detect_packed_executables "$fs_path" "$findings_file" + + # 5. Hidden and system attribute anomalies + detect_attribute_anomalies "$fs_path" "$findings_file" + + # Calculate total findings + total_anomalies=$((${STATS[file_extension_mismatch]} + ${STATS[file_ads]} + \ + ${STATS[file_suspicious_paths]} + ${STATS[file_packed]:-0} + \ + ${STATS[file_attribute_anomalies]:-0})) + + STATS[file_anomalies]=$total_anomalies + RESULTS["file_anomalies"]=$total_anomalies + + if [ $total_anomalies -gt 0 ]; then + print_warning "Found $total_anomalies file anomalies - review recommended" + + GUIDANCE_RECOMMENDATIONS+=("Review file anomalies in $findings_file") + + if [ "$ATTACK_MAPPING" = true ]; then + print_status "Anomaly types detected:" + [ "${STATS[file_extension_mismatch]}" -gt 0 ] && echo " - Magic/extension mismatch: ${STATS[file_extension_mismatch]} (${ATTACK_TECHNIQUES[masquerading]})" + [ "${STATS[file_ads]}" -gt 0 ] && echo " - Alternate Data Streams: ${STATS[file_ads]}" + [ "${STATS[file_packed]:-0}" -gt 0 ] && echo " - Packed/encrypted executables: ${STATS[file_packed]:-0}" + fi + else + print_success "No significant file anomalies detected" + GUIDANCE_NO_ACTION+=("File anomaly detection: No suspicious findings") + fi + + return 0 +} + +detect_magic_mismatch() { + local fs_path="$1" + local findings_file="$2" + + print_status "Checking for magic byte vs extension mismatches..." + + echo "" >> "$findings_file" + echo "=== MAGIC BYTE / EXTENSION MISMATCH ===" >> "$findings_file" + echo "Files where content type doesn't match extension (potential masquerading)" >> "$findings_file" + echo "" >> "$findings_file" + + local mismatch_count=0 + + # Define expected magic bytes for common extensions + # Format: extension|expected_magic_pattern + local -a magic_map=( + "exe|MZ" + "dll|MZ" + "pdf|%PDF" + "zip|PK" + "docx|PK" + "xlsx|PK" + "pptx|PK" + "jpg|JFIF|Exif|^\xff\xd8" + "jpeg|JFIF|Exif|^\xff\xd8" + "png|PNG" + "gif|GIF8" + "rar|Rar!" + "7z|7z" + ) + + # Find files and check magic bytes + while IFS= read -r -d '' file; do + [ -f "$file" ] || continue + + local filename=$(basename "$file") + local ext="${filename##*.}" + ext=$(echo "$ext" | tr '[:upper:]' '[:lower:]') + + # Skip if no extension + [ "$ext" = "$filename" ] && continue + + # Get first 16 bytes as hex + local magic_hex=$(xxd -l 16 -p "$file" 2>/dev/null || continue) + local magic_ascii=$(head -c 16 "$file" 2>/dev/null | tr -cd '[:print:]') + + local expected_type="" + local actual_type="" + local is_mismatch=false + + case "$ext" in + exe|dll|scr) + # Should start with MZ + if [[ ! "$magic_ascii" =~ ^MZ ]]; then + is_mismatch=true + expected_type="PE executable (MZ)" + actual_type="$magic_ascii" + fi + ;; + pdf) + if [[ ! "$magic_ascii" =~ ^%PDF ]]; then + is_mismatch=true + expected_type="PDF (%PDF)" + actual_type="$magic_ascii" + fi + ;; + zip|docx|xlsx|pptx|jar) + if [[ ! "$magic_ascii" =~ ^PK ]]; then + is_mismatch=true + expected_type="ZIP archive (PK)" + actual_type="$magic_ascii" + fi + ;; + jpg|jpeg) + if [[ ! "$magic_hex" =~ ^ffd8 ]]; then + is_mismatch=true + expected_type="JPEG image" + actual_type="$magic_ascii" + fi + ;; + png) + if [[ ! "$magic_hex" =~ ^89504e47 ]]; then + is_mismatch=true + expected_type="PNG image" + actual_type="$magic_ascii" + fi + ;; + gif) + if [[ ! "$magic_ascii" =~ ^GIF8 ]]; then + is_mismatch=true + expected_type="GIF image" + actual_type="$magic_ascii" + fi + ;; + esac + + if [ "$is_mismatch" = true ]; then + ((mismatch_count++)) + + # Check if it's actually an executable masquerading + local severity="MED" + if [[ "$magic_ascii" =~ ^MZ ]] && [[ ! "$ext" =~ ^(exe|dll|scr|sys)$ ]]; then + severity="HIGH" + fi + + echo "[$severity] Magic/extension mismatch:" >> "$findings_file" + echo " File: $file" >> "$findings_file" + echo " Extension: .$ext (expected: $expected_type)" >> "$findings_file" + echo " Actual magic: $actual_type" >> "$findings_file" + echo " Magic hex: ${magic_hex:0:32}" >> "$findings_file" + + # If it's an executable masquerading as something else + if [[ "$magic_ascii" =~ ^MZ ]]; then + echo " WARNING: Executable masquerading as .$ext file!" >> "$findings_file" + echo " ATT&CK: ${ATTACK_TECHNIQUES[masquerading]}" >> "$findings_file" + fi + echo "" >> "$findings_file" + fi + + done < <(find "$fs_path" -type f \( -name "*.exe" -o -name "*.dll" -o -name "*.pdf" -o -name "*.doc*" -o -name "*.xls*" -o -name "*.jpg" -o -name "*.jpeg" -o -name "*.png" -o -name "*.gif" -o -name "*.zip" \) -print0 2>/dev/null | head -c 2000000) + + # Also check for executables with document extensions + print_status " Checking for executables disguised as documents..." + + while IFS= read -r -d '' file; do + [ -f "$file" ] || continue + + local magic_ascii=$(head -c 2 "$file" 2>/dev/null) + + if [[ "$magic_ascii" == "MZ" ]]; then + ((mismatch_count++)) + echo "[HIGH] Executable disguised as document:" >> "$findings_file" + echo " File: $file" >> "$findings_file" + echo " This is a PE executable with a document extension!" >> "$findings_file" + echo " ATT&CK: ${ATTACK_TECHNIQUES[masquerading]}" >> "$findings_file" + + if command -v sha256sum &> /dev/null; then + local hash=$(sha256sum "$file" 2>/dev/null | awk '{print $1}') + [ -n "$hash" ] && echo " SHA256: $hash" >> "$findings_file" + fi + echo "" >> "$findings_file" + fi + + done < <(find "$fs_path" -type f \( -iname "*.pdf" -o -iname "*.doc" -o -iname "*.docx" -o -iname "*.xls" -o -iname "*.xlsx" -o -iname "*.txt" -o -iname "*.jpg" -o -iname "*.png" -o -iname "*.mp3" -o -iname "*.mp4" \) -print0 2>/dev/null) + + print_status "Magic/extension check complete: $mismatch_count mismatches found" + STATS[file_extension_mismatch]=$mismatch_count + + return 0 +} + +detect_alternate_data_streams() { + local fs_path="$1" + local findings_file="$2" + + print_status "Checking for Alternate Data Streams (NTFS ADS)..." + + echo "" >> "$findings_file" + echo "=== ALTERNATE DATA STREAMS (ADS) ===" >> "$findings_file" + echo "Hidden data stored in NTFS alternate streams" >> "$findings_file" + echo "" >> "$findings_file" + + local ads_count=0 + + # ADS detection requires NTFS-3G or mounted NTFS filesystem + # Check for $DATA stream indicators in file names + while IFS= read -r -d '' file; do + local filename=$(basename "$file") + + # Files with : in name indicate ADS (on Linux extraction) + if [[ "$filename" == *":"* ]]; then + ((ads_count++)) + echo "[MED] Alternate Data Stream detected:" >> "$findings_file" + echo " File: $file" >> "$findings_file" + + # Get size + local size=$(stat -c %s "$file" 2>/dev/null || echo "Unknown") + echo " Size: $size bytes" >> "$findings_file" + + # Check if it's an executable in ADS + local magic_ascii=$(head -c 2 "$file" 2>/dev/null) + if [[ "$magic_ascii" == "MZ" ]]; then + echo " WARNING: Executable hidden in ADS!" >> "$findings_file" + echo " ATT&CK: T1564.004 (Hide Artifacts: NTFS File Attributes)" >> "$findings_file" + fi + echo "" >> "$findings_file" + fi + + done < <(find "$fs_path" -type f -name "*:*" -print0 2>/dev/null) + + # Also use getfattr if available (Linux NTFS-3G) + if command -v getfattr &> /dev/null; then + while IFS= read -r -d '' file; do + local attrs=$(getfattr -d "$file" 2>/dev/null | grep -c "user\." || true) + if [ "$attrs" -gt 0 ]; then + # Check for suspicious extended attributes + local attr_list=$(getfattr -d "$file" 2>/dev/null) + if echo "$attr_list" | grep -qiE "(stream|data|zone\.identifier)"; then + ((ads_count++)) + echo "[INFO] Extended attributes on file:" >> "$findings_file" + echo " File: $file" >> "$findings_file" + echo " Attributes: $attr_list" >> "$findings_file" + echo "" >> "$findings_file" + fi + fi + done < <(find "$fs_path" -type f -print0 2>/dev/null | head -c 500000) + fi + + print_status "ADS detection complete: $ads_count streams found" + STATS[file_ads]=$ads_count + + return 0 +} + +detect_suspicious_file_locations() { + local fs_path="$1" + local findings_file="$2" + + print_status "Checking for executables in suspicious locations..." + + echo "" >> "$findings_file" + echo "=== SUSPICIOUS FILE LOCATIONS ===" >> "$findings_file" + echo "Executables found in locations commonly abused by malware" >> "$findings_file" + echo "" >> "$findings_file" + + local suspicious_count=0 + + # Suspicious locations for executables + local -a suspicious_paths=( + "Windows/Temp" + "Users/*/AppData/Local/Temp" + "Users/Public" + "Users/*/Downloads" + '$Recycle.Bin' + "System Volume Information" + "ProgramData/Temp" + "Recovery" + "Windows/debug" + "Windows/Fonts" # Not a typical place for non-font files + ) + + for susp_pattern in "${suspicious_paths[@]}"; do + while IFS= read -r -d '' exec_file; do + [ -f "$exec_file" ] || continue + + local filename=$(basename "$exec_file") + local ext="${filename##*.}" + ext=$(echo "$ext" | tr '[:upper:]' '[:lower:]') + + # Check if it's an executable type + if echo "$ext" | grep -qiE "^(exe|dll|scr|bat|cmd|ps1|vbs|js|wsf|hta|com|pif)$"; then + ((suspicious_count++)) + + # Determine severity + local severity="MED" + if echo "$exec_file" | grep -qiE '(\$Recycle\.Bin|System Volume Information)'; then + severity="HIGH" + fi + + echo "[$severity] Executable in suspicious location:" >> "$findings_file" + echo " File: $exec_file" >> "$findings_file" + echo " Type: $ext" >> "$findings_file" + + # Get file info + if command -v stat &> /dev/null; then + local mtime=$(stat -c '%y' "$exec_file" 2>/dev/null || echo "Unknown") + local size=$(stat -c '%s' "$exec_file" 2>/dev/null || echo "Unknown") + echo " Modified: $mtime" >> "$findings_file" + echo " Size: $size bytes" >> "$findings_file" + fi + + # Get hash + if command -v sha256sum &> /dev/null; then + local hash=$(sha256sum "$exec_file" 2>/dev/null | awk '{print $1}') + [ -n "$hash" ] && echo " SHA256: $hash" >> "$findings_file" + fi + + echo " ATT&CK: ${ATTACK_TECHNIQUES[suspicious_execution_path]:-T1204.002}" >> "$findings_file" + echo "" >> "$findings_file" + fi + + done < <(find "$fs_path" -ipath "*$susp_pattern*" -type f -print0 2>/dev/null) + done + + print_status "Location check complete: $suspicious_count suspicious files" + STATS[file_suspicious_paths]=$suspicious_count + + return 0 +} + +detect_packed_executables() { + local fs_path="$1" + local findings_file="$2" + + print_status "Detecting packed/encrypted executables via entropy analysis..." + + echo "" >> "$findings_file" + echo "=== PACKED/ENCRYPTED EXECUTABLE DETECTION ===" >> "$findings_file" + echo "High-entropy sections indicate packing/encryption" >> "$findings_file" + echo "" >> "$findings_file" + + local packed_count=0 + + # Use pefile for PE analysis if available + if python3 -c "import pefile" 2>/dev/null; then + python3 << 'PACKER_PY' +import sys +import os +import math + +try: + import pefile +except ImportError: + sys.exit(0) + +fs_path = os.environ.get('FS_PATH', '') +findings_file = os.environ.get('FINDINGS_FILE', '') + +if not fs_path or not findings_file: + sys.exit(0) + +def calculate_entropy(data): + if len(data) == 0: + return 0.0 + freq = [0] * 256 + for byte in data: + freq[byte] += 1 + entropy = 0.0 + for f in freq: + if f > 0: + p = f / len(data) + entropy -= p * math.log2(p) + return entropy + +packed_count = 0 +checked = 0 + +# Known packer signatures +packer_signatures = { + 'UPX': [b'UPX0', b'UPX1', b'UPX!'], + 'ASPack': [b'.aspack', b'ASPack'], + 'FSG': [b'FSG!'], + 'PECompact': [b'PEC2'], + 'Themida': [b'.themida'], + 'VMProtect': [b'.vmp0', b'.vmp1'], + 'Enigma': [b'.enigma'], +} + +# Walk filesystem looking for PE files +for root, dirs, files in os.walk(fs_path): + for filename in files: + if not filename.lower().endswith(('.exe', '.dll', '.scr', '.sys')): + continue + + filepath = os.path.join(root, filename) + checked += 1 + + if checked > 1000: # Limit for performance + break + + try: + pe = pefile.PE(filepath, fast_load=True) + + # Check for packer signatures in section names + packer_detected = None + for section in pe.sections: + section_name = section.Name.rstrip(b'\x00').decode('utf-8', errors='ignore') + for packer, sigs in packer_signatures.items(): + for sig in sigs: + if sig in section.Name or sig.decode('utf-8', errors='ignore') in section_name: + packer_detected = packer + break + + # Calculate section entropies + high_entropy_sections = [] + for section in pe.sections: + section_name = section.Name.rstrip(b'\x00').decode('utf-8', errors='ignore') + section_data = section.get_data() + entropy = calculate_entropy(section_data) + + # High entropy threshold (>7.0 is very suspicious for code) + if entropy > 7.0: + high_entropy_sections.append({ + 'name': section_name, + 'entropy': entropy, + 'size': len(section_data) + }) + + # Report if packed or high entropy + if packer_detected or len(high_entropy_sections) > 0: + packed_count += 1 + with open(findings_file, 'a') as f: + if packer_detected: + f.write(f"[HIGH] Known packer detected: {packer_detected}\n") + else: + f.write(f"[MED] High-entropy executable (likely packed):\n") + + f.write(f" File: {filepath}\n") + + for sec in high_entropy_sections: + f.write(f" Section '{sec['name']}': entropy={sec['entropy']:.2f}, size={sec['size']}\n") + + f.write(f" ATT&CK: T1027.002 (Obfuscated Files: Software Packing)\n\n") + + pe.close() + + except Exception: + pass + + if checked > 1000: + break + +print(f"PACKED_COUNT={packed_count}") +PACKER_PY + local python_result + python_result=$(FS_PATH="$fs_path" FINDINGS_FILE="$findings_file" python3 2>/dev/null << 'PACKER_STATS' +import sys, os, math + +try: + import pefile +except ImportError: + print("PACKED_COUNT=0") + sys.exit(0) + +fs_path = os.environ.get('FS_PATH', '') +findings_file = os.environ.get('FINDINGS_FILE', '') + +def calculate_entropy(data): + if len(data) == 0: + return 0.0 + freq = [0] * 256 + for byte in data: + freq[byte] += 1 + entropy = 0.0 + for f in freq: + if f > 0: + p = f / len(data) + entropy -= p * math.log2(p) + return entropy + +packed_count = 0 +checked = 0 + +packer_signatures = { + 'UPX': [b'UPX0', b'UPX1', b'UPX!'], + 'ASPack': [b'.aspack'], + 'FSG': [b'FSG!'], + 'VMProtect': [b'.vmp0', b'.vmp1'], +} + +for root, dirs, files in os.walk(fs_path): + for filename in files: + if not filename.lower().endswith(('.exe', '.dll', '.scr')): + continue + filepath = os.path.join(root, filename) + checked += 1 + if checked > 1000: + break + try: + pe = pefile.PE(filepath, fast_load=True) + for section in pe.sections: + section_name = section.Name.rstrip(b'\x00') + for packer, sigs in packer_signatures.items(): + for sig in sigs: + if sig in section_name: + packed_count += 1 + with open(findings_file, 'a') as f: + f.write(f"[HIGH] Packer {packer}: {filepath}\n\n") + break + section_data = section.get_data() + if len(section_data) > 0 and calculate_entropy(section_data) > 7.2: + packed_count += 1 + with open(findings_file, 'a') as f: + f.write(f"[MED] High entropy section in: {filepath}\n\n") + break + pe.close() + except: + pass + if checked > 1000: + break + +print(f"PACKED_COUNT={packed_count}") +PACKER_STATS + ) || python_result="PACKED_COUNT=0" + + packed_count=$(echo "$python_result" | grep -oP 'PACKED_COUNT=\K\d+' || echo "0") + else + echo " pefile module not available - install with: pip3 install pefile" >> "$findings_file" + fi + + print_status "Packer detection complete: $packed_count packed files found" + STATS[file_packed]=$packed_count + + return 0 +} + +detect_attribute_anomalies() { + local fs_path="$1" + local findings_file="$2" + + print_status "Checking for attribute anomalies..." + + echo "" >> "$findings_file" + echo "=== FILE ATTRIBUTE ANOMALIES ===" >> "$findings_file" + echo "Unusual file attributes that may indicate hiding" >> "$findings_file" + echo "" >> "$findings_file" + + local anomaly_count=0 + + # Files with very deep paths (>10 levels) - often used to hide malware + print_status " Checking for excessively deep paths..." + + while IFS= read -r file; do + local depth=$(echo "$file" | tr '/' '\n' | wc -l) + if [ "$depth" -gt 15 ]; then + ((anomaly_count++)) + echo "[MED] Excessively deep path ($depth levels):" >> "$findings_file" + echo " File: $file" >> "$findings_file" + echo "" >> "$findings_file" + fi + done < <(find "$fs_path" -type f 2>/dev/null | head -10000) + + # Files with Unicode tricks (RTL override, zero-width chars) + print_status " Checking for Unicode obfuscation..." + + while IFS= read -r -d '' file; do + local filename=$(basename "$file") + + # Check for RTL override character (U+202E) + if echo "$filename" | grep -qP '\x{202E}'; then + ((anomaly_count++)) + echo "[HIGH] RTL Override character in filename:" >> "$findings_file" + echo " File: $file" >> "$findings_file" + echo " This can reverse displayed text to hide true extension!" >> "$findings_file" + echo " ATT&CK: T1036.002 (Masquerading: Right-to-Left Override)" >> "$findings_file" + echo "" >> "$findings_file" + fi + + # Check for zero-width characters + if echo "$filename" | grep -qP '[\x{200B}\x{200C}\x{200D}\x{FEFF}]'; then + ((anomaly_count++)) + echo "[MED] Zero-width characters in filename:" >> "$findings_file" + echo " File: $file" >> "$findings_file" + echo "" >> "$findings_file" + fi + + done < <(find "$fs_path" -type f -print0 2>/dev/null | head -c 500000) + + print_status "Attribute anomaly check complete: $anomaly_count anomalies" + STATS[file_attribute_anomalies]=$anomaly_count + + return 0 +} + +# ========================================== +# REVERSE ENGINEERING TRIAGE +# ========================================== + +scan_re_triage() { + print_section "Reverse Engineering Triage" + + # Check if we have access to filesystem + local fs_path="" + if [ -n "$MOUNTED_FS_PATH" ] && [ -d "$MOUNTED_FS_PATH" ]; then + fs_path="$MOUNTED_FS_PATH" + elif [ -n "$MOUNT_POINT" ] && [ -d "$MOUNT_POINT" ]; then + fs_path="$MOUNT_POINT" + fi + + # Also check carved files + local carved_dir="$SCAN_OUTPUT_DIR/carved" + if [ ! -d "$carved_dir" ]; then + carved_dir="$SCAN_OUTPUT_DIR" + fi + + if [ -z "$fs_path" ]; then + fs_path="$carved_dir" + fi + + print_status "Running automated RE triage on suspicious executables..." + + local findings_file="$SCAN_OUTPUT_DIR/re_triage.txt" + local total_findings=0 + + # Initialize findings file + cat > "$findings_file" << 'EOF' +================================================================================ +REVERSE ENGINEERING TRIAGE REPORT +================================================================================ +Generated: $(date) + +Automated analysis of suspicious executables for malware indicators. + +EOF + + # 1. capa analysis (MITRE ATT&CK capability mapping) + run_capa_analysis "$fs_path" "$findings_file" + + # 2. Suspicious API import analysis + analyze_suspicious_imports "$fs_path" "$findings_file" + + # 3. Similarity hashing (ssdeep, imphash) + compute_similarity_hashes "$fs_path" "$findings_file" + + # 4. Shellcode detection + detect_shellcode_patterns "$fs_path" "$findings_file" + + # 5. String analysis for IOCs + extract_suspicious_strings "$fs_path" "$findings_file" + + # Calculate total findings + total_findings=$((${STATS[re_capa_matches]} + ${STATS[re_suspicious_imports]} + \ + ${STATS[re_shellcode_detected]} + ${STATS[re_suspicious_strings]:-0})) + + STATS[re_triaged_files]=$total_findings + RESULTS["re_triage"]=$total_findings + + if [ $total_findings -gt 0 ]; then + print_warning "Found $total_findings RE triage findings - detailed analysis recommended" + + GUIDANCE_RECOMMENDATIONS+=("Review RE triage results in $findings_file") + GUIDANCE_RECOMMENDATIONS+=("Consider submitting suspicious files to sandbox for dynamic analysis") + + if [ "$ATTACK_MAPPING" = true ] && [ -n "${STATS[re_attack_techniques]}" ]; then + print_status "MITRE ATT&CK techniques detected:" + echo "${STATS[re_attack_techniques]}" | tr ',' '\n' | sort -u | head -20 | while read tech; do + [ -n "$tech" ] && echo " - $tech" + done + fi + else + print_success "No significant RE triage findings" + GUIDANCE_NO_ACTION+=("RE triage: No suspicious capabilities detected") + fi + + return 0 +} + +run_capa_analysis() { + local fs_path="$1" + local findings_file="$2" + + print_status "Running capa analysis (ATT&CK capability detection)..." + + echo "" >> "$findings_file" + echo "=== CAPA ANALYSIS ===" >> "$findings_file" + echo "Automated capability detection mapped to MITRE ATT&CK" >> "$findings_file" + echo "" >> "$findings_file" + + local capa_matches=0 + local attack_techniques="" + + # Check if capa is available + local capa_cmd="" + if [ -n "$CAPA_PATH" ] && [ -x "$CAPA_PATH" ]; then + capa_cmd="$CAPA_PATH" + elif command -v capa &> /dev/null; then + capa_cmd="capa" + elif [ -x "$PORTABLE_TOOLS_DIR/capa" ]; then + capa_cmd="$PORTABLE_TOOLS_DIR/capa" + fi + + if [ -z "$capa_cmd" ]; then + echo " capa not available - install with: install_capa_portable" >> "$findings_file" + echo " Download from: https://github.com/mandiant/capa/releases" >> "$findings_file" + STATS[re_capa_matches]=0 + return 0 + fi + + # Find suspicious executables to analyze + local -a target_files=() + + # Prioritize files from suspicious locations + while IFS= read -r -d '' file; do + target_files+=("$file") + [ ${#target_files[@]} -ge 50 ] && break # Limit for performance + done < <(find "$fs_path" -type f \( -name "*.exe" -o -name "*.dll" -o -name "*.scr" \) \ + \( -path "*Temp*" -o -path "*Downloads*" -o -path "*Public*" -o -path "*AppData*" \) \ + -print0 2>/dev/null) + + # Also check carved files + while IFS= read -r -d '' file; do + target_files+=("$file") + [ ${#target_files[@]} -ge 100 ] && break + done < <(find "$fs_path" -type f \( -name "*.exe" -o -name "*.dll" \) -print0 2>/dev/null) + + local analyzed=0 + for target in "${target_files[@]}"; do + [ -f "$target" ] || continue + + local capa_output + capa_output=$("$capa_cmd" -q -j "$target" 2>/dev/null) || continue + + ((analyzed++)) + + # Parse JSON output for ATT&CK techniques + if echo "$capa_output" | grep -q "attack"; then + local techniques=$(echo "$capa_output" | python3 -c " +import sys, json +try: + data = json.load(sys.stdin) + if 'rules' in data: + for rule_name, rule_data in data['rules'].items(): + if 'attack' in rule_data.get('meta', {}): + for attack in rule_data['meta']['attack']: + print(f\"{attack.get('technique', '')} - {attack.get('id', '')}\") +except: + pass +" 2>/dev/null) + + if [ -n "$techniques" ]; then + ((capa_matches++)) + echo "[HIGH] capa matches for: $(basename "$target")" >> "$findings_file" + echo " File: $target" >> "$findings_file" + echo " ATT&CK techniques detected:" >> "$findings_file" + echo "$techniques" | head -10 | sed 's/^/ - /' >> "$findings_file" + + # Collect techniques for summary + local tech_ids=$(echo "$techniques" | grep -oE 'T[0-9]+(\.[0-9]+)?' | tr '\n' ',' | sed 's/,$//') + attack_techniques="${attack_techniques}${tech_ids}," + + echo "" >> "$findings_file" + fi + fi + + done + + print_status "capa analysis complete: analyzed $analyzed files, $capa_matches with capabilities" + STATS[re_capa_matches]=$capa_matches + STATS[re_attack_techniques]="${STATS[re_attack_techniques]}${attack_techniques}" + + return 0 +} + +analyze_suspicious_imports() { + local fs_path="$1" + local findings_file="$2" + + print_status "Analyzing suspicious API imports..." + + echo "" >> "$findings_file" + echo "=== SUSPICIOUS API IMPORT ANALYSIS ===" >> "$findings_file" + echo "Detecting imports commonly used by malware" >> "$findings_file" + echo "" >> "$findings_file" + + local suspicious_count=0 + + # Suspicious API categories + # Process Injection APIs + local -a injection_apis=( + "CreateRemoteThread" "NtCreateThreadEx" "RtlCreateUserThread" + "WriteProcessMemory" "NtWriteVirtualMemory" + "VirtualAllocEx" "NtAllocateVirtualMemory" + "NtMapViewOfSection" "ZwMapViewOfSection" + "SetThreadContext" "NtSetContextThread" + "QueueUserAPC" "NtQueueApcThread" + ) + + # Process Hollowing APIs (user emphasized this!) + local -a hollowing_apis=( + "NtUnmapViewOfSection" "ZwUnmapViewOfSection" + "NtResumeThread" "ResumeThread" + "SetThreadContext" "NtSetContextThread" + "NtWriteVirtualMemory" "WriteProcessMemory" + "CreateProcess.*SUSPENDED" + ) + + # Evasion APIs + local -a evasion_apis=( + "IsDebuggerPresent" "CheckRemoteDebuggerPresent" + "NtQueryInformationProcess" "NtSetInformationThread" + "GetTickCount" "QueryPerformanceCounter" + "OutputDebugString" + ) + + # Credential Access APIs + local -a credential_apis=( + "CredEnumerate" "CredRead" + "LsaRetrievePrivateData" "LsaStorePrivateData" + "CryptUnprotectData" "CryptProtectData" + "SamConnect" "SamEnumerateUsersInDomain" + ) + + # Persistence APIs + local -a persistence_apis=( + "RegSetValueEx" "RegCreateKeyEx" + "CreateService" "StartService" + "SetWindowsHookEx" + ) + + if python3 -c "import pefile" 2>/dev/null; then + python3 << 'IMPORT_ANALYSIS_PY' +import sys +import os + +try: + import pefile +except ImportError: + sys.exit(0) + +fs_path = os.environ.get('FS_PATH', '') +findings_file = os.environ.get('FINDINGS_FILE', '') + +# Suspicious API categories +api_categories = { + "Process Injection": [ + "CreateRemoteThread", "NtCreateThreadEx", "RtlCreateUserThread", + "WriteProcessMemory", "NtWriteVirtualMemory", + "VirtualAllocEx", "NtAllocateVirtualMemory", + "NtMapViewOfSection", "ZwMapViewOfSection", + "QueueUserAPC", "NtQueueApcThread", + ], + "Process Hollowing": [ + "NtUnmapViewOfSection", "ZwUnmapViewOfSection", + "NtResumeThread", "ResumeThread", + "SetThreadContext", "NtSetContextThread", + ], + "Anti-Debug/Evasion": [ + "IsDebuggerPresent", "CheckRemoteDebuggerPresent", + "NtQueryInformationProcess", "NtSetInformationThread", + "GetTickCount", "QueryPerformanceCounter", + ], + "Credential Access": [ + "CredEnumerate", "CredRead", + "LsaRetrievePrivateData", "CryptUnprotectData", + "SamConnect", "SamEnumerateUsersInDomain", + ], + "Persistence": [ + "RegSetValueEx", "RegCreateKeyEx", + "CreateServiceA", "CreateServiceW", "StartService", + "SetWindowsHookEx", + ], +} + +attack_mapping = { + "Process Injection": "T1055", + "Process Hollowing": "T1055.012", + "Anti-Debug/Evasion": "T1497", + "Credential Access": "T1003", + "Persistence": "T1547", +} + +suspicious_count = 0 +checked = 0 + +for root, dirs, files in os.walk(fs_path): + for filename in files: + if not filename.lower().endswith(('.exe', '.dll', '.scr')): + continue + + filepath = os.path.join(root, filename) + checked += 1 + + if checked > 500: + break + + try: + pe = pefile.PE(filepath, fast_load=True) + pe.parse_data_directories(directories=[ + pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_IMPORT'] + ]) + + if not hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'): + pe.close() + continue + + # Collect all imports + imports = [] + for entry in pe.DIRECTORY_ENTRY_IMPORT: + dll_name = entry.dll.decode('utf-8', errors='ignore') + for imp in entry.imports: + if imp.name: + imports.append(imp.name.decode('utf-8', errors='ignore')) + + # Check against suspicious categories + file_findings = {} + for category, apis in api_categories.items(): + matched = [api for api in apis if any(api.lower() in imp.lower() for imp in imports)] + if matched: + file_findings[category] = matched + + if file_findings: + suspicious_count += 1 + with open(findings_file, 'a') as f: + # Determine severity + severity = "MED" + if "Process Hollowing" in file_findings or "Process Injection" in file_findings: + severity = "HIGH" + + f.write(f"[{severity}] Suspicious imports in: {filename}\n") + f.write(f" File: {filepath}\n") + + for category, apis in file_findings.items(): + f.write(f" {category} ({attack_mapping.get(category, 'N/A')}):\n") + for api in apis[:5]: # Limit to first 5 + f.write(f" - {api}\n") + + f.write("\n") + + pe.close() + + except Exception: + pass + + if checked > 500: + break + +print(f"SUSPICIOUS_IMPORTS={suspicious_count}") +IMPORT_ANALYSIS_PY + local python_result + python_result=$(FS_PATH="$fs_path" FINDINGS_FILE="$findings_file" python3 2>/dev/null << 'IMPORT_STATS' +import sys, os +try: + import pefile +except ImportError: + print("SUSPICIOUS_IMPORTS=0") + sys.exit(0) + +fs_path = os.environ.get('FS_PATH', '') +findings_file = os.environ.get('FINDINGS_FILE', '') + +hollowing_apis = ["NtUnmapViewOfSection", "ZwUnmapViewOfSection", "SetThreadContext"] +injection_apis = ["CreateRemoteThread", "WriteProcessMemory", "VirtualAllocEx", "NtMapViewOfSection"] + +suspicious_count = 0 +checked = 0 + +for root, dirs, files in os.walk(fs_path): + for filename in files: + if not filename.lower().endswith(('.exe', '.dll')): + continue + filepath = os.path.join(root, filename) + checked += 1 + if checked > 500: + break + try: + pe = pefile.PE(filepath, fast_load=True) + pe.parse_data_directories(directories=[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_IMPORT']]) + if not hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'): + pe.close() + continue + imports = [] + for entry in pe.DIRECTORY_ENTRY_IMPORT: + for imp in entry.imports: + if imp.name: + imports.append(imp.name.decode('utf-8', errors='ignore')) + all_suspicious = hollowing_apis + injection_apis + matched = [api for api in all_suspicious if any(api.lower() in imp.lower() for imp in imports)] + if matched: + suspicious_count += 1 + with open(findings_file, 'a') as f: + f.write(f"[HIGH] Injection/Hollowing APIs in: {filepath}\n") + f.write(f" APIs: {', '.join(matched)}\n\n") + pe.close() + except: + pass + if checked > 500: + break + +print(f"SUSPICIOUS_IMPORTS={suspicious_count}") +IMPORT_STATS + ) || python_result="SUSPICIOUS_IMPORTS=0" + + suspicious_count=$(echo "$python_result" | grep -oP 'SUSPICIOUS_IMPORTS=\K\d+' || echo "0") + else + echo " pefile module not available for import analysis" >> "$findings_file" + suspicious_count=0 + fi + + print_status "Import analysis complete: $suspicious_count files with suspicious imports" + STATS[re_suspicious_imports]=$suspicious_count + + return 0 +} + +compute_similarity_hashes() { + local fs_path="$1" + local findings_file="$2" + + print_status "Computing similarity hashes (imphash, ssdeep)..." + + echo "" >> "$findings_file" + echo "=== SIMILARITY HASHING ===" >> "$findings_file" + echo "Fuzzy hashes for malware family correlation" >> "$findings_file" + echo "" >> "$findings_file" + + local hash_file="$SCAN_OUTPUT_DIR/similarity_hashes.csv" + echo "filename,sha256,imphash,ssdeep" > "$hash_file" + + local processed=0 + + # Compute hashes for executables + while IFS= read -r -d '' file; do + [ -f "$file" ] || continue + ((processed++)) + + local filename=$(basename "$file") + local sha256="" + local imphash="" + local ssdeep_hash="" + + # SHA256 + if command -v sha256sum &> /dev/null; then + sha256=$(sha256sum "$file" 2>/dev/null | awk '{print $1}') + fi + + # imphash (requires pefile) + if python3 -c "import pefile" 2>/dev/null; then + imphash=$(python3 -c " +import pefile +import sys +try: + pe = pefile.PE('$file', fast_load=True) + pe.parse_data_directories(directories=[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_IMPORT']]) + print(pe.get_imphash() or '') + pe.close() +except: + pass +" 2>/dev/null) + fi + + # ssdeep + if command -v ssdeep &> /dev/null; then + ssdeep_hash=$(ssdeep -b "$file" 2>/dev/null | tail -1 | cut -d',' -f1) + fi + + # Write to CSV + echo "\"$filename\",\"$sha256\",\"$imphash\",\"$ssdeep_hash\"" >> "$hash_file" + + done < <(find "$fs_path" -type f \( -name "*.exe" -o -name "*.dll" -o -name "*.scr" \) -print0 2>/dev/null | head -c 500000) + + echo "Hashes computed for $processed executables" >> "$findings_file" + echo "Hash CSV: $hash_file" >> "$findings_file" + echo "" >> "$findings_file" + + # Check for known bad imphashes (common malware families) + local -a known_bad_imphashes=( + "d41d8cd98f00b204e9800998ecf8427e" # Empty import table + "81c39c9f04c951a456bf3016ae56c2ac" # Emotet variant + "6e0f8c0e8c0c0c8d8e8f8f0f0f8e8c8d" # Example - replace with real IOCs + ) + + if [ -f "$hash_file" ]; then + while IFS=',' read -r fn sha imp ssdeep; do + [ "$fn" = "filename" ] && continue # Skip header + imp=$(echo "$imp" | tr -d '"') + + for bad_hash in "${known_bad_imphashes[@]}"; do + if [ "$imp" = "$bad_hash" ]; then + echo "[HIGH] Known malicious imphash match:" >> "$findings_file" + echo " File: $fn" >> "$findings_file" + echo " Imphash: $imp" >> "$findings_file" + echo "" >> "$findings_file" + fi + done + done < "$hash_file" + fi + + print_status "Similarity hashing complete: $processed files processed" + + return 0 +} + +detect_shellcode_patterns() { + local fs_path="$1" + local findings_file="$2" + + print_status "Detecting shellcode patterns..." + + echo "" >> "$findings_file" + echo "=== SHELLCODE DETECTION ===" >> "$findings_file" + echo "Common shellcode patterns and position-independent code indicators" >> "$findings_file" + echo "" >> "$findings_file" + + local shellcode_count=0 + + # Common shellcode patterns (hex) + local -a shellcode_signatures=( + # GetPC techniques (position-independent code) + "e8.*00.*00.*00.*00.*58" # call $+5; pop eax + "e8.*00.*00.*00.*00.*5b" # call $+5; pop ebx + "eb.*5e" # jmp short; pop esi + "d9.*74.*24" # fnstenv [esp-c] + + # API hashing (common in shellcode) + "64.*a1.*30.*00.*00.*00" # mov eax, fs:[30h] (PEB access) + "64.*8b.*1d.*30.*00.*00" # mov ebx, fs:[30h] + + # Metasploit patterns + "fc.*e8.*.*00.*00.*00" # cld; call + "31.*c9.*64.*8b" # xor ecx,ecx; mov..fs: + + # Cobalt Strike beacon patterns + "4d.*5a.*41.*52.*55.*48" # MZ header in shellcode + ) + + # Search for shellcode patterns in files + while IFS= read -r -d '' file; do + [ -f "$file" ] || continue + + local filename=$(basename "$file") + local file_hex=$(xxd -p "$file" 2>/dev/null | tr -d '\n' | head -c 100000) + + for pattern in "${shellcode_signatures[@]}"; do + if echo "$file_hex" | grep -qiE "$pattern"; then + ((shellcode_count++)) + echo "[HIGH] Shellcode pattern detected:" >> "$findings_file" + echo " File: $file" >> "$findings_file" + echo " Pattern: $pattern" >> "$findings_file" + echo " ATT&CK: T1059.006 (Command and Scripting Interpreter: Python)" >> "$findings_file" + echo "" >> "$findings_file" + break + fi + done + + done < <(find "$fs_path" -type f \( -name "*.exe" -o -name "*.dll" -o -name "*.bin" -o -name "*.dat" -o -name "*.tmp" \) -size -10M -print0 2>/dev/null | head -c 500000) + + # Also check for raw shellcode files + while IFS= read -r -d '' file; do + [ -f "$file" ] || continue + + local file_size=$(stat -c %s "$file" 2>/dev/null || echo "0") + + # Small files with high entropy might be shellcode + if [ "$file_size" -lt 100000 ] && [ "$file_size" -gt 100 ]; then + local entropy=$(python3 -c " +import math +import sys +data = open('$file', 'rb').read() +if len(data) == 0: + print(0) + sys.exit() +freq = [0]*256 +for b in data: + freq[b] += 1 +ent = 0 +for f in freq: + if f > 0: + p = f/len(data) + ent -= p * math.log2(p) +print(f'{ent:.2f}') +" 2>/dev/null || echo "0") + + if (( $(echo "$entropy > 7.5" | bc -l 2>/dev/null || echo 0) )); then + # High entropy small file - check for shellcode indicators + local magic=$(xxd -l 10 -p "$file" 2>/dev/null) + # Not a PE file + if [[ ! "$magic" =~ ^4d5a ]]; then + ((shellcode_count++)) + echo "[MED] Potential shellcode (high entropy small file):" >> "$findings_file" + echo " File: $file" >> "$findings_file" + echo " Size: $file_size bytes" >> "$findings_file" + echo " Entropy: $entropy" >> "$findings_file" + echo "" >> "$findings_file" + fi + fi + fi + + done < <(find "$fs_path" -type f \( -name "*.bin" -o -name "*.dat" -o -name "*.tmp" -o -name "*.raw" \) -print0 2>/dev/null | head -c 200000) + + print_status "Shellcode detection complete: $shellcode_count potential shellcode found" + STATS[re_shellcode_detected]=$shellcode_count + + return 0 +} + +extract_suspicious_strings() { + local fs_path="$1" + local findings_file="$2" + + print_status "Extracting suspicious strings (IOCs, URLs, IPs)..." + + echo "" >> "$findings_file" + echo "=== SUSPICIOUS STRING EXTRACTION ===" >> "$findings_file" + echo "URLs, IPs, and potential IOCs found in executables" >> "$findings_file" + echo "" >> "$findings_file" + + local strings_file="$SCAN_OUTPUT_DIR/extracted_strings.txt" + local suspicious_strings=0 + + # IOC patterns + local ip_pattern='([0-9]{1,3}\.){3}[0-9]{1,3}' + local url_pattern='https?://[a-zA-Z0-9\.\-_/\?=&%]+' + local email_pattern='[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}' + + # Suspicious string patterns + local -a suspicious_patterns=( + "powershell" + "cmd.exe" + "WScript.Shell" + "HKEY_" + "\\\\\\\\.*\\\\.*" # UNC paths + "bitcoin" + "ransom" + "decrypt" + "password" + "mimikatz" + "sekurlsa" + "lsadump" + "hashdump" + "CobaltStrike" + "beacon" + "meterpreter" + "reverse_tcp" + "bind_shell" + ) + + echo "# Extracted suspicious strings" > "$strings_file" + echo "# Generated: $(date)" >> "$strings_file" + echo "" >> "$strings_file" + + while IFS= read -r -d '' file; do + [ -f "$file" ] || continue + + local filename=$(basename "$file") + + # Extract strings + local file_strings=$(strings -n 6 "$file" 2>/dev/null) + + # Check for IPs (excluding common internal/local) + local ips=$(echo "$file_strings" | grep -oE "$ip_pattern" | grep -vE '^(127\.|10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[01])\.|0\.0\.0\.|255\.255)' | sort -u) + + # Check for URLs + local urls=$(echo "$file_strings" | grep -oiE "$url_pattern" | sort -u) + + # Check for suspicious patterns + local found_suspicious="" + for pattern in "${suspicious_patterns[@]}"; do + if echo "$file_strings" | grep -qi "$pattern"; then + found_suspicious="${found_suspicious}${pattern}, " + fi + done + + # Report if interesting strings found + if [ -n "$ips" ] || [ -n "$urls" ] || [ -n "$found_suspicious" ]; then + ((suspicious_strings++)) + + echo "[INFO] Interesting strings in: $filename" >> "$findings_file" + echo " File: $file" >> "$findings_file" + + if [ -n "$ips" ]; then + echo " External IPs:" >> "$findings_file" + echo "$ips" | head -5 | sed 's/^/ /' >> "$findings_file" + fi + + if [ -n "$urls" ]; then + echo " URLs:" >> "$findings_file" + echo "$urls" | head -5 | sed 's/^/ /' >> "$findings_file" + fi + + if [ -n "$found_suspicious" ]; then + echo " Suspicious keywords: ${found_suspicious%, }" >> "$findings_file" + fi + + echo "" >> "$findings_file" + + # Also save to strings file + echo "=== $filename ===" >> "$strings_file" + [ -n "$ips" ] && echo "IPs: $ips" >> "$strings_file" + [ -n "$urls" ] && echo "URLs: $urls" >> "$strings_file" + echo "" >> "$strings_file" + fi + + done < <(find "$fs_path" -type f \( -name "*.exe" -o -name "*.dll" -o -name "*.scr" \) -print0 2>/dev/null | head -c 500000) + + print_status "String extraction complete: $suspicious_strings files with interesting strings" + STATS[re_suspicious_strings]=$suspicious_strings + + return 0 +} + +# ========================================== +# FILESYSTEM FORENSICS +# ========================================== + +scan_filesystem_forensics() { + print_section "Filesystem Forensics Analysis" + + # Check if we have access to filesystem + local fs_path="" + if [ -n "$MOUNTED_FS_PATH" ] && [ -d "$MOUNTED_FS_PATH" ]; then + fs_path="$MOUNTED_FS_PATH" + elif [ -n "$MOUNT_POINT" ] && [ -d "$MOUNT_POINT" ]; then + fs_path="$MOUNT_POINT" + fi + + # For MFT analysis, we need either: + # 1. Direct access to $MFT file in mounted NTFS + # 2. Raw device access + # 3. Extracted MFT file + + print_status "Running filesystem forensics analysis..." + + local findings_file="$SCAN_OUTPUT_DIR/filesystem_forensics.txt" + local total_findings=0 + + # Initialize findings file + cat > "$findings_file" << 'EOF' +================================================================================ +FILESYSTEM FORENSICS REPORT +================================================================================ +Generated: $(date) + +Analysis of MFT, USN Journal, and filesystem metadata for forensic artifacts. + +EOF + + # 1. MFT Analysis + analyze_mft "$fs_path" "$findings_file" + + # 2. USN Journal Analysis + analyze_usn_journal "$fs_path" "$findings_file" + + # 3. Timestomping Detection + detect_timestomping "$fs_path" "$findings_file" + + # 4. Deleted File Recovery Enumeration + enumerate_deleted_files "$fs_path" "$findings_file" + + # Calculate total findings + total_findings=$((${STATS[mft_deleted_recovered]} + ${STATS[mft_timestomping]} + \ + ${STATS[usn_entries]:-0} + ${STATS[filesystem_anomalies]})) + + RESULTS["filesystem_forensics"]=$total_findings + + if [ $total_findings -gt 0 ]; then + print_warning "Found $total_findings filesystem forensic findings - review recommended" + + GUIDANCE_RECOMMENDATIONS+=("Review filesystem forensics in $findings_file") + + if [ "$ATTACK_MAPPING" = true ]; then + print_status "Filesystem forensic summary:" + [ "${STATS[mft_timestomping]}" -gt 0 ] && echo " - Timestomping detected: ${STATS[mft_timestomping]} files (${ATTACK_TECHNIQUES[timestomping]})" + [ "${STATS[mft_deleted_recovered]}" -gt 0 ] && echo " - Deleted records found: ${STATS[mft_deleted_recovered]}" + fi + else + print_success "No significant filesystem forensic findings" + GUIDANCE_NO_ACTION+=("Filesystem forensics: No suspicious findings") + fi + + return 0 +} + +analyze_mft() { + local fs_path="$1" + local findings_file="$2" + + print_status "Analyzing Master File Table (MFT)..." + + echo "" >> "$findings_file" + echo "=== MFT ANALYSIS ===" >> "$findings_file" + echo "Master File Table contains metadata for all NTFS files" >> "$findings_file" + echo "" >> "$findings_file" + + local mft_file="" + local mft_analyzed=false + + # Try to find $MFT file + if [ -f "$fs_path/\$MFT" ]; then + mft_file="$fs_path/\$MFT" + elif [ -f "$fs_path/MFT" ]; then + mft_file="$fs_path/MFT" + fi + + # Check for extracted/carved MFT + if [ -z "$mft_file" ]; then + while IFS= read -r -d '' file; do + mft_file="$file" + break + done < <(find "$SCAN_OUTPUT_DIR" -iname "*mft*" -type f -print0 2>/dev/null) + fi + + if [ -n "$mft_file" ] && [ -f "$mft_file" ]; then + print_status "Found MFT file: $mft_file" + + # Use analyzeMFT if available + if command -v analyzeMFT.py &> /dev/null || python3 -c "from analyzemft import mft" 2>/dev/null; then + local mft_output="$SCAN_OUTPUT_DIR/mft_analysis.csv" + + python3 << 'MFT_ANALYSIS_PY' 2>/dev/null +import sys +import os +import csv +from datetime import datetime + +try: + from analyzemft import mft +except ImportError: + sys.exit(1) + +mft_file = "$mft_file" +output_file = "$mft_output" +findings_file = "$findings_file" + +print(f"Analyzing MFT: {mft_file}") + +deleted_count = 0 +timestomp_count = 0 + +try: + mft_session = mft.MftSession() + mft_session.open(mft_file) + + with open(output_file, 'w', newline='') as csvfile: + writer = csv.writer(csvfile) + writer.writerow(['Record', 'Filename', 'Path', 'SI_Created', 'FN_Created', 'Deleted', 'Flags']) + + for record in mft_session: + if record is None: + continue + + is_deleted = record.flags.deleted if hasattr(record, 'flags') else False + filename = getattr(record, 'filename', 'Unknown') + path = getattr(record, 'path', 'Unknown') + + # Check for timestomping ($SI vs $FN time difference) + si_created = getattr(record, 'si_crtime', None) + fn_created = getattr(record, 'fn_crtime', None) + + timestomped = False + if si_created and fn_created: + try: + diff = abs((si_created - fn_created).total_seconds()) + if diff > 1: # More than 1 second difference is suspicious + timestomped = True + timestomp_count += 1 + except: + pass + + if is_deleted: + deleted_count += 1 + + writer.writerow([ + getattr(record, 'recordnum', 'N/A'), + filename, + path, + str(si_created) if si_created else 'N/A', + str(fn_created) if fn_created else 'N/A', + str(is_deleted), + 'TIMESTOMPED' if timestomped else '' + ]) + + with open(findings_file, 'a') as f: + f.write(f"MFT records analyzed successfully\n") + f.write(f" Deleted records found: {deleted_count}\n") + f.write(f" Potential timestomping: {timestomp_count}\n") + f.write(f" Full analysis: {output_file}\n\n") + + print(f"MFT_DELETED={deleted_count}") + print(f"MFT_TIMESTOMP={timestomp_count}") + +except Exception as e: + with open(findings_file, 'a') as f: + f.write(f" MFT analysis error: {str(e)}\n\n") +MFT_ANALYSIS_PY + mft_analyzed=true + else + echo " analyzeMFT not available - basic analysis only" >> "$findings_file" + echo " Install with: pip3 install analyzemft" >> "$findings_file" + + # Basic MFT analysis using strings + if command -v strings &> /dev/null; then + local mft_strings=$(strings -n 6 "$mft_file" 2>/dev/null) + + # Look for suspicious filenames in MFT + local suspicious_in_mft=$(echo "$mft_strings" | grep -iE "(mimikatz|psexec|procdump|lazagne|beacon|cobaltstrike)" | wc -l) + + if [ "$suspicious_in_mft" -gt 0 ]; then + echo "[HIGH] Suspicious filenames found in MFT:" >> "$findings_file" + echo "$mft_strings" | grep -iE "(mimikatz|psexec|procdump|lazagne|beacon|cobaltstrike)" | head -20 | sed 's/^/ /' >> "$findings_file" + echo "" >> "$findings_file" + fi + fi + fi + else + echo " $MFT file not accessible (normal for mounted NTFS on Linux)" >> "$findings_file" + echo " To analyze MFT, extract it using:" >> "$findings_file" + echo " - FTK Imager" >> "$findings_file" + echo " - icat (from sleuthkit): icat /dev/sdX 0 > mft.bin" >> "$findings_file" + echo "" >> "$findings_file" + fi + + return 0 +} + +analyze_usn_journal() { + local fs_path="$1" + local findings_file="$2" + + print_status "Analyzing USN Journal (change journal)..." + + echo "" >> "$findings_file" + echo "=== USN JOURNAL ANALYSIS ===" >> "$findings_file" + echo "NTFS change journal tracks file system changes" >> "$findings_file" + echo "" >> "$findings_file" + + local usn_file="" + local usn_count=0 + + # Try to find $UsnJrnl:$J + if [ -f "$fs_path/\$Extend/\$UsnJrnl:\$J" ]; then + usn_file="$fs_path/\$Extend/\$UsnJrnl:\$J" + elif [ -f "$fs_path/\$UsnJrnl" ]; then + usn_file="$fs_path/\$UsnJrnl" + fi + + # Check for extracted USN journal + if [ -z "$usn_file" ]; then + while IFS= read -r -d '' file; do + usn_file="$file" + break + done < <(find "$SCAN_OUTPUT_DIR" -iname "*usnjrnl*" -o -iname "*usn*journal*" -type f -print0 2>/dev/null) + fi + + if [ -n "$usn_file" ] && [ -f "$usn_file" ]; then + print_status "Found USN Journal: $usn_file" + + # Parse USN journal with Python + python3 << 'USN_ANALYSIS_PY' 2>/dev/null +import sys +import struct +import os +from datetime import datetime, timedelta + +usn_file = "$usn_file" +findings_file = "$findings_file" + +# USN record structure (simplified) +USN_RECORD_V2 = struct.Struct(' len(data): + return None, 0 + + record_len, major_version, minor_version, file_ref, parent_ref, usn, \ + timestamp, reason, source_info, security_id, file_attributes, \ + filename_len, filename_offset = USN_RECORD_V2.unpack_from(data, offset) + + if record_len == 0 or record_len > 4096: + return None, 0 + + # Get filename + filename_start = offset + filename_offset + filename_end = filename_start + filename_len + if filename_end > len(data): + return None, record_len + + filename = data[filename_start:filename_end].decode('utf-16-le', errors='ignore') + + return { + 'filename': filename, + 'timestamp': filetime_to_datetime(timestamp), + 'reason': reason, + 'file_ref': file_ref, + 'parent_ref': parent_ref, + }, record_len + + except Exception as e: + return None, 0 + +# Reason flags +REASON_DATA_OVERWRITE = 0x00000001 +REASON_FILE_CREATE = 0x00000100 +REASON_FILE_DELETE = 0x00000200 +REASON_RENAME_OLD_NAME = 0x00001000 +REASON_RENAME_NEW_NAME = 0x00002000 +REASON_SECURITY_CHANGE = 0x00000800 + +suspicious_patterns = ['mimikatz', 'psexec', 'procdump', 'lazagne', 'beacon', 'cobaltstrike', + 'powershell', 'cmd.exe', '.ps1', '.bat', '.vbs', '.hta'] + +try: + with open(usn_file, 'rb') as f: + data = f.read(50 * 1024 * 1024) # Read up to 50MB + + offset = 0 + while offset < len(data) - 64: + record, record_len = parse_usn_record(data, offset) + + if record is None: + offset += 8 # Skip to next potential record + continue + + total_records += 1 + offset += record_len + + filename = record.get('filename', '').lower() + + # Check for suspicious filenames + for pattern in suspicious_patterns: + if pattern in filename: + suspicious_operations.append({ + 'filename': record.get('filename'), + 'timestamp': record.get('timestamp'), + 'reason': record.get('reason'), + 'pattern': pattern + }) + break + + # Write findings + with open(findings_file, 'a') as f: + f.write(f"USN Journal records parsed: {total_records}\n") + f.write(f"Suspicious operations found: {len(suspicious_operations)}\n\n") + + if suspicious_operations: + f.write("[MED] Suspicious file operations in USN Journal:\n") + for op in suspicious_operations[:20]: + f.write(f" {op['timestamp']}: {op['filename']} (matched: {op['pattern']})\n") + f.write("\n") + + print(f"USN_ENTRIES={len(suspicious_operations)}") + +except Exception as e: + with open(findings_file, 'a') as f: + f.write(f" USN Journal parsing error: {str(e)}\n\n") +USN_ANALYSIS_PY + else + echo " USN Journal not accessible" >> "$findings_file" + echo " To extract USN Journal, use:" >> "$findings_file" + echo " - FTK Imager" >> "$findings_file" + echo " - ExtractUsnJrnl tool" >> "$findings_file" + echo "" >> "$findings_file" + fi + + STATS[usn_entries]=$usn_count + + return 0 +} + +detect_timestomping() { + local fs_path="$1" + local findings_file="$2" + + print_status "Detecting timestomping (time manipulation)..." + + echo "" >> "$findings_file" + echo "=== TIMESTOMPING DETECTION ===" >> "$findings_file" + echo "Looking for signs of timestamp manipulation" >> "$findings_file" + echo "" >> "$findings_file" + + local timestomp_count=0 + + # Method 1: Files with creation time after modification time (impossible normally) + print_status " Checking for impossible timestamp combinations..." + + while IFS= read -r -d '' file; do + [ -f "$file" ] || continue + + # Get timestamps + local ctime=$(stat -c '%W' "$file" 2>/dev/null) # Birth time (if available) + local mtime=$(stat -c '%Y' "$file" 2>/dev/null) # Modification time + local atime=$(stat -c '%X' "$file" 2>/dev/null) # Access time + + # On Linux, birth time might not be available + if [ "$ctime" != "0" ] && [ -n "$ctime" ] && [ -n "$mtime" ]; then + if [ "$ctime" -gt "$mtime" ]; then + ((timestomp_count++)) + echo "[HIGH] Impossible timestamp (creation > modification):" >> "$findings_file" + echo " File: $file" >> "$findings_file" + echo " Creation: $(date -d @$ctime 2>/dev/null || echo $ctime)" >> "$findings_file" + echo " Modified: $(date -d @$mtime 2>/dev/null || echo $mtime)" >> "$findings_file" + echo " ATT&CK: ${ATTACK_TECHNIQUES[timestomping]}" >> "$findings_file" + echo "" >> "$findings_file" + fi + fi + + done < <(find "$fs_path" -type f \( -name "*.exe" -o -name "*.dll" -o -name "*.ps1" -o -name "*.bat" \) -print0 2>/dev/null | head -c 500000) + + # Method 2: Files with suspiciously round timestamps (exactly on the hour/minute) + print_status " Checking for suspiciously precise timestamps..." + + while IFS= read -r -d '' file; do + [ -f "$file" ] || continue + + local mtime=$(stat -c '%Y' "$file" 2>/dev/null) + if [ -n "$mtime" ]; then + # Check if timestamp is exactly on a minute boundary (seconds = 0) + local seconds=$((mtime % 60)) + local minutes=$(( (mtime / 60) % 60 )) + + # Suspiciously round: seconds=0 AND minutes=0 (exactly on the hour) + if [ "$seconds" -eq 0 ] && [ "$minutes" -eq 0 ]; then + local filename=$(basename "$file") + # Skip common system files that might legitimately have round timestamps + if ! echo "$filename" | grep -qiE "^(desktop\.ini|thumbs\.db|ntuser\.dat)$"; then + ((timestomp_count++)) + echo "[LOW] Suspiciously round timestamp (exactly on hour):" >> "$findings_file" + echo " File: $file" >> "$findings_file" + echo " Modified: $(date -d @$mtime 2>/dev/null || echo $mtime)" >> "$findings_file" + echo "" >> "$findings_file" + fi + fi + fi + + done < <(find "$fs_path" -type f \( -name "*.exe" -o -name "*.dll" \) -print0 2>/dev/null | head -c 200000) + + # Method 3: Files in temp directories with very old timestamps + print_status " Checking for anachronistic files in temp directories..." + + local two_years_ago=$(date -d "2 years ago" +%s 2>/dev/null || echo $(($(date +%s) - 63072000))) + + while IFS= read -r -d '' file; do + [ -f "$file" ] || continue + + local mtime=$(stat -c '%Y' "$file" 2>/dev/null) + if [ -n "$mtime" ] && [ "$mtime" -lt "$two_years_ago" ]; then + ((timestomp_count++)) + echo "[MED] Old file in temp directory (possible timestomping):" >> "$findings_file" + echo " File: $file" >> "$findings_file" + echo " Modified: $(date -d @$mtime 2>/dev/null || echo $mtime)" >> "$findings_file" + echo " ATT&CK: ${ATTACK_TECHNIQUES[timestomping]}" >> "$findings_file" + echo "" >> "$findings_file" + fi + + done < <(find "$fs_path" -type f \( -path "*Temp*" -o -path "*tmp*" \) \( -name "*.exe" -o -name "*.dll" -o -name "*.ps1" \) -print0 2>/dev/null) + + print_status "Timestomping detection complete: $timestomp_count potential cases" + STATS[mft_timestomping]=$timestomp_count + + return 0 +} + +enumerate_deleted_files() { + local fs_path="$1" + local findings_file="$2" + + print_status "Enumerating potentially recoverable deleted files..." + + echo "" >> "$findings_file" + echo "=== DELETED FILE ENUMERATION ===" >> "$findings_file" + echo "Files that may be recoverable from slack space or MFT" >> "$findings_file" + echo "" >> "$findings_file" + + local deleted_count=0 + + # Check Recycle Bin contents + print_status " Checking Recycle Bin..." + + local recycle_dirs=( + "$fs_path/\$Recycle.Bin" + "$fs_path/RECYCLER" + "$fs_path/Recycled" + ) + + for recycle_dir in "${recycle_dirs[@]}"; do + if [ -d "$recycle_dir" ]; then + local bin_count=$(find "$recycle_dir" -type f 2>/dev/null | wc -l) + if [ "$bin_count" -gt 0 ]; then + echo "Recycle Bin contents ($recycle_dir): $bin_count files" >> "$findings_file" + + # List suspicious deleted files + while IFS= read -r -d '' file; do + local filename=$(basename "$file") + local ext="${filename##*.}" + + if echo "$ext" | grep -qiE "^(exe|dll|ps1|bat|vbs|js)$"; then + ((deleted_count++)) + echo " [INFO] Deleted executable: $file" >> "$findings_file" + + # Try to get original filename from $I file + local info_file="${file/$filename/\$I${filename#\$R}}" + if [ -f "$info_file" ]; then + # Parse $I file for original path (simplified) + local orig_path=$(strings "$info_file" 2>/dev/null | grep -E "^[A-Z]:" | head -1) + [ -n "$orig_path" ] && echo " Original path: $orig_path" >> "$findings_file" + fi + fi + + done < <(find "$recycle_dir" -type f -name '$R*' -print0 2>/dev/null) + + echo "" >> "$findings_file" + fi + fi + done + + # Check for orphaned files (files with no parent directory entry) + # This is limited without raw disk access + print_status " Checking for file system anomalies..." + + # Look for files with unusual permissions or ownership + while IFS= read -r -d '' file; do + local filename=$(basename "$file") + + # Files named with $ prefix (system files) + if [[ "$filename" == \$* ]] && [[ ! "$filename" =~ ^\$(Recycle|MFT|Extend) ]]; then + ((deleted_count++)) + echo "[INFO] System/hidden file outside expected location:" >> "$findings_file" + echo " File: $file" >> "$findings_file" + echo "" >> "$findings_file" + fi + + done < <(find "$fs_path" -maxdepth 5 -type f -name '$*' -print0 2>/dev/null) + + # Summary + if [ "$deleted_count" -gt 0 ]; then + echo "Total deleted/suspicious files found: $deleted_count" >> "$findings_file" + echo "" >> "$findings_file" + echo "For deeper deleted file recovery, use:" >> "$findings_file" + echo " - Autopsy / Sleuth Kit (file carving)" >> "$findings_file" + echo " - foremost / scalpel (signature-based carving)" >> "$findings_file" + echo " - R-Studio / PhotoRec (data recovery)" >> "$findings_file" + else + echo "No deleted executables found in accessible locations" >> "$findings_file" + echo "Note: Deep deleted file recovery requires raw disk access" >> "$findings_file" + fi + + print_status "Deleted file enumeration complete: $deleted_count items" + STATS[mft_deleted_recovered]=$deleted_count + + return 0 +} + +# ========================================== +# DEEP SCAN FUNCTIONS +# ========================================== + +scan_entropy() { + print_section "Entropy Analysis" + + print_status "Analyzing disk entropy by region (detecting encryption/packing)..." + + local high_entropy_regions=0 + local device_path="$DEVICE" + + python3 << ENTROPY_EOF +import subprocess +import math +import sys +import os + +chunk_size_mb = 50 +num_chunks = 20 +device = os.environ.get('SCAN_DEVICE', '$device_path') + +print(f"Scanning {num_chunks} regions of {chunk_size_mb}MB each...") +print("-" * 70) + +suspicious = 0 +for i in range(num_chunks): + offset = i * chunk_size_mb + try: + proc = subprocess.Popen( + ['dd', 'if=' + device, 'bs=1M', f'count={chunk_size_mb}', f'skip={offset}'], + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL + ) + data, _ = proc.communicate(timeout=60) + except Exception as e: + print(f"Error reading region {offset}MB: {e}") + continue + + if len(data) == 0: + continue + + freq = [0]*256 + for b in data: + freq[b] += 1 + + entropy = 0 + for f in freq: + if f > 0: + p = f / len(data) + entropy -= p * math.log2(p) + + zero_pct = (freq[0] / len(data)) * 100 + + status = "" + if entropy > 7.5: + status = "HIGH ENTROPY" + suspicious += 1 + elif entropy < 0.5: + status = "Empty" + elif entropy > 6.0: + status = "Data present" + else: + status = "Low entropy" + + if entropy > 7.5 or (entropy > 5.0 and entropy < 7.5): + print(f"Region {offset:5d}MB: Entropy={entropy:.2f}/8.0, Zeros={zero_pct:5.1f}% - {status}") + +print("-" * 70) +if suspicious > 0: + print(f"Found {suspicious} high-entropy regions (possible encryption/compression)") + sys.exit(1) +else: + print("No suspicious high-entropy regions detected") + sys.exit(0) +ENTROPY_EOF + + if [ $? -eq 1 ]; then + RESULTS["entropy"]=1 + else + RESULTS["entropy"]=0 + fi + + return 0 +} + +scan_file_carving() { + print_section "File Carving (Deleted File Recovery)" + + local carve_dir="$SCAN_OUTPUT_DIR/carved" + + # Check disk space before carving + local estimated_space=$((DEVICE_SIZE_MB / 10)) # Rough estimate + if ! check_disk_space $estimated_space "/tmp"; then + print_warning "Skipping file carving due to insufficient disk space" + RESULTS["carved_files"]=0 + RESULTS["carved_malware"]=0 + return 0 + fi + + mkdir -p "$carve_dir" + + print_status "Running foremost to recover deleted files..." + + foremost -t all -i "$DEVICE" -o "$carve_dir" 2>&1 | tail -10 || true + + # Check results + local carved_count=$(find "$carve_dir" -type f ! -name "audit.txt" 2>/dev/null | wc -l) if [ "$carved_count" -gt 0 ]; then print_warning "Recovered $carved_count deleted files" @@ -3926,6 +7644,10 @@ generate_html_report() { .guidance-box { background: #fff3cd; padding: 15px; border-radius: 5px; margin: 15px 0; border-left: 4px solid #ffc107; } .timestamp { color: #666; font-size: 0.9em; } .stat-value { font-weight: bold; color: #333; } + .forensic-box { background: #f0f8ff; padding: 15px; border-radius: 5px; margin: 15px 0; border-left: 4px solid #6c5ce7; } + .forensic-finding { background: #fff5f5; padding: 10px; border-radius: 4px; margin: 8px 0; border-left: 3px solid #e74c3c; } + .attack-badge { display: inline-block; background: #d63031; color: white; padding: 2px 8px; border-radius: 3px; font-size: 0.85em; margin: 2px; } + .finding-category { font-weight: bold; color: #6c5ce7; margin-bottom: 5px; } @@ -3982,6 +7704,116 @@ HTMLEOF cat >> "$html_file" << HTMLEOF +HTMLEOF + + # Add forensic findings section if forensic analysis was enabled + if [ "$FORENSIC_ANALYSIS" = true ] || [ "$DO_PERSISTENCE_SCAN" = true ] || \ + [ "$DO_EXECUTION_SCAN" = true ] || [ "$DO_FILE_ANOMALIES" = true ] || \ + [ "$DO_RE_TRIAGE" = true ] || [ "$DO_MFT_ANALYSIS" = true ]; then + + cat >> "$html_file" << HTMLEOF +

Behavioral & Forensic Analysis

+
+

Forensic modules executed: + $([ "$DO_PERSISTENCE_SCAN" = true ] || [ "$FORENSIC_ANALYSIS" = true ] && echo "Persistence, ") + $([ "$DO_EXECUTION_SCAN" = true ] || [ "$FORENSIC_ANALYSIS" = true ] && echo "Execution, ") + $([ "$DO_FILE_ANOMALIES" = true ] || [ "$FORENSIC_ANALYSIS" = true ] && echo "File Anomalies, ") + $([ "$DO_RE_TRIAGE" = true ] || [ "$FORENSIC_ANALYSIS" = true ] && echo "RE Triage, ") + $([ "$DO_MFT_ANALYSIS" = true ] || [ "$FORENSIC_ANALYSIS" = true ] && echo "MFT/Filesystem") +

+
+ + + +HTMLEOF + + # Persistence findings + if [ "$DO_PERSISTENCE_SCAN" = true ] || [ "$FORENSIC_ANALYSIS" = true ]; then + cat >> "$html_file" << HTMLEOF + + + + + + +HTMLEOF + fi + + # Execution findings + if [ "$DO_EXECUTION_SCAN" = true ] || [ "$FORENSIC_ANALYSIS" = true ]; then + cat >> "$html_file" << HTMLEOF + + + + + + +HTMLEOF + fi + + # File anomalies findings + if [ "$DO_FILE_ANOMALIES" = true ] || [ "$FORENSIC_ANALYSIS" = true ]; then + cat >> "$html_file" << HTMLEOF + + + + + + +HTMLEOF + fi + + # RE triage findings + if [ "$DO_RE_TRIAGE" = true ] || [ "$FORENSIC_ANALYSIS" = true ]; then + cat >> "$html_file" << HTMLEOF + + + + + +HTMLEOF + fi + + # MFT findings + if [ "$DO_MFT_ANALYSIS" = true ] || [ "$FORENSIC_ANALYSIS" = true ]; then + cat >> "$html_file" << HTMLEOF + + + + +HTMLEOF + fi + + cat >> "$html_file" << HTMLEOF +
CategoryCountDescription
Persistence Artifacts
  Registry Run Keys${STATS[persistence_run_keys]:-0}T1547.001
  Services${STATS[persistence_services]:-0}T1543.003
  Scheduled Tasks${STATS[persistence_tasks]:-0}T1053.005
  Startup Folders${STATS[persistence_startup]:-0}T1547.001
  WMI Subscriptions${STATS[persistence_wmi]:-0}T1546.003
Execution Artifacts
  Prefetch Files${STATS[execution_prefetch]:-0}Execution evidence
  Amcache Entries${STATS[execution_amcache]:-0}SHA1 hashes available
  Shimcache Entries${STATS[execution_shimcache]:-0}Application compatibility
  UserAssist Records${STATS[execution_userassist]:-0}GUI execution
  Execution Anomalies${STATS[execution_anomalies]:-0}Suspicious paths/names
File Anomalies
  Magic/Extension Mismatch${STATS[file_magic_mismatch]:-0}T1036.005
  Alternate Data Streams${STATS[file_ads]:-0}Hidden data (NTFS)
  Suspicious Locations${STATS[file_suspicious_location]:-0}Executables in temp/etc
  Packed Executables${STATS[file_packed]:-0}Possible obfuscation
  Timestomping Detected${STATS[timestomping_detected]:-0}T1070.006
RE Triage
  Files Analyzed${STATS[re_triaged_files]:-0}capa/pefile analysis
  Suspicious Imports${STATS[re_suspicious_imports]:-0}Process injection/hollowing APIs
  Suspicious Strings${STATS[re_suspicious_strings]:-0}URLs, IPs, commands
  ATT&CK Techniques${STATS[attack_techniques_mapped]:-0}MITRE mappings found
Filesystem Forensics
  MFT Records Parsed${STATS[mft_records_parsed]:-0}\$MFT analysis
  Deleted Files Found${STATS[mft_deleted_recovered]:-0}Recoverable entries
  USN Journal Entries${STATS[usn_entries_parsed]:-0}\$UsnJrnl analysis
+HTMLEOF + + # Show MITRE ATT&CK summary if any techniques mapped + local total_attack_mapped=${STATS[attack_techniques_mapped]:-0} + if [ "$total_attack_mapped" -gt 0 ]; then + cat >> "$html_file" << HTMLEOF +

MITRE ATT&CK Techniques Detected

+
+

The following techniques were identified during forensic analysis:

+

+HTMLEOF + # Add badges for detected techniques + [ "${STATS[persistence_run_keys]:-0}" -gt 0 ] && echo 'T1547.001 Registry Run Keys' >> "$html_file" + [ "${STATS[persistence_services]:-0}" -gt 0 ] && echo 'T1543.003 Windows Service' >> "$html_file" + [ "${STATS[persistence_tasks]:-0}" -gt 0 ] && echo 'T1053.005 Scheduled Task' >> "$html_file" + [ "${STATS[persistence_wmi]:-0}" -gt 0 ] && echo 'T1546.003 WMI Event Sub' >> "$html_file" + [ "${STATS[file_magic_mismatch]:-0}" -gt 0 ] && echo 'T1036.005 Masquerading' >> "$html_file" + [ "${STATS[timestomping_detected]:-0}" -gt 0 ] && echo 'T1070.006 Timestomping' >> "$html_file" + [ "${STATS[re_suspicious_imports]:-0}" -gt 0 ] && echo 'T1055 Process Injection' >> "$html_file" + + cat >> "$html_file" << HTMLEOF +

+
+HTMLEOF + fi + fi + + cat >> "$html_file" << HTMLEOF

Output Location

@@ -4073,7 +7905,58 @@ generate_json_report() { "total_findings": $total_findings, "items_to_review": $total_findings }, - "output_directory": "$SCAN_OUTPUT_DIR" + "output_directory": "$SCAN_OUTPUT_DIR", + "behavioral_findings": { + "enabled": $([ "$FORENSIC_ANALYSIS" = true ] || [ "$DO_PERSISTENCE_SCAN" = true ] || [ "$DO_EXECUTION_SCAN" = true ] || [ "$DO_FILE_ANOMALIES" = true ] || [ "$DO_RE_TRIAGE" = true ] || [ "$DO_MFT_ANALYSIS" = true ] && echo "true" || echo "false"), + "persistence": { + "run_keys": ${STATS[persistence_run_keys]:-0}, + "services": ${STATS[persistence_services]:-0}, + "scheduled_tasks": ${STATS[persistence_tasks]:-0}, + "startup_folders": ${STATS[persistence_startup]:-0}, + "wmi_subscriptions": ${STATS[persistence_wmi]:-0}, + "dll_hijack_paths": ${STATS[persistence_dll_hijack]:-0} + }, + "execution_evidence": { + "prefetch_files": ${STATS[execution_prefetch]:-0}, + "amcache_entries": ${STATS[execution_amcache]:-0}, + "shimcache_entries": ${STATS[execution_shimcache]:-0}, + "userassist_records": ${STATS[execution_userassist]:-0}, + "srum_entries": ${STATS[execution_srum]:-0}, + "bam_entries": ${STATS[execution_bam]:-0}, + "execution_anomalies": ${STATS[execution_anomalies]:-0} + }, + "file_anomalies": { + "magic_mismatch": ${STATS[file_magic_mismatch]:-0}, + "alternate_data_streams": ${STATS[file_ads]:-0}, + "suspicious_locations": ${STATS[file_suspicious_location]:-0}, + "packed_executables": ${STATS[file_packed]:-0}, + "attribute_anomalies": ${STATS[file_attribute_anomalies]:-0}, + "timestomping_detected": ${STATS[timestomping_detected]:-0} + }, + "re_triage": { + "files_analyzed": ${STATS[re_triaged_files]:-0}, + "suspicious_imports": ${STATS[re_suspicious_imports]:-0}, + "suspicious_strings": ${STATS[re_suspicious_strings]:-0}, + "capa_capabilities": ${STATS[capa_capabilities]:-0} + }, + "filesystem_forensics": { + "mft_records_parsed": ${STATS[mft_records_parsed]:-0}, + "deleted_files_recovered": ${STATS[mft_deleted_recovered]:-0}, + "usn_entries_parsed": ${STATS[usn_entries_parsed]:-0} + }, + "attack_techniques": { + "total_mapped": ${STATS[attack_techniques_mapped]:-0}, + "techniques": [ + $([ "${STATS[persistence_run_keys]:-0}" -gt 0 ] && echo '"T1547.001",' || echo "") + $([ "${STATS[persistence_services]:-0}" -gt 0 ] && echo '"T1543.003",' || echo "") + $([ "${STATS[persistence_tasks]:-0}" -gt 0 ] && echo '"T1053.005",' || echo "") + $([ "${STATS[persistence_wmi]:-0}" -gt 0 ] && echo '"T1546.003",' || echo "") + $([ "${STATS[file_magic_mismatch]:-0}" -gt 0 ] && echo '"T1036.005",' || echo "") + $([ "${STATS[timestomping_detected]:-0}" -gt 0 ] && echo '"T1070.006",' || echo "") + $([ "${STATS[re_suspicious_imports]:-0}" -gt 0 ] && echo '"T1055"' || echo '"NONE"') + ] + } + } } JSONEOF @@ -4100,10 +7983,57 @@ declare -g TUI_OPT_HTML=false declare -g TUI_OPT_JSON=false declare -g TUI_OPT_VERIFY_HASH=false declare -g TUI_OPT_KEEP_OUTPUT=false +# Forensic analysis options +declare -g TUI_OPT_FORENSIC_ALL=false +declare -g TUI_OPT_PERSISTENCE=false +declare -g TUI_OPT_EXECUTION=false +declare -g TUI_OPT_FILE_ANOMALIES=false +declare -g TUI_OPT_RE_TRIAGE=false +declare -g TUI_OPT_MFT_ANALYSIS=false declare -g TUI_DETECTED_TYPE="" # Menu items count (added 2 for scan scope: full, slack) -TUI_MENU_ITEMS=17 +TUI_MENU_ITEMS=23 + +# Track if TUI has been rendered before (for in-place updates) +declare -g TUI_RENDERED=false + +# Clear to end of line escape sequence +CLR_EOL="\033[K" + +# TUI box drawing constants +TUI_TOP="${HEADER_COLOR}╔══════════════════════════════════════════════════════════════════════╗${NC}" +TUI_SEP="${HEADER_COLOR}╠══════════════════════════════════════════════════════════════════════╣${NC}" +TUI_DIV="${HEADER_COLOR}╟──────────────────────────────────────────────────────────────────────╢${NC}" +TUI_BOT="${HEADER_COLOR}╚══════════════════════════════════════════════════════════════════════╝${NC}" + +# Print a TUI line directly +tui_line() { + echo -e "$1" +} + +# Print a section header +# Usage: tui_section "TITLE" +tui_section() { + local title="$1" + local title_len=${#title} + local padding=$((68 - title_len)) # 70 - 2 (prefix " ") + [ $padding -lt 0 ] && padding=0 + local pad_str=$(printf '%*s' "$padding" '') + tui_line "$TUI_DIV" + tui_line "${HEADER_COLOR}║${NC} ${BOLD}${title}${NC}${pad_str}${HEADER_COLOR}║${NC}" + tui_line "$TUI_DIV" +} + +# Print a menu item line +# Usage: tui_item "content_with_trailing_spaces_to_fill_width" +tui_item() { + local idx="$1" + local content="$2" + local prefix=" " + [ $TUI_SELECTED -eq $idx ] && prefix="${CYAN}▶ ${NC}" + tui_line "${HEADER_COLOR}║${NC}${prefix}${content}${HEADER_COLOR}║${NC}" +} # Read a single keypress (including arrow keys) tui_read_key() { @@ -4111,23 +8041,25 @@ tui_read_key() { IFS= read -rsn1 key 2>/dev/null if [[ $key == $'\x1b' ]]; then - read -rsn2 -t 0.1 key 2>/dev/null + read -rsn2 -t 0.05 key 2>/dev/null + # Drain any remaining escape sequence bytes (mouse events, etc) + while read -rsn1 -t 0.001 _ 2>/dev/null; do :; done case "$key" in '[A') echo "UP" ;; '[B') echo "DOWN" ;; '[C') echo "RIGHT" ;; '[D') echo "LEFT" ;; - *) echo "ESC" ;; + *) echo "IGNORED" ;; esac elif [[ $key == "" ]]; then echo "ENTER" elif [[ $key == " " ]]; then echo "SPACE" - elif [[ $key == "q" ]] || [[ $key == "Q" ]]; then + elif [[ $key == "q" || $key == "Q" ]]; then echo "QUIT" - elif [[ $key == "s" ]] || [[ $key == "S" ]]; then + elif [[ $key == "s" || $key == "S" ]]; then echo "START" - elif [[ $key == "i" ]] || [[ $key == "I" ]]; then + elif [[ $key == "i" || $key == "I" ]]; then echo "INPUT" elif [[ $key == "1" ]]; then echo "1" @@ -4136,163 +8068,102 @@ tui_read_key() { elif [[ $key == "3" ]]; then echo "3" else - echo "$key" + echo "IGNORED" fi } -# Get checkbox display +# Get checkbox display (cleaner characters) tui_checkbox() { if [ "$1" = true ]; then - echo -e "${GREEN}[✓]${NC}" + printf "${GREEN}[✓]${NC}" else - echo -e "${MUTED_COLOR}[ ]${NC}" + printf "${MUTED_COLOR}[ ]${NC}" fi } -# Get radio button display +# Get radio button display (cleaner characters) tui_radio() { if [ "$1" = "$2" ]; then - echo -e "${GREEN}(●)${NC}" + printf "${GREEN}(●)${NC}" else - echo -e "${MUTED_COLOR}( )${NC}" + printf "${MUTED_COLOR}( )${NC}" fi } # Render the TUI tui_render() { - # Clear screen and move to top - printf "\033[H\033[2J" - - local width=70 - local hl="${HEADER_COLOR}" - local nc="${NC}" - local sel_prefix="" - local sel_suffix="" + # Clear screen and hide cursor + printf "\033[2J\033[H\033[?25l" # Header - echo -e "${hl}╔══════════════════════════════════════════════════════════════════════╗${nc}" - echo -e "${hl}║${nc} ${BOLD}DMS - DRIVE MALWARE SCAN${nc} ${hl}║${nc}" - echo -e "${hl}║${nc} ${MUTED_COLOR}Use ↑↓ to navigate, Space/Enter to toggle, S to start${nc} ${hl}║${nc}" - echo -e "${hl}╠══════════════════════════════════════════════════════════════════════╣${nc}" - - # Input source section - echo -e "${hl}║${nc} ${BOLD}INPUT SOURCE${nc} ${hl}║${nc}" - echo -e "${hl}╟──────────────────────────────────────────────────────────────────────╢${nc}" - - # Item 0: Input path - if [ $TUI_SELECTED -eq 0 ]; then - sel_prefix="${CYAN}▶ " - else - sel_prefix=" " - fi - local path_display - local type_display="" - if [ -n "$TUI_INPUT_PATH" ]; then - path_display="${TUI_INPUT_PATH}" - [ -n "$TUI_DETECTED_TYPE" ] && type_display=" ${MUTED_COLOR}[${TUI_DETECTED_TYPE}]${nc}" - else - path_display="${MUTED_COLOR}(press Enter/I to set path)${nc}" - fi - echo -e "${hl}║${nc}${sel_prefix}Path: ${path_display}${type_display} ${hl}║${nc}" - - echo -e "${hl}╟──────────────────────────────────────────────────────────────────────╢${nc}" - echo -e "${hl}║${nc} ${BOLD}SCAN TYPE${nc} ${hl}║${nc}" - echo -e "${hl}╟──────────────────────────────────────────────────────────────────────╢${nc}" - - # Item 1: Quick scan - if [ $TUI_SELECTED -eq 1 ]; then sel_prefix="${CYAN}▶ "; else sel_prefix=" "; fi - printf "${hl}║${nc}${sel_prefix}$(tui_radio "$TUI_SCAN_TYPE" "quick") Quick Scan ${MUTED_COLOR}Fast sample-based analysis${nc} ${hl}║${nc}\n" - - # Item 2: Standard scan - if [ $TUI_SELECTED -eq 2 ]; then sel_prefix="${CYAN}▶ "; else sel_prefix=" "; fi - printf "${hl}║${nc}${sel_prefix}$(tui_radio "$TUI_SCAN_TYPE" "standard") Standard Scan ${MUTED_COLOR}ClamAV + YARA + Strings + Binwalk${nc} ${hl}║${nc}\n" - - # Item 3: Deep scan - if [ $TUI_SELECTED -eq 3 ]; then sel_prefix="${CYAN}▶ "; else sel_prefix=" "; fi - printf "${hl}║${nc}${sel_prefix}$(tui_radio "$TUI_SCAN_TYPE" "deep") Deep Scan ${MUTED_COLOR}All scanners + entropy + carving${nc} ${hl}║${nc}\n" - - echo -e "${hl}╟──────────────────────────────────────────────────────────────────────╢${nc}" - echo -e "${hl}║${nc} ${BOLD}SCAN SCOPE${nc} ${hl}║${nc}" - echo -e "${hl}╟──────────────────────────────────────────────────────────────────────╢${nc}" - - # Item 4: Full drive - if [ $TUI_SELECTED -eq 4 ]; then sel_prefix="${CYAN}▶ "; else sel_prefix=" "; fi - printf "${hl}║${nc}${sel_prefix}$(tui_radio "$TUI_SCAN_SCOPE" "full") Full Drive ${MUTED_COLOR}Scan entire device including all data${nc} ${hl}║${nc}\n" - - # Item 5: Slack space only - if [ $TUI_SELECTED -eq 5 ]; then sel_prefix="${CYAN}▶ "; else sel_prefix=" "; fi - printf "${hl}║${nc}${sel_prefix}$(tui_radio "$TUI_SCAN_SCOPE" "slack") Slack Space ${MUTED_COLOR}Scan only unallocated/deleted areas${nc} ${hl}║${nc}\n" - - echo -e "${hl}╟──────────────────────────────────────────────────────────────────────╢${nc}" - echo -e "${hl}║${nc} ${BOLD}OPTIONS${nc} ${hl}║${nc}" - echo -e "${hl}╟──────────────────────────────────────────────────────────────────────╢${nc}" - - # Item 6: Mount - if [ $TUI_SELECTED -eq 6 ]; then sel_prefix="${CYAN}▶ "; else sel_prefix=" "; fi - printf "${hl}║${nc}${sel_prefix}$(tui_checkbox $TUI_OPT_MOUNT) Mount device before scanning ${hl}║${nc}\n" - - # Item 7: Update ClamAV - if [ $TUI_SELECTED -eq 7 ]; then sel_prefix="${CYAN}▶ "; else sel_prefix=" "; fi - printf "${hl}║${nc}${sel_prefix}$(tui_checkbox $TUI_OPT_UPDATE) Update ClamAV databases ${hl}║${nc}\n" - - # Item 8: Parallel - if [ $TUI_SELECTED -eq 8 ]; then sel_prefix="${CYAN}▶ "; else sel_prefix=" "; fi - printf "${hl}║${nc}${sel_prefix}$(tui_checkbox $TUI_OPT_PARALLEL) Parallel scanning mode ${hl}║${nc}\n" - - # Item 9: Auto chunk - if [ $TUI_SELECTED -eq 9 ]; then sel_prefix="${CYAN}▶ "; else sel_prefix=" "; fi - printf "${hl}║${nc}${sel_prefix}$(tui_checkbox $TUI_OPT_AUTOCHUNK) Auto-calculate chunk size ${hl}║${nc}\n" - - # Item 10: Verify hash (EWF) - if [ $TUI_SELECTED -eq 10 ]; then sel_prefix="${CYAN}▶ "; else sel_prefix=" "; fi - printf "${hl}║${nc}${sel_prefix}$(tui_checkbox $TUI_OPT_VERIFY_HASH) Verify EWF hash before scan ${MUTED_COLOR}(forensic integrity)${nc} ${hl}║${nc}\n" - - echo -e "${hl}╟──────────────────────────────────────────────────────────────────────╢${nc}" - echo -e "${hl}║${nc} ${BOLD}ADDITIONAL FEATURES${nc} ${hl}║${nc}" - echo -e "${hl}╟──────────────────────────────────────────────────────────────────────╢${nc}" - - # Item 11: VirusTotal - if [ $TUI_SELECTED -eq 11 ]; then sel_prefix="${CYAN}▶ "; else sel_prefix=" "; fi - printf "${hl}║${nc}${sel_prefix}$(tui_checkbox $TUI_OPT_VIRUSTOTAL) VirusTotal hash lookup ${MUTED_COLOR}(requires API key)${nc} ${hl}║${nc}\n" - - # Item 12: Rootkit - if [ $TUI_SELECTED -eq 12 ]; then sel_prefix="${CYAN}▶ "; else sel_prefix=" "; fi - printf "${hl}║${nc}${sel_prefix}$(tui_checkbox $TUI_OPT_ROOTKIT) Rootkit detection ${MUTED_COLOR}(requires mount)${nc} ${hl}║${nc}\n" - - # Item 13: Timeline - if [ $TUI_SELECTED -eq 13 ]; then sel_prefix="${CYAN}▶ "; else sel_prefix=" "; fi - printf "${hl}║${nc}${sel_prefix}$(tui_checkbox $TUI_OPT_TIMELINE) Generate file timeline ${hl}║${nc}\n" - - echo -e "${hl}╟──────────────────────────────────────────────────────────────────────╢${nc}" - echo -e "${hl}║${nc} ${BOLD}OUTPUT${nc} ${hl}║${nc}" - echo -e "${hl}╟──────────────────────────────────────────────────────────────────────╢${nc}" - - # Item 14: HTML report - if [ $TUI_SELECTED -eq 14 ]; then sel_prefix="${CYAN}▶ "; else sel_prefix=" "; fi - printf "${hl}║${nc}${sel_prefix}$(tui_checkbox $TUI_OPT_HTML) Generate HTML report ${hl}║${nc}\n" - - # Item 15: JSON report - if [ $TUI_SELECTED -eq 15 ]; then sel_prefix="${CYAN}▶ "; else sel_prefix=" "; fi - printf "${hl}║${nc}${sel_prefix}$(tui_checkbox $TUI_OPT_JSON) Generate JSON report ${hl}║${nc}\n" - - # Item 16: Keep output - if [ $TUI_SELECTED -eq 16 ]; then sel_prefix="${CYAN}▶ "; else sel_prefix=" "; fi - printf "${hl}║${nc}${sel_prefix}$(tui_checkbox $TUI_OPT_KEEP_OUTPUT) Keep output directory after scan ${hl}║${nc}\n" - - echo -e "${hl}╠══════════════════════════════════════════════════════════════════════╣${nc}" - - # Actions - echo -e "${hl}║${nc} ${hl}║${nc}" - echo -e "${hl}║${nc} ${GREEN}[S]${nc} Start Scan ${YELLOW}[I]${nc} Set Input Path ${RED}[Q]${nc} Quit ${hl}║${nc}" - echo -e "${hl}║${nc} ${hl}║${nc}" - echo -e "${hl}╚══════════════════════════════════════════════════════════════════════╝${nc}" + tui_line "$TUI_TOP" + tui_line "${HEADER_COLOR}║${NC} ${BOLD}DMS - DRIVE MALWARE SCAN${NC} ${HEADER_COLOR}║${NC}" + tui_line "${HEADER_COLOR}║${NC} ${MUTED_COLOR}Use ↑↓ to navigate, Space/Enter to toggle, S to start${NC} ${HEADER_COLOR}║${NC}" + tui_line "$TUI_SEP" + + # INPUT SOURCE + tui_section "INPUT SOURCE" + local path_display="${TUI_INPUT_PATH:-${MUTED_COLOR}(press Enter/I to set path)${NC}}" + local type_display="" && [ -n "$TUI_DETECTED_TYPE" ] && type_display=" ${MUTED_COLOR}[${TUI_DETECTED_TYPE}]${NC}" + tui_item 0 "Path: ${path_display}${type_display} " + + # SCAN TYPE + tui_section "SCAN TYPE" + tui_item 1 "$(tui_radio "$TUI_SCAN_TYPE" "quick") Quick Scan ${MUTED_COLOR}Fast sample-based analysis${NC} " + tui_item 2 "$(tui_radio "$TUI_SCAN_TYPE" "standard") Standard Scan ${MUTED_COLOR}ClamAV + YARA + Strings + Binwalk${NC} " + tui_item 3 "$(tui_radio "$TUI_SCAN_TYPE" "deep") Deep Scan ${MUTED_COLOR}All scanners + entropy + carving${NC} " + + # SCAN SCOPE + tui_section "SCAN SCOPE" + tui_item 4 "$(tui_radio "$TUI_SCAN_SCOPE" "full") Full Drive ${MUTED_COLOR}Scan entire device including all data${NC} " + tui_item 5 "$(tui_radio "$TUI_SCAN_SCOPE" "slack") Slack Space ${MUTED_COLOR}Scan only unallocated/deleted areas${NC} " + + # OPTIONS + tui_section "OPTIONS" + tui_item 6 "$(tui_checkbox $TUI_OPT_MOUNT) Mount device before scanning " + tui_item 7 "$(tui_checkbox $TUI_OPT_UPDATE) Update ClamAV databases " + tui_item 8 "$(tui_checkbox $TUI_OPT_PARALLEL) Parallel scanning mode " + tui_item 9 "$(tui_checkbox $TUI_OPT_AUTOCHUNK) Auto-calculate chunk size " + tui_item 10 "$(tui_checkbox $TUI_OPT_VERIFY_HASH) Verify EWF hash before scan ${MUTED_COLOR}(forensic integrity)${NC} " + + # ADDITIONAL FEATURES + tui_section "ADDITIONAL FEATURES" + tui_item 11 "$(tui_checkbox $TUI_OPT_VIRUSTOTAL) VirusTotal hash lookup ${MUTED_COLOR}(requires API key)${NC} " + tui_item 12 "$(tui_checkbox $TUI_OPT_ROOTKIT) Rootkit detection ${MUTED_COLOR}(requires mount)${NC} " + tui_item 13 "$(tui_checkbox $TUI_OPT_TIMELINE) Generate file timeline " + + # FORENSIC ANALYSIS + tui_line "$TUI_DIV" + tui_line "${HEADER_COLOR}║${NC} ${BOLD}FORENSIC ANALYSIS${NC} ${MUTED_COLOR}(Windows artifacts)${NC} ${HEADER_COLOR}║${NC}" + tui_line "$TUI_DIV" + tui_item 14 "$(tui_checkbox $TUI_OPT_FORENSIC_ALL) ${BOLD}Full forensic analysis${NC} ${MUTED_COLOR}(enables all below)${NC} " + tui_item 15 "$(tui_checkbox $TUI_OPT_PERSISTENCE) Persistence artifacts ${MUTED_COLOR}(registry, tasks, services)${NC} " + tui_item 16 "$(tui_checkbox $TUI_OPT_EXECUTION) Execution artifacts ${MUTED_COLOR}(prefetch, amcache, shimcache)${NC} " + tui_item 17 "$(tui_checkbox $TUI_OPT_FILE_ANOMALIES) File anomalies ${MUTED_COLOR}(magic mismatch, ADS, timestomping)${NC} " + tui_item 18 "$(tui_checkbox $TUI_OPT_RE_TRIAGE) RE triage ${MUTED_COLOR}(capa, suspicious imports, shellcode)${NC} " + tui_item 19 "$(tui_checkbox $TUI_OPT_MFT_ANALYSIS) MFT/filesystem forensics ${MUTED_COLOR}(deleted files, USN)${NC} " + + # OUTPUT + tui_section "OUTPUT" + tui_item 20 "$(tui_checkbox $TUI_OPT_HTML) Generate HTML report " + tui_item 21 "$(tui_checkbox $TUI_OPT_JSON) Generate JSON report " + tui_item 22 "$(tui_checkbox $TUI_OPT_KEEP_OUTPUT) Keep output directory after scan " + + # Actions footer + tui_line "$TUI_SEP" + tui_line "${HEADER_COLOR}║${NC} ${HEADER_COLOR}║${NC}" + tui_line "${HEADER_COLOR}║${NC} ${GREEN}[S]${NC} Start Scan ${YELLOW}[I]${NC} Set Input Path ${RED}[Q]${NC} Quit ${HEADER_COLOR}║${NC}" + tui_line "${HEADER_COLOR}║${NC} ${HEADER_COLOR}║${NC}" + tui_line "$TUI_BOT" # Status line - echo "" + tui_line "" if [ -z "$TUI_INPUT_PATH" ]; then - echo -e " ${YELLOW}⚠${nc} Please set an input path before starting the scan" + tui_line " ${YELLOW}⚠${NC} Please set an input path before starting the scan" elif [ -n "$TUI_DETECTED_TYPE" ]; then - echo -e " ${GREEN}✓${nc} Ready to scan: ${BOLD}$TUI_INPUT_PATH${nc} (${TUI_DETECTED_TYPE})" + tui_line " ${GREEN}✓${NC} Ready to scan: ${BOLD}$TUI_INPUT_PATH${NC} (${TUI_DETECTED_TYPE})" + else + tui_line "" fi } @@ -4315,17 +8186,33 @@ tui_toggle() { 11) TUI_OPT_VIRUSTOTAL=$([ "$TUI_OPT_VIRUSTOTAL" = true ] && echo false || echo true) ;; 12) TUI_OPT_ROOTKIT=$([ "$TUI_OPT_ROOTKIT" = true ] && echo false || echo true) ;; 13) TUI_OPT_TIMELINE=$([ "$TUI_OPT_TIMELINE" = true ] && echo false || echo true) ;; - 14) TUI_OPT_HTML=$([ "$TUI_OPT_HTML" = true ] && echo false || echo true) ;; - 15) TUI_OPT_JSON=$([ "$TUI_OPT_JSON" = true ] && echo false || echo true) ;; - 16) TUI_OPT_KEEP_OUTPUT=$([ "$TUI_OPT_KEEP_OUTPUT" = true ] && echo false || echo true) ;; + # Forensic analysis options + 14) # Full forensic - enables all forensic options + TUI_OPT_FORENSIC_ALL=$([ "$TUI_OPT_FORENSIC_ALL" = true ] && echo false || echo true) + if [ "$TUI_OPT_FORENSIC_ALL" = true ]; then + TUI_OPT_PERSISTENCE=true + TUI_OPT_EXECUTION=true + TUI_OPT_FILE_ANOMALIES=true + TUI_OPT_RE_TRIAGE=true + TUI_OPT_MFT_ANALYSIS=true + fi + ;; + 15) TUI_OPT_PERSISTENCE=$([ "$TUI_OPT_PERSISTENCE" = true ] && echo false || echo true) ;; + 16) TUI_OPT_EXECUTION=$([ "$TUI_OPT_EXECUTION" = true ] && echo false || echo true) ;; + 17) TUI_OPT_FILE_ANOMALIES=$([ "$TUI_OPT_FILE_ANOMALIES" = true ] && echo false || echo true) ;; + 18) TUI_OPT_RE_TRIAGE=$([ "$TUI_OPT_RE_TRIAGE" = true ] && echo false || echo true) ;; + 19) TUI_OPT_MFT_ANALYSIS=$([ "$TUI_OPT_MFT_ANALYSIS" = true ] && echo false || echo true) ;; + # Output options + 20) TUI_OPT_HTML=$([ "$TUI_OPT_HTML" = true ] && echo false || echo true) ;; + 21) TUI_OPT_JSON=$([ "$TUI_OPT_JSON" = true ] && echo false || echo true) ;; + 22) TUI_OPT_KEEP_OUTPUT=$([ "$TUI_OPT_KEEP_OUTPUT" = true ] && echo false || echo true) ;; esac } # Input path dialog tui_input_path() { - # Show cursor for input - printf "\033[?25h" - printf "\033[H\033[2J" + # Show cursor for input, clear screen + printf "\033[?25h\033[2J\033[H" echo -e "${HEADER_COLOR}╔══════════════════════════════════════════════════════════════════════╗${NC}" echo -e "${HEADER_COLOR}║${NC} ${BOLD}SET INPUT PATH${NC} ${HEADER_COLOR}║${NC}" @@ -4393,15 +8280,25 @@ tui_apply_selections() { HTML_REPORT="$TUI_OPT_HTML" JSON_REPORT="$TUI_OPT_JSON" KEEP_OUTPUT="$TUI_OPT_KEEP_OUTPUT" + + # Forensic analysis options + if [ "$TUI_OPT_FORENSIC_ALL" = true ]; then + FORENSIC_ANALYSIS=true + fi + DO_PERSISTENCE_SCAN="$TUI_OPT_PERSISTENCE" + DO_EXECUTION_SCAN="$TUI_OPT_EXECUTION" + DO_FILE_ANOMALIES="$TUI_OPT_FILE_ANOMALIES" + DO_RE_TRIAGE="$TUI_OPT_RE_TRIAGE" + DO_MFT_ANALYSIS="$TUI_OPT_MFT_ANALYSIS" } # Main interactive mode loop run_interactive_mode() { - # Hide cursor - printf "\033[?25l" + # Disable mouse tracking, hide cursor, clear screen + printf "\033[?1000l\033[?1002l\033[?1003l\033[?25l\033[2J\033[H" - # Restore cursor on exit - trap 'printf "\033[?25h"' EXIT + # On exit: show cursor, reset terminal + trap 'printf "\033[?25h\033[0m"' EXIT INT TERM # Initialize TUI_SELECTED=0 @@ -4433,8 +8330,7 @@ run_interactive_mode() { else # Apply selections and start scan tui_apply_selections - printf "\033[?25h" # Show cursor - printf "\033[H\033[2J" # Clear screen + printf "\033[?25h\033[2J\033[H" # Show cursor, clear, home # Show what was selected echo "" @@ -4447,14 +8343,23 @@ run_interactive_mode() { [ "$VERIFY_EWF_HASH" = true ] && echo -e " ${SYMBOL_CHECK} Verify EWF hash" [ "$HTML_REPORT" = true ] && echo -e " ${SYMBOL_CHECK} HTML report" [ "$JSON_REPORT" = true ] && echo -e " ${SYMBOL_CHECK} JSON report" + # Forensic analysis status + if [ "$FORENSIC_ANALYSIS" = true ]; then + echo -e " ${SYMBOL_CHECK} Full forensic analysis" + else + [ "$DO_PERSISTENCE_SCAN" = true ] && echo -e " ${SYMBOL_CHECK} Persistence artifacts" + [ "$DO_EXECUTION_SCAN" = true ] && echo -e " ${SYMBOL_CHECK} Execution artifacts" + [ "$DO_FILE_ANOMALIES" = true ] && echo -e " ${SYMBOL_CHECK} File anomalies" + [ "$DO_RE_TRIAGE" = true ] && echo -e " ${SYMBOL_CHECK} RE triage" + [ "$DO_MFT_ANALYSIS" = true ] && echo -e " ${SYMBOL_CHECK} MFT/filesystem forensics" + fi echo "" sleep 1 return 0 fi ;; - QUIT|q|Q|ESC) - printf "\033[?25h" # Show cursor - printf "\033[H\033[2J" # Clear screen + QUIT|q|Q) + printf "\033[?25h\033[2J\033[H" # Show cursor, clear, home echo "Exiting..." exit 0 ;; @@ -4467,6 +8372,9 @@ run_interactive_mode() { 3) TUI_SCAN_TYPE="deep" ;; + *) + # Ignore unknown keys (including mouse events) + ;; esac done } @@ -4611,6 +8519,42 @@ main() { SCAN_MODE="slack" shift ;; + --forensic-analysis) + FORENSIC_ANALYSIS=true + DO_PERSISTENCE_SCAN=true + DO_EXECUTION_SCAN=true + DO_FILE_ANOMALIES=true + DO_RE_TRIAGE=true + shift + ;; + --persistence-scan) + DO_PERSISTENCE_SCAN=true + shift + ;; + --execution-scan) + DO_EXECUTION_SCAN=true + shift + ;; + --file-anomalies) + DO_FILE_ANOMALIES=true + shift + ;; + --re-triage) + DO_RE_TRIAGE=true + shift + ;; + --mft-analysis) + DO_MFT_ANALYSIS=true + shift + ;; + --attack-mapping) + ATTACK_MAPPING=true + shift + ;; + --no-attack-mapping) + ATTACK_MAPPING=false + shift + ;; --no-color) NO_COLOR=true shift @@ -4804,6 +8748,58 @@ main() { run_scan "bulk_extractor" scan_bulk_extractor run_scan "hashes" scan_hashes fi + + # Forensic Analysis scans + if [ "$FORENSIC_ANALYSIS" = true ] || [ "$DO_PERSISTENCE_SCAN" = true ] || \ + [ "$DO_EXECUTION_SCAN" = true ] || [ "$DO_FILE_ANOMALIES" = true ] || \ + [ "$DO_RE_TRIAGE" = true ] || [ "$DO_MFT_ANALYSIS" = true ]; then + + print_section "Starting Forensic Analysis" + + # Setup forensic tools if needed + setup_forensic_tools + + # Persistence artifact analysis + if [ "$FORENSIC_ANALYSIS" = true ] || [ "$DO_PERSISTENCE_SCAN" = true ]; then + run_scan "persistence" scan_persistence_artifacts + fi + + # Execution artifact analysis (Phase 2 - to be implemented) + if [ "$FORENSIC_ANALYSIS" = true ] || [ "$DO_EXECUTION_SCAN" = true ]; then + if type scan_execution_artifacts &>/dev/null; then + run_scan "execution" scan_execution_artifacts + else + log "DEBUG" "Execution artifact scanning not yet implemented" + fi + fi + + # File anomaly detection (Phase 3 - to be implemented) + if [ "$FORENSIC_ANALYSIS" = true ] || [ "$DO_FILE_ANOMALIES" = true ]; then + if type scan_file_anomalies &>/dev/null; then + run_scan "file_anomalies" scan_file_anomalies + else + log "DEBUG" "File anomaly detection not yet implemented" + fi + fi + + # RE triage (Phase 3 - to be implemented) + if [ "$FORENSIC_ANALYSIS" = true ] || [ "$DO_RE_TRIAGE" = true ]; then + if type scan_re_triage &>/dev/null; then + run_scan "re_triage" scan_re_triage + else + log "DEBUG" "RE triage not yet implemented" + fi + fi + + # MFT/filesystem analysis (Phase 4 - to be implemented) + if [ "$DO_MFT_ANALYSIS" = true ]; then + if type scan_filesystem_forensics &>/dev/null; then + run_scan "filesystem_forensics" scan_filesystem_forensics + else + log "DEBUG" "Filesystem forensics not yet implemented" + fi + fi + fi fi # Generate reports