<?php
/**
 * VMPFence Binary Malware Detector
 * 
 * Ultra-fast binary signature matching using 4-byte prefixes
 * 
 * 
 * @package VMPFence
 * @since 1.0.0
 */

if (!defined('ABSPATH')) {
    exit;
}

class VMPFence_Binary_Detector {
    
    private $malware_prefixes = array();
    private $binary_db_path;
    private $debug_log = array();
    private $detection_stats = array(
        'files_scanned' => 0,
        'binary_matches' => 0,
        'scan_time' => 0,
        'prefixes_loaded' => 0
    );
    
    public function __construct() {
        $this->binary_db_path = plugin_dir_path(__FILE__) . '../data/malware_prefixes_local.bin';
        $this->load_malware_prefixes();
    }
    
    /**
     * Load malware prefixes from binary database
     * Implements high-speed binary signature loading
     */
    public function load_malware_prefixes() {
        $start_time = microtime(true);
        
        // TEMPORARY: Disable binary detection to prevent false positives
        // Force rebuild with minimal patterns only
        if (file_exists($this->binary_db_path)) {
            // Attempt to delete the file - wp_delete_file handles permissions internally
            $deleted = wp_delete_file($this->binary_db_path);
            if (!$deleted && file_exists($this->binary_db_path)) {
                // Log the issue but continue - file will be overwritten if possible
                $this->debug_log[] = 'Binary database file could not be deleted, attempting to overwrite...';
            }
        }
        
        // Build the database - this will create or overwrite the file
        $build_success = $this->build_local_prefix_database();
        if (!$build_success) {
            $this->debug_log[] = 'Failed to build binary database - file permissions issue';
            return false;
        }
        
        // Load binary prefixes
        $binary_data = file_get_contents($this->binary_db_path);
        if ($binary_data === false) {
            $this->debug_log[] = 'Failed to load binary database';
            return false;
        }
        
        // Parse 4-byte prefixes from binary data
        $this->malware_prefixes = array();
        $data_length = strlen($binary_data);
        
        for ($i = 0; $i < $data_length; $i += 8) { // 4 bytes prefix + 4 bytes metadata
            if ($i + 7 < $data_length) {
                $prefix = substr($binary_data, $i, 4);
                $metadata = unpack('V', substr($binary_data, $i + 4, 4))[1]; // Little-endian uint32
                
                $this->malware_prefixes[bin2hex($prefix)] = array(
                    'prefix' => $prefix,
                    'hex' => bin2hex($prefix),
                    'severity' => ($metadata >> 24) & 0xFF,
                    'type' => ($metadata >> 16) & 0xFF,
                    'flags' => $metadata & 0xFFFF
                );
            }
        }
        
        $load_time = microtime(true) - $start_time;
        $this->detection_stats['prefixes_loaded'] = count($this->malware_prefixes);
        $this->debug_log[] = sprintf('Loaded %d binary prefixes in %.4f seconds', 
            $this->detection_stats['prefixes_loaded'], $load_time);
        
        return true;
    }
    
    /**
     * Check file for binary malware signatures
     * Ultra-fast 4-byte prefix matching
     */
    public function check_binary_signatures($file_path, $file_content = null) {
        $start_time = microtime(true);
        $this->detection_stats['files_scanned']++;
        
        // Get file content if not provided
        if ($file_content === null) {
            if (!file_exists($file_path)) {
                return array('detected' => false, 'error' => 'File not found');
            }
            
            $file_content = file_get_contents($file_path);
            if ($file_content === false) {
                return array('detected' => false, 'error' => 'Failed to read file');
            }
        }
        
        $file_size = strlen($file_content);
        if ($file_size < 4) {
            return array('detected' => false, 'reason' => 'File too small for binary detection');
        }
        
        $detections = array();
        
        // Check first 4 bytes (most common malware location)
        $first_prefix = substr($file_content, 0, 4);
        $first_hex = bin2hex($first_prefix);
        
        if (isset($this->malware_prefixes[$first_hex])) {
            $match = $this->malware_prefixes[$first_hex];
            $detections[] = array(
                'position' => 0,
                'prefix' => $first_hex,
                'severity' => $match['severity'],
                'type' => $this->get_malware_type_name($match['type']),
                'confidence' => 'HIGH'
            );
        }
        
        // Scan for additional prefixes throughout file (sliding window)
        $scan_positions = min(1000, $file_size - 4); // Limit scanning for performance
        
        for ($pos = 1; $pos < $scan_positions; $pos += 4) {
            $prefix = substr($file_content, $pos, 4);
            $hex = bin2hex($prefix);
            
            if (isset($this->malware_prefixes[$hex])) {
                $match = $this->malware_prefixes[$hex];
                $detections[] = array(
                    'position' => $pos,
                    'prefix' => $hex,
                    'severity' => $match['severity'],
                    'type' => $this->get_malware_type_name($match['type']),
                    'confidence' => $pos < 100 ? 'HIGH' : 'MEDIUM'
                );
            }
        }
        
        $scan_time = microtime(true) - $start_time;
        $this->detection_stats['scan_time'] += $scan_time;
        
        if (!empty($detections)) {
            $this->detection_stats['binary_matches']++;
            $this->debug_log[] = sprintf('Binary malware detected in %s: %d signatures found', 
                basename($file_path), count($detections));
        }
        
        return array(
            'detected' => !empty($detections),
            'detections' => $detections,
            'scan_time' => $scan_time,
            'file_size' => $file_size,
            'positions_scanned' => $scan_positions
        );
    }
    
    /**
     * Build local prefix database from malware samples
     * Creates optimized binary database for fast loading
     */
    public function build_local_prefix_database($samples_dir = null) {
        $this->debug_log[] = 'Building local binary prefix database';
        
        // Ensure data directory exists
        $data_dir = dirname($this->binary_db_path);
        if (!file_exists($data_dir)) {
            wp_mkdir_p($data_dir);
        }
        
        // EXTREMELY CONSERVATIVE patterns only
        // Disable aggressive webshell detection to prevent false positives
        $known_prefixes = array(
            // ONLY Windows/Linux executables in web directories (extremely suspicious)
            '4d5a9000' => array('severity' => 5, 'type' => 5, 'desc' => 'Windows executable in web directory'),
            '7f454c46' => array('severity' => 5, 'type' => 5, 'desc' => 'Linux ELF executable in web directory'),
            
            // DISABLE webshell patterns - too many false positives
            // '43393920' => array('severity' => 5, 'type' => 4, 'desc' => 'C99 shell header'), // DISABLED
            // '72353720' => array('severity' => 5, 'type' => 4, 'desc' => 'R57 shell signature'), // DISABLED
            // '57534f20' => array('severity' => 5, 'type' => 4, 'desc' => 'WSO shell header'), // DISABLED
            
            // Keep only the most obvious malware
        );
        
        // DISABLE additional patterns to prevent false positives
        $github_patterns = array(); // Disabled
        $yara_patterns = array(); // Disabled
        
        $all_prefixes = array_merge($known_prefixes, $github_patterns, $yara_patterns);
        
        // Build optimized binary database
        $binary_data = '';
        foreach ($all_prefixes as $hex_prefix => $metadata) {
            // Ensure hex prefix has even length and is exactly 8 characters (4 bytes)
            $hex_prefix = str_pad($hex_prefix, 8, '0', STR_PAD_RIGHT);
            if (strlen($hex_prefix) % 2 !== 0) {
                $hex_prefix = $hex_prefix . '0'; // Make it even length
            }
            
            // Convert hex to binary (4 bytes)
            $prefix_binary = hex2bin($hex_prefix);
            if ($prefix_binary === false || strlen($prefix_binary) !== 4) continue;
            
            // Pack metadata (severity, type, flags)
            $packed_metadata = pack('V', 
                ($metadata['severity'] << 24) | 
                ($metadata['type'] << 16) | 
                ($metadata['flags'] ?? 0)
            );
            
            $binary_data .= $prefix_binary . $packed_metadata;
        }
        
        // Write to binary file
        $write_result = file_put_contents($this->binary_db_path, $binary_data);
        
        if ($write_result !== false) {
            $this->debug_log[] = sprintf('Binary database built: %d prefixes, %d bytes written', 
                count($all_prefixes), $write_result);
            return true;
        } else {
            $this->debug_log[] = 'Failed to write binary database';
            return false;
        }
    }
    
    /**
     * Get malware type name from numeric code
     */
    private function get_malware_type_name($type_code) {
        $types = array(
            1 => 'PHP_TAG',
            2 => 'PHP_EVAL',
            3 => 'SYSTEM_CALL',
            4 => 'WEBSHELL',
            5 => 'EXECUTABLE',
            6 => 'ARCHIVE',
            7 => 'IMAGE_PAYLOAD',
            8 => 'ENCODED_CONTENT',
            9 => 'JAVASCRIPT',
            10 => 'BACKDOOR_SIGNATURE'
        );
        
        return isset($types[$type_code]) ? $types[$type_code] : 'UNKNOWN';
    }
    
    /**
     * Harvest binary patterns from GitHub malware samples
     * FIXED: Only highly specific malware signatures to prevent false positives
     */
    private function harvest_github_binary_patterns() {
        // Only very specific shell signatures that won't match legitimate code
        return array(
            // Specific shell headers (exact matches)
            '43393920' => array('severity' => 5, 'type' => 4, 'flags' => 0x01, 'desc' => 'C99 shell header'),
            '72353720' => array('severity' => 5, 'type' => 4, 'flags' => 0x02, 'desc' => 'R57 shell signature'),
            '57534f20' => array('severity' => 5, 'type' => 4, 'flags' => 0x04, 'desc' => 'WSO shell header'),
            
            // Remove other patterns that could match legitimate files
        );
    }
    
    /**
     * Harvest binary patterns from YARA rules
     * FIXED: Only highly specific malware families to prevent false positives
     */
    private function harvest_yara_binary_patterns() {
        // Only specific malware family signatures
        return array(
            // Known malware families (very specific signatures)
            '544e544e' => array('severity' => 5, 'type' => 5, 'flags' => 0x100, 'desc' => 'TNT malware family'),
            '444f4f4d' => array('severity' => 5, 'type' => 5, 'flags' => 0x200, 'desc' => 'DOOM malware family'),
            
            // Remove compression signatures - these are legitimate
        );
    }
    
    /**
     * Get detection statistics
     */
    public function get_detection_stats() {
        return $this->detection_stats;
    }
    
    /**
     * Get debug log
     */
    public function get_debug_log() {
        return $this->debug_log;
    }
    
    /**
     * Reset statistics and logs
     */
    public function reset_stats() {
        $this->detection_stats = array(
            'files_scanned' => 0,
            'binary_matches' => 0,
            'scan_time' => 0,
            'prefixes_loaded' => count($this->malware_prefixes)
        );
        $this->debug_log = array();
    }
    
    /**
     * Benchmark binary detection performance
     */
    public function benchmark_performance($test_files, $iterations = 1000) {
        $start_time = microtime(true);
        $total_detections = 0;
        
        for ($i = 0; $i < $iterations; $i++) {
            foreach ($test_files as $file_path) {
                $result = $this->check_binary_signatures($file_path);
                if ($result['detected']) {
                    $total_detections++;
                }
            }
        }
        
        $total_time = microtime(true) - $start_time;
        $files_per_second = (count($test_files) * $iterations) / $total_time;
        
        return array(
            'total_time' => $total_time,
            'files_per_second' => $files_per_second,
            'total_detections' => $total_detections,
            'iterations' => $iterations,
            'files_tested' => count($test_files)
        );
    }
}
