From 9b06a8dc568d82a49c28facd0967ba8404db825a Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Tue, 7 Jun 2022 22:09:54 -0700 Subject: [PATCH] authenticode.js can now break and re-create a Windows resource region. --- authenticode.js | 313 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 294 insertions(+), 19 deletions(-) diff --git a/authenticode.js b/authenticode.js index 33d5195f..e443ec3d 100644 --- a/authenticode.js +++ b/authenticode.js @@ -120,10 +120,10 @@ function createAuthenticodeHandler(path) { // Open the file descriptor obj.path = path; - try { obj.fd = fs.openSync(path); } catch (ex) { return false; } // Unable to open file + try { obj.fd = fs.openSync(path); } catch (ex) { console.log('E1'); return false; } // Unable to open file obj.stats = fs.fstatSync(obj.fd); obj.filesize = obj.stats.size; - if (obj.filesize < 64) { obj.close(); return false; } // File too short. + if (obj.filesize < 64) { obj.close(); console.log('E2'); return false; } // File too short. // Read the DOS header (64 bytes) var buf = readFileSlice(60, 4); @@ -131,8 +131,8 @@ function createAuthenticodeHandler(path) { obj.header.peOptionalHeaderLocation = obj.header.peHeaderLocation + 24; // The PE optional header is located just after the PE header which is 24 bytes long. // Check file size and signature - if (obj.filesize < (160 + obj.header.peHeaderLocation)) { obj.close(); return false; } // Invalid SizeOfHeaders. - if (readFileSlice(obj.header.peHeaderLocation, 4).toString('hex') != '50450000') { obj.close(); return false; } // Invalid PE header, must start with "PE" (HEX: 50 45 00 00). + if (obj.filesize < (160 + obj.header.peHeaderLocation)) { obj.close(); console.log('E3'); return false; } // Invalid SizeOfHeaders. + if (readFileSlice(obj.header.peHeaderLocation, 4).toString('hex') != '50450000') { obj.close(); console.log('E4'); return false; } // Invalid PE header, must start with "PE" (HEX: 50 45 00 00). // Read the COFF header // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#coff-file-header-object-and-image @@ -156,7 +156,7 @@ function createAuthenticodeHandler(path) { switch (obj.header.peStandard.magic) { // Check magic value case 0x020B: obj.header.pe32plus = 1; break; case 0x010B: obj.header.pe32plus = 0; break; - default: { obj.close(); return false; } // Invalid Magic in PE + default: { obj.close(); console.log('E5'); return false; } // Invalid Magic in PE } obj.header.peStandard.majorLinkerVersion = optinalHeader[2]; obj.header.peStandard.minorLinkerVersion = optinalHeader[3]; @@ -224,10 +224,10 @@ function createAuthenticodeHandler(path) { const pePlusOffset = (obj.header.pe32plus == 0) ? 0 : 16; // This header is the same for 32 and 64 bit, but 64bit is offset by 16 bytes. obj.header.dataDirectories.exportTable = { addr: optinalHeader.readUInt32LE(96 + pePlusOffset), size: optinalHeader.readUInt32LE(100 + pePlusOffset) }; obj.header.dataDirectories.importTable = { addr: optinalHeader.readUInt32LE(104 + pePlusOffset), size: optinalHeader.readUInt32LE(108 + pePlusOffset) }; - obj.header.dataDirectories.resourceTable = { addr: optinalHeader.readUInt32LE(112 + pePlusOffset), size: optinalHeader.readUInt32LE(116 + pePlusOffset) }; - obj.header.dataDirectories.exceptionTableAddr = { addr: optinalHeader.readUInt32LE(120 + pePlusOffset), size: optinalHeader.readUInt32LE(124 + pePlusOffset) }; + obj.header.dataDirectories.resourceTable = { addr: optinalHeader.readUInt32LE(112 + pePlusOffset), size: optinalHeader.readUInt32LE(116 + pePlusOffset) }; // Same as .rsrc virtual address & size + obj.header.dataDirectories.exceptionTableAddr = { addr: optinalHeader.readUInt32LE(120 + pePlusOffset), size: optinalHeader.readUInt32LE(124 + pePlusOffset) }; // Same as .pdata virtual address & size obj.header.dataDirectories.certificateTable = { addr: optinalHeader.readUInt32LE(128 + pePlusOffset), size: optinalHeader.readUInt32LE(132 + pePlusOffset) }; - obj.header.dataDirectories.baseRelocationTable = { addr: optinalHeader.readUInt32LE(136 + pePlusOffset), size: optinalHeader.readUInt32LE(140 + pePlusOffset) }; + obj.header.dataDirectories.baseRelocationTable = { addr: optinalHeader.readUInt32LE(136 + pePlusOffset), size: optinalHeader.readUInt32LE(140 + pePlusOffset) }; // Same as .reloc virtual address & size obj.header.dataDirectories.debug = { addr: optinalHeader.readUInt32LE(144 + pePlusOffset), size: optinalHeader.readUInt32LE(148 + pePlusOffset) }; // obj.header.dataDirectories.architecture = optinalHeader.readBigUInt64LE(152 + pePlusOffset); // Must be zero obj.header.dataDirectories.globalPtr = { addr: optinalHeader.readUInt32LE(160 + pePlusOffset), size: optinalHeader.readUInt32LE(164 + pePlusOffset) }; @@ -252,10 +252,11 @@ function createAuthenticodeHandler(path) { for (var i = 0; i < obj.header.coff.numberOfSections; i++) { var section = {}; buf = readFileSlice(obj.header.SectionHeadersPtr + (i * 40), 40); - if (buf[0] != 46) { obj.close(); return false; }; // Name of the section must start with a dot. If not, something is wrong. + if (buf[0] != 46) { obj.close(); console.log('E6'); return false; }; // Name of the section must start with a dot. If not, something is wrong. var sectionName = buf.slice(0, 8).toString().trim('\0'); var j = sectionName.indexOf('\0'); if (j >= 0) { sectionName = sectionName.substring(0, j); } // Trim any trailing zeroes + section.ptr = obj.header.SectionHeadersPtr + (i * 40); section.virtualSize = buf.readUInt32LE(8); section.virtualAddr = buf.readUInt32LE(12); section.rawSize = buf.readUInt32LE(16); @@ -267,11 +268,11 @@ function createAuthenticodeHandler(path) { section.characteristics = buf.readUInt32LE(36); obj.header.sections[sectionName] = section; } - //console.log(obj.header.sections); // If there is a .rsrc section, read the resource information and locations if (obj.header.sections['.rsrc'] != null) { - var ptr = obj.header.sections['.rsrc'].rawAddr; + const ptr = obj.header.sections['.rsrc'].rawAddr; + console.log('.rsrc section', ptr, obj.header.sections['.rsrc'].rawSize); obj.resources = readResourceTable(ptr, 0); // Read all resources recursively } @@ -279,7 +280,7 @@ function createAuthenticodeHandler(path) { // Read signature block // Check if the file size allows for the signature block - if (obj.filesize < (obj.header.sigpos + obj.header.siglen)) { obj.close(); return false; } // Executable file too short to contain the signature block. + if (obj.filesize < (obj.header.sigpos + obj.header.siglen)) { obj.close(); console.log('E7'); return false; } // Executable file too short to contain the signature block. // Remove the padding if needed var i, pkcs7raw = readFileSlice(obj.header.sigpos + 8, obj.header.siglen - 8); @@ -371,15 +372,21 @@ function createAuthenticodeHandler(path) { r.majorVersion = buf.readUInt16LE(8); r.minorVersion = buf.readUInt16LE(10); var numberOfNamedEntries = buf.readUInt16LE(12); - var numberofIdEntries = buf.readUInt16LE(14); + var numberOfIdEntries = buf.readUInt16LE(14); + r.entries = []; - var totalResources = numberOfNamedEntries + numberofIdEntries; + var totalResources = numberOfNamedEntries + numberOfIdEntries; + //console.log('readResourceTable', offset, 16 + (totalResources) * 8, offset + (16 + (totalResources) * 8)); for (var i = 0; i < totalResources; i++) { buf = readFileSlice(ptr + offset + 16 + (i * 8), 8); var resource = {}; resource.name = buf.readUInt32LE(0); var offsetToData = buf.readUInt32LE(4); - if ((resource.name & 0x80000000) != 0) { resource.name = readLenPrefixUnicodeString(ptr + (resource.name - 0x80000000)); } + if ((resource.name & 0x80000000) != 0) { + var oname = resource.name; + resource.name = readLenPrefixUnicodeString(ptr + (resource.name - 0x80000000)); + //console.log('readResourceName', offset + (oname - 0x80000000), 2 + (resource.name.length * 2), offset + (oname - 0x80000000) + (2 + resource.name.length * 2), resource.name); + } if ((offsetToData & 0x80000000) != 0) { resource.table = readResourceTable(ptr, offsetToData - 0x80000000); } else { resource.item = readResourceItem(ptr, offsetToData); } r.entries.push(resource); } @@ -390,9 +397,11 @@ function createAuthenticodeHandler(path) { // ptr: The pointer to the start of the resource section // offset: The offset start of the resource item to read function readResourceItem(ptr, offset) { + //console.log('readResourceItem', offset, 16, offset + 16); var buf = readFileSlice(ptr + offset, 16), r = {}; r.offsetToData = buf.readUInt32LE(0); 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); return r; @@ -400,12 +409,125 @@ function createAuthenticodeHandler(path) { // Read a unicode stting that starts with the string length as the first byte. function readLenPrefixUnicodeString(ptr) { - var nameLen = readFileSlice(ptr, 1)[0]; - var buf = readFileSlice(ptr + 1, nameLen * 2), name = ''; - for (var i = 0; i < nameLen; i++) { name += String.fromCharCode(buf.readUInt16BE(i * 2)); } + var nameLen = readFileSlice(ptr, 2).readUInt16LE(0); + var buf = readFileSlice(ptr + 2, nameLen * 2), name = ''; + for (var i = 0; i < nameLen; i++) { name += String.fromCharCode(buf.readUInt16LE(i * 2)); } return name; } + // Generate a complete resource section and pad the section + function generateResourceSection(resources) { + // Call a resursive method the compute the size needed for each element + const resSizes = { tables: 0, items: 0, names: 0, data: 0 }; + getResourceSectionSize(resources, resSizes); + + // Pad the resource section & allocate the buffer + const fileAlign = obj.header.peWindows.fileAlignment + var resSizeTotal = resSizes.tables + resSizes.items + resSizes.names + resSizes.data; + if ((resSizeTotal % fileAlign) != 0) { resSizeTotal += (fileAlign - (resSizeTotal % fileAlign)); } + const resSectionBuffer = Buffer.alloc(resSizeTotal); + + // Write the resource section, calling a recusrize method + const resPointers = { tables: 0, items: resSizes.tables, names: resSizes.tables + resSizes.items, data: resSizes.tables + resSizes.items + resSizes.names }; + createResourceSection(resources, resSectionBuffer, resPointers); + //console.log('generateResourceSection', resPointers); + + // Done, return the result + return resSectionBuffer; + } + + // Return the total size of a resource header, this is a recursive method + function getResourceSectionSize(resources, sizes) { + sizes.tables += (16 + (resources.entries.length * 8)); + for (var i in resources.entries) { + if (typeof resources.entries[i].name == 'string') { + var dataSize = (2 + (resources.entries[i].name.length * 2)); + if ((dataSize % 8) != 0) { dataSize += (8 - (dataSize % 8)); } + sizes.names += dataSize; + } + 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; + } + } + } + + // Write the resource section in the buffer, this is a recursive method + function createResourceSection(resources, buf, resPointers) { + var numberOfNamedEntries = 0, numberOfIdEntries = 0, ptr = resPointers.tables; + //console.log('createResourceSection', resPointers, ptr); + + // Figure out how many items we have to save + for (var i in resources.entries) { + if (typeof resources.entries[i].name == 'string') { numberOfNamedEntries++; } else { numberOfIdEntries++; } + } + + // Move the table pointer forward + resPointers.tables += (16 + (8 * numberOfNamedEntries) + (8 * numberOfIdEntries)); + + // Write the table header + buf.writeUInt32LE(resources.characteristics, ptr); + buf.writeUInt32LE(resources.timeDateStamp, ptr + 4); + buf.writeUInt16LE(resources.majorVersion, ptr + 8); + buf.writeUInt16LE(resources.minorVersion, ptr + 10); + buf.writeUInt16LE(numberOfNamedEntries, ptr + 12); + buf.writeUInt16LE(numberOfIdEntries, ptr + 14); + + // For each table entry, write the entry for it + for (var i in resources.entries) { + // Write the name + var name = resources.entries[i].name; + if (typeof resources.entries[i].name == 'string') { + // Set the pointer to the name + name = resPointers.names + 0x80000000; + + // Write the name length, followed by the name string in unicode + buf.writeUInt16LE(resources.entries[i].name.length, resPointers.names); + for (var j = 0; j < resources.entries[i].name.length; j++) { + buf.writeUInt16LE(resources.entries[i].name.charCodeAt(j), 2 + resPointers.names + (j * 2)); + } + + // Move the names pointer forward, 8 byte align + var dataSize = (2 + (resources.entries[i].name.length * 2)); + if ((dataSize % 8) != 0) { dataSize += (8 - (dataSize % 8)); } + resPointers.names += dataSize; + } + buf.writeUInt32LE(name, ptr + 16 + (i * 8)); + + // Write the data + var data; + if (resources.entries[i].table) { + // This is a pointer to a table entry + data = resPointers.tables + 0x80000000; + createResourceSection(resources.entries[i].table, buf, resPointers); + } else if (resources.entries[i].item) { + // This is a pointer to a data entry + data = resPointers.items; + + // 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(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; + if ((dataSize % 8) != 0) { dataSize += (8 - (dataSize % 8)); } + resPointers.data += dataSize; + } + buf.writeUInt32LE(data, ptr + 20 + (i * 8)); + } + } + // Convert a unicode buffer to a string function unicodeToString(buf) { var r = ''; @@ -619,6 +741,25 @@ function createAuthenticodeHandler(path) { while (ptr < end) { const buf = readFileSlice(ptr, Math.min(65536, end - ptr)); hash.update(buf); ptr += buf.length; } } + // Compute the PE checksum of a file (this is not yet tested) + function getChecksum(data, PECheckSumLocation) { + var checksum = 0, top = Math.pow(2, 32); + + for (var i = 0; i < (data.length / 4); i++) { + if (i == PECheckSumLocation / 4) continue; + var dword = data.readUInt32LE(i * 4); + checksum = (checksum & 0xffffffff) + dword + (checksum >> 32); + if (checksum > top) { checksum = (checksum & 0xffffffff) + (checksum >> 32); } + } + + checksum = (checksum & 0xffff) + (checksum >> 16); + checksum = (checksum) + (checksum >> 16); + checksum = checksum & 0xffff; + + checksum += data.length; + return checksum; + } + // Sign the file using the certificate and key. If none is specified, generate a dummy one obj.sign = function (cert, args) { if (cert == null) { cert = createSelfSignedCert({ cn: 'Test' }); } @@ -739,6 +880,123 @@ function createAuthenticodeHandler(path) { fs.closeSync(output); } + // Save the executable + obj.writeExecutable = function (args) { + // Open the file + var output = fs.openSync(args.out, 'w'); + var tmp, written = 0; + + // Compute the size of the complete executable header up to after the sections header + var fullHeaderLen = obj.header.SectionHeadersPtr + (obj.header.coff.numberOfSections * 40); + var fullHeader = readFileSlice(written, fullHeaderLen); + + // Calculate the location and original and new size of the resource segment + var fileAlign = obj.header.peWindows.fileAlignment + var resPtr = obj.header.sections['.rsrc'].rawAddr; + var oldResSize = obj.header.sections['.rsrc'].rawSize; + var newResSize = obj.header.sections['.rsrc'].rawSize; // Testing 102400 + var resDeltaSize = newResSize - oldResSize; + + console.log('fileAlign', fileAlign); + console.log('resPtr', resPtr); + console.log('oldResSize', oldResSize); + console.log('newResSize', newResSize); + console.log('resDeltaSize', resDeltaSize); + + // Change PE optional header sizeOfInitializedData standard field + fullHeader.writeUInt32LE(obj.header.peStandard.sizeOfInitializedData + resDeltaSize, obj.header.peOptionalHeaderLocation + 8); + fullHeader.writeUInt32LE(obj.header.peWindows.sizeOfImage, obj.header.peOptionalHeaderLocation + 56); // TODO: resDeltaSize + + // Update the checksum, set to zero since it's not used + // TODO: Take a look at computing this correctly in the future + fullHeader.writeUInt32LE(0, obj.header.peOptionalHeaderLocation + 64); + + // Make change to the data directories header to fix resource segment size and add/remove signature + const pePlusOffset = (obj.header.pe32plus == 0) ? 0 : 16; // This header is the same for 32 and 64 bit, but 64bit is offset by 16 bytes. + if (obj.header.dataDirectories.exportTable.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.exportTable.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 96 + pePlusOffset); } + if (obj.header.dataDirectories.importTable.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.importTable.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 104 + pePlusOffset); } + //fullHeader.writeUInt32LE(obj.header.dataDirectories.resourceTable.size + resDeltaSize, obj.header.peOptionalHeaderLocation + 116 + pePlusOffset); // Change the resource segment size + if (obj.header.dataDirectories.exceptionTableAddr.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.exceptionTableAddr.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 120 + pePlusOffset); } + fullHeader.writeUInt32LE(0, obj.header.peOptionalHeaderLocation + 128 + pePlusOffset); // certificate table addr (TODO) + fullHeader.writeUInt32LE(0, obj.header.peOptionalHeaderLocation + 132 + pePlusOffset); // certificate table size (TODO) + if (obj.header.dataDirectories.baseRelocationTable.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.baseRelocationTable.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 136 + pePlusOffset); } + if (obj.header.dataDirectories.debug.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.debug.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 144 + pePlusOffset); } + if (obj.header.dataDirectories.globalPtr.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.globalPtr.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 160 + pePlusOffset); } + if (obj.header.dataDirectories.tLSTable.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.tLSTable.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 168 + pePlusOffset); } + if (obj.header.dataDirectories.loadConfigTable.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.loadConfigTable.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 176 + pePlusOffset); } + if (obj.header.dataDirectories.boundImport.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.boundImport.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 184 + pePlusOffset); } + if (obj.header.dataDirectories.iAT.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.iAT.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 192 + pePlusOffset); } + if (obj.header.dataDirectories.delayImportDescriptor.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.delayImportDescriptor.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 200 + pePlusOffset); } + if (obj.header.dataDirectories.clrRuntimeHeader.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.clrRuntimeHeader.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 208 + pePlusOffset); } + + // Make changes to the segments table + for (var i in obj.header.sections) { + const section = obj.header.sections[i]; + if (i == '.rsrc') { + // Change the size of the resource section + fullHeader.writeUInt32LE(section.rawSize + resDeltaSize, section.ptr + 8); // virtualSize (TODO) + fullHeader.writeUInt32LE(section.rawSize + resDeltaSize, section.ptr + 16); // rawSize + } else { + // Change the location of any other section if located after the resource section + if (section.virtualAddr > resPtr) { fullHeader.writeUInt32LE(section.virtualAddr + resDeltaSize, section.ptr + 12); } + if (section.rawAddr > resPtr) { fullHeader.writeUInt32LE(section.rawAddr + resDeltaSize, section.ptr + 20); } + } + } + + // Write the entire header to the destination file + console.log('Write header', fullHeader.length); + fs.writeSync(output, fullHeader); + written += fullHeader.length; + + // Write the entire executable until the start to the resource segment + var totalWrite = resPtr; + console.log('Write until res', totalWrite); + while ((totalWrite - written) > 0) { + tmp = readFileSlice(written, Math.min(totalWrite - written, 65536)); + fs.writeSync(output, tmp); + written += tmp.length; + } + + // Write the new resource section + var rsrcSection = generateResourceSection(obj.resources); + fs.writeSync(output, rsrcSection); + written += rsrcSection.length; + + /* + // Write the old resource segment (debug) + totalWrite = resPtr + oldResSize; + console.log('Write res', totalWrite); + while ((totalWrite - written) > 0) { + tmp = readFileSlice(written, Math.min(totalWrite - written, 65536)); + fs.writeSync(output, tmp); + written += tmp.length; + } + */ + + /* + // Write a dummy 102400 bytes + tmp = Buffer.alloc(resDeltaSize); + console.log('Write dummy', resDeltaSize); + fs.writeSync(output, tmp); + written += tmp.length; + */ + + // Write until the signature block + totalWrite = obj.header.sigpos + resDeltaSize; + console.log('Write until signature', totalWrite); + while ((totalWrite - written) > 0) { + tmp = readFileSlice(written - resDeltaSize, Math.min(totalWrite - written, 65536)); + fs.writeSync(output, tmp); + written += tmp.length; + } + + // Write the signature if needed + // TODO + + // Close the file + fs.closeSync(output); + } + // Return null if we could not open the file return (openFile() ? obj : null); } @@ -782,7 +1040,7 @@ function start() { } // Check that a valid command is passed in - if (['info', 'sign', 'unsign', 'createcert', 'icons', 'saveicon'].indexOf(process.argv[2].toLowerCase()) == -1) { + if (['info', 'sign', 'unsign', 'createcert', 'icons', 'saveicon', 'header', 'test'].indexOf(process.argv[2].toLowerCase()) == -1) { console.log("Invalid command: " + process.argv[2]); console.log("Valid commands are: info, sign, unsign, createcert"); return; @@ -795,6 +1053,7 @@ function start() { try { stats = require('fs').statSync(args.exe); } catch (ex) { } if (stats == null) { console.log("Unable to executable open file: " + args.exe); return; } exe = createAuthenticodeHandler(args.exe); + if (exe == null) { console.log("Unable to parse executable file: " + args.exe); return; } } // Execute the command @@ -826,6 +1085,10 @@ function start() { if (exe.signingAttribs && exe.signingAttribs.length > 0) { console.log("Signature Attributes:"); for (var i in exe.signingAttribs) { console.log(' ' + exe.signingAttribs[i]); } } } } + if (command == 'header') { // Display the full executable header in JSON format + if (exe == null) { console.log("Missing --exe [filename]"); return; } + console.log(exe.header); + } if (command == 'sign') { // Sign an executable if (typeof args.exe != 'string') { console.log("Missing --exe [filename]"); return; } if (typeof args.hash == 'string') { args.hash = args.hash.toLowerCase(); if (['md5', 'sha224', 'sha256', 'sha384', 'sha512'].indexOf(args.hash) == -1) { console.log("Invalid hash method, must be SHA256 or SHA384"); return; } } @@ -888,6 +1151,18 @@ function start() { fs.writeFileSync(args.out, Buffer.concat([buf, icon.icon])); console.log("Done."); } + if (command == 'test') { // Grow the resource segment by 100k + if (exe == null) { console.log("Missing --exe [filename]"); return; } + createOutFile(args, args.exe); + console.log("Writting to " + args.out); + exe.resourcesChanged = true; // Indicate the resources have changed + exe.writeExecutable(args); + + // Parse the output file + var exe2 = createAuthenticodeHandler(args.out); + if (exe2 == null) { console.log("XX Unable to parse executable file: " + args.out); return; } + console.log('XX Parse OK'); + } // Close the file if (exe != null) { exe.close(); }