Authenticode.js can now computer PE header checksum.
Ylian Saint-Hilaire committed
Jun 7, 2022 at 23:31 UTC
86429258dd094aa21da052635137e2dc0c9991c3
1 file changed
+30
-23
authenticode.js
+30
-23
@@ -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) { console.log('E1'); return false; } // Unable to open file
123
+ try { obj.fd = fs.openSync(path); } catch (ex) { 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(); console.log('E2'); return false; } // File too short.
126
+ if (obj.filesize < 64) { obj.close(); 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(); 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).
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).
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(); console.log('E5'); return false; } // Invalid Magic in PE
159
+ default: { obj.close(); return false; } // Invalid Magic in PE
160
}
161
obj.header.peStandard.majorLinkerVersion = optinalHeader[2];
162
obj.header.peStandard.minorLinkerVersion = optinalHeader[3];
@@ -244,6 +244,9 @@ function createAuthenticodeHandler(path) {
244
obj.header.siglen = obj.header.dataDirectories.certificateTable.size
245
obj.header.signed = ((obj.header.sigpos != 0) && (obj.header.siglen != 0));
246
247
+ // Compute the checkSum value for this file
248
+ obj.header.peWindows.checkSumActual = getChecksum(readFileSlice(0, obj.filesize));
249
+
250
// The section headers are located after the optional PE header
251
obj.header.SectionHeadersPtr = obj.header.peOptionalHeaderLocation + obj.header.coff.sizeOfOptionalHeader;
252
@@ -252,7 +255,7 @@ function createAuthenticodeHandler(path) {
255
for (var i = 0; i < obj.header.coff.numberOfSections; i++) {
256
var section = {};
257
buf = readFileSlice(obj.header.SectionHeadersPtr + (i * 40), 40);
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.
258
+ if (buf[0] != 46) { obj.close(); return false; }; // Name of the section must start with a dot. If not, something is wrong.
259
var sectionName = buf.slice(0, 8).toString().trim('\0');
260
var j = sectionName.indexOf('\0');
261
if (j >= 0) { sectionName = sectionName.substring(0, j); } // Trim any trailing zeroes
@@ -271,16 +274,14 @@ function createAuthenticodeHandler(path) {
274
275
// If there is a .rsrc section, read the resource information and locations
276
if (obj.header.sections['.rsrc'] != null) {
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
+ obj.resources = readResourceTable(obj.header.sections['.rsrc'].rawAddr, 0); // Read all resources recursively
278
}
279
280
if (obj.header.signed) {
281
// Read signature block
282
283
// Check if the file size allows for 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
+ if (obj.filesize < (obj.header.sigpos + obj.header.siglen)) { obj.close(); return false; } // Executable file too short to contain the signature block.
285
286
// Remove the padding if needed
287
var i, pkcs7raw = readFileSlice(obj.header.sigpos + 8, obj.header.siglen - 8);
@@ -741,21 +742,24 @@ function createAuthenticodeHandler(path) {
742
while (ptr < end) { const buf = readFileSlice(ptr, Math.min(65536, end - ptr)); hash.update(buf); ptr += buf.length; }
743
}
744
744
- // Compute the PE checksum of a file (this is not yet tested)
745
- function getChecksum(data, PECheckSumLocation) {
745
+ // Compute the PE checksum of an entire file
746
+ function getChecksum(data) {
747
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;
748
+ for (var i = 0; i < (data.length / 4) ; i++) {
749
+ if (i == 54) continue; // Skip PE checksum location
750
var dword = data.readUInt32LE(i * 4);
751
- checksum = (checksum & 0xffffffff) + dword + (checksum >> 32);
752
- if (checksum > top) { checksum = (checksum & 0xffffffff) + (checksum >> 32); }
751
+ var checksumlo = (checksum > top) ? (checksum - top) : checksum;
752
+ var checksumhi = (checksum > top) ? 1 : 0;
753
+ checksum = checksumlo + dword + checksumhi;
754
+ if (checksum > top) {
755
+ checksumlo = (checksum > top) ? (checksum - top) : checksum;
756
+ checksumhi = (checksum > top) ? 1 : 0;
757
+ checksum = checksumlo + checksumhi;
758
+ }
759
}
754
-
755
- checksum = (checksum & 0xffff) + (checksum >> 16);
756
- checksum = (checksum) + (checksum >> 16);
760
+ checksum = (checksum & 0xffff) + (checksum >>> 16);
761
+ checksum = (checksum) + (checksum >>> 16);
762
checksum = checksum & 0xffff;
758
-
763
checksum += data.length;
764
return checksum;
765
}
@@ -1074,6 +1078,9 @@ function start() {
1078
} else {
1079
var versionInfo = exe.getVersionInfo();
1080
if (versionInfo != null) { console.log("Version Information:"); for (var i in versionInfo) { if (versionInfo[i] == null) { console.log(' ' + i + ': (Empty)'); } else { console.log(' ' + i + ': \"' + versionInfo[i] + '\"'); } } }
1081
+ console.log("Checksum Information:");
1082
+ console.log(" Header CheckSum: 0x" + exe.header.peWindows.checkSum.toString(16));
1083
+ console.log(" Actual CheckSum: 0x" + exe.header.peWindows.checkSumActual.toString(16));
1084
console.log("Signature Information:");
1085
if (exe.fileHashAlgo != null) {
1086
console.log(" Hash Method:", exe.fileHashAlgo);
@@ -1160,8 +1167,8 @@ function start() {
1167
1168
// Parse the output file
1169
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');
1170
+ if (exe2 == null) { console.log("Unable to parse output executable file: " + args.out); return; }
1171
+ console.log('Output executable parsed correctly.');
1172
}
1173
1174
// Close the file