authenticode.js can now break and re-create a Windows resource region.

Ylian Saint-Hilaire committed Jun 7, 2022 at 22:09 UTC 9b06a8dc568d82a49c28facd0967ba8404db825a
1 file changed +294 -19
authenticode.js
+294 -19
@@ -120,10 +120,10 @@ function createAuthenticodeHandler(path) {
120
121 // Open the file descriptor
122 obj.path = path;
123 - try { obj.fd = fs.openSync(path); } catch (ex) { return false; } // Unable to open file
123 + try { obj.fd = fs.openSync(path); } catch (ex) { console.log('E1'); return false; } // Unable to open file
124 obj.stats = fs.fstatSync(obj.fd);
125 obj.filesize = obj.stats.size;
126 - if (obj.filesize < 64) { obj.close(); return false; } // File too short.
126 + if (obj.filesize < 64) { obj.close(); console.log('E2'); return false; } // File too short.
127
128 // Read the DOS header (64 bytes)
129 var buf = readFileSlice(60, 4);
@@ -131,8 +131,8 @@ function createAuthenticodeHandler(path) {
131 obj.header.peOptionalHeaderLocation = obj.header.peHeaderLocation + 24; // The PE optional header is located just after the PE header which is 24 bytes long.
132
133 // Check file size and signature
134 - if (obj.filesize < (160 + obj.header.peHeaderLocation)) { obj.close(); return false; } // Invalid SizeOfHeaders.
135 - 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).
134 + if (obj.filesize < (160 + obj.header.peHeaderLocation)) { obj.close(); console.log('E3'); return false; } // Invalid SizeOfHeaders.
135 + 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).
136
137 // Read the COFF header
138 // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#coff-file-header-object-and-image
@@ -156,7 +156,7 @@ function createAuthenticodeHandler(path) {
156 switch (obj.header.peStandard.magic) { // Check magic value
157 case 0x020B: obj.header.pe32plus = 1; break;
158 case 0x010B: obj.header.pe32plus = 0; break;
159 - default: { obj.close(); return false; } // Invalid Magic in PE
159 + default: { obj.close(); console.log('E5'); return false; } // Invalid Magic in PE
160 }
161 obj.header.peStandard.majorLinkerVersion = optinalHeader[2];
162 obj.header.peStandard.minorLinkerVersion = optinalHeader[3];
@@ -224,10 +224,10 @@ function createAuthenticodeHandler(path) {
224 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.
225 obj.header.dataDirectories.exportTable = { addr: optinalHeader.readUInt32LE(96 + pePlusOffset), size: optinalHeader.readUInt32LE(100 + pePlusOffset) };
226 obj.header.dataDirectories.importTable = { addr: optinalHeader.readUInt32LE(104 + pePlusOffset), size: optinalHeader.readUInt32LE(108 + pePlusOffset) };
227 - obj.header.dataDirectories.resourceTable = { addr: optinalHeader.readUInt32LE(112 + pePlusOffset), size: optinalHeader.readUInt32LE(116 + pePlusOffset) };
228 - obj.header.dataDirectories.exceptionTableAddr = { addr: optinalHeader.readUInt32LE(120 + pePlusOffset), size: optinalHeader.readUInt32LE(124 + pePlusOffset) };
227 + obj.header.dataDirectories.resourceTable = { addr: optinalHeader.readUInt32LE(112 + pePlusOffset), size: optinalHeader.readUInt32LE(116 + pePlusOffset) }; // Same as .rsrc virtual address & size
228 + obj.header.dataDirectories.exceptionTableAddr = { addr: optinalHeader.readUInt32LE(120 + pePlusOffset), size: optinalHeader.readUInt32LE(124 + pePlusOffset) }; // Same as .pdata virtual address & size
229 obj.header.dataDirectories.certificateTable = { addr: optinalHeader.readUInt32LE(128 + pePlusOffset), size: optinalHeader.readUInt32LE(132 + pePlusOffset) };
230 - obj.header.dataDirectories.baseRelocationTable = { addr: optinalHeader.readUInt32LE(136 + pePlusOffset), size: optinalHeader.readUInt32LE(140 + pePlusOffset) };
230 + obj.header.dataDirectories.baseRelocationTable = { addr: optinalHeader.readUInt32LE(136 + pePlusOffset), size: optinalHeader.readUInt32LE(140 + pePlusOffset) }; // Same as .reloc virtual address & size
231 obj.header.dataDirectories.debug = { addr: optinalHeader.readUInt32LE(144 + pePlusOffset), size: optinalHeader.readUInt32LE(148 + pePlusOffset) };
232 // obj.header.dataDirectories.architecture = optinalHeader.readBigUInt64LE(152 + pePlusOffset); // Must be zero
233 obj.header.dataDirectories.globalPtr = { addr: optinalHeader.readUInt32LE(160 + pePlusOffset), size: optinalHeader.readUInt32LE(164 + pePlusOffset) };
@@ -252,10 +252,11 @@ function createAuthenticodeHandler(path) {
252 for (var i = 0; i < obj.header.coff.numberOfSections; i++) {
253 var section = {};
254 buf = readFileSlice(obj.header.SectionHeadersPtr + (i * 40), 40);
255 - if (buf[0] != 46) { obj.close(); return false; }; // Name of the section must start with a dot. If not, something is wrong.
255 + 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.
256 var sectionName = buf.slice(0, 8).toString().trim('\0');
257 var j = sectionName.indexOf('\0');
258 if (j >= 0) { sectionName = sectionName.substring(0, j); } // Trim any trailing zeroes
259 + section.ptr = obj.header.SectionHeadersPtr + (i * 40);
260 section.virtualSize = buf.readUInt32LE(8);
261 section.virtualAddr = buf.readUInt32LE(12);
262 section.rawSize = buf.readUInt32LE(16);
@@ -267,11 +268,11 @@ function createAuthenticodeHandler(path) {
268 section.characteristics = buf.readUInt32LE(36);
269 obj.header.sections[sectionName] = section;
270 }
270 - //console.log(obj.header.sections);
271
272 // If there is a .rsrc section, read the resource information and locations
273 if (obj.header.sections['.rsrc'] != null) {
274 - var ptr = obj.header.sections['.rsrc'].rawAddr;
274 + const ptr = obj.header.sections['.rsrc'].rawAddr;
275 + console.log('.rsrc section', ptr, obj.header.sections['.rsrc'].rawSize);
276 obj.resources = readResourceTable(ptr, 0); // Read all resources recursively
277 }
278
@@ -279,7 +280,7 @@ function createAuthenticodeHandler(path) {
280 // Read signature block
281
282 // Check if the file size allows for the signature block
282 - if (obj.filesize < (obj.header.sigpos + obj.header.siglen)) { obj.close(); return false; } // Executable file too short to contain the signature block.
283 + 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.
284
285 // Remove the padding if needed
286 var i, pkcs7raw = readFileSlice(obj.header.sigpos + 8, obj.header.siglen - 8);
@@ -371,15 +372,21 @@ function createAuthenticodeHandler(path) {
372 r.majorVersion = buf.readUInt16LE(8);
373 r.minorVersion = buf.readUInt16LE(10);
374 var numberOfNamedEntries = buf.readUInt16LE(12);
374 - var numberofIdEntries = buf.readUInt16LE(14);
375 + var numberOfIdEntries = buf.readUInt16LE(14);
376 +
377 r.entries = [];
376 - var totalResources = numberOfNamedEntries + numberofIdEntries;
378 + var totalResources = numberOfNamedEntries + numberOfIdEntries;
379 + //console.log('readResourceTable', offset, 16 + (totalResources) * 8, offset + (16 + (totalResources) * 8));
380 for (var i = 0; i < totalResources; i++) {
381 buf = readFileSlice(ptr + offset + 16 + (i * 8), 8);
382 var resource = {};
383 resource.name = buf.readUInt32LE(0);
384 var offsetToData = buf.readUInt32LE(4);
382 - if ((resource.name & 0x80000000) != 0) { resource.name = readLenPrefixUnicodeString(ptr + (resource.name - 0x80000000)); }
385 + if ((resource.name & 0x80000000) != 0) {
386 + var oname = resource.name;
387 + resource.name = readLenPrefixUnicodeString(ptr + (resource.name - 0x80000000));
388 + //console.log('readResourceName', offset + (oname - 0x80000000), 2 + (resource.name.length * 2), offset + (oname - 0x80000000) + (2 + resource.name.length * 2), resource.name);
389 + }
390 if ((offsetToData & 0x80000000) != 0) { resource.table = readResourceTable(ptr, offsetToData - 0x80000000); } else { resource.item = readResourceItem(ptr, offsetToData); }
391 r.entries.push(resource);
392 }
@@ -390,9 +397,11 @@ function createAuthenticodeHandler(path) {
397 // ptr: The pointer to the start of the resource section
398 // offset: The offset start of the resource item to read
399 function readResourceItem(ptr, offset) {
400 + //console.log('readResourceItem', offset, 16, offset + 16);
401 var buf = readFileSlice(ptr + offset, 16), r = {};
402 r.offsetToData = buf.readUInt32LE(0);
403 r.size = buf.readUInt32LE(4);
404 + //console.log('readResourceData', r.offsetToData - obj.header.sections['.rsrc'].virtualAddr, r.size, r.offsetToData + r.size - obj.header.sections['.rsrc'].virtualAddr);
405 r.codePage = buf.readUInt32LE(8);
406 r.reserved = buf.readUInt32LE(12);
407 return r;
@@ -400,12 +409,125 @@ function createAuthenticodeHandler(path) {
409
410 // Read a unicode stting that starts with the string length as the first byte.
411 function readLenPrefixUnicodeString(ptr) {
403 - var nameLen = readFileSlice(ptr, 1)[0];
404 - var buf = readFileSlice(ptr + 1, nameLen * 2), name = '';
405 - for (var i = 0; i < nameLen; i++) { name += String.fromCharCode(buf.readUInt16BE(i * 2)); }
412 + var nameLen = readFileSlice(ptr, 2).readUInt16LE(0);
413 + var buf = readFileSlice(ptr + 2, nameLen * 2), name = '';
414 + for (var i = 0; i < nameLen; i++) { name += String.fromCharCode(buf.readUInt16LE(i * 2)); }
415 return name;
416 }
417
418 + // Generate a complete resource section and pad the section
419 + function generateResourceSection(resources) {
420 + // Call a resursive method the compute the size needed for each element
421 + const resSizes = { tables: 0, items: 0, names: 0, data: 0 };
422 + getResourceSectionSize(resources, resSizes);
423 +
424 + // Pad the resource section & allocate the buffer
425 + const fileAlign = obj.header.peWindows.fileAlignment
426 + var resSizeTotal = resSizes.tables + resSizes.items + resSizes.names + resSizes.data;
427 + if ((resSizeTotal % fileAlign) != 0) { resSizeTotal += (fileAlign - (resSizeTotal % fileAlign)); }
428 + const resSectionBuffer = Buffer.alloc(resSizeTotal);
429 +
430 + // Write the resource section, calling a recusrize method
431 + const resPointers = { tables: 0, items: resSizes.tables, names: resSizes.tables + resSizes.items, data: resSizes.tables + resSizes.items + resSizes.names };
432 + createResourceSection(resources, resSectionBuffer, resPointers);
433 + //console.log('generateResourceSection', resPointers);
434 +
435 + // Done, return the result
436 + return resSectionBuffer;
437 + }
438 +
439 + // Return the total size of a resource header, this is a recursive method
440 + function getResourceSectionSize(resources, sizes) {
441 + sizes.tables += (16 + (resources.entries.length * 8));
442 + for (var i in resources.entries) {
443 + if (typeof resources.entries[i].name == 'string') {
444 + var dataSize = (2 + (resources.entries[i].name.length * 2));
445 + if ((dataSize % 8) != 0) { dataSize += (8 - (dataSize % 8)); }
446 + sizes.names += dataSize;
447 + }
448 + if (resources.entries[i].table) { getResourceSectionSize(resources.entries[i].table, sizes); }
449 + else if (resources.entries[i].item) {
450 + sizes.items += 16;
451 + var dataSize = resources.entries[i].item.size;
452 + if ((dataSize % 8) != 0) { dataSize += (8 - (dataSize % 8)); }
453 + sizes.data += dataSize;
454 + }
455 + }
456 + }
457 +
458 + // Write the resource section in the buffer, this is a recursive method
459 + function createResourceSection(resources, buf, resPointers) {
460 + var numberOfNamedEntries = 0, numberOfIdEntries = 0, ptr = resPointers.tables;
461 + //console.log('createResourceSection', resPointers, ptr);
462 +
463 + // Figure out how many items we have to save
464 + for (var i in resources.entries) {
465 + if (typeof resources.entries[i].name == 'string') { numberOfNamedEntries++; } else { numberOfIdEntries++; }
466 + }
467 +
468 + // Move the table pointer forward
469 + resPointers.tables += (16 + (8 * numberOfNamedEntries) + (8 * numberOfIdEntries));
470 +
471 + // Write the table header
472 + buf.writeUInt32LE(resources.characteristics, ptr);
473 + buf.writeUInt32LE(resources.timeDateStamp, ptr + 4);
474 + buf.writeUInt16LE(resources.majorVersion, ptr + 8);
475 + buf.writeUInt16LE(resources.minorVersion, ptr + 10);
476 + buf.writeUInt16LE(numberOfNamedEntries, ptr + 12);
477 + buf.writeUInt16LE(numberOfIdEntries, ptr + 14);
478 +
479 + // For each table entry, write the entry for it
480 + for (var i in resources.entries) {
481 + // Write the name
482 + var name = resources.entries[i].name;
483 + if (typeof resources.entries[i].name == 'string') {
484 + // Set the pointer to the name
485 + name = resPointers.names + 0x80000000;
486 +
487 + // Write the name length, followed by the name string in unicode
488 + buf.writeUInt16LE(resources.entries[i].name.length, resPointers.names);
489 + for (var j = 0; j < resources.entries[i].name.length; j++) {
490 + buf.writeUInt16LE(resources.entries[i].name.charCodeAt(j), 2 + resPointers.names + (j * 2));
491 + }
492 +
493 + // Move the names pointer forward, 8 byte align
494 + var dataSize = (2 + (resources.entries[i].name.length * 2));
495 + if ((dataSize % 8) != 0) { dataSize += (8 - (dataSize % 8)); }
496 + resPointers.names += dataSize;
497 + }
498 + buf.writeUInt32LE(name, ptr + 16 + (i * 8));
499 +
500 + // Write the data
501 + var data;
502 + if (resources.entries[i].table) {
503 + // This is a pointer to a table entry
504 + data = resPointers.tables + 0x80000000;
505 + createResourceSection(resources.entries[i].table, buf, resPointers);
506 + } else if (resources.entries[i].item) {
507 + // This is a pointer to a data entry
508 + data = resPointers.items;
509 +
510 + // Write the item entry
511 + buf.writeUInt32LE(resPointers.data + obj.header.sections['.rsrc'].virtualAddr, resPointers.items); // Write the pointer relative to the virtual address
512 + buf.writeUInt32LE(resources.entries[i].item.size, resPointers.items + 4);
513 + buf.writeUInt32LE(resources.entries[i].item.codePage, resPointers.items + 8);
514 + buf.writeUInt32LE(resources.entries[i].item.reserved, resPointers.items + 12);
515 +
516 + // Write the data
517 + const actualPtr = (resources.entries[i].item.offsetToData - obj.header.sections['.rsrc'].virtualAddr) + obj.header.sections['.rsrc'].rawAddr;
518 + const tmp = readFileSlice(actualPtr, resources.entries[i].item.size);
519 + tmp.copy(buf, resPointers.data, 0, tmp.length);
520 +
521 + // Move items pointers forward
522 + resPointers.items += 16;
523 + var dataSize = resources.entries[i].item.size;
524 + if ((dataSize % 8) != 0) { dataSize += (8 - (dataSize % 8)); }
525 + resPointers.data += dataSize;
526 + }
527 + buf.writeUInt32LE(data, ptr + 20 + (i * 8));
528 + }
529 + }
530 +
531 // Convert a unicode buffer to a string
532 function unicodeToString(buf) {
533 var r = '';
@@ -619,6 +741,25 @@ function createAuthenticodeHandler(path) {
741 while (ptr < end) { const buf = readFileSlice(ptr, Math.min(65536, end - ptr)); hash.update(buf); ptr += buf.length; }
742 }
743
744 + // Compute the PE checksum of a file (this is not yet tested)
745 + function getChecksum(data, PECheckSumLocation) {
746 + var checksum = 0, top = Math.pow(2, 32);
747 +
748 + for (var i = 0; i < (data.length / 4); i++) {
749 + if (i == PECheckSumLocation / 4) continue;
750 + var dword = data.readUInt32LE(i * 4);
751 + checksum = (checksum & 0xffffffff) + dword + (checksum >> 32);
752 + if (checksum > top) { checksum = (checksum & 0xffffffff) + (checksum >> 32); }
753 + }
754 +
755 + checksum = (checksum & 0xffff) + (checksum >> 16);
756 + checksum = (checksum) + (checksum >> 16);
757 + checksum = checksum & 0xffff;
758 +
759 + checksum += data.length;
760 + return checksum;
761 + }
762 +
763 // Sign the file using the certificate and key. If none is specified, generate a dummy one
764 obj.sign = function (cert, args) {
765 if (cert == null) { cert = createSelfSignedCert({ cn: 'Test' }); }
@@ -739,6 +880,123 @@ function createAuthenticodeHandler(path) {
880 fs.closeSync(output);
881 }
882
883 + // Save the executable
884 + obj.writeExecutable = function (args) {
885 + // Open the file
886 + var output = fs.openSync(args.out, 'w');
887 + var tmp, written = 0;
888 +
889 + // Compute the size of the complete executable header up to after the sections header
890 + var fullHeaderLen = obj.header.SectionHeadersPtr + (obj.header.coff.numberOfSections * 40);
891 + var fullHeader = readFileSlice(written, fullHeaderLen);
892 +
893 + // Calculate the location and original and new size of the resource segment
894 + var fileAlign = obj.header.peWindows.fileAlignment
895 + var resPtr = obj.header.sections['.rsrc'].rawAddr;
896 + var oldResSize = obj.header.sections['.rsrc'].rawSize;
897 + var newResSize = obj.header.sections['.rsrc'].rawSize; // Testing 102400
898 + var resDeltaSize = newResSize - oldResSize;
899 +
900 + console.log('fileAlign', fileAlign);
901 + console.log('resPtr', resPtr);
902 + console.log('oldResSize', oldResSize);
903 + console.log('newResSize', newResSize);
904 + console.log('resDeltaSize', resDeltaSize);
905 +
906 + // Change PE optional header sizeOfInitializedData standard field
907 + fullHeader.writeUInt32LE(obj.header.peStandard.sizeOfInitializedData + resDeltaSize, obj.header.peOptionalHeaderLocation + 8);
908 + fullHeader.writeUInt32LE(obj.header.peWindows.sizeOfImage, obj.header.peOptionalHeaderLocation + 56); // TODO: resDeltaSize
909 +
910 + // Update the checksum, set to zero since it's not used
911 + // TODO: Take a look at computing this correctly in the future
912 + fullHeader.writeUInt32LE(0, obj.header.peOptionalHeaderLocation + 64);
913 +
914 + // Make change to the data directories header to fix resource segment size and add/remove signature
915 + 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.
916 + if (obj.header.dataDirectories.exportTable.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.exportTable.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 96 + pePlusOffset); }
917 + if (obj.header.dataDirectories.importTable.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.importTable.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 104 + pePlusOffset); }
918 + //fullHeader.writeUInt32LE(obj.header.dataDirectories.resourceTable.size + resDeltaSize, obj.header.peOptionalHeaderLocation + 116 + pePlusOffset); // Change the resource segment size
919 + if (obj.header.dataDirectories.exceptionTableAddr.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.exceptionTableAddr.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 120 + pePlusOffset); }
920 + fullHeader.writeUInt32LE(0, obj.header.peOptionalHeaderLocation + 128 + pePlusOffset); // certificate table addr (TODO)
921 + fullHeader.writeUInt32LE(0, obj.header.peOptionalHeaderLocation + 132 + pePlusOffset); // certificate table size (TODO)
922 + if (obj.header.dataDirectories.baseRelocationTable.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.baseRelocationTable.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 136 + pePlusOffset); }
923 + if (obj.header.dataDirectories.debug.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.debug.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 144 + pePlusOffset); }
924 + if (obj.header.dataDirectories.globalPtr.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.globalPtr.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 160 + pePlusOffset); }
925 + if (obj.header.dataDirectories.tLSTable.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.tLSTable.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 168 + pePlusOffset); }
926 + if (obj.header.dataDirectories.loadConfigTable.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.loadConfigTable.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 176 + pePlusOffset); }
927 + if (obj.header.dataDirectories.boundImport.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.boundImport.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 184 + pePlusOffset); }
928 + if (obj.header.dataDirectories.iAT.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.iAT.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 192 + pePlusOffset); }
929 + if (obj.header.dataDirectories.delayImportDescriptor.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.delayImportDescriptor.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 200 + pePlusOffset); }
930 + if (obj.header.dataDirectories.clrRuntimeHeader.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.clrRuntimeHeader.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 208 + pePlusOffset); }
931 +
932 + // Make changes to the segments table
933 + for (var i in obj.header.sections) {
934 + const section = obj.header.sections[i];
935 + if (i == '.rsrc') {
936 + // Change the size of the resource section
937 + fullHeader.writeUInt32LE(section.rawSize + resDeltaSize, section.ptr + 8); // virtualSize (TODO)
938 + fullHeader.writeUInt32LE(section.rawSize + resDeltaSize, section.ptr + 16); // rawSize
939 + } else {
940 + // Change the location of any other section if located after the resource section
941 + if (section.virtualAddr > resPtr) { fullHeader.writeUInt32LE(section.virtualAddr + resDeltaSize, section.ptr + 12); }
942 + if (section.rawAddr > resPtr) { fullHeader.writeUInt32LE(section.rawAddr + resDeltaSize, section.ptr + 20); }
943 + }
944 + }
945 +
946 + // Write the entire header to the destination file
947 + console.log('Write header', fullHeader.length);
948 + fs.writeSync(output, fullHeader);
949 + written += fullHeader.length;
950 +
951 + // Write the entire executable until the start to the resource segment
952 + var totalWrite = resPtr;
953 + console.log('Write until res', totalWrite);
954 + while ((totalWrite - written) > 0) {
955 + tmp = readFileSlice(written, Math.min(totalWrite - written, 65536));
956 + fs.writeSync(output, tmp);
957 + written += tmp.length;
958 + }
959 +
960 + // Write the new resource section
961 + var rsrcSection = generateResourceSection(obj.resources);
962 + fs.writeSync(output, rsrcSection);
963 + written += rsrcSection.length;
964 +
965 + /*
966 + // Write the old resource segment (debug)
967 + totalWrite = resPtr + oldResSize;
968 + console.log('Write res', totalWrite);
969 + while ((totalWrite - written) > 0) {
970 + tmp = readFileSlice(written, Math.min(totalWrite - written, 65536));
971 + fs.writeSync(output, tmp);
972 + written += tmp.length;
973 + }
974 + */
975 +
976 + /*
977 + // Write a dummy 102400 bytes
978 + tmp = Buffer.alloc(resDeltaSize);
979 + console.log('Write dummy', resDeltaSize);
980 + fs.writeSync(output, tmp);
981 + written += tmp.length;
982 + */
983 +
984 + // Write until the signature block
985 + totalWrite = obj.header.sigpos + resDeltaSize;
986 + console.log('Write until signature', totalWrite);
987 + while ((totalWrite - written) > 0) {
988 + tmp = readFileSlice(written - resDeltaSize, Math.min(totalWrite - written, 65536));
989 + fs.writeSync(output, tmp);
990 + written += tmp.length;
991 + }
992 +
993 + // Write the signature if needed
994 + // TODO
995 +
996 + // Close the file
997 + fs.closeSync(output);
998 + }
999 +
1000 // Return null if we could not open the file
1001 return (openFile() ? obj : null);
1002 }
@@ -782,7 +1040,7 @@ function start() {
1040 }
1041
1042 // Check that a valid command is passed in
785 - if (['info', 'sign', 'unsign', 'createcert', 'icons', 'saveicon'].indexOf(process.argv[2].toLowerCase()) == -1) {
1043 + if (['info', 'sign', 'unsign', 'createcert', 'icons', 'saveicon', 'header', 'test'].indexOf(process.argv[2].toLowerCase()) == -1) {
1044 console.log("Invalid command: " + process.argv[2]);
1045 console.log("Valid commands are: info, sign, unsign, createcert");
1046 return;
@@ -795,6 +1053,7 @@ function start() {
1053 try { stats = require('fs').statSync(args.exe); } catch (ex) { }
1054 if (stats == null) { console.log("Unable to executable open file: " + args.exe); return; }
1055 exe = createAuthenticodeHandler(args.exe);
1056 + if (exe == null) { console.log("Unable to parse executable file: " + args.exe); return; }
1057 }
1058
1059 // Execute the command
@@ -826,6 +1085,10 @@ function start() {
1085 if (exe.signingAttribs && exe.signingAttribs.length > 0) { console.log("Signature Attributes:"); for (var i in exe.signingAttribs) { console.log(' ' + exe.signingAttribs[i]); } }
1086 }
1087 }
1088 + if (command == 'header') { // Display the full executable header in JSON format
1089 + if (exe == null) { console.log("Missing --exe [filename]"); return; }
1090 + console.log(exe.header);
1091 + }
1092 if (command == 'sign') { // Sign an executable
1093 if (typeof args.exe != 'string') { console.log("Missing --exe [filename]"); return; }
1094 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() {
1151 fs.writeFileSync(args.out, Buffer.concat([buf, icon.icon]));
1152 console.log("Done.");
1153 }
1154 + if (command == 'test') { // Grow the resource segment by 100k
1155 + if (exe == null) { console.log("Missing --exe [filename]"); return; }
1156 + createOutFile(args, args.exe);
1157 + console.log("Writting to " + args.out);
1158 + exe.resourcesChanged = true; // Indicate the resources have changed
1159 + exe.writeExecutable(args);
1160 +
1161 + // Parse the output file
1162 + var exe2 = createAuthenticodeHandler(args.out);
1163 + if (exe2 == null) { console.log("XX Unable to parse executable file: " + args.out); return; }
1164 + console.log('XX Parse OK');
1165 + }
1166
1167 // Close the file
1168 if (exe != null) { exe.close(); }