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();