Authenticode.js full header parsing.
Ylian Saint-Hilaire committed
Jun 7, 2022 at 11:55 UTC
8a2def42979587bb2e4598cfbe98a8a337566adb
1 file changed
+122
-30
authenticode.js
+122
-30
@@ -125,43 +125,134 @@ function createAuthenticodeHandler(path) {
125
obj.filesize = obj.stats.size;
126
if (obj.filesize < 64) { obj.close(); return false; } // File too short.
127
128
- // Read the PE header pointer
128
+ // Read the DOS header (64 bytes)
129
var buf = readFileSlice(60, 4);
130
- obj.header.PeHeaderLocation = buf.readUInt32LE(0); // The DOS header is 64 bytes long, the last 4 bytes are a pointer to the PE header.
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 PE header
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 size of the optional header
138
- obj.header.PeOptionalHeaderSize = readFileSlice(obj.header.PeHeaderLocation + 20, 2).readUInt16LE(0);
139
-
140
- // The section headers are located after the optional PE header
141
- obj.header.SectionHeadersPtr = obj.header.PeOptionalHeaderLocation + obj.header.PeOptionalHeaderSize;
142
-
143
- // Check header magic data
144
- var magic = readFileSlice(obj.header.PeOptionalHeaderLocation, 2).readUInt16LE(0);
145
- switch (magic) {
130
+ obj.header.peHeaderLocation = buf.readUInt32LE(0); // The DOS header is 64 bytes long, the last 4 bytes are a pointer to the PE header.
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).
136
+
137
+ // Read the COFF header
138
+ // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#coff-file-header-object-and-image
139
+ var coffHeader = readFileSlice(obj.header.peHeaderLocation + 4, 20)
140
+ obj.header.coff = {};
141
+ obj.header.coff.machine = coffHeader.readUInt16LE(0);
142
+ obj.header.coff.numberOfSections = coffHeader.readUInt16LE(2);
143
+ obj.header.coff.timeDateStamp = coffHeader.readUInt32LE(4);
144
+ obj.header.coff.pointerToSymbolTable = coffHeader.readUInt32LE(8);
145
+ obj.header.coff.numberOfSymbols = coffHeader.readUInt32LE(12);
146
+ obj.header.coff.sizeOfOptionalHeader = coffHeader.readUInt16LE(16);
147
+ obj.header.coff.characteristics = coffHeader.readUInt16LE(18);
148
+
149
+ // Read the entire PE optional header
150
+ var optinalHeader = readFileSlice(obj.header.peOptionalHeaderLocation, obj.header.coff.sizeOfOptionalHeader);
151
+
152
+ // Decode the PE optional header standard fields
153
+ // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#optional-header-standard-fields-image-only
154
+ obj.header.peStandard = {};
155
+ obj.header.peStandard.magic = optinalHeader.readUInt16LE(0);
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
160
}
161
+ obj.header.peStandard.majorLinkerVersion = optinalHeader[2];
162
+ obj.header.peStandard.minorLinkerVersion = optinalHeader[3];
163
+ obj.header.peStandard.sizeOfCode = optinalHeader.readUInt32LE(4);
164
+ obj.header.peStandard.sizeOfInitializedData = optinalHeader.readUInt32LE(8);
165
+ obj.header.peStandard.sizeOfUninitializedData = optinalHeader.readUInt32LE(12);
166
+ obj.header.peStandard.addressOfEntryPoint = optinalHeader.readUInt32LE(16);
167
+ obj.header.peStandard.baseOfCode = optinalHeader.readUInt32LE(20);
168
+ if (obj.header.pe32plus == 0) { obj.header.peStandard.baseOfData = optinalHeader.readUInt32LE(24); }
169
+
170
+ // Decode the PE optional header windows fields
171
+ // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#optional-header-windows-specific-fields-image-only
172
+ obj.header.peWindows = {}
173
+ if (obj.header.pe32plus == 0) {
174
+ // 32bit header
175
+ obj.header.peWindows.imageBase = optinalHeader.readUInt32LE(28);
176
+ obj.header.peWindows.sectionAlignment = optinalHeader.readUInt32LE(32);
177
+ obj.header.peWindows.fileAlignment = optinalHeader.readUInt32LE(36);
178
+ obj.header.peWindows.majorOperatingSystemVersion = optinalHeader.readUInt16LE(40);
179
+ obj.header.peWindows.minorOperatingSystemVersion = optinalHeader.readUInt16LE(42);
180
+ obj.header.peWindows.majorImageVersion = optinalHeader.readUInt16LE(44);
181
+ obj.header.peWindows.minorImageVersion = optinalHeader.readUInt16LE(46);
182
+ obj.header.peWindows.majorSubsystemVersion = optinalHeader.readUInt16LE(48);
183
+ obj.header.peWindows.minorSubsystemVersion = optinalHeader.readUInt16LE(50);
184
+ obj.header.peWindows.win32VersionValue = optinalHeader.readUInt32LE(52);
185
+ obj.header.peWindows.sizeOfImage = optinalHeader.readUInt32LE(56);
186
+ obj.header.peWindows.sizeOfHeaders = optinalHeader.readUInt32LE(60);
187
+ obj.header.peWindows.checkSum = optinalHeader.readUInt32LE(64);
188
+ obj.header.peWindows.subsystem = optinalHeader.readUInt16LE(68);
189
+ obj.header.peWindows.dllCharacteristics = optinalHeader.readUInt16LE(70);
190
+ obj.header.peWindows.sizeOfStackReserve = optinalHeader.readUInt32LE(72);
191
+ obj.header.peWindows.sizeOfStackCommit = optinalHeader.readUInt32LE(76);
192
+ obj.header.peWindows.sizeOfHeapReserve = optinalHeader.readUInt32LE(80);
193
+ obj.header.peWindows.sizeOfHeapCommit = optinalHeader.readUInt32LE(84);
194
+ obj.header.peWindows.loaderFlags = optinalHeader.readUInt32LE(88);
195
+ obj.header.peWindows.numberOfRvaAndSizes = optinalHeader.readUInt32LE(92);
196
+ } else {
197
+ // 64bit header
198
+ obj.header.peWindows.imageBase = optinalHeader.readBigUInt64LE(24);
199
+ obj.header.peWindows.sectionAlignment = optinalHeader.readUInt32LE(32);
200
+ obj.header.peWindows.fileAlignment = optinalHeader.readUInt32LE(36);
201
+ obj.header.peWindows.majorOperatingSystemVersion = optinalHeader.readUInt16LE(40);
202
+ obj.header.peWindows.minorOperatingSystemVersion = optinalHeader.readUInt16LE(42);
203
+ obj.header.peWindows.majorImageVersion = optinalHeader.readUInt16LE(44);
204
+ obj.header.peWindows.minorImageVersion = optinalHeader.readUInt16LE(46);
205
+ obj.header.peWindows.majorSubsystemVersion = optinalHeader.readUInt16LE(48);
206
+ obj.header.peWindows.minorSubsystemVersion = optinalHeader.readUInt16LE(50);
207
+ obj.header.peWindows.win32VersionValue = optinalHeader.readUInt32LE(52);
208
+ obj.header.peWindows.sizeOfImage = optinalHeader.readUInt32LE(56);
209
+ obj.header.peWindows.sizeOfHeaders = optinalHeader.readUInt32LE(60);
210
+ obj.header.peWindows.checkSum = optinalHeader.readUInt32LE(64);
211
+ obj.header.peWindows.subsystem = optinalHeader.readUInt16LE(68);
212
+ obj.header.peWindows.dllCharacteristics = optinalHeader.readUInt16LE(70);
213
+ obj.header.peWindows.sizeOfStackReserve = optinalHeader.readBigUInt64LE(72);
214
+ obj.header.peWindows.sizeOfStackCommit = optinalHeader.readBigUInt64LE(80);
215
+ obj.header.peWindows.sizeOfHeapReserve = optinalHeader.readBigUInt64LE(88);
216
+ obj.header.peWindows.sizeOfHeapCommit = optinalHeader.readBigUInt64LE(96);
217
+ obj.header.peWindows.loaderFlags = optinalHeader.readUInt32LE(104);
218
+ obj.header.peWindows.numberOfRvaAndSizes = optinalHeader.readUInt32LE(108);
219
+ }
220
151
- // Read optional PE header information
152
- obj.header.pe_checksum = readFileSlice(obj.header.PeOptionalHeaderLocation + 64, 4).readUInt32LE(0);
153
- obj.header.numRVA = readFileSlice(obj.header.PeOptionalHeaderLocation + 92 + (obj.header.pe32plus * 16), 4).readUInt32LE(0);
154
- buf = readFileSlice(obj.header.PeOptionalHeaderLocation + 128 + (obj.header.pe32plus * 16), 8);
155
- obj.header.sigpos = buf.readUInt32LE(0);
156
- obj.header.siglen = buf.readUInt32LE(4);
221
+ // Decode the PE optional header data directories
222
+ // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#optional-header-data-directories-image-only
223
+ obj.header.dataDirectories = {}
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) };
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) };
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) };
234
+ obj.header.dataDirectories.tLSTable = { addr: optinalHeader.readUInt32LE(168 + pePlusOffset), size: optinalHeader.readUInt32LE(172 + pePlusOffset) };
235
+ obj.header.dataDirectories.loadConfigTable = { addr: optinalHeader.readUInt32LE(176 + pePlusOffset), size: optinalHeader.readUInt32LE(180 + pePlusOffset) };
236
+ obj.header.dataDirectories.boundImport = { addr: optinalHeader.readUInt32LE(184 + pePlusOffset), size: optinalHeader.readUInt32LE(188 + pePlusOffset) };
237
+ obj.header.dataDirectories.iAT = { addr: optinalHeader.readUInt32LE(192 + pePlusOffset), size: optinalHeader.readUInt32LE(196 + pePlusOffset) };
238
+ obj.header.dataDirectories.delayImportDescriptor = { addr: optinalHeader.readUInt32LE(200 + pePlusOffset), size: optinalHeader.readUInt32LE(204 + pePlusOffset) };
239
+ obj.header.dataDirectories.clrRuntimeHeader = { addr: optinalHeader.readUInt32LE(208 + pePlusOffset), size: optinalHeader.readUInt32LE(212 + pePlusOffset) };
240
+ // obj.header.dataDirectories.reserved = optinalHeader.readBigUInt64LE(216 + pePlusOffset); // Must be zero
241
+
242
+ // Get the certificate table location and size
243
+ obj.header.sigpos = obj.header.dataDirectories.certificateTable.addr;
244
+ obj.header.siglen = obj.header.dataDirectories.certificateTable.size
245
obj.header.signed = ((obj.header.sigpos != 0) && (obj.header.siglen != 0));
246
247
+ // The section headers are located after the optional PE header
248
+ obj.header.SectionHeadersPtr = obj.header.peOptionalHeaderLocation + obj.header.coff.sizeOfOptionalHeader;
249
+
250
// Read the sections
251
obj.header.sections = {};
161
- for (var i = 0; i < 16; i++) {
252
+ for (var i = 0; i < obj.header.coff.numberOfSections; i++) {
253
var section = {};
254
buf = readFileSlice(obj.header.SectionHeadersPtr + (i * 40), 40);
164
- if (buf[0] != 46) break; // Name of the section must start with a dot. If not, we are done reading sections.
255
+ if (buf[0] != 46) { obj.close(); 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
@@ -176,6 +267,7 @@ function createAuthenticodeHandler(path) {
267
section.characteristics = buf.readUInt32LE(36);
268
obj.header.sections[sectionName] = section;
269
}
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) {
@@ -515,9 +607,9 @@ function createAuthenticodeHandler(path) {
607
// Hash the file using the selected hashing system
608
obj.getHash = function(algo) {
609
var hash = crypto.createHash(algo);
518
- runHash(hash, 0, obj.header.PeHeaderLocation + 88);
519
- runHash(hash, obj.header.PeHeaderLocation + 88 + 4, obj.header.PeHeaderLocation + 152 + (obj.header.pe32plus * 16));
520
- runHash(hash, obj.header.PeHeaderLocation + 152 + (obj.header.pe32plus * 16) + 8, obj.header.sigpos > 0 ? obj.header.sigpos : obj.filesize);
610
+ runHash(hash, 0, obj.header.peHeaderLocation + 88);
611
+ runHash(hash, obj.header.peHeaderLocation + 88 + 4, obj.header.peHeaderLocation + 152 + (obj.header.pe32plus * 16));
612
+ runHash(hash, obj.header.peHeaderLocation + 152 + (obj.header.pe32plus * 16) + 8, obj.header.sigpos > 0 ? obj.header.sigpos : obj.filesize);
613
return hash.digest();
614
}
615
@@ -585,7 +677,7 @@ function createAuthenticodeHandler(path) {
677
var executableSize = obj.header.sigpos ? obj.header.sigpos : this.filesize;
678
679
// Compute pre-header length and copy that to the new file
588
- var preHeaderLen = (obj.header.PeHeaderLocation + 152 + (obj.header.pe32plus * 16));
680
+ var preHeaderLen = (obj.header.peHeaderLocation + 152 + (obj.header.pe32plus * 16));
681
var tmp = readFileSlice(written, preHeaderLen);
682
fs.writeSync(output, tmp);
683
written += tmp.length;
@@ -629,7 +721,7 @@ function createAuthenticodeHandler(path) {
721
var written = 0, totalWrite = obj.header.sigpos;
722
723
// Compute pre-header length and copy that to the new file
632
- var preHeaderLen = (obj.header.PeHeaderLocation + 152 + (obj.header.pe32plus * 16));
724
+ var preHeaderLen = (obj.header.peHeaderLocation + 152 + (obj.header.pe32plus * 16));
725
var tmp = readFileSlice(written, preHeaderLen);
726
fs.writeSync(output, tmp);
727
written += tmp.length;