Updated authenticode sign/unsign commands.

Ylian Saint-Hilaire committed May 26, 2022 at 21:08 UTC d1e1309c4c381b0201212690397e8db7e42b9c54
1 file changed +81 -28
authenticode.js
+81 -28
@@ -1,4 +1,4 @@
1 -/**
1 +/**
2 * @description Authenticode parsing
3 * @author Bryan Roe & Ylian Saint-Hilaire
4 * @copyright Intel Corporation 2018-2022
@@ -57,15 +57,15 @@ function createAuthenticodeHandler(path) {
57 obj.fd = fs.openSync(path);
58 obj.stats = fs.fstatSync(obj.fd);
59 obj.filesize = obj.stats.size;
60 - if (obj.filesize < 64) { throw ('File too short'); }
60 + if (obj.filesize < 64) { throw ('File too short.'); }
61
62 // Read the PE header size
63 var buf = readFileSlice(60, 4);
64 obj.header.header_size = buf.readUInt32LE(0);
65
66 // Check file size and PE header
67 - if (obj.filesize < (160 + obj.header.header_size)) { throw ('Invalid SizeOfHeaders'); }
68 - if (readFileSlice(obj.header.header_size, 4).toString('hex') != '50450000') { throw ('Invalid PE File'); }
67 + if (obj.filesize < (160 + obj.header.header_size)) { throw ('Invalid SizeOfHeaders.'); }
68 + if (readFileSlice(obj.header.header_size, 4).toString('hex') != '50450000') { throw ('Invalid PE File.'); }
69
70 // Check header magic data
71 var magic = readFileSlice(obj.header.header_size + 24, 2).readUInt16LE(0);
@@ -86,6 +86,9 @@ function createAuthenticodeHandler(path) {
86 if (obj.header.signed) {
87 // Read signature block
88
89 + // Check if the file size allows for the signature block
90 + if (obj.filesize < (obj.header.sigpos + obj.header.siglen)) { throw ('Executable file too short to contain the signature block.'); }
91 +
92 // Remove the padding if needed
93 var i, pkcs7raw = readFileSlice(obj.header.sigpos + 8, obj.header.siglen - 8);
94 var derlen = forge.asn1.getBerValueLength(forge.util.createBuffer(pkcs7raw.slice(1, 5))) + 4;
@@ -110,6 +113,11 @@ function createAuthenticodeHandler(path) {
113 var pkcs7 = p7.messageFromAsn1(pkcs7der);
114 var pkcs7content = forge.asn1.fromDer(pkcs7.rawCapture.content.value[0].value);
115
116 + //console.log('p7content', JSON.stringify(pkcs7content));
117 +
118 + // DEBUG: Print out the content
119 + //console.log(Buffer.from(pkcs7.rawCapture.content.value[0].value, 'binary').toString('hex'));
120 +
121 // Set the certificate chain
122 obj.certificates = pkcs7.certificates;
123
@@ -175,7 +183,8 @@ function createAuthenticodeHandler(path) {
183 if ((cert == null) || (key == null)) { var c = obj.createSelfSignedCert(); cert = c.cert; key = c.key; }
184 var fileHash = getHash('sha384');
185 var p7 = forge.pkcs7.createSignedData();
178 - p7.content = forge.util.createBuffer(fileHash, 'utf8');
186 + p7.content = forge.util.createBuffer(fileHash, 'utf8'); // DEBUG: NOT CORRRECT
187 + //p7.content = { "tagClass": 0, "type": 16, "constructed": true, "composed": true, "value": [{ "tagClass": 0, "type": 16, "constructed": true, "composed": true, "value": [{ "tagClass": 0, "type": 6, "constructed": false, "composed": false, "value": forge.asn1.oidToDer("1.3.6.1.4.1.311.2.1.15") }, { "tagClass": 0, "type": 16, "constructed": true, "composed": true, "value": [{ "tagClass": 0, "type": 3, "constructed": false, "composed": false, "value": "\u0000", "bitStringContents": "\u0000", "original": { "tagClass": 0, "type": 3, "constructed": false, "composed": false, "value": "\u0000" } }, { "tagClass": 128, "type": 0, "constructed": true, "composed": true, "value": [{ "tagClass": 128, "type": 2, "constructed": true, "composed": true, "value": [{ "tagClass": 128, "type": 0, "constructed": false, "composed": false, "value": "" }] }] }] }] }, { "tagClass": 0, "type": 16, "constructed": true, "composed": true, "value": [{ "tagClass": 0, "type": 16, "constructed": true, "composed": true, "value": [{ "tagClass": 0, "type": 6, "constructed": false, "composed": false, "value": forge.asn1.oidToDer(forge.pki.oids.sha384).data }, { "tagClass": 0, "type": 5, "constructed": false, "composed": false, "value": "" }] }, { "tagClass": 0, "type": 4, "constructed": false, "composed": false, "value": fileHash.toString('binary') }] }] };
188 p7.addCertificate(cert);
189 p7.addSigner({
190 key: key,
@@ -205,40 +214,82 @@ function createAuthenticodeHandler(path) {
214 var p7signature = Buffer.from(forge.pkcs7.messageToPem(p7).split('-----BEGIN PKCS7-----')[1].split('-----END PKCS7-----')[0], 'base64');
215 console.log('p7signature', p7signature.toString('base64'));
216
217 + // Create the output filename
218 + var outputFileName = this.path.split('.');
219 + outputFileName[outputFileName.length - 2] += '-jsigned';
220 + outputFileName = outputFileName.join('.');
221 +
222 + // Open the file
223 + var output = fs.openSync(outputFileName, 'w');
224 + var tmp, written = 0;
225 + var executableSize = obj.header.sigpos ? obj.header.sigpos : this.filesize;
226 +
227 + // Compute pre-header length and copy that to the new file
228 + var preHeaderLen = (obj.header.header_size + 152 + (obj.header.pe32plus * 16));
229 + var tmp = readFileSlice(written, preHeaderLen);
230 + fs.writeSync(output, tmp);
231 + written += tmp.length;
232 +
233 // Quad Align the results, adding padding if necessary
209 - var len = this.filesize + p7signature.length;
234 + var len = executableSize + p7signature.length;
235 var padding = (8 - ((len) % 8)) % 8;
236
237 + // Write the signature header
238 var addresstable = Buffer.alloc(8);
213 - addresstable.writeUInt32LE(this.filesize);
239 + console.log('executableSize', executableSize);
240 + console.log('signLength', p7signature.length, padding);
241 + addresstable.writeUInt32LE(executableSize);
242 addresstable.writeUInt32LE(8 + p7signature.length + padding, 4);
243 + fs.writeSync(output, addresstable);
244 + written += addresstable.length;
245
216 - var b = this.path.split('.');
217 - b[b.length - 2] += '-jsigned';
218 -
219 - var output = fs.openSync(b.join('.'), 'w');
220 - var written = 0;
221 - var bytesLeft = this.filesize;
222 - var tmp;
223 -
224 - // TODO: This copies the entire file including the old signature block.
225 - // Need to be fixed to only copy the file without the signature block
226 - while ((this.filesize - written) > 0) {
227 - tmp = readFileSlice(written, (this.filesize - written) > 65535 ? 65535 : this.filesize - written);
246 + // Copy the rest of the file until the start of the signature block
247 + while ((executableSize - written) > 0) {
248 + tmp = readFileSlice(written, Math.min(executableSize - written, 65536));
249 fs.writeSync(output, tmp);
250 written += tmp.length;
251 }
252
232 - // Write the signature block
253 + // Write the signature block header and signature
254 var win = Buffer.alloc(8); // WIN CERTIFICATE Structure
255 win.writeUInt32LE(p7signature.length + padding + 8); // DWORD length
256 win.writeUInt16LE(512, 4); // WORD revision
257 win.writeUInt16LE(2, 6); // WORD type
237 -
258 fs.writeSync(output, win);
259 fs.writeSync(output, p7signature);
260 if (padding > 0) { fs.writeSync(output, Buffer.alloc(padding, 0)); }
241 - fs.writeSync(output, addresstable, 0, addresstable.length, this.header.header_size + 152 + (this.header.pe32plus * 16));
261 +
262 + // Close the file
263 + fs.closeSync(output);
264 + }
265 +
266 + // Save an executable without the signature
267 + obj.unsign = function (cert, key) {
268 + // Create the output filename
269 + var outputFileName = this.path.split('.');
270 + outputFileName[outputFileName.length - 2] += '-junsigned';
271 + outputFileName = outputFileName.join('.');
272 +
273 + // Open the file
274 + var output = fs.openSync(outputFileName, 'w');
275 + var written = 0, totalWrite = obj.header.sigpos;
276 +
277 + // Compute pre-header length and copy that to the new file
278 + var preHeaderLen = (obj.header.header_size + 152 + (obj.header.pe32plus * 16));
279 + var tmp = readFileSlice(written, preHeaderLen);
280 + fs.writeSync(output, tmp);
281 + written += tmp.length;
282 +
283 + // Write the new signature header
284 + fs.writeSync(output, Buffer.alloc(8));
285 + written += 8;
286 +
287 + // Copy the rest of the file until the start of the signature block
288 + while ((totalWrite - written) > 0) {
289 + tmp = readFileSlice(written, Math.min(totalWrite - written, 65536));
290 + fs.writeSync(output, tmp);
291 + written += tmp.length;
292 + }
293 fs.closeSync(output);
294 }
295
@@ -253,13 +304,14 @@ function start() {
304 console.log("Usage:");
305 console.log(" node authenticode.js [command] [exepath]");
306 console.log("Commands:");
256 - console.log(" info - Show information about this executable.");
257 - console.log(" sign - Sign the executable using a dummy certificate.");
307 + console.log(" info - Show information about an executable.");
308 + console.log(" sign - Sign an executable using a dummy certificate.");
309 + console.log(" unsign - Remove the signature from the executable.");
310 return;
311 }
312
313 // Check that a valid command is passed in
262 - if (['info', 'sign'].indexOf(process.argv[2].toLowerCase()) == -1) {
314 + if (['info', 'sign', 'unsign'].indexOf(process.argv[2].toLowerCase()) == -1) {
315 console.log("Invalid command: " + process.argv[2]);
316 return;
317 }
@@ -285,10 +337,11 @@ function start() {
337 if (exe.signatureBlock) { console.log('Signature', exe.signatureBlock.toString('hex')); }
338 console.log('FileLen: ' + exe.filesize);
339 }
288 -
340 if (command == 'sign') {
290 - console.log('Signing...');
291 - exe.sign();
341 + console.log('Signing...'); exe.sign();
342 + }
343 + if (command == 'unsign') {
344 + if (exe.header.signed) { console.log('Unsigning...'); exe.unsign(); } else { console.log('Executable is not signed.'); }
345 }
346
347 // Close the file