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(); }