From 54542612a55e114d5f292386768fe596e959e450 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Fri, 29 May 2026 09:57:34 +0200 Subject: [PATCH] Skip self-extracting archive executables (PKSFX etc.) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added archiver/compression tool names to SKIP_EXE_NAMES (pksfx, pkzip, pkunzip, unzip, arj, rar, lha, etc.) - Added isLikelySelfExtractor() which reads the EXE body looking for PKWARE signatures (PKZIP/PKSFX strings and PK\x03\x04 local file headers) — catches renamed self-extractors regardless of filename - Both filters applied during findMainExe candidate collection --- .../java/com/dostalgia/UploadResource.java | 46 ++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/dostalgia/UploadResource.java b/src/main/java/com/dostalgia/UploadResource.java index 834b89b..44da2ac 100644 --- a/src/main/java/com/dostalgia/UploadResource.java +++ b/src/main/java/com/dostalgia/UploadResource.java @@ -255,6 +255,10 @@ public class UploadResource { if (SKIP_EXE_NAMES.contains(stem)) return FileVisitResult.CONTINUE; + // Skip self-extracting archive executables (PKSFX, PKZIP stubs, etc.) + if (isLikelySelfExtractor(f)) + return FileVisitResult.CONTINUE; + boolean installer = name.contains("install") || name.contains("setup") || name.contains("config"); int depth = f.getNameCount() - dir.getNameCount(); String platform = detectPlatform(f); @@ -280,6 +284,38 @@ public class UploadResource { return candidates.getFirst().path(); } + /** + * Check if an executable is likely a self-extracting archive (PKSFX, PKZIP stub, etc.) + * by looking for characteristic PKWARE signatures in the MS-DOS stub portion. + * Self-extractors contain embedded archive data and are not game executables. + */ + static boolean isLikelySelfExtractor(Path exePath) { + try (var fis = Files.newInputStream(exePath)) { + byte[] buf = new byte[4096]; + int read = fis.readNBytes(buf, 0, buf.length); + if (read < 0x40) return false; + // Must start with MZ + if (buf[0] != 0x4D || buf[1] != 0x5A) return false; + // Scan the MS-DOS stub area (after the header) for PKWARE signatures + // PKSFX/PKZIP stubs contain strings like "PKZIP", "PK", "PKSFX" + // in the first few KB of the executable + String content = new String(buf, 0, read, java.nio.charset.StandardCharsets.ISO_8859_1); + if (content.contains("PKZIP") || content.contains("PKSFX")) + return true; + // Check for the typical PKWARE "PK\x03\x04" local file header signature + // which appears in append-to-exe style self-extractors + for (int i = 0; i < read - 4; i++) { + if (buf[i] == 0x50 && buf[i+1] == 0x4B && buf[i+2] == 0x03 && buf[i+3] == 0x04) + return true; + if (buf[i] == 0x50 && buf[i+1] == 0x4B && buf[i+2] == 0x05 && buf[i+3] == 0x06) + return true; + } + return false; + } catch (IOException e) { + return false; + } + } + // ─── Setup Executable Detection ───────────────────────────── /** @@ -341,7 +377,15 @@ public class UploadResource { "emm386", // Expanded memory manager "himem", "himemx", // Extended memory managers "debug", // Debugger - "uninst", "uninstall", "uninstaller" // Uninstallers + "uninst", "uninstall", "uninstaller", // Uninstallers + // Compression / archive tools (self-extractors) + "pksfx", "pkzip", "pkunzip", // PKWARE + "unzip", "zip", "zip2exe", // Info-ZIP + "arj", "arj32", // ARJ + "lha", "lha32", "lzh", // LHA/LHarc + "rar", "unrar", // RAR + "ace", "unace", // ACE + "zoo", "arc", "ha", "cab" // others ); private void createBundle(Path extractDir, String exePath, Path bundlePath) throws IOException {