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;