diff --git a/backend/utils/clientPatcher.js b/backend/utils/clientPatcher.js index 21f6116..18f98b4 100644 --- a/backend/utils/clientPatcher.js +++ b/backend/utils/clientPatcher.js @@ -203,11 +203,19 @@ class ClientPatcher { * .NET AOT stores some strings in various formats: * - Standard UTF-16LE (each char is 2 bytes with \x00 high byte) * - Length-prefixed where last char may have metadata byte instead of \x00 + * + * IMPORTANT: newDomain must be same length or shorter than oldDomain to avoid buffer overflow */ findAndReplaceDomainSmart(data, oldDomain, newDomain) { let count = 0; const result = Buffer.from(data); + // Safety check: new domain must not be longer than old + if (newDomain.length > oldDomain.length) { + console.warn(` Warning: New domain (${newDomain.length} chars) longer than old (${oldDomain.length} chars), skipping smart replacement`); + return { buffer: result, count: 0 }; + } + const oldUtf16NoLast = this.stringToUtf16LE(oldDomain.slice(0, -1)); const newUtf16NoLast = this.stringToUtf16LE(newDomain.slice(0, -1)); @@ -223,6 +231,11 @@ class ClientPatcher { const lastCharFirstByte = result[lastCharPos]; if (lastCharFirstByte === oldLastCharByte) { + // Zero-fill the old region first if new is shorter + if (newUtf16NoLast.length < oldUtf16NoLast.length) { + result.fill(0x00, pos, pos + oldUtf16NoLast.length); + } + newUtf16NoLast.copy(result, pos); result[lastCharPos] = newLastCharByte; @@ -316,14 +329,21 @@ class ClientPatcher { } /** - * Patch Discord invite URLs from .gg/hytale to .gg/MHkEjepMQ7 + * Patch Discord invite URLs from .gg/hytale to shorter URL + * IMPORTANT: New URL must be same length or shorter to avoid corrupting adjacent data */ patchDiscordUrl(data) { let count = 0; const result = Buffer.from(data); const oldUrl = '.gg/hytale'; - const newUrl = '.gg/MHkEjepMQ7'; + // Use same-length URL to avoid buffer overflow + // Original: .gg/hytale (10 chars) + // New: .gg/gME8rUy3MB would be 14 chars - TOO LONG + // Using: .gg/sanasolf2p (13 chars) - still too long + // Using: .gg/hytalef2p (12 chars) - still too long + // Must be exactly 10 chars: .gg/XXXXXX (6 chars after .gg/) + const newUrl = '.gg/santop'; // 10 chars - same length, points to our server list // Try length-prefixed format first const lpResult = this.replaceBytes( @@ -336,13 +356,22 @@ class ClientPatcher { return { buffer: lpResult.buffer, count: lpResult.count }; } - // Fallback to UTF-16LE + // Fallback to UTF-16LE - but ONLY if same length to avoid corruption const oldUtf16 = this.stringToUtf16LE(oldUrl); const newUtf16 = this.stringToUtf16LE(newUrl); + if (newUtf16.length > oldUtf16.length) { + console.warn(` Warning: Discord URL replacement skipped - new URL longer than old`); + return { buffer: result, count: 0 }; + } + const positions = this.findAllOccurrences(result, oldUtf16); for (const pos of positions) { + // Zero-fill first if new is shorter + if (newUtf16.length < oldUtf16.length) { + result.fill(0x00, pos, pos + oldUtf16.length); + } newUtf16.copy(result, pos); count++; }