#!/usr/bin/env python3 """Exploit and shellcode analyzer — extract, identify, and emulate exploit payloads.""" from __future__ import annotations import argparse import hashlib import json import math import struct import sys from collections import Counter from pathlib import Path def calculate_entropy(data: bytes) -> float: """Calculate Shannon entropy of byte data.""" if not data: return 0.0 freq = Counter(data) length = len(data) return -sum((c / length) * math.log2(c / length) for c in freq.values()) def detect_shellcode_arch(data: bytes) -> dict: """Attempt to identify shellcode architecture by disassembly heuristics.""" result = {"architecture": "unknown", "bits": 0, "confidence": "low"} # Common x86 shellcode prologues x86_patterns = [ b"\xfc\xe8", # cld; call b"\xeb\x03\x5d\x31", # jmp; pop ebp; xor b"\x31\xc0", # xor eax, eax b"\x33\xc0", # xor eax, eax (alternative encoding) b"\x64\xa1\x30", # fs:[0x30] — PEB access b"\xd9\xee\xd9\x74", # fldz; fnstenv — GetPC via FPU ] # Common x64 shellcode patterns x64_patterns = [ b"\x48\x31\xc0", # xor rax, rax b"\x48\x89\xe5", # mov rbp, rsp b"\x65\x48\x8b\x04", # gs:[...] — TEB access b"\x4c\x8b", # mov r8-r15 ] for pattern in x86_patterns: if pattern in data[:64]: result = {"architecture": "x86", "bits": 32, "confidence": "medium"} break for pattern in x64_patterns: if pattern in data[:64]: result = {"architecture": "x86_64", "bits": 64, "confidence": "medium"} break return result def find_xor_key(data: bytes, known_plaintext: bytes = b"This program") -> list[dict]: """Search for single-byte and multi-byte XOR keys using known plaintext.""" keys_found = [] # Single-byte XOR scan for key in range(1, 256): decoded = bytes(b ^ key for b in data) if known_plaintext in decoded: offset = decoded.index(known_plaintext) keys_found.append({ "type": "single_byte", "key": f"0x{key:02x}", "offset": offset, }) return keys_found def extract_urls_from_shellcode(data: bytes) -> list[str]: """Extract URLs and IPs from shellcode bytes.""" urls = [] # Look for http:// and https:// strings for prefix in [b"http://", b"https://"]: idx = 0 while True: idx = data.find(prefix, idx) if idx == -1: break end = idx + len(prefix) while end < len(data) and data[end] in range(0x20, 0x7f) and data[end] not in (0x20, 0x22, 0x27, 0x3e): end += 1 url = data[idx:end].decode("ascii", errors="ignore") if len(url) > len(prefix.decode()): urls.append(url) idx = end return urls def detect_heap_spray(html_content: str) -> dict: """Detect heap spray patterns in HTML/JavaScript.""" indicators = { "spray_detected": False, "techniques": [], "target_addresses": [], "nop_sleds": [], } spray_patterns = [ ("unescape", "unescape() shellcode encoding"), ("String.fromCharCode", "character code shellcode construction"), ("substr", "string manipulation for spray blocks"), ("repeat", "string repetition for NOP sled"), ("ArrayBuffer", "typed array heap manipulation"), ("spray", "explicit spray variable naming"), ] for pattern, description in spray_patterns: if pattern in html_content: indicators["techniques"].append(description) # Check for common target addresses target_addrs = ["0c0c0c0c", "0a0a0a0a", "0d0d0d0d", "04040404", "06060606"] for addr in target_addrs: if addr in html_content.lower(): indicators["target_addresses"].append(f"0x{addr}") indicators["spray_detected"] = len(indicators["techniques"]) >= 2 return indicators def analyze_rop_chain(data: bytes, base_address: int = 0) -> dict: """Look for ROP chain patterns in exploit data.""" result = { "rop_detected": False, "gadget_count": 0, "pivot_detected": False, "target_apis": [], } # Look for sequences of addresses (4-byte aligned for x86) # ROP chains typically have many pointers in a narrow address range if len(data) < 16: return result addresses = [] for i in range(0, len(data) - 3, 4): addr = struct.unpack_from(" 5: result["rop_detected"] = True result["gadget_count"] = len(addresses) result["address_range"] = f"0x{min(addresses):08x}-0x{max(addresses):08x}" return result def main() -> None: parser = argparse.ArgumentParser(description="Exploit and Shellcode Analyzer") parser.add_argument("--input", required=True, help="Input file (shellcode, document, HTML, binary)") parser.add_argument( "--mode", choices=["extract", "identify", "emulate", "rop", "heapspray", "browser", "cve-id"], default="identify", help="Analysis mode", ) parser.add_argument("--arch", choices=["x86", "x64", "arm"], default="x86", help="Target architecture") parser.add_argument("--target-dll", help="Target DLL for ROP analysis") parser.add_argument("--output", default="exploit_analysis.json", help="Output file path") parser.add_argument("--format", choices=["json", "csv", "markdown"], default="json") args = parser.parse_args() input_path = Path(args.input) if not input_path.exists(): print(f"[!] Input file not found: {args.input}", file=sys.stderr) sys.exit(1) data = input_path.read_bytes() sha256 = hashlib.sha256(data).hexdigest() results = { "input_file": str(input_path), "sha256": sha256, "size_bytes": len(data), "mode": args.mode, } print(f"[*] Exploit Analysis — mode: {args.mode}") print(f"[*] Input: {args.input} ({len(data)} bytes)") print(f"[*] SHA-256: {sha256}") if args.mode == "identify": results["architecture"] = detect_shellcode_arch(data) results["entropy"] = round(calculate_entropy(data), 2) results["urls"] = extract_urls_from_shellcode(data) results["xor_keys"] = find_xor_key(data) elif args.mode == "rop": results["rop_analysis"] = analyze_rop_chain(data) elif args.mode == "heapspray": text = data.decode("utf-8", errors="ignore") results["heap_spray"] = detect_heap_spray(text) elif args.mode == "extract": print("[*] Extracting embedded payloads...") results["extracted_payloads"] = [] elif args.mode == "emulate": print(f"[*] Emulating shellcode ({args.arch})...") print("[!] Full emulation requires speakeasy or unicorn engine") results["emulation"] = {"status": "requires_speakeasy_or_unicorn"} output_path = Path(args.output) output_path.parent.mkdir(parents=True, exist_ok=True) output_path.write_text(json.dumps(results, indent=2)) print(f"[*] Results written to {args.output}") if __name__ == "__main__": main()