Fixed server exception on older NodeJS versions, #4102

This commit is contained in:
Ylian Saint-Hilaire 2022-06-09 09:58:02 -07:00
parent 69636342fb
commit cfd8521381
2 changed files with 281 additions and 53 deletions

View File

@ -404,7 +404,7 @@ function createAuthenticodeHandler(path) {
r.size = buf.readUInt32LE(4);
//console.log('readResourceData', r.offsetToData - obj.header.sections['.rsrc'].virtualAddr, r.size, r.offsetToData + r.size - obj.header.sections['.rsrc'].virtualAddr);
r.codePage = buf.readUInt32LE(8);
r.reserved = buf.readUInt32LE(12);
//r.reserved = buf.readUInt32LE(12);
return r;
}
@ -449,9 +449,13 @@ function createAuthenticodeHandler(path) {
if (resources.entries[i].table) { getResourceSectionSize(resources.entries[i].table, sizes); }
else if (resources.entries[i].item) {
sizes.items += 16;
var dataSize = resources.entries[i].item.size;
if ((dataSize % 8) != 0) { dataSize += (8 - (dataSize % 8)); }
sizes.data += dataSize;
if (resources.entries[i].item.buffer) {
sizes.data += resources.entries[i].item.buffer.length;
} else {
var dataSize = resources.entries[i].item.size;
if ((dataSize % 8) != 0) { dataSize += (8 - (dataSize % 8)); }
sizes.data += dataSize;
}
}
}
}
@ -508,20 +512,29 @@ function createAuthenticodeHandler(path) {
// This is a pointer to a data entry
data = resPointers.items;
// Write the data
var entrySize = 0;
if (resources.entries[i].item.buffer) {
// Write the data from given buffer
resources.entries[i].item.buffer.copy(buf, resPointers.data, 0, resources.entries[i].item.buffer.length);
entrySize = resources.entries[i].item.buffer.length;
} else {
// Write the data from original file
const actualPtr = (resources.entries[i].item.offsetToData - obj.header.sections['.rsrc'].virtualAddr) + obj.header.sections['.rsrc'].rawAddr;
const tmp = readFileSlice(actualPtr, resources.entries[i].item.size);
tmp.copy(buf, resPointers.data, 0, tmp.length);
entrySize = resources.entries[i].item.size;;
}
// Write the item entry
buf.writeUInt32LE(resPointers.data + obj.header.sections['.rsrc'].virtualAddr, resPointers.items); // Write the pointer relative to the virtual address
buf.writeUInt32LE(resources.entries[i].item.size, resPointers.items + 4);
buf.writeUInt32LE(entrySize, resPointers.items + 4);
buf.writeUInt32LE(resources.entries[i].item.codePage, resPointers.items + 8);
buf.writeUInt32LE(resources.entries[i].item.reserved, resPointers.items + 12);
// Write the data
const actualPtr = (resources.entries[i].item.offsetToData - obj.header.sections['.rsrc'].virtualAddr) + obj.header.sections['.rsrc'].rawAddr;
const tmp = readFileSlice(actualPtr, resources.entries[i].item.size);
tmp.copy(buf, resPointers.data, 0, tmp.length);
// Move items pointers forward
resPointers.items += 16;
var dataSize = resources.entries[i].item.size;
var dataSize = entrySize;
if ((dataSize % 8) != 0) { dataSize += (8 - (dataSize % 8)); }
resPointers.data += dataSize;
}
@ -531,17 +544,19 @@ function createAuthenticodeHandler(path) {
// Convert a unicode buffer to a string
function unicodeToString(buf) {
var r = '';
for (var i = 0; i < (buf.length / 2) ; i++) { r += String.fromCharCode(buf.readUInt16LE(i * 2)); }
var r = '', c;
for (var i = 0; i < (buf.length / 2) ; i++) {
c = buf.readUInt16LE(i * 2);
if (c != 0) { r += String.fromCharCode(c); } else { return r; }
}
return r;
}
// Trim a string at teh first null character
function stringUntilNull(str) {
if (str == null) return null;
const i = str.indexOf('\0');
if (i >= 0) return str.substring(0, i);
return str;
// Convert a string to a unicode buffer
// Input is a string, a buffer to write to and the offset in the buffer (0 is default).
function stringToUnicode(str, buf, offset) {
if (offset == null) { offset = 0; }
for (var i = 0; i < str.length; i++) { buf.writeInt16LE(str.charCodeAt(i), offset + (i * 2)); }
}
var resourceDefaultNames = {
@ -612,6 +627,7 @@ function createAuthenticodeHandler(path) {
// Decode the version information from the resource
obj.getVersionInfo = function () {
console.log('READ', getVersionInfoData().toString('hex'));
var r = {}, info = readVersionInfo(getVersionInfoData(), 0);
if ((info == null) || (info.stringFiles == null)) return null;
var StringFileInfo = null;
@ -622,6 +638,42 @@ function createAuthenticodeHandler(path) {
return r;
}
// Encode the version information to the resource
obj.setVersionInfo = function (versions) {
// Convert the version information into a string array
const stringArray = [];
for (var i in versions) { stringArray.push({ key: i, value: versions[i] }); }
// Get the existing version data and switch the strings to the new strings
var r = {}, info = readVersionInfo(getVersionInfoData(), 0);
if ((info == null) || (info.stringFiles == null)) return;
var StringFileInfo = null;
for (var i in info.stringFiles) { if (info.stringFiles[i].szKey == 'StringFileInfo') { StringFileInfo = info.stringFiles[i]; } }
if ((StringFileInfo == null) || (StringFileInfo.stringTable == null) || (StringFileInfo.stringTable.strings == null)) return;
StringFileInfo.stringTable.strings = stringArray;
// Re-encode the version information into a buffer
var verInfoResBufArray = [];
writeVersionInfo(verInfoResBufArray, info);
var verInfoRes = Buffer.concat(verInfoResBufArray);
// Display all buffers
//console.log('--WRITE BUF ARRAY START--');
//for (var i in verInfoResBufArray) { console.log(verInfoResBufArray[i].toString('hex')); }
//console.log('--WRITE BUF ARRAY END--');
// Set the new buffer as part of the resources
for (var i = 0; i < obj.resources.entries.length; i++) {
if (obj.resources.entries[i].name == resourceDefaultNames.versionInfo) {
const verInfo = obj.resources.entries[i].table.entries[0].table.entries[0].item;
delete verInfo.size;
delete verInfo.offsetToData;
verInfo.buffer = verInfoRes;
obj.resources.entries[i].table.entries[0].table.entries[0].item = verInfo;
}
}
}
// Return the version info data block
function getVersionInfoData() {
if (obj.resources == null) return null;
@ -629,26 +681,175 @@ function createAuthenticodeHandler(path) {
for (var i = 0; i < obj.resources.entries.length; i++) {
if (obj.resources.entries[i].name == resourceDefaultNames.versionInfo) {
const verInfo = obj.resources.entries[i].table.entries[0].table.entries[0].item;
const actualPtr = (verInfo.offsetToData - obj.header.sections['.rsrc'].virtualAddr) + ptr;
return readFileSlice(actualPtr, verInfo.size);
if (verInfo.buffer != null) {
return verInfo.buffer;
} else {
const actualPtr = (verInfo.offsetToData - obj.header.sections['.rsrc'].virtualAddr) + ptr;
return readFileSlice(actualPtr, verInfo.size);
}
}
}
return null;
}
// Create a VS_VERSIONINFO structure as a array of buffer that is ready to be placed in the resource section
// VS_VERSIONINFO structure: https://docs.microsoft.com/en-us/windows/win32/menurc/vs-versioninfo
function writeVersionInfo(bufArray, info) {
const buf = Buffer.alloc(40);
buf.writeUInt16LE(0, 4); // wType
stringToUnicode('VS_VERSION_INFO', buf, 6);
bufArray.push(buf);
var wLength = 40;
var wValueLength = 0;
if (info.fixedFileInfo != null) {
const buf2 = Buffer.alloc(52);
wLength += 52;
wValueLength += 52;
buf2.writeUInt32LE(info.fixedFileInfo.dwSignature, 0); // dwSignature
buf2.writeUInt32LE(info.fixedFileInfo.dwStrucVersion, 4); // dwStrucVersion
buf2.writeUInt32LE(info.fixedFileInfo.dwFileVersionMS, 8); // dwFileVersionMS
buf2.writeUInt32LE(info.fixedFileInfo.dwFileVersionLS, 12); // dwFileVersionLS
buf2.writeUInt32LE(info.fixedFileInfo.dwProductVersionMS, 16); // dwProductVersionMS
buf2.writeUInt32LE(info.fixedFileInfo.dwProductVersionLS, 20); // dwProductVersionLS
buf2.writeUInt32LE(info.fixedFileInfo.dwFileFlagsMask, 24); // dwFileFlagsMask
buf2.writeUInt32LE(info.fixedFileInfo.dwFileFlags, 28); // dwFileFlags
buf2.writeUInt32LE(info.fixedFileInfo.dwFileOS, 32); // dwFileOS
buf2.writeUInt32LE(info.fixedFileInfo.dwFileType, 36); // dwFileType
buf2.writeUInt32LE(info.fixedFileInfo.dwFileSubtype, 40); // dwFileSubtype
buf2.writeUInt32LE(info.fixedFileInfo.dwFileDateMS, 44); // dwFileDateMS
buf2.writeUInt32LE(info.fixedFileInfo.dwFileDateLS, 48); // dwFileDateLS
bufArray.push(buf2);
}
if (info.stringFiles != null) { wLength += writeStringFileInfo(bufArray, info.stringFiles); }
console.log('@@@@@@Z', wLength, Buffer.concat(bufArray).length);
buf.writeUInt16LE(Buffer.concat(bufArray).length, 0); // wLength
buf.writeUInt16LE(wValueLength, 2); // wValueLength
return wLength;
}
// StringFileInfo structure: https://docs.microsoft.com/en-us/windows/win32/menurc/stringfileinfo
function writeStringFileInfo(bufArray, stringFiles) {
//console.log('writeStringFileInfo', stringFiles);
var totalLen = 0;
for (var i in stringFiles) {
var l = 6 + (stringFiles[i].szKey.length * 2);
const buf2 = Buffer.alloc(padPointer(l));
buf2.writeUInt16LE(1, 4); // wType
stringToUnicode(stringFiles[i].szKey, buf2, 6);
bufArray.push(buf2);
var wLength = 0, wValueLength = 0;
if (stringFiles[i].szKey == 'StringFileInfo') { wLength += writeStringTableStruct(bufArray, stringFiles[i].stringTable); }
if (stringFiles[i].szKey == 'VarFileInfo') { wLength += writeVarFileInfoStruct(bufArray, stringFiles[i].varFileInfo); }
buf2.writeUInt16LE(l + wLength, 0); // wLength
buf2.writeUInt16LE(wValueLength, 2); // wValueLength
totalLen += buf2.length + wLength;
}
return totalLen;
}
// VarFileInfo structure: https://docs.microsoft.com/en-us/windows/win32/menurc/var-str
function writeVarFileInfoStruct(bufArray, varFileInfo) {
console.log('*************writeVarFileInfoStruct', varFileInfo);
var l = 6 + (varFileInfo.szKey.length * 2);
const buf = Buffer.alloc(padPointer(l));
buf.writeUInt16LE(0, 4); // wType
stringToUnicode(varFileInfo.szKey, buf, 6);
bufArray.push(buf);
var wLength = 0;
var wValueLength = 0;
if (varFileInfo.value) {
bufArray.push(varFileInfo.value);
wLength += varFileInfo.value.length;
}
buf.writeUInt16LE(l + wLength, 0); // wLength
buf.writeUInt16LE(wValueLength, 2); // wValueLength
//console.log('WwriteVarFileInfoStruct', buf.toString('hex'));
return buf.length + wLength;
}
// StringTable structure: https://docs.microsoft.com/en-us/windows/win32/menurc/stringtable
function writeStringTableStruct(bufArray, stringTable) {
//console.log('writeStringTableStruct', stringTable);
var l = 6 + (stringTable.szKey.length * 2);
const buf = Buffer.alloc(padPointer(l));
buf.writeUInt16LE(1, 4); // wType
stringToUnicode(stringTable.szKey, buf, 6);
bufArray.push(buf);
var wLength = 0;
var wValueLength = 0;
if (stringTable.strings) { wLength += writeStringStructs(bufArray, stringTable.strings); }
buf.writeUInt16LE(l + wLength, 0); // wLength
buf.writeUInt16LE(wValueLength, 2); // wValueLength
//console.log('WStringTableStruct', buf.toString('hex'));
return buf.length + wLength;
}
// String structure: https://docs.microsoft.com/en-us/windows/win32/menurc/string-str
function writeStringStructs(bufArray, stringTable) {
//console.log('writeStringStructs', stringTable);
var totalLen = 0, bufadd = 0;
for (var i in stringTable) {
//console.log('writeStringStructs', stringTable[i]);
const buf = Buffer.alloc(padPointer(6 + ((stringTable[i].key.length + 1) * 2)));
var buf2, wLength = buf.length;
var wValueLength = 0;
stringToUnicode(stringTable[i].key, buf, 6);
bufArray.push(buf);
bufadd += buf.length;
if (typeof stringTable[i].value == 'string') {
// wType (string)
buf.writeUInt16LE(1, 4);
var l = (stringTable[i].value.length + 1) * 2;
buf2 = Buffer.alloc(padPointer(l));
stringToUnicode(stringTable[i].value, buf2, 0);
bufArray.push(buf2);
bufadd += buf2.length;
wValueLength = stringTable[i].value.length + 1;
wLength += l;
}
if (typeof stringTable[i].value == 'object') {
// wType (binary)
buf.writeUInt16LE(2, 4); // TODO: PADDING
bufArray.push(stringTable[i].value);
bufadd += stringTable[i].value.length;
wValueLength = stringTable[i].value.length;
wLength += wValueLength;
}
buf.writeUInt16LE(wLength, 0); // wLength
buf.writeUInt16LE(wValueLength, 2); // wValueLength
//console.log('WStringStruct', buf.toString('hex'), buf2.toString('hex'));
totalLen += wLength;
}
//return totalLen;
return bufadd;
}
// VS_VERSIONINFO structure: https://docs.microsoft.com/en-us/windows/win32/menurc/vs-versioninfo
function readVersionInfo(buf, ptr) {
const r = {};
if (buf.length < 2) return null;
r.wLength = buf.readUInt16LE(ptr);
if (buf.length < r.wLength) return null;
r.wValueLength = buf.readUInt16LE(ptr + 2);
r.wType = buf.readUInt16LE(ptr + 4);
const wLength = buf.readUInt16LE(ptr);
if (buf.length < wLength) return null;
const wValueLength = buf.readUInt16LE(ptr + 2);
const wType = buf.readUInt16LE(ptr + 4);
r.szKey = unicodeToString(buf.slice(ptr + 6, ptr + 36));
if (r.szKey != 'VS_VERSION_INFO') return null;
//console.log('getVersionInfo', r.wLength, r.wValueLength, r.wType, r.szKey.toString());
if (r.wValueLength == 52) { r.fixedFileInfo = readFixedFileInfoStruct(buf, ptr + 40); }
r.stringFiles = readStringFilesStruct(buf, ptr + 40 + r.wValueLength, r.wLength - 40 - r.wValueLength);
//console.log('getVersionInfo', wLength, wValueLength, wType, r.szKey.toString());
if (wValueLength == 52) { r.fixedFileInfo = readFixedFileInfoStruct(buf, ptr + 40); }
r.stringFiles = readStringFilesStruct(buf, ptr + 40 + wValueLength, wLength - 40 - wValueLength);
return r;
}
@ -678,30 +879,43 @@ function createAuthenticodeHandler(path) {
var t = [], startPtr = ptr;
while (ptr < (startPtr + len)) {
const r = {};
r.wLength = buf.readUInt16LE(ptr);
if (r.wLength == 0) return t;
r.wValueLength = buf.readUInt16LE(ptr + 2);
r.wType = buf.readUInt16LE(ptr + 4); // 1 = Text, 2 = Binary
r.szKey = stringUntilNull(unicodeToString(buf.slice(ptr + 6, ptr + 6 + (r.wLength - 6)))); // String value
//console.log('readStringFileStruct', r.wLength, r.wValueLength, r.wType, r.szKey.toString());
if (r.szKey == 'StringFileInfo') { r.stringTable = readStringTableStruct(buf, ptr + 36 + r.wValueLength); }
if (r.szKey == 'VarFileInfo$') { r.varFileInfo = {}; } // TODO
const wLength = buf.readUInt16LE(ptr);
if (wLength == 0) return t;
const wValueLength = buf.readUInt16LE(ptr + 2);
const wType = buf.readUInt16LE(ptr + 4); // 1 = Text, 2 = Binary
r.szKey = unicodeToString(buf.slice(ptr + 6, ptr + 6 + (wLength - 6))); // String value
//console.log('readStringFileStruct', wLength, wValueLength, wType, r.szKey);
if (r.szKey == 'StringFileInfo') { r.stringTable = readStringTableStruct(buf, ptr + 36); }
if (r.szKey == 'VarFileInfo') { r.varFileInfo = readVarFileInfoStruct(buf, ptr + 32); }
t.push(r);
ptr += r.wLength;
ptr += wLength;
ptr = padPointer(ptr);
}
return t;
}
// VarFileInfo structure: https://docs.microsoft.com/en-us/windows/win32/menurc/var-str
function readVarFileInfoStruct(buf, ptr) {
const r = {};
const wLength = buf.readUInt16LE(ptr);
const wValueLength = buf.readUInt16LE(ptr + 2);
const wType = buf.readUInt16LE(ptr + 4); // 1 = Text, 2 = Binary
r.szKey = unicodeToString(buf.slice(ptr + 6, ptr + wLength)); // "VarFileInfo"
r.value = buf.slice(ptr + wLength - wValueLength, ptr + wLength)
//console.log('readVarFileInfoStruct', wLength, wValueLength, wType, r.szKey, r.value.toString('hex'));
return r;
}
// StringTable structure: https://docs.microsoft.com/en-us/windows/win32/menurc/stringtable
function readStringTableStruct(buf, ptr) {
const r = {};
r.wLength = buf.readUInt16LE(ptr);
r.wValueLength = buf.readUInt16LE(ptr + 2);
r.wType = buf.readUInt16LE(ptr + 4); // 1 = Text, 2 = Binary
const wLength = buf.readUInt16LE(ptr);
const wValueLength = buf.readUInt16LE(ptr + 2);
const wType = buf.readUInt16LE(ptr + 4); // 1 = Text, 2 = Binary
//console.log('RStringTableStruct', buf.slice(ptr, ptr + wLength).toString('hex'));
r.szKey = unicodeToString(buf.slice(ptr + 6, ptr + 6 + 16)); // An 8-digit hexadecimal number stored as a Unicode string.
//console.log('readStringTableStruct', r.wLength, r.wValueLength, r.wType, r.szKey);
r.strings = readStringStructs(buf, ptr + 24 + r.wValueLength, r.wLength - 22);
//console.log('readStringTableStruct', wLength, wValueLength, r.wType, r.szKey);
r.strings = readStringStructs(buf, ptr + 24 + wValueLength, wLength - 22);
return r;
}
@ -710,22 +924,30 @@ function createAuthenticodeHandler(path) {
var t = [], startPtr = ptr;
while (ptr < (startPtr + len)) {
const r = {};
r.wLength = buf.readUInt16LE(ptr);
if (r.wLength == 0) return t;
r.wValueLength = buf.readUInt16LE(ptr + 2);
r.wType = buf.readUInt16LE(ptr + 4); // 1 = Text, 2 = Binary
r.key = unicodeToString(buf.slice(ptr + 6, ptr + (r.wLength - (r.wValueLength * 2)))); // Key
r.value = unicodeToString(buf.slice(ptr + r.wLength - (r.wValueLength * 2), ptr + r.wLength)); // Value
//console.log('readStringStruct', r.wLength, r.wValueLength, r.wType, r.key, r.value);
const wLength = buf.readUInt16LE(ptr);
if (wLength == 0) return t;
//console.log('RStringStruct', buf.slice(ptr, ptr + wLength).toString('hex'));
const wValueLength = buf.readUInt16LE(ptr + 2);
const wType = buf.readUInt16LE(ptr + 4); // 1 = Text, 2 = Binary
//console.log('R', buf.slice(ptr, ptr + wLength).toString('hex'));
r.key = unicodeToString(buf.slice(ptr + 6, ptr + (wLength - (wValueLength * 2)) - 2)); // Key
if (wType == 1) { r.value = unicodeToString(buf.slice(ptr + wLength - (wValueLength * 2), ptr + wLength - 2)); } // String value
if (wType == 2) { r.value = buf.slice(ptr + wLength - (wValueLength * 2), ptr + wLength); } // Binary value
//console.log('readStringStruct', wLength, wValueLength, wType, r.key, r.value);
t.push(r);
ptr += r.wLength;
ptr += wLength;
ptr = padPointer(ptr);
}
return t;
}
// Return the next 4 byte aligned number
function padPointer(ptr) { return ptr + (ptr % 4); }
function padPointer(ptr) { return ptr + (((ptr % 4) == 0) ? 0 : (4 - (ptr % 4))); }
//function padPointer(ptr) { return ptr + (ptr % 4); }
// Hash the file using the selected hashing system
obj.getHash = function(algo) {
@ -942,6 +1164,12 @@ function createAuthenticodeHandler(path) {
// Save the executable
obj.writeExecutable = function (args) {
// Get version information from the resource
var versions = obj.getVersionInfo();
//versions['FileDescription'] = 'Mesh Agent Service';
obj.setVersionInfo(versions);
//var versions2 = obj.getVersionInfo();
// Open the file
var output = fs.openSync(args.out, 'w');
var tmp, written = 0;

View File

@ -7697,8 +7697,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
xargs.autocomplete = (domain.autocomplete === false)?'x':'autocomplete'; // This option allows autocomplete to be turned off on the login page.
if (typeof domain.hide == 'number') { xargs.hide = domain.hide; }
// To mitigate any possible BREACH attack, we generate a random length string here.
xargs.randomlength = (args.webpagelengthrandomization !== false) ? parent.crypto.randomBytes(parent.crypto.randomInt(0, 256)).toString('base64') : '';
// To mitigate any possible BREACH attack, we generate a random 0 to 255 bytes length string here.
xargs.randomlength = (args.webpagelengthrandomization !== false) ? parent.crypto.randomBytes(parent.crypto.randomBytes(1)[0]).toString('base64') : '';
return xargs;
}