Added bitmap list/remove/add/change support to authenticode.js.
This commit is contained in:
parent
37bcae368b
commit
e306af5fc9
141
authenticode.js
141
authenticode.js
|
@ -52,11 +52,20 @@ function createOutFile(args, filename) {
|
|||
|
||||
// Hash an object
|
||||
function hashObject(obj) {
|
||||
if (obj == null) { return null; }
|
||||
const hash = crypto.createHash('sha384');
|
||||
hash.update(JSON.stringify(obj));
|
||||
if (Buffer.isBuffer(obj)) { hash.update(obj); } else { hash.update(JSON.stringify(obj)); }
|
||||
return hash.digest().toString('hex');
|
||||
}
|
||||
|
||||
// Load a .bmp file.
|
||||
function loadBitmap(bitmapFile) {
|
||||
var bitmapData = null;
|
||||
try { bitmapData = fs.readFileSync(bitmapFile); } catch (ex) { }
|
||||
if ((bitmapData == null) || (bitmapData.length < 14) || (bitmapData[0] != 0x42) || (bitmapData[1] != 0x4D)) return null;
|
||||
return bitmapData.slice(14);
|
||||
}
|
||||
|
||||
// Load a .ico file. This will load all icons in the file into a icon group object
|
||||
function loadIcon(iconFile) {
|
||||
var iconData = null;
|
||||
|
@ -753,6 +762,28 @@ function createAuthenticodeHandler(path) {
|
|||
return pkcs7raw;
|
||||
}
|
||||
|
||||
|
||||
// Get bitmaps information from resource
|
||||
obj.getBitmapInfo = function () {
|
||||
const r = {}, ptr = obj.header.sections['.rsrc'].rawAddr;
|
||||
|
||||
// Find and parse each icon
|
||||
const bitmaps = {}
|
||||
for (var i = 0; i < obj.resources.entries.length; i++) {
|
||||
if (obj.resources.entries[i].name == resourceDefaultNames.bitmaps) {
|
||||
for (var j = 0; j < obj.resources.entries[i].table.entries.length; j++) {
|
||||
const bitmapName = obj.resources.entries[i].table.entries[j].name;
|
||||
const offsetToData = obj.resources.entries[i].table.entries[j].table.entries[0].item.offsetToData;
|
||||
const size = obj.resources.entries[i].table.entries[j].table.entries[0].item.size;
|
||||
const actualPtr = (offsetToData - obj.header.sections['.rsrc'].virtualAddr) + ptr;
|
||||
bitmaps[bitmapName] = readFileSlice(actualPtr, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bitmaps;
|
||||
}
|
||||
|
||||
// Get icon information from resource
|
||||
obj.getIconInfo = function () {
|
||||
const r = {}, ptr = obj.header.sections['.rsrc'].rawAddr;
|
||||
|
@ -810,9 +841,49 @@ function createAuthenticodeHandler(path) {
|
|||
return r;
|
||||
}
|
||||
|
||||
// Set bitmap information
|
||||
obj.setBitmapInfo = function (bitmapInfo) {
|
||||
// Delete all bitmaps resources
|
||||
var resourcesEntries = [];
|
||||
for (var i = 0; i < obj.resources.entries.length; i++) {
|
||||
if (obj.resources.entries[i].name != resourceDefaultNames.bitmaps) {
|
||||
resourcesEntries.push(obj.resources.entries[i]);
|
||||
}
|
||||
}
|
||||
obj.resources.entries = resourcesEntries;
|
||||
|
||||
// Add all bitmap entries
|
||||
const bitmapEntry = { name: resourceDefaultNames.bitmaps, table: { characteristics: 0, timeDateStamp: 0, majorVersion: 0, minorVersion: 0, entries: [] } };
|
||||
for (var i in bitmapInfo) {
|
||||
var name = i;
|
||||
if (parseInt(i) == name) { name = parseInt(i); }
|
||||
const bitmapItemEntry = { name: name, table: { characteristics: 0, timeDateStamp: 0, majorVersion: 0, minorVersion: 0, entries: [{ name: 1033, item: { buffer: bitmapInfo[i], codePage: 0 } }] } }
|
||||
bitmapEntry.table.entries.push(bitmapItemEntry);
|
||||
}
|
||||
obj.resources.entries.push(bitmapEntry);
|
||||
|
||||
// Sort the resources by name. This is required.
|
||||
function resSort(a, b) {
|
||||
if ((typeof a == 'string') && (typeof b == 'string')) { if (a < b) return -1; if (a > b) return 1; return 0; }
|
||||
if ((typeof a == 'number') && (typeof b == 'number')) { return a - b; }
|
||||
if ((typeof a == 'string') && (typeof b == 'number')) { return -1; }
|
||||
return 1;
|
||||
}
|
||||
const names = [];
|
||||
for (var i = 0; i < obj.resources.entries.length; i++) { names.push(obj.resources.entries[i].name); }
|
||||
names.sort(resSort);
|
||||
var newEntryOrder = [];
|
||||
for (var i in names) {
|
||||
for (var j = 0; j < obj.resources.entries.length; j++) {
|
||||
if (obj.resources.entries[j].name == names[i]) { newEntryOrder.push(obj.resources.entries[j]); }
|
||||
}
|
||||
}
|
||||
obj.resources.entries = newEntryOrder;
|
||||
}
|
||||
|
||||
// Set icon information
|
||||
obj.setIconInfo = function (iconInfo) {
|
||||
// Delete all icon and icon groups the the resources
|
||||
// Delete all icon and icon groups resources
|
||||
var resourcesEntries = [];
|
||||
for (var i = 0; i < obj.resources.entries.length; i++) {
|
||||
if ((obj.resources.entries[i].name != resourceDefaultNames.icon) && (obj.resources.entries[i].name != resourceDefaultNames.iconGroups)) {
|
||||
|
@ -2023,6 +2094,12 @@ function start() {
|
|||
console.log(" --out [file] Resulting signed executable.");
|
||||
console.log(" --time [url] The time signing server URL.");
|
||||
console.log(" --proxy [url] The HTTP proxy to use to contact the time signing server, must start with http://");
|
||||
console.log(" bitmaps: Show bitmap resources in the executable.");
|
||||
console.log(" --exe [file] Input executable.");
|
||||
console.log(" savebitmap: Save a single bitmap to a .bmp file.");
|
||||
console.log(" --exe [file] Input executable.");
|
||||
console.log(" --out [file] Resulting .ico file.");
|
||||
console.log(" --bitmap [number] Bitmap number to save to file.");
|
||||
console.log(" icons: Show the icon resources in the executable.");
|
||||
console.log(" --exe [file] Input executable.");
|
||||
console.log(" saveicon: Save a single icon bitmap to a .ico file.");
|
||||
|
@ -2049,12 +2126,14 @@ function start() {
|
|||
console.log(" --productname [value]");
|
||||
console.log(" --productversion [value]");
|
||||
console.log(" --removeicongroup [number]");
|
||||
console.log(" --removebitmap [number]");
|
||||
console.log(" --icon [groupNumber],[filename.ico]");
|
||||
console.log(" --bitmap [number],[filename.bmp]");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check that a valid command is passed in
|
||||
if (['info', 'sign', 'unsign', 'createcert', 'icons', 'saveicon', 'saveicons', 'header', 'sections', 'timestamp', 'signblock'].indexOf(process.argv[2].toLowerCase()) == -1) {
|
||||
if (['info', 'sign', 'unsign', 'createcert', 'icons', 'bitmaps', 'saveicon', 'saveicons', 'savebitmap', 'header', 'sections', 'timestamp', 'signblock'].indexOf(process.argv[2].toLowerCase()) == -1) {
|
||||
console.log("Invalid command: " + process.argv[2]);
|
||||
console.log("Valid commands are: info, sign, unsign, createcert, timestamp");
|
||||
return;
|
||||
|
@ -2096,15 +2175,22 @@ function start() {
|
|||
|
||||
// Parse the icon changes
|
||||
resChanges = false;
|
||||
var icons = null;
|
||||
var icons = null, bitmaps = null;
|
||||
if (exe != null) {
|
||||
icons = exe.getIconInfo();
|
||||
bitmaps = exe.getBitmapInfo();
|
||||
if (typeof args['removeicongroup'] == 'string') { // If --removeicongroup is used, it's to remove an existing icon group
|
||||
const groupsToRemove = args['removeicongroup'].split(',');
|
||||
for (var i in groupsToRemove) { if (icons[groupsToRemove[i]] != null) { delete icons[groupsToRemove[i]]; resChanges = true; } }
|
||||
} else if (typeof args['removeicongroup'] == 'number') {
|
||||
if (icons[args['removeicongroup']] != null) { delete icons[args['removeicongroup']]; resChanges = true; }
|
||||
}
|
||||
if (typeof args['removebitmap'] == 'string') { // If --removebitmap is used
|
||||
const bitmapsToRemove = args['removebitmap'].split(',');
|
||||
for (var i in bitmapsToRemove) { if (bitmaps[bitmapsToRemove[i]] != null) { delete bitmaps[bitmapsToRemove[i]]; resChanges = true; } }
|
||||
} else if (typeof args['removebitmap'] == 'number') {
|
||||
if (bitmaps[args['removebitmap']] != null) { delete bitmaps[args['removebitmap']]; resChanges = true; }
|
||||
}
|
||||
if (typeof args['icon'] == 'string') { // If --icon is used, it's to add or replace an existing icon group
|
||||
const iconToAddSplit = args['icon'].split(',');
|
||||
if (iconToAddSplit.length != 2) { console.log("The --icon format is: --icon [number],[file]."); return; }
|
||||
|
@ -2121,7 +2207,26 @@ function start() {
|
|||
resChanges = true;
|
||||
}
|
||||
}
|
||||
if (resChanges == true) { exe.setIconInfo(icons); }
|
||||
if (typeof args['bitmap'] == 'string') { // If --bitmap is used, it's to add or replace an existing bitmap
|
||||
const bitmapToAddSplit = args['bitmap'].split(',');
|
||||
if (bitmapToAddSplit.length != 2) { console.log("The --bitmap format is: --bitmap [number],[file]."); return; }
|
||||
const bitmapName = parseInt(bitmapToAddSplit[0]);
|
||||
const bitmapFile = bitmapToAddSplit[1];
|
||||
const bitmap = loadBitmap(bitmapFile);
|
||||
if (bitmap == null) { console.log("Unable to load bitmap: " + bitmapFile); return; }
|
||||
if (bitmaps[bitmapName] != null) {
|
||||
const bitmapHash = hashObject(bitmap); // Compute the new bitmap hash
|
||||
const bitmapHash2 = hashObject(bitmaps[bitmapName]); // Computer the old bitmap hash
|
||||
if (bitmapHash != bitmapHash2) { bitmaps[bitmapName] = bitmap; resChanges = true; } // If different, replace the new bitmap
|
||||
} else {
|
||||
bitmaps[bitmapName] = bitmap; // We are adding an new bitmap
|
||||
resChanges = true;
|
||||
}
|
||||
}
|
||||
if (resChanges == true) {
|
||||
exe.setIconInfo(icons);
|
||||
exe.setBitmapInfo(bitmaps);
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the command
|
||||
|
@ -2241,6 +2346,32 @@ function start() {
|
|||
fs.writeFileSync(args.out, pki.certificateToPem(cert.cert) + '\r\n' + pki.privateKeyToPem(cert.key));
|
||||
console.log("Done.");
|
||||
}
|
||||
if (command == 'bitmaps') { // Show bitmaps in the executable
|
||||
if (exe == null) { console.log("Missing --exe [filename]"); return; }
|
||||
if (args.json) {
|
||||
var bitmapInfo = exe.getBitmapInfo();
|
||||
console.log(JSON.stringify(bitmapInfo, null, 2));
|
||||
} else {
|
||||
var bitmapInfo = exe.getBitmapInfo();
|
||||
if (bitmapInfo != null) {
|
||||
console.log("Bitmap Information:");
|
||||
for (var i in bitmapInfo) { console.log(' ' + i + ': ' + bitmapInfo[i].length + ' byte' + ((bitmapInfo[i].length > 1) ? 's' : '') + '.'); }
|
||||
}
|
||||
}
|
||||
}
|
||||
if (command == 'savebitmap') { // Save an bitmap to file
|
||||
if (exe == null) { console.log("Missing --exe [filename]"); return; }
|
||||
if (typeof args.out != 'string') { console.log("Missing --out [filename]"); return; }
|
||||
if (typeof args.bitmap != 'number') { console.log("Missing or incorrect --bitmap [number]"); return; }
|
||||
const bitmapInfo = exe.getBitmapInfo();
|
||||
if (bitmapInfo[args.bitmap] == null) { console.log("Unknown bitmap: " + args.bitmap); return; }
|
||||
|
||||
console.log("Writing to " + args.out);
|
||||
var bitmapHeader = Buffer.from('424D000000000000000036000000', 'hex');
|
||||
bitmapHeader.writeUInt32LE(14 + bitmapInfo[args.bitmap].length, 2); // Write the full size of the bitmap file
|
||||
fs.writeFileSync(args.out, Buffer.concat([bitmapHeader, bitmapInfo[args.bitmap]]));
|
||||
console.log("Done.");
|
||||
}
|
||||
if (command == 'icons') { // Show icons in the executable
|
||||
if (exe == null) { console.log("Missing --exe [filename]"); return; }
|
||||
if (args.json) {
|
||||
|
|
Loading…
Reference in New Issue