""" AUTARCH Analyze Module Forensics and analysis tools File analysis, hash generation, string extraction, and more. """ import os import sys import subprocess import hashlib import re try: import magic except ImportError: magic = None from pathlib import Path from datetime import datetime # Module metadata DESCRIPTION = "Forensics & file analysis tools" AUTHOR = "darkHal" VERSION = "1.0" CATEGORY = "analyze" sys.path.insert(0, str(Path(__file__).parent.parent)) from core.banner import Colors, clear_screen, display_banner class Analyzer: """Forensics and analysis tools.""" def __init__(self): pass def print_status(self, message: str, status: str = "info"): colors = {"info": Colors.CYAN, "success": Colors.GREEN, "warning": Colors.YELLOW, "error": Colors.RED} symbols = {"info": "*", "success": "+", "warning": "!", "error": "X"} print(f"{colors.get(status, Colors.WHITE)}[{symbols.get(status, '*')}] {message}{Colors.RESET}") def run_cmd(self, cmd: str) -> tuple: try: result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=60) return result.returncode == 0, result.stdout.strip() except: return False, "" def get_file_hashes(self, filepath: str) -> dict: """Calculate various hashes for a file.""" p = Path(filepath) if not p.exists() or not p.is_file(): return {} hashes = {} with open(p, 'rb') as f: content = f.read() hashes['md5'] = hashlib.md5(content).hexdigest() hashes['sha1'] = hashlib.sha1(content).hexdigest() hashes['sha256'] = hashlib.sha256(content).hexdigest() return hashes def analyze_file(self): """Comprehensive file analysis.""" print(f"\n{Colors.BOLD}File Analysis{Colors.RESET}") filepath = input(f"{Colors.WHITE}Enter file path: {Colors.RESET}").strip() if not filepath: return p = Path(filepath).expanduser() if not p.exists(): self.print_status(f"File not found: {filepath}", "error") return print(f"\n{Colors.CYAN}{'─' * 50}{Colors.RESET}") print(f"{Colors.BOLD}File: {p.name}{Colors.RESET}") print(f"{Colors.CYAN}{'─' * 50}{Colors.RESET}\n") # Basic info stat = p.stat() print(f"{Colors.CYAN}Basic Info:{Colors.RESET}") print(f" Path: {p.absolute()}") print(f" Size: {stat.st_size:,} bytes") print(f" Modified: {datetime.fromtimestamp(stat.st_mtime)}") print(f" Created: {datetime.fromtimestamp(stat.st_ctime)}") print(f" Mode: {oct(stat.st_mode)}") # File type print(f"\n{Colors.CYAN}File Type:{Colors.RESET}") try: file_magic = magic.Magic(mime=True) mime_type = file_magic.from_file(str(p)) print(f" MIME: {mime_type}") file_magic = magic.Magic() file_desc = file_magic.from_file(str(p)) print(f" Type: {file_desc}") except: success, output = self.run_cmd(f"file '{p}'") if success: print(f" Type: {output.split(':', 1)[-1].strip()}") # Hashes print(f"\n{Colors.CYAN}Hashes:{Colors.RESET}") hashes = self.get_file_hashes(str(p)) for algo, value in hashes.items(): print(f" {algo.upper():8} {value}") # Check if executable if p.suffix in ['.exe', '.dll', '.so', '.elf', ''] or stat.st_mode & 0o111: self.analyze_executable(str(p)) def analyze_executable(self, filepath: str): """Additional analysis for executables.""" print(f"\n{Colors.CYAN}Executable Analysis:{Colors.RESET}") # Strings success, output = self.run_cmd(f"strings '{filepath}' 2>/dev/null | head -50") if success and output: # Look for interesting strings interesting = [] patterns = [ r'https?://[^\s]+', # URLs r'\d+\.\d+\.\d+\.\d+', # IPs r'password|passwd|secret|key|token', # Credentials r'/bin/sh|/bin/bash|cmd\.exe', # Shells ] for line in output.split('\n'): for pattern in patterns: if re.search(pattern, line, re.I): interesting.append(line.strip()) break if interesting: print(f" {Colors.YELLOW}Interesting strings found:{Colors.RESET}") for s in interesting[:10]: print(f" {s[:80]}") # Check for packing success, output = self.run_cmd(f"readelf -h '{filepath}' 2>/dev/null") if success: if 'Entry point' in output: print(f" ELF executable detected") def extract_strings(self): """Extract strings from file.""" print(f"\n{Colors.BOLD}String Extraction{Colors.RESET}") filepath = input(f"{Colors.WHITE}Enter file path: {Colors.RESET}").strip() if not filepath: return p = Path(filepath).expanduser() if not p.exists(): self.print_status(f"File not found", "error") return min_len = input(f"{Colors.WHITE}Minimum string length [4]: {Colors.RESET}").strip() or "4" print(f"\n{Colors.CYAN}Extracting strings...{Colors.RESET}\n") success, output = self.run_cmd(f"strings -n {min_len} '{p}' 2>/dev/null") if success: lines = output.split('\n') print(f"Found {len(lines)} strings\n") # Categorize urls = [l for l in lines if re.search(r'https?://', l)] ips = [l for l in lines if re.search(r'\b\d+\.\d+\.\d+\.\d+\b', l)] paths = [l for l in lines if re.search(r'^/[a-z]', l, re.I)] emails = [l for l in lines if re.search(r'[\w.-]+@[\w.-]+', l)] if urls: print(f"{Colors.CYAN}URLs ({len(urls)}):{Colors.RESET}") for u in urls[:10]: print(f" {u}") if ips: print(f"\n{Colors.CYAN}IP Addresses ({len(ips)}):{Colors.RESET}") for ip in ips[:10]: print(f" {ip}") if emails: print(f"\n{Colors.CYAN}Emails ({len(emails)}):{Colors.RESET}") for e in emails[:10]: print(f" {e}") if paths: print(f"\n{Colors.CYAN}Paths ({len(paths)}):{Colors.RESET}") for p in paths[:10]: print(f" {p}") # Save option save = input(f"\n{Colors.WHITE}Save all strings to file? (y/n): {Colors.RESET}").strip().lower() if save == 'y': outfile = f"{p.stem}_strings.txt" with open(outfile, 'w') as f: f.write(output) self.print_status(f"Saved to {outfile}", "success") def hash_lookup(self): """Look up hash in threat intel.""" print(f"\n{Colors.BOLD}Hash Lookup{Colors.RESET}") hash_input = input(f"{Colors.WHITE}Enter hash (MD5/SHA1/SHA256): {Colors.RESET}").strip() if not hash_input: return # Determine hash type hash_len = len(hash_input) if hash_len == 32: hash_type = "MD5" elif hash_len == 40: hash_type = "SHA1" elif hash_len == 64: hash_type = "SHA256" else: self.print_status("Invalid hash length", "error") return print(f"\n{Colors.CYAN}Hash Type: {hash_type}{Colors.RESET}") print(f"{Colors.CYAN}Hash: {hash_input}{Colors.RESET}\n") # VirusTotal URL print(f"{Colors.DIM}VirusTotal: https://www.virustotal.com/gui/file/{hash_input}{Colors.RESET}") print(f"{Colors.DIM}Hybrid Analysis: https://www.hybrid-analysis.com/search?query={hash_input}{Colors.RESET}") def analyze_log(self): """Analyze log files for anomalies.""" print(f"\n{Colors.BOLD}Log Analysis{Colors.RESET}") print(f"{Colors.DIM}Common logs: /var/log/auth.log, /var/log/syslog, /var/log/apache2/access.log{Colors.RESET}\n") filepath = input(f"{Colors.WHITE}Enter log file path: {Colors.RESET}").strip() if not filepath: return p = Path(filepath).expanduser() if not p.exists(): self.print_status(f"File not found", "error") return print(f"\n{Colors.CYAN}Analyzing {p.name}...{Colors.RESET}\n") # Read log try: with open(p, 'r', errors='ignore') as f: lines = f.readlines() except Exception as e: self.print_status(f"Error reading file: {e}", "error") return print(f"Total lines: {len(lines)}") # Extract IPs all_ips = [] for line in lines: ips = re.findall(r'\b(\d+\.\d+\.\d+\.\d+)\b', line) all_ips.extend(ips) if all_ips: from collections import Counter ip_counts = Counter(all_ips) print(f"\n{Colors.CYAN}Top IP Addresses:{Colors.RESET}") for ip, count in ip_counts.most_common(10): print(f" {ip:20} {count:>6} occurrences") # Look for error patterns errors = [l for l in lines if re.search(r'error|fail|denied|invalid', l, re.I)] if errors: print(f"\n{Colors.YELLOW}Error/Failure entries: {len(errors)}{Colors.RESET}") print(f"{Colors.DIM}Recent errors:{Colors.RESET}") for e in errors[-5:]: print(f" {e.strip()[:100]}") # Timestamps timestamps = [] for line in lines: match = re.search(r'(\w{3}\s+\d+\s+\d+:\d+:\d+)', line) if match: timestamps.append(match.group(1)) if timestamps: print(f"\n{Colors.CYAN}Time Range:{Colors.RESET}") print(f" First: {timestamps[0]}") print(f" Last: {timestamps[-1]}") def hex_dump(self): """Create hex dump of file.""" print(f"\n{Colors.BOLD}Hex Dump{Colors.RESET}") filepath = input(f"{Colors.WHITE}Enter file path: {Colors.RESET}").strip() if not filepath: return p = Path(filepath).expanduser() if not p.exists(): self.print_status(f"File not found", "error") return offset = input(f"{Colors.WHITE}Start offset [0]: {Colors.RESET}").strip() or "0" length = input(f"{Colors.WHITE}Length [256]: {Colors.RESET}").strip() or "256" try: offset = int(offset, 0) # Support hex input length = int(length, 0) except: self.print_status("Invalid offset/length", "error") return print(f"\n{Colors.CYAN}Hex dump of {p.name} (offset={hex(offset)}, length={length}):{Colors.RESET}\n") with open(p, 'rb') as f: f.seek(offset) data = f.read(length) # Format hex dump for i in range(0, len(data), 16): chunk = data[i:i+16] hex_part = ' '.join(f'{b:02x}' for b in chunk) ascii_part = ''.join(chr(b) if 32 <= b < 127 else '.' for b in chunk) print(f" {offset+i:08x} {hex_part:<48} {ascii_part}") def compare_files(self): """Compare two files.""" print(f"\n{Colors.BOLD}File Comparison{Colors.RESET}") file1 = input(f"{Colors.WHITE}First file: {Colors.RESET}").strip() file2 = input(f"{Colors.WHITE}Second file: {Colors.RESET}").strip() if not file1 or not file2: return p1 = Path(file1).expanduser() p2 = Path(file2).expanduser() if not p1.exists() or not p2.exists(): self.print_status("One or both files not found", "error") return print(f"\n{Colors.CYAN}Comparing files...{Colors.RESET}\n") # Size comparison s1, s2 = p1.stat().st_size, p2.stat().st_size print(f"File 1 size: {s1:,} bytes") print(f"File 2 size: {s2:,} bytes") print(f"Difference: {abs(s1-s2):,} bytes") # Hash comparison h1 = self.get_file_hashes(str(p1)) h2 = self.get_file_hashes(str(p2)) print(f"\n{Colors.CYAN}Hash Comparison:{Colors.RESET}") for algo in ['md5', 'sha256']: match = h1.get(algo) == h2.get(algo) status = f"{Colors.GREEN}MATCH{Colors.RESET}" if match else f"{Colors.RED}DIFFERENT{Colors.RESET}" print(f" {algo.upper()}: {status}") if h1.get('sha256') != h2.get('sha256'): # Show diff if text files success, output = self.run_cmd(f"diff '{p1}' '{p2}' 2>/dev/null | head -30") if success and output: print(f"\n{Colors.CYAN}Differences (first 30 lines):{Colors.RESET}") print(output) def show_menu(self): clear_screen() display_banner() print(f"{Colors.CYAN}{Colors.BOLD} Analysis & Forensics{Colors.RESET}") print(f"{Colors.DIM} File analysis and forensics tools{Colors.RESET}") print(f"{Colors.DIM} {'─' * 50}{Colors.RESET}") print() print(f" {Colors.CYAN}[1]{Colors.RESET} Analyze File") print(f" {Colors.CYAN}[2]{Colors.RESET} Extract Strings") print(f" {Colors.CYAN}[3]{Colors.RESET} Hash Lookup") print(f" {Colors.CYAN}[4]{Colors.RESET} Analyze Log") print(f" {Colors.CYAN}[5]{Colors.RESET} Hex Dump") print(f" {Colors.CYAN}[6]{Colors.RESET} Compare Files") print() print(f" {Colors.DIM}[0]{Colors.RESET} Back") print() def run(self): while True: self.show_menu() try: choice = input(f"{Colors.WHITE} Select: {Colors.RESET}").strip() if choice == "0": break elif choice == "1": self.analyze_file() elif choice == "2": self.extract_strings() elif choice == "3": self.hash_lookup() elif choice == "4": self.analyze_log() elif choice == "5": self.hex_dump() elif choice == "6": self.compare_files() if choice in ["1", "2", "3", "4", "5", "6"]: input(f"\n{Colors.WHITE}Press Enter to continue...{Colors.RESET}") except (EOFError, KeyboardInterrupt): break def run(): Analyzer().run() if __name__ == "__main__": run()