mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2025-02-12 11:01:52 +00:00
Completed support for chaning windows executable file information resources.
This commit is contained in:
parent
63de362bc0
commit
34fdb39dcd
4 changed files with 86 additions and 27 deletions
|
@ -1111,8 +1111,9 @@ function createAuthenticodeHandler(path) {
|
||||||
//function padPointer(ptr) { return ptr + (ptr % 4); }
|
//function padPointer(ptr) { return ptr + (ptr % 4); }
|
||||||
|
|
||||||
// Hash the file using the selected hashing system
|
// Hash the file using the selected hashing system
|
||||||
|
// This hash skips the executables CRC and code signing data and signing block
|
||||||
obj.getHash = function(algo) {
|
obj.getHash = function(algo) {
|
||||||
var hash = crypto.createHash(algo);
|
const hash = crypto.createHash(algo);
|
||||||
runHash(hash, 0, obj.header.peHeaderLocation + 88);
|
runHash(hash, 0, obj.header.peHeaderLocation + 88);
|
||||||
runHash(hash, obj.header.peHeaderLocation + 88 + 4, obj.header.peHeaderLocation + 152 + (obj.header.pe32plus * 16));
|
runHash(hash, obj.header.peHeaderLocation + 88 + 4, obj.header.peHeaderLocation + 152 + (obj.header.pe32plus * 16));
|
||||||
runHash(hash, obj.header.peHeaderLocation + 152 + (obj.header.pe32plus * 16) + 8, obj.header.sigpos > 0 ? obj.header.sigpos : obj.filesize);
|
runHash(hash, obj.header.peHeaderLocation + 152 + (obj.header.pe32plus * 16) + 8, obj.header.sigpos > 0 ? obj.header.sigpos : obj.filesize);
|
||||||
|
@ -1120,14 +1121,41 @@ function createAuthenticodeHandler(path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash of an open file using the selected hashing system
|
// Hash of an open file using the selected hashing system
|
||||||
obj.getHashOfFile = function (fd, algo, filesize) {
|
// This hash skips the executables CRC and code signing data and signing block
|
||||||
var hash = crypto.createHash(algo);
|
obj.getHashOfFile = function(fd, algo, filesize) {
|
||||||
|
const hash = crypto.createHash(algo);
|
||||||
runHashOnFile(fd, hash, 0, obj.header.peHeaderLocation + 88);
|
runHashOnFile(fd, hash, 0, obj.header.peHeaderLocation + 88);
|
||||||
runHashOnFile(fd, hash, obj.header.peHeaderLocation + 88 + 4, obj.header.peHeaderLocation + 152 + (obj.header.pe32plus * 16));
|
runHashOnFile(fd, hash, obj.header.peHeaderLocation + 88 + 4, obj.header.peHeaderLocation + 152 + (obj.header.pe32plus * 16));
|
||||||
runHashOnFile(fd, hash, obj.header.peHeaderLocation + 152 + (obj.header.pe32plus * 16) + 8, obj.header.sigpos > 0 ? obj.header.sigpos : filesize);
|
runHashOnFile(fd, hash, obj.header.peHeaderLocation + 152 + (obj.header.pe32plus * 16) + 8, obj.header.sigpos > 0 ? obj.header.sigpos : filesize);
|
||||||
return hash.digest();
|
return hash.digest();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hash the file using the selected hashing system skipping resource section
|
||||||
|
// This hash skips the executables CRC, sections table, resource section, code signing data and signing block
|
||||||
|
obj.getHashNoResources = function (algo) {
|
||||||
|
if (obj.header.sections['.rsrc'] == null) { return obj.getHash(algo); } // No resources in this executable, return a normal hash
|
||||||
|
|
||||||
|
// Get the sections table start and size
|
||||||
|
const sectionHeaderPtr = obj.header.SectionHeadersPtr;
|
||||||
|
const sectionHeaderSize = obj.header.coff.numberOfSections * 40;
|
||||||
|
|
||||||
|
// Get the resource section start and size
|
||||||
|
const resPtr = obj.header.sections['.rsrc'].rawAddr;
|
||||||
|
const resSize = obj.header.sections['.rsrc'].rawSize;
|
||||||
|
|
||||||
|
// Get the end-of-file location
|
||||||
|
const eof = obj.header.sigpos > 0 ? obj.header.sigpos : obj.filesize;
|
||||||
|
|
||||||
|
// Hash the remaining data
|
||||||
|
const hash = crypto.createHash(algo);
|
||||||
|
runHash(hash, 0, obj.header.peHeaderLocation + 88);
|
||||||
|
runHash(hash, obj.header.peHeaderLocation + 88 + 4, obj.header.peHeaderLocation + 152 + (obj.header.pe32plus * 16));
|
||||||
|
runHash(hash, obj.header.peHeaderLocation + 152 + (obj.header.pe32plus * 16) + 8, sectionHeaderPtr);
|
||||||
|
runHash(hash, sectionHeaderPtr + sectionHeaderSize, resPtr);
|
||||||
|
runHash(hash, resPtr + resSize, eof);
|
||||||
|
return hash.digest();
|
||||||
|
}
|
||||||
|
|
||||||
// Hash the file from start to end loading 64k chunks
|
// Hash the file from start to end loading 64k chunks
|
||||||
function runHash(hash, start, end) {
|
function runHash(hash, start, end) {
|
||||||
var ptr = start;
|
var ptr = start;
|
||||||
|
@ -1137,8 +1165,8 @@ function createAuthenticodeHandler(path) {
|
||||||
// Hash the open file loading 64k chunks
|
// Hash the open file loading 64k chunks
|
||||||
// TODO: Do chunks on this!!!
|
// TODO: Do chunks on this!!!
|
||||||
function runHashOnFile(fd, hash, start, end) {
|
function runHashOnFile(fd, hash, start, end) {
|
||||||
var buf = Buffer.alloc(end - start);
|
const buf = Buffer.alloc(end - start);
|
||||||
var len = fs.readSync(fd, buf, 0, buf.length, start);
|
const len = fs.readSync(fd, buf, 0, buf.length, start);
|
||||||
if (len != buf.length) { console.log('BAD runHashOnFile'); }
|
if (len != buf.length) { console.log('BAD runHashOnFile'); }
|
||||||
hash.update(buf);
|
hash.update(buf);
|
||||||
}
|
}
|
||||||
|
|
|
@ -543,6 +543,20 @@
|
||||||
"backgroundColor": { "type": "string", "default": null, "description": "Background color, valid values are RBG in format 0,0,0 to 255,255,255 or format #000000 to #FFFFFF." }
|
"backgroundColor": { "type": "string", "default": null, "description": "Background color, valid values are RBG in format 0,0,0 to 255,255,255 or format #000000 to #FFFFFF." }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"agentFileInfo": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"description": "Use this section to set resource metadata of the Windows agents prior to signing. In Windows, you can right-click and select properties to view these values.",
|
||||||
|
"properties": {
|
||||||
|
"fileDescription": { "type": "string", "description": "Executable file description." },
|
||||||
|
"fileVersion": { "type": "string", "description": "Executable file version, generally in the form of 1.2.3.4." },
|
||||||
|
"internalName": { "type": "string", "description": "Executable internal name." },
|
||||||
|
"legalCopyright": { "type": "string", "description": "Executable legal copyright." },
|
||||||
|
"originalFilename": { "type": "string", "description": "Executable original file name." },
|
||||||
|
"productName": { "type": "string", "description": "Executable product name." },
|
||||||
|
"productCersion": { "type": "string", "description": "Executable product version, generally in the form of 1.2.3.4." }
|
||||||
|
}
|
||||||
|
},
|
||||||
"assistantCustomization": {
|
"assistantCustomization": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
|
|
|
@ -2886,21 +2886,6 @@ function CreateMeshCentralServer(config, args) {
|
||||||
if (args.agenttimestampserver === false) { timeStampUrl = null; }
|
if (args.agenttimestampserver === false) { timeStampUrl = null; }
|
||||||
else if (typeof args.agenttimestampserver == 'string') { timeStampUrl = args.agenttimestampserver; }
|
else if (typeof args.agenttimestampserver == 'string') { timeStampUrl = args.agenttimestampserver; }
|
||||||
|
|
||||||
// Setup agent signing arguments
|
|
||||||
const signingArguments = { desc: signDesc, url: signUrl, time: timeStampUrl };
|
|
||||||
|
|
||||||
// See if we have any resources we need to change in the agent
|
|
||||||
var resChanges = false;
|
|
||||||
if ((domain.agentfileinfo != null) && (typeof domain.agentfileinfo == 'object')) {
|
|
||||||
if (typeof domain.agentfileinfo.filedescription == 'string') { signingArguments.FileDescription = domain.agentfileinfo.filedescription; resChanges = true; }
|
|
||||||
if (typeof domain.agentfileinfo.fileversion == 'string') { signingArguments.FileVersion = domain.agentfileinfo.fileversion; resChanges = true; }
|
|
||||||
if (typeof domain.agentfileinfo.internalname == 'string') { signingArguments.InternalName = domain.agentfileinfo.internalname; resChanges = true; }
|
|
||||||
if (typeof domain.agentfileinfo.legalcopyright == 'string') { signingArguments.LegalCopyright = domain.agentfileinfo.legalcopyright; resChanges = true; }
|
|
||||||
if (typeof domain.agentfileinfo.originalfilename == 'string') { signingArguments.OriginalFilename = domain.agentfileinfo.originalfilename; resChanges = true; }
|
|
||||||
if (typeof domain.agentfileinfo.productname == 'string') { signingArguments.ProductName = domain.agentfileinfo.productname; resChanges = true; }
|
|
||||||
if (typeof domain.agentfileinfo.productversion == 'string') { signingArguments.ProductVersion = domain.agentfileinfo.productversion; resChanges = true; }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup the pending operations counter
|
// Setup the pending operations counter
|
||||||
var pendingOperations = 1;
|
var pendingOperations = 1;
|
||||||
|
|
||||||
|
@ -2929,11 +2914,33 @@ function CreateMeshCentralServer(config, args) {
|
||||||
(destinationAgent != null) &&
|
(destinationAgent != null) &&
|
||||||
(destinationAgent.fileHashSigned != null) &&
|
(destinationAgent.fileHashSigned != null) &&
|
||||||
(Buffer.compare(destinationAgent.fileHashSigned, destinationAgent.fileHashActual) == 0) &&
|
(Buffer.compare(destinationAgent.fileHashSigned, destinationAgent.fileHashActual) == 0) &&
|
||||||
((Buffer.compare(destinationAgent.fileHashSigned, originalAgent.getHash(destinationAgent.fileHashAlgo))) == 0) &&
|
|
||||||
(destinationAgent.signingAttribs.indexOf(signUrl) >= 0) &&
|
(destinationAgent.signingAttribs.indexOf(signUrl) >= 0) &&
|
||||||
(destinationAgent.signingAttribs.indexOf(signDesc) >= 0)
|
(destinationAgent.signingAttribs.indexOf(signDesc) >= 0)
|
||||||
);
|
);
|
||||||
if (destinationAgent != null) { destinationAgent.close(); }
|
|
||||||
|
if (destinationAgent != null) {
|
||||||
|
// If the agent is signed correctly, look to see if the resources in the destination agent are correct
|
||||||
|
var orgVersionStrings = originalAgent.getVersionInfo();
|
||||||
|
if (destinationAgentOk == true) {
|
||||||
|
var versionStrings = destinationAgent.getVersionInfo();
|
||||||
|
var versionProperties = ['FileDescription', 'FileVersion', 'InternalName', 'LegalCopyright', 'OriginalFilename', 'ProductName', 'ProductVersion'];
|
||||||
|
for (var i in versionProperties) {
|
||||||
|
const prop = versionProperties[i], propl = prop.toLowerCase();
|
||||||
|
if ((domain.agentfileinfo != null) && (typeof domain.agentfileinfo == 'object') && (typeof domain.agentfileinfo[propl] == 'string')) {
|
||||||
|
if (domain.agentfileinfo[propl] != versionStrings[prop]) { destinationAgentOk = false; } // If the resource we want is not the same as the destination executable, we need to re-sign the agent.
|
||||||
|
} else {
|
||||||
|
if (orgVersionStrings[prop] != versionStrings[prop]) { destinationAgentOk = false; } // if the resource of the orginal agent not the same as the destination executable, we need to re-sign the agent.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If everything looks ok, runs a hash of the original and destination agent skipping the CRC, resource and signature blocks. If different, sign the agent again.
|
||||||
|
if ((destinationAgentOk == true) && (originalAgent.getHashNoResources('sha384').compare(destinationAgent.getHashNoResources('sha384')) != 0)) { destinationAgentOk = false; }
|
||||||
|
|
||||||
|
// We are done comparing the destination agent, close it.
|
||||||
|
destinationAgent.close();
|
||||||
|
}
|
||||||
|
|
||||||
if (destinationAgentOk == false) {
|
if (destinationAgentOk == false) {
|
||||||
// If not signed correctly, sign it. First, create the server signed agent folder if needed
|
// If not signed correctly, sign it. First, create the server signed agent folder if needed
|
||||||
try { obj.fs.mkdirSync(serverSignedAgentsPath); } catch (ex) { }
|
try { obj.fs.mkdirSync(serverSignedAgentsPath); } catch (ex) { }
|
||||||
|
@ -2952,17 +2959,28 @@ function CreateMeshCentralServer(config, args) {
|
||||||
xagentSignedFunc.objx = objx;
|
xagentSignedFunc.objx = objx;
|
||||||
xagentSignedFunc.archid = archid;
|
xagentSignedFunc.archid = archid;
|
||||||
xagentSignedFunc.signeedagentpath = signeedagentpath;
|
xagentSignedFunc.signeedagentpath = signeedagentpath;
|
||||||
const xsigningArguments = Object.assign({}, signingArguments); // Shallow clone
|
|
||||||
xsigningArguments.out = signeedagentpath;
|
|
||||||
|
|
||||||
|
// Parse the resources in the executable and make any required changes
|
||||||
|
var resChanges = false, versionStrings = null;
|
||||||
|
if ((domain.agentfileinfo != null) && (typeof domain.agentfileinfo == 'object')) {
|
||||||
|
versionStrings = originalAgent.getVersionInfo();
|
||||||
|
var versionProperties = ['FileDescription', 'FileVersion', 'InternalName', 'LegalCopyright', 'OriginalFilename', 'ProductName', 'ProductVersion'];
|
||||||
|
for (var i in versionProperties) {
|
||||||
|
const prop = versionProperties[i], propl = prop.toLowerCase();
|
||||||
|
if (domain.agentfileinfo[propl] && (domain.agentfileinfo[propl] != versionStrings[prop])) { versionStrings[prop] = domain.agentfileinfo[propl]; resChanges = true; }
|
||||||
|
}
|
||||||
|
if (resChanges == true) { originalAgent.setVersionInfo(versionStrings); }
|
||||||
|
}
|
||||||
|
|
||||||
|
const signingArguments = { out: signeedagentpath, desc: signDesc, url: signUrl, time: timeStampUrl }; // Shallow clone
|
||||||
obj.debug('main', "Code signing agent with arguments: " + JSON.stringify(signingArguments));
|
obj.debug('main', "Code signing agent with arguments: " + JSON.stringify(signingArguments));
|
||||||
if (resChanges == false) {
|
if (resChanges == false) {
|
||||||
// Sign the agent the simple way, without changing any resources.
|
// Sign the agent the simple way, without changing any resources.
|
||||||
originalAgent.sign(agentSignCertInfo, xsigningArguments, xagentSignedFunc);
|
originalAgent.sign(agentSignCertInfo, signingArguments, xagentSignedFunc);
|
||||||
} else {
|
} else {
|
||||||
// Change the agent resources and sign the agent, this is a much more involved process.
|
// Change the agent resources and sign the agent, this is a much more involved process.
|
||||||
// NOTE: This is experimental and could corupt the agent.
|
// NOTE: This is experimental and could corupt the agent.
|
||||||
originalAgent.writeExecutable(xsigningArguments, agentSignCertInfo, xagentSignedFunc);
|
originalAgent.writeExecutable(signingArguments, agentSignCertInfo, xagentSignedFunc);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Signed agent is already ok, use it.
|
// Signed agent is already ok, use it.
|
||||||
|
|
|
@ -287,7 +287,6 @@
|
||||||
"fileName": "compagnyagent"
|
"fileName": "compagnyagent"
|
||||||
},
|
},
|
||||||
"_agentFileInfo": {
|
"_agentFileInfo": {
|
||||||
"__COMMENT__": "This section is experimental",
|
|
||||||
"_filedescription": "sample_filedescription",
|
"_filedescription": "sample_filedescription",
|
||||||
"_fileversion": "0.1.2.3",
|
"_fileversion": "0.1.2.3",
|
||||||
"_internalname": "sample_internalname",
|
"_internalname": "sample_internalname",
|
||||||
|
|
Loading…
Reference in a new issue