*/ function virenscan_name_findings(string $name): array { $out = []; if (preg_match('/[\x{202A}-\x{202E}\x{2066}-\x{2069}]/u', $name)) $out[] = ['exec', 'Verschleierter Dateiname (Zeichen zur Umkehr der Schreibrichtung)']; $ext = virenscan_file_ext($name); if (in_array($ext, VIRENSCAN_BAD_EXT, true)) $out[] = ['exec', 'Gefährlicher Dateityp (.' . $ext . ')']; elseif (preg_match('/\.(' . implode('|', array_map('preg_quote', VIRENSCAN_BAD_EXT)) . ')\s*\.[a-z0-9]{2,5}$/i', $name, $m)) $out[] = ['exec', 'Doppelte Endung (.' . strtolower($m[1]) . ' versteckt)']; return $out; } /** * Untersucht eine Datei. @return list Funde als [Kategorie, Text]. Kategorien: exec (Programme, Skripte, Tricks), * macro (Office-Makros), script (Skripte in HTML/SVG/RTF/PDF), archive (auffällige Archive), encrypted (passwortgeschützt), unscannable (nicht prüfbare Archive), eicar. */ function virenscan_analyze(string $path, string $name, int $depth = 0): array { $f = []; $size = (int) @filesize($path); $head = (string) @file_get_contents($path, false, null, 0, 4096); if ($head === '' && $size === 0) return $f; $ext = virenscan_file_ext($name); if (str_contains((string) @file_get_contents($path, false, null, 0, 2 * 1024 * 1024), virenscan_eicar())) return [['eicar', 'Eicar-Test-Signature']]; foreach (virenscan_name_findings($name) as $x) $f[] = $x; if (($kind = virenscan_exec_magic($head)) !== null) $f[] = ['exec', $kind . ($ext !== '' && !in_array($ext, VIRENSCAN_BAD_EXT, true) ? ', getarnt als .' . $ext : '')]; if (in_array($ext, VIRENSCAN_MACRO_EXT, true)) $f[] = ['macro', 'Office-Datei mit Makros (.' . $ext . ')']; if (in_array($ext, VIRENSCAN_OTHER_ARCHIVES, true)) $f[] = ['unscannable', 'Archiv, das nicht geöffnet werden kann (.' . $ext . ')']; // ZIP (auch Office-Dateien): Inhalt prüfen if (str_starts_with($head, "PK\x03\x04") && class_exists('ZipArchive')) { $za = new ZipArchive(); if ($za->open($path) === true) { $n = $za->numFiles; if ($n > 2000) $f[] = ['archive', 'Archiv mit sehr vielen Dateien (' . $n . ')']; $total = 0; $encrypted = false; $macro = false; $bad = []; for ($i = 0; $i < min($n, 2000); $i++) { $st = $za->statIndex($i); if (!$st) continue; $total += (int) $st['size']; $en = (string) $st['name']; if (!empty($st['encryption_method'])) { $encrypted = true; continue; } if (str_ends_with(strtolower($en), 'vbaproject.bin')) $macro = true; foreach (virenscan_name_findings(basename($en)) as $x) { $bad[] = basename($en); break; } if ($i < 300 && (int) $st['size'] > 0 && virenscan_exec_magic((string) $za->getFromIndex($i, 8)) !== null) $bad[] = basename($en); } if ($total > 1024 * 1024 * 1024 || ($size > 0 && $total / max(1, $size) > 2000 && $total > 100 * 1024 * 1024)) $f[] = ['archive', 'Archiv entpackt sich auf ein Vielfaches (Zip-Bombe)']; if ($macro) $f[] = ['macro', 'Office-Datei mit Makros (VBA)']; if ($bad) $f[] = ['exec', 'Archiv enthält ' . implode(', ', array_slice(array_values(array_unique($bad)), 0, 3))]; if ($encrypted) $f[] = ['encrypted', 'Passwortgeschütztes Archiv (Inhalt nicht prüfbar)']; $za->close(); } elseif (!in_array($ext, ['docx', 'xlsx', 'pptx', 'odt', 'ods', 'jar', 'apk'], true)) { $f[] = ['unscannable', 'Archiv lässt sich nicht öffnen']; } } // Altes Office-Format (OLE): Makros erkennen if (str_starts_with($head, "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1")) { $body = (string) @file_get_contents($path, false, null, 0, 4 * 1024 * 1024); if (str_contains($body, '_VBA_PROJECT') || str_contains($body, "_\0V\0B\0A\0_\0P\0R\0O\0J\0E\0C\0T") || str_contains($body, "M\0a\0c\0r\0o\0s\0")) $f[] = ['macro', 'Office-Datei mit Makros (VBA)']; } // PDF: startende Programme oder automatisches JavaScript if (str_contains(substr($head, 0, 1024), '%PDF')) { $body = (string) @file_get_contents($path, false, null, 0, 4 * 1024 * 1024); if (preg_match('/\/Launch\b/', $body)) $f[] = ['script', 'PDF startet Programme (/Launch)']; if (preg_match('/\/(JavaScript|JS)\b/', $body) && preg_match('/\/(OpenAction|AA)\b/', $body)) $f[] = ['script', 'PDF mit automatisch startendem JavaScript']; if (preg_match('/\/EmbeddedFile\b/', $body) && preg_match('/\.(exe|scr|bat|cmd|vbs|js|jar|ps1|msi)\b/i', $body)) $f[] = ['exec', 'PDF mit eingebetteter ausführbarer Datei']; } // Webseiten und Bilder mit Skriptcode if (in_array($ext, ['html', 'htm', 'xhtml', 'shtml', 'svg', 'mht', 'mhtml', 'xml'], true)) { $body = (string) @file_get_contents($path, false, null, 0, 1024 * 1024); if (preg_match('/ Gründe, leer = sauber */ function virenscan_block_reasons(array $findings, array $cfg): array { $reasons = []; foreach ($findings as [$cat, $text]) { $on = match ($cat) { 'exec', 'archive', 'script', 'eicar' => true, 'macro' => $cfg['block_macros'], 'encrypted' => $cfg['block_encrypted'], 'unscannable' => $cfg['block_unscannable'], default => true, }; if ($on) $reasons[] = $text; } return array_values(array_unique($reasons)); }