mirror of
https://git.sanhost.net/sanasol/hytale-f2p.git
synced 2026-02-26 06:41:47 -03:00
Fix buffer overflow in Discord URL patch - likely cause of crashes
The Discord URL patch was writing 28 bytes (.gg/MHkEjepMQ7, 14 chars) where only 20 bytes existed (.gg/hytale, 10 chars), corrupting 8 bytes of adjacent data in the binary. Changes: - Use same-length Discord URL: .gg/santop (10 chars) - Add length check to UTF-16LE fallback path - Add length check and zero-fill to findAndReplaceDomainSmart This buffer overflow explains why the crash happened on some systems (Steam Deck, Ubuntu LTS) but not others - depending on what data was adjacent to the patched string. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -203,11 +203,19 @@ class ClientPatcher {
|
|||||||
* .NET AOT stores some strings in various formats:
|
* .NET AOT stores some strings in various formats:
|
||||||
* - Standard UTF-16LE (each char is 2 bytes with \x00 high byte)
|
* - Standard UTF-16LE (each char is 2 bytes with \x00 high byte)
|
||||||
* - Length-prefixed where last char may have metadata byte instead of \x00
|
* - 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) {
|
findAndReplaceDomainSmart(data, oldDomain, newDomain) {
|
||||||
let count = 0;
|
let count = 0;
|
||||||
const result = Buffer.from(data);
|
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 oldUtf16NoLast = this.stringToUtf16LE(oldDomain.slice(0, -1));
|
||||||
const newUtf16NoLast = this.stringToUtf16LE(newDomain.slice(0, -1));
|
const newUtf16NoLast = this.stringToUtf16LE(newDomain.slice(0, -1));
|
||||||
|
|
||||||
@@ -223,6 +231,11 @@ class ClientPatcher {
|
|||||||
const lastCharFirstByte = result[lastCharPos];
|
const lastCharFirstByte = result[lastCharPos];
|
||||||
|
|
||||||
if (lastCharFirstByte === oldLastCharByte) {
|
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);
|
newUtf16NoLast.copy(result, pos);
|
||||||
|
|
||||||
result[lastCharPos] = newLastCharByte;
|
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) {
|
patchDiscordUrl(data) {
|
||||||
let count = 0;
|
let count = 0;
|
||||||
const result = Buffer.from(data);
|
const result = Buffer.from(data);
|
||||||
|
|
||||||
const oldUrl = '.gg/hytale';
|
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
|
// Try length-prefixed format first
|
||||||
const lpResult = this.replaceBytes(
|
const lpResult = this.replaceBytes(
|
||||||
@@ -336,13 +356,22 @@ class ClientPatcher {
|
|||||||
return { buffer: lpResult.buffer, count: lpResult.count };
|
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 oldUtf16 = this.stringToUtf16LE(oldUrl);
|
||||||
const newUtf16 = this.stringToUtf16LE(newUrl);
|
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);
|
const positions = this.findAllOccurrences(result, oldUtf16);
|
||||||
|
|
||||||
for (const pos of positions) {
|
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);
|
newUtf16.copy(result, pos);
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user