First working version of Windows agent icon replacement.

Ylian Saint-Hilaire committed Aug 11, 2022 at 13:34 UTC 37bcae368becb855dd80ba7f0c5a2814c0a7a6ee
3 files changed +47 -14
authenticode.js
+15
@@ -1268,6 +1268,21 @@ function createAuthenticodeHandler(path) {
1268 return hash.digest();
1269 }
1270
1271 + // Hash the file using the selected hashing system skipping resource section
1272 + // This hash skips the executables CRC, sections table, resource section, code signing data and signing block
1273 + obj.getHashOfSection = function (algo, sectionName) {
1274 + if (obj.header.sections[sectionName] == null) return null;
1275 +
1276 + // Get the section start and size
1277 + const sectionPtr = obj.header.sections[sectionName].rawAddr;
1278 + const sectionSize = obj.header.sections[sectionName].rawSize;
1279 +
1280 + // Hash the remaining data
1281 + const hash = crypto.createHash(algo);
1282 + runHash(hash, sectionPtr, sectionPtr + sectionSize);
1283 + return hash.digest();
1284 + }
1285 +
1286 // Hash the file from start to end loading 64k chunks
1287 function runHash(hash, start, end) {
1288 var ptr = start;
meshcentral-config-schema.json
+1 -1
@@ -574,7 +574,7 @@
574 "additionalProperties": false,
575 "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.",
576 "properties": {
577 - "icon": { "type": "string", "default": null, "description": "DO NOT USE. THIS FEATURE DOES NOT WORK YET. Sets the agent icon, this is the name of a .ico file with the file placed in the meshcentral-data folder." },
577 + "icon": { "type": "string", "description": "Sets the agent icon, this is the name of a .ico file with the file placed in the meshcentral-data folder." },
578 "fileDescription": { "type": "string", "description": "Executable file description." },
579 "fileVersion": { "type": "string", "description": "Executable file version, in the form of 'n.n.n.n', for example: '1.2.3.4'." },
580 "internalName": { "type": "string", "description": "Executable internal name." },
meshcentral.js
+31 -13
@@ -1375,17 +1375,14 @@ function CreateMeshCentralServer(config, args) {
1375 if (icon != null) {
1376 // The icon file was correctly loaded
1377 obj.config.domains[i].agentfileinfo.icon = icon;
1378 - obj.config.domains[i].agentfileinfo.iconhash = require('./authenticode.js').hashObject(icon);
1378 } else {
1379 // Failed to load the icon file, display a server warning
1380 addServerWarning("Unable to load agent icon file: " + obj.config.domains[i].agentfileinfo.icon + ".", 23, [obj.config.domains[i].agentfileinfo.icon]);
1381 delete obj.config.domains[i].agentfileinfo.icon;
1383 - delete obj.config.domains[i].agentfileinfo.iconhash;
1382 }
1383 } else {
1384 // Invalid icon file path
1385 delete obj.config.domains[i].agentfileinfo.icon;
1388 - delete obj.config.domains[i].agentfileinfo.iconhash;
1386 }
1387 }
1388 }
@@ -2995,21 +2992,42 @@ function CreateMeshCentralServer(config, args) {
2992 }
2993
2994 // Check the agent icon
2998 - if ((destinationAgentOk == true) && (domain.agentfileinfo != null) && (domain.agentfileinfo.iconhash != null)) {
2999 - const agentIconGroups = destinationAgent.getIconInfo();
3000 - if (agentIconGroups != null) {
3001 - const agentIconGroupNames = Object.keys(agentIconGroups);
3002 - if (agentIconGroupNames.length > 0) {
3003 - const agentMainIconGroupName = agentIconGroupNames[0];
3004 - const agentMainIconGroupHash = require('./authenticode.js').hashObject(agentIconGroups[agentMainIconGroupName]);
3005 - if (agentMainIconGroupHash != domain.agentfileinfo.iconhash) { destinationAgentOk = false; } // If the existing agent icon does not match the desired icon, we need to re-sign the agent.
2995 + if (destinationAgentOk == true) {
2996 + if ((domain.agentfileinfo != null) && (domain.agentfileinfo.icon != null)) {
2997 + // Check if the destination agent matches the icon we want
2998 + const agentIconGroups = destinationAgent.getIconInfo();
2999 + if (agentIconGroups != null) {
3000 + const agentIconGroupNames = Object.keys(agentIconGroups);
3001 + if (agentIconGroupNames.length > 0) {
3002 + const agentMainIconGroup = agentIconGroups[agentIconGroupNames[0]];
3003 + if (agentMainIconGroup.resCount != domain.agentfileinfo.icon.resCount) {
3004 + destinationAgentOk = false; // The icon image count is different, don't bother hashing to see if the icons are different.
3005 + } else {
3006 + const agentMainIconGroupHash = require('./authenticode.js').hashObject(agentMainIconGroup);
3007 + const iconHash = require('./authenticode.js').hashObject(domain.agentfileinfo.icon);
3008 + if (agentMainIconGroupHash != iconHash) { destinationAgentOk = false; } // If the existing agent icon does not match the desired icon, we need to re-sign the agent.
3009 + }
3010 + }
3011 + }
3012 + } else {
3013 + // Check if the destination agent has the default icon
3014 + const agentIconGroups1 = destinationAgent.getIconInfo();
3015 + const agentIconGroups2 = originalAgent.getIconInfo();
3016 + if (agentIconGroups1.resCount != agentIconGroups2.resCount) {
3017 + destinationAgentOk = false; // The icon image count is different, don't bother hashing to see if the icons are different.
3018 + } else {
3019 + const iconHash1 = require('./authenticode.js').hashObject(agentIconGroups1);
3020 + const iconHash2 = require('./authenticode.js').hashObject(agentIconGroups2);
3021 + if (iconHash1 != iconHash2) { destinationAgentOk = false; } // If the existing agent icon does not match the desired icon, we need to re-sign the agent.
3022 }
3023 }
3024 }
3025 }
3026
3011 - // 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.
3012 - if ((destinationAgentOk == true) && (originalAgent.getHashNoResources('sha384').compare(destinationAgent.getHashNoResources('sha384')) != 0)) { destinationAgentOk = false; }
3027 + // If everything looks ok, runs a hash of the original and destination agent .text, .data and .rdata sections. If different, sign the agent again.
3028 + if ((destinationAgentOk == true) && (originalAgent.getHashOfSection('sha384', '.text').compare(destinationAgent.getHashOfSection('sha384', '.text')) != 0)) { destinationAgentOk = false; }
3029 + if ((destinationAgentOk == true) && (originalAgent.getHashOfSection('sha384', '.data').compare(destinationAgent.getHashOfSection('sha384', '.data')) != 0)) { destinationAgentOk = false; }
3030 + if ((destinationAgentOk == true) && (originalAgent.getHashOfSection('sha384', '.rdata').compare(destinationAgent.getHashOfSection('sha384', '.rdata')) != 0)) { destinationAgentOk = false; }
3031
3032 // We are done comparing the destination agent, close it.
3033 destinationAgent.close();