@zhuixin/engine 0.2.3 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/engine-main.js +1095 -140
- package/package.json +6 -4
package/dist/engine-main.js
CHANGED
|
@@ -363,25 +363,25 @@ var require_utils = __commonJS({
|
|
|
363
363
|
}
|
|
364
364
|
mkdirSync(folder);
|
|
365
365
|
};
|
|
366
|
-
Utils.prototype.writeFileTo = function(
|
|
366
|
+
Utils.prototype.writeFileTo = function(path10, content, overwrite, attr) {
|
|
367
367
|
const self = this;
|
|
368
|
-
if (self.fs.existsSync(
|
|
368
|
+
if (self.fs.existsSync(path10)) {
|
|
369
369
|
if (!overwrite) return false;
|
|
370
|
-
var stat = self.fs.statSync(
|
|
370
|
+
var stat = self.fs.statSync(path10);
|
|
371
371
|
if (stat.isDirectory()) {
|
|
372
372
|
return false;
|
|
373
373
|
}
|
|
374
374
|
}
|
|
375
|
-
var folder = pth.dirname(
|
|
375
|
+
var folder = pth.dirname(path10);
|
|
376
376
|
if (!self.fs.existsSync(folder)) {
|
|
377
377
|
self.makeDir(folder);
|
|
378
378
|
}
|
|
379
379
|
var fd;
|
|
380
380
|
try {
|
|
381
|
-
fd = self.fs.openSync(
|
|
381
|
+
fd = self.fs.openSync(path10, "w", 438);
|
|
382
382
|
} catch (e) {
|
|
383
|
-
self.fs.chmodSync(
|
|
384
|
-
fd = self.fs.openSync(
|
|
383
|
+
self.fs.chmodSync(path10, 438);
|
|
384
|
+
fd = self.fs.openSync(path10, "w", 438);
|
|
385
385
|
}
|
|
386
386
|
if (fd) {
|
|
387
387
|
try {
|
|
@@ -390,31 +390,31 @@ var require_utils = __commonJS({
|
|
|
390
390
|
self.fs.closeSync(fd);
|
|
391
391
|
}
|
|
392
392
|
}
|
|
393
|
-
self.fs.chmodSync(
|
|
393
|
+
self.fs.chmodSync(path10, attr || 438);
|
|
394
394
|
return true;
|
|
395
395
|
};
|
|
396
|
-
Utils.prototype.writeFileToAsync = function(
|
|
396
|
+
Utils.prototype.writeFileToAsync = function(path10, content, overwrite, attr, callback) {
|
|
397
397
|
if (typeof attr === "function") {
|
|
398
398
|
callback = attr;
|
|
399
399
|
attr = void 0;
|
|
400
400
|
}
|
|
401
401
|
const self = this;
|
|
402
|
-
self.fs.exists(
|
|
402
|
+
self.fs.exists(path10, function(exist) {
|
|
403
403
|
if (exist && !overwrite) return callback(false);
|
|
404
|
-
self.fs.stat(
|
|
404
|
+
self.fs.stat(path10, function(err, stat) {
|
|
405
405
|
if (exist && stat.isDirectory()) {
|
|
406
406
|
return callback(false);
|
|
407
407
|
}
|
|
408
|
-
var folder = pth.dirname(
|
|
408
|
+
var folder = pth.dirname(path10);
|
|
409
409
|
self.fs.exists(folder, function(exists) {
|
|
410
410
|
if (!exists) self.makeDir(folder);
|
|
411
|
-
self.fs.open(
|
|
411
|
+
self.fs.open(path10, "w", 438, function(err2, fd) {
|
|
412
412
|
if (err2) {
|
|
413
|
-
self.fs.chmod(
|
|
414
|
-
self.fs.open(
|
|
413
|
+
self.fs.chmod(path10, 438, function() {
|
|
414
|
+
self.fs.open(path10, "w", 438, function(err3, fd2) {
|
|
415
415
|
self.fs.write(fd2, content, 0, content.length, 0, function() {
|
|
416
416
|
self.fs.close(fd2, function() {
|
|
417
|
-
self.fs.chmod(
|
|
417
|
+
self.fs.chmod(path10, attr || 438, function() {
|
|
418
418
|
callback(true);
|
|
419
419
|
});
|
|
420
420
|
});
|
|
@@ -424,13 +424,13 @@ var require_utils = __commonJS({
|
|
|
424
424
|
} else if (fd) {
|
|
425
425
|
self.fs.write(fd, content, 0, content.length, 0, function() {
|
|
426
426
|
self.fs.close(fd, function() {
|
|
427
|
-
self.fs.chmod(
|
|
427
|
+
self.fs.chmod(path10, attr || 438, function() {
|
|
428
428
|
callback(true);
|
|
429
429
|
});
|
|
430
430
|
});
|
|
431
431
|
});
|
|
432
432
|
} else {
|
|
433
|
-
self.fs.chmod(
|
|
433
|
+
self.fs.chmod(path10, attr || 438, function() {
|
|
434
434
|
callback(true);
|
|
435
435
|
});
|
|
436
436
|
}
|
|
@@ -439,7 +439,7 @@ var require_utils = __commonJS({
|
|
|
439
439
|
});
|
|
440
440
|
});
|
|
441
441
|
};
|
|
442
|
-
Utils.prototype.findFiles = function(
|
|
442
|
+
Utils.prototype.findFiles = function(path10) {
|
|
443
443
|
const self = this;
|
|
444
444
|
function findSync(dir, pattern, recursive) {
|
|
445
445
|
if (typeof pattern === "boolean") {
|
|
@@ -448,16 +448,16 @@ var require_utils = __commonJS({
|
|
|
448
448
|
}
|
|
449
449
|
let files = [];
|
|
450
450
|
self.fs.readdirSync(dir).forEach(function(file) {
|
|
451
|
-
const
|
|
452
|
-
const stat = self.fs.statSync(
|
|
453
|
-
if (!pattern || pattern.test(
|
|
454
|
-
files.push(pth.normalize(
|
|
451
|
+
const path11 = pth.join(dir, file);
|
|
452
|
+
const stat = self.fs.statSync(path11);
|
|
453
|
+
if (!pattern || pattern.test(path11)) {
|
|
454
|
+
files.push(pth.normalize(path11) + (stat.isDirectory() ? self.sep : ""));
|
|
455
455
|
}
|
|
456
|
-
if (stat.isDirectory() && recursive) files = files.concat(findSync(
|
|
456
|
+
if (stat.isDirectory() && recursive) files = files.concat(findSync(path11, pattern, recursive));
|
|
457
457
|
});
|
|
458
458
|
return files;
|
|
459
459
|
}
|
|
460
|
-
return findSync(
|
|
460
|
+
return findSync(path10, void 0, true);
|
|
461
461
|
};
|
|
462
462
|
Utils.prototype.findFilesAsync = function(dir, cb) {
|
|
463
463
|
const self = this;
|
|
@@ -512,14 +512,14 @@ var require_utils = __commonJS({
|
|
|
512
512
|
return "UNSUPPORTED (" + method + ")";
|
|
513
513
|
}
|
|
514
514
|
};
|
|
515
|
-
Utils.canonical = function(
|
|
516
|
-
if (!
|
|
517
|
-
const safeSuffix = pth.posix.normalize("/" +
|
|
515
|
+
Utils.canonical = function(path10) {
|
|
516
|
+
if (!path10) return "";
|
|
517
|
+
const safeSuffix = pth.posix.normalize("/" + path10.split("\\").join("/"));
|
|
518
518
|
return pth.join(".", safeSuffix);
|
|
519
519
|
};
|
|
520
|
-
Utils.zipnamefix = function(
|
|
521
|
-
if (!
|
|
522
|
-
const safeSuffix = pth.posix.normalize("/" +
|
|
520
|
+
Utils.zipnamefix = function(path10) {
|
|
521
|
+
if (!path10) return "";
|
|
522
|
+
const safeSuffix = pth.posix.normalize("/" + path10.split("\\").join("/"));
|
|
523
523
|
return pth.posix.join(".", safeSuffix);
|
|
524
524
|
};
|
|
525
525
|
Utils.findLast = function(arr, callback) {
|
|
@@ -536,9 +536,9 @@ var require_utils = __commonJS({
|
|
|
536
536
|
prefix = pth.resolve(pth.normalize(prefix));
|
|
537
537
|
var parts = name.split("/");
|
|
538
538
|
for (var i = 0, l = parts.length; i < l; i++) {
|
|
539
|
-
var
|
|
540
|
-
if (
|
|
541
|
-
return
|
|
539
|
+
var path10 = pth.normalize(pth.join(prefix, parts.slice(i, l).join(pth.sep)));
|
|
540
|
+
if (path10 === prefix || path10.startsWith(prefix + pth.sep)) {
|
|
541
|
+
return path10;
|
|
542
542
|
}
|
|
543
543
|
}
|
|
544
544
|
return pth.normalize(pth.join(prefix, pth.basename(name)));
|
|
@@ -585,8 +585,8 @@ var require_fattr = __commonJS({
|
|
|
585
585
|
"node_modules/.pnpm/adm-zip@0.5.18/node_modules/adm-zip/util/fattr.js"(exports2, module2) {
|
|
586
586
|
"use strict";
|
|
587
587
|
var pth = require("path");
|
|
588
|
-
module2.exports = function(
|
|
589
|
-
var _path =
|
|
588
|
+
module2.exports = function(path10, { fs: fs12 }) {
|
|
589
|
+
var _path = path10 || "", _obj = newAttr(), _stat = null;
|
|
590
590
|
function newAttr() {
|
|
591
591
|
return {
|
|
592
592
|
directory: false,
|
|
@@ -597,8 +597,8 @@ var require_fattr = __commonJS({
|
|
|
597
597
|
atime: 0
|
|
598
598
|
};
|
|
599
599
|
}
|
|
600
|
-
if (_path &&
|
|
601
|
-
_stat =
|
|
600
|
+
if (_path && fs12.existsSync(_path)) {
|
|
601
|
+
_stat = fs12.statSync(_path);
|
|
602
602
|
_obj.directory = _stat.isDirectory();
|
|
603
603
|
_obj.mtime = _stat.mtime;
|
|
604
604
|
_obj.atime = _stat.atime;
|
|
@@ -15019,7 +15019,7 @@ var require_BigInteger = __commonJS({
|
|
|
15019
15019
|
var require_bplistParser = __commonJS({
|
|
15020
15020
|
"node_modules/.pnpm/bplist-parser@0.3.2/node_modules/bplist-parser/bplistParser.js"(exports2) {
|
|
15021
15021
|
"use strict";
|
|
15022
|
-
var
|
|
15022
|
+
var fs12 = require("fs");
|
|
15023
15023
|
var bigInt = require_BigInteger();
|
|
15024
15024
|
var debug = false;
|
|
15025
15025
|
exports2.maxObjectSize = 100 * 1e3 * 1e3;
|
|
@@ -15046,7 +15046,7 @@ var require_bplistParser = __commonJS({
|
|
|
15046
15046
|
if (Buffer.isBuffer(fileNameOrBuffer)) {
|
|
15047
15047
|
return tryParseBuffer(fileNameOrBuffer);
|
|
15048
15048
|
}
|
|
15049
|
-
|
|
15049
|
+
fs12.readFile(fileNameOrBuffer, function(err, data) {
|
|
15050
15050
|
if (err) {
|
|
15051
15051
|
reject(err);
|
|
15052
15052
|
return callback(err);
|
|
@@ -15057,7 +15057,7 @@ var require_bplistParser = __commonJS({
|
|
|
15057
15057
|
};
|
|
15058
15058
|
exports2.parseFileSync = function(fileNameOrBuffer) {
|
|
15059
15059
|
if (!Buffer.isBuffer(fileNameOrBuffer)) {
|
|
15060
|
-
fileNameOrBuffer =
|
|
15060
|
+
fileNameOrBuffer = fs12.readFileSync(fileNameOrBuffer);
|
|
15061
15061
|
}
|
|
15062
15062
|
return parseBuffer2(fileNameOrBuffer);
|
|
15063
15063
|
};
|
|
@@ -15347,8 +15347,8 @@ var require_bplistParser = __commonJS({
|
|
|
15347
15347
|
});
|
|
15348
15348
|
|
|
15349
15349
|
// src/engine-main.ts
|
|
15350
|
-
var
|
|
15351
|
-
var
|
|
15350
|
+
var import_node_fs11 = __toESM(require("fs"));
|
|
15351
|
+
var import_node_path9 = __toESM(require("path"));
|
|
15352
15352
|
var import_node_readline = __toESM(require("readline"));
|
|
15353
15353
|
|
|
15354
15354
|
// node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
|
|
@@ -15829,8 +15829,8 @@ function getErrorMap() {
|
|
|
15829
15829
|
|
|
15830
15830
|
// node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
|
|
15831
15831
|
var makeIssue = (params) => {
|
|
15832
|
-
const { data, path:
|
|
15833
|
-
const fullPath = [...
|
|
15832
|
+
const { data, path: path10, errorMaps, issueData } = params;
|
|
15833
|
+
const fullPath = [...path10, ...issueData.path || []];
|
|
15834
15834
|
const fullIssue = {
|
|
15835
15835
|
...issueData,
|
|
15836
15836
|
path: fullPath
|
|
@@ -15946,11 +15946,11 @@ var errorUtil;
|
|
|
15946
15946
|
|
|
15947
15947
|
// node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js
|
|
15948
15948
|
var ParseInputLazyPath = class {
|
|
15949
|
-
constructor(parent, value,
|
|
15949
|
+
constructor(parent, value, path10, key) {
|
|
15950
15950
|
this._cachedPath = [];
|
|
15951
15951
|
this.parent = parent;
|
|
15952
15952
|
this.data = value;
|
|
15953
|
-
this._path =
|
|
15953
|
+
this._path = path10;
|
|
15954
15954
|
this._key = key;
|
|
15955
15955
|
}
|
|
15956
15956
|
get path() {
|
|
@@ -19392,11 +19392,12 @@ var coerce = {
|
|
|
19392
19392
|
};
|
|
19393
19393
|
var NEVER = INVALID;
|
|
19394
19394
|
|
|
19395
|
-
// node_modules/.pnpm/@zhuixin+protocol@0.2.
|
|
19395
|
+
// node_modules/.pnpm/@zhuixin+protocol@0.2.6/node_modules/@zhuixin/protocol/dist/index.js
|
|
19396
19396
|
var PROTOCOL_VERSION = "1";
|
|
19397
19397
|
var engineCapabilities = [
|
|
19398
19398
|
"inspect:ipa",
|
|
19399
19399
|
"inspect:mac-app",
|
|
19400
|
+
"inspect:pe",
|
|
19400
19401
|
"extract:app",
|
|
19401
19402
|
"javascript:analyze",
|
|
19402
19403
|
"native:inspect",
|
|
@@ -19472,8 +19473,8 @@ var DEFINITIONS = [
|
|
|
19472
19473
|
id: "javascript:analyze",
|
|
19473
19474
|
label: "Analyze JS",
|
|
19474
19475
|
description: "\u5BF9\u89E3\u5305\u5E94\u7528\u4E2D\u7684 uni-app / WebView \u4E1A\u52A1\u811A\u672C\u505A\u9759\u6001\u5206\u6790\u3002",
|
|
19475
|
-
targetPlatforms: ["ios", "macos"],
|
|
19476
|
-
packageFormats: ["ipa", "app"],
|
|
19476
|
+
targetPlatforms: ["ios", "macos", "windows"],
|
|
19477
|
+
packageFormats: ["ipa", "app", "exe"],
|
|
19477
19478
|
requiresArtifact: true,
|
|
19478
19479
|
requiresRea: true
|
|
19479
19480
|
},
|
|
@@ -19515,13 +19516,12 @@ var DEFINITIONS = [
|
|
|
19515
19516
|
},
|
|
19516
19517
|
{
|
|
19517
19518
|
id: "inspect:pe",
|
|
19518
|
-
label: "Inspect
|
|
19519
|
-
description: "PE
|
|
19519
|
+
label: "Inspect Windows app",
|
|
19520
|
+
description: "Application directory layout, main executable PE headers, sections, imports, .NET clues, version resource, technology hints and Authenticode signature.",
|
|
19520
19521
|
targetPlatforms: ["windows"],
|
|
19521
19522
|
packageFormats: ["exe"],
|
|
19522
19523
|
requiresArtifact: true,
|
|
19523
|
-
|
|
19524
|
-
reason: "Windows Target Provider \u5F85 v1.1"
|
|
19524
|
+
builtIn: true
|
|
19525
19525
|
},
|
|
19526
19526
|
{
|
|
19527
19527
|
id: "inspect:msix",
|
|
@@ -19531,7 +19531,7 @@ var DEFINITIONS = [
|
|
|
19531
19531
|
packageFormats: ["msix", "appx"],
|
|
19532
19532
|
requiresArtifact: true,
|
|
19533
19533
|
available: false,
|
|
19534
|
-
reason: "
|
|
19534
|
+
reason: "Not implemented yet for Windows targets."
|
|
19535
19535
|
},
|
|
19536
19536
|
{
|
|
19537
19537
|
id: "inspect:msi",
|
|
@@ -19541,7 +19541,7 @@ var DEFINITIONS = [
|
|
|
19541
19541
|
packageFormats: ["msi"],
|
|
19542
19542
|
requiresArtifact: true,
|
|
19543
19543
|
available: false,
|
|
19544
|
-
reason: "
|
|
19544
|
+
reason: "Not implemented yet for Windows targets."
|
|
19545
19545
|
},
|
|
19546
19546
|
{
|
|
19547
19547
|
id: "signature:check",
|
|
@@ -19551,7 +19551,7 @@ var DEFINITIONS = [
|
|
|
19551
19551
|
packageFormats: ["exe", "msi", "msix", "appx"],
|
|
19552
19552
|
requiresArtifact: true,
|
|
19553
19553
|
available: false,
|
|
19554
|
-
reason: "Windows
|
|
19554
|
+
reason: "Not implemented yet for Windows targets. inspect:pe already reports the main executable's signature."
|
|
19555
19555
|
}
|
|
19556
19556
|
];
|
|
19557
19557
|
var CapabilityRegistry = class {
|
|
@@ -19688,8 +19688,8 @@ function defaultWorkspaceRoot(hostPlatform, env) {
|
|
|
19688
19688
|
|
|
19689
19689
|
// src/jobs/capability.ts
|
|
19690
19690
|
var import_node_crypto = require("crypto");
|
|
19691
|
-
var
|
|
19692
|
-
var
|
|
19691
|
+
var import_node_fs7 = __toESM(require("fs"));
|
|
19692
|
+
var import_node_path6 = __toESM(require("path"));
|
|
19693
19693
|
|
|
19694
19694
|
// src/errors.ts
|
|
19695
19695
|
var CoreFailure = class extends Error {
|
|
@@ -20074,6 +20074,872 @@ async function inspectMacApp(bundlePath) {
|
|
|
20074
20074
|
};
|
|
20075
20075
|
}
|
|
20076
20076
|
|
|
20077
|
+
// src/inspectors/windowsApp.ts
|
|
20078
|
+
var import_node_child_process2 = require("child_process");
|
|
20079
|
+
var import_node_fs6 = __toESM(require("fs"));
|
|
20080
|
+
var import_node_path4 = __toESM(require("path"));
|
|
20081
|
+
var import_node_util2 = require("util");
|
|
20082
|
+
|
|
20083
|
+
// src/inspectors/pe.ts
|
|
20084
|
+
var import_node_fs5 = __toESM(require("fs"));
|
|
20085
|
+
var MACHINES = {
|
|
20086
|
+
332: "x86",
|
|
20087
|
+
34404: "x64",
|
|
20088
|
+
448: "arm",
|
|
20089
|
+
452: "arm (Thumb-2)",
|
|
20090
|
+
43620: "arm64",
|
|
20091
|
+
42561: "arm64ec",
|
|
20092
|
+
42574: "arm64x",
|
|
20093
|
+
512: "ia64"
|
|
20094
|
+
};
|
|
20095
|
+
var SUBSYSTEMS = {
|
|
20096
|
+
1: "native",
|
|
20097
|
+
2: "windows-gui",
|
|
20098
|
+
3: "windows-console",
|
|
20099
|
+
5: "os2-console",
|
|
20100
|
+
7: "posix-console",
|
|
20101
|
+
9: "windows-ce-gui",
|
|
20102
|
+
10: "efi-application",
|
|
20103
|
+
11: "efi-boot-service-driver",
|
|
20104
|
+
12: "efi-runtime-driver",
|
|
20105
|
+
14: "xbox",
|
|
20106
|
+
16: "windows-boot-application"
|
|
20107
|
+
};
|
|
20108
|
+
var CERTIFICATE_TYPES = {
|
|
20109
|
+
1: "x509",
|
|
20110
|
+
2: "pkcs-signed-data",
|
|
20111
|
+
3: "reserved",
|
|
20112
|
+
4: "ts-stack-signed"
|
|
20113
|
+
};
|
|
20114
|
+
var MAX_SECTIONS = 96;
|
|
20115
|
+
var MAX_IMPORT_LIBRARIES = 256;
|
|
20116
|
+
var MAX_FUNCTIONS_PER_LIBRARY = 12;
|
|
20117
|
+
var MAX_THUNKS_PER_LIBRARY = 2e4;
|
|
20118
|
+
var ENTROPY_SAMPLE_BYTES = 4 * 1024 * 1024;
|
|
20119
|
+
var RESOURCE_MAX_DEPTH = 4;
|
|
20120
|
+
function fileReader(fd, size) {
|
|
20121
|
+
return {
|
|
20122
|
+
size,
|
|
20123
|
+
read(offset, length) {
|
|
20124
|
+
if (!Number.isSafeInteger(offset) || offset < 0 || length < 0) return void 0;
|
|
20125
|
+
if (offset + length > size) return void 0;
|
|
20126
|
+
const buffer = Buffer.alloc(length);
|
|
20127
|
+
let done = 0;
|
|
20128
|
+
while (done < length) {
|
|
20129
|
+
const count = import_node_fs5.default.readSync(fd, buffer, done, length - done, offset + done);
|
|
20130
|
+
if (count <= 0) return void 0;
|
|
20131
|
+
done += count;
|
|
20132
|
+
}
|
|
20133
|
+
return buffer;
|
|
20134
|
+
}
|
|
20135
|
+
};
|
|
20136
|
+
}
|
|
20137
|
+
function hex(value) {
|
|
20138
|
+
return `0x${value.toString(16)}`;
|
|
20139
|
+
}
|
|
20140
|
+
function cString(buffer, start = 0, limit = buffer.length) {
|
|
20141
|
+
let end = start;
|
|
20142
|
+
const max = Math.min(limit, buffer.length);
|
|
20143
|
+
while (end < max && buffer[end] !== 0) end += 1;
|
|
20144
|
+
return buffer.toString("latin1", start, end);
|
|
20145
|
+
}
|
|
20146
|
+
function entropyOf(buffer) {
|
|
20147
|
+
if (buffer.length === 0) return 0;
|
|
20148
|
+
const counts = new Array(256).fill(0);
|
|
20149
|
+
for (const byte of buffer) counts[byte] += 1;
|
|
20150
|
+
let entropy = 0;
|
|
20151
|
+
for (const count of counts) {
|
|
20152
|
+
if (count === 0) continue;
|
|
20153
|
+
const p = count / buffer.length;
|
|
20154
|
+
entropy -= p * Math.log2(p);
|
|
20155
|
+
}
|
|
20156
|
+
return Math.round(entropy * 100) / 100;
|
|
20157
|
+
}
|
|
20158
|
+
function isPeFile(filePath) {
|
|
20159
|
+
let fd;
|
|
20160
|
+
try {
|
|
20161
|
+
fd = import_node_fs5.default.openSync(filePath, "r");
|
|
20162
|
+
const head = Buffer.alloc(2);
|
|
20163
|
+
return import_node_fs5.default.readSync(fd, head, 0, 2, 0) === 2 && head[0] === 77 && head[1] === 90;
|
|
20164
|
+
} catch {
|
|
20165
|
+
return false;
|
|
20166
|
+
} finally {
|
|
20167
|
+
if (fd !== void 0) import_node_fs5.default.closeSync(fd);
|
|
20168
|
+
}
|
|
20169
|
+
}
|
|
20170
|
+
function inspectPeFile(filePath) {
|
|
20171
|
+
const fd = import_node_fs5.default.openSync(filePath, "r");
|
|
20172
|
+
try {
|
|
20173
|
+
const size = import_node_fs5.default.fstatSync(fd).size;
|
|
20174
|
+
return inspectPe(fileReader(fd, size));
|
|
20175
|
+
} finally {
|
|
20176
|
+
import_node_fs5.default.closeSync(fd);
|
|
20177
|
+
}
|
|
20178
|
+
}
|
|
20179
|
+
function inspectPe(reader) {
|
|
20180
|
+
const warnings = [];
|
|
20181
|
+
const dos = reader.read(0, 64);
|
|
20182
|
+
if (!dos || dos.readUInt16LE(0) !== 23117) {
|
|
20183
|
+
throw new Error("Not a PE file: the MZ signature is missing.");
|
|
20184
|
+
}
|
|
20185
|
+
const peOffset = dos.readUInt32LE(60);
|
|
20186
|
+
const signature = reader.read(peOffset, 4);
|
|
20187
|
+
if (!signature || signature.toString("latin1") !== "PE\0\0") {
|
|
20188
|
+
throw new Error("Not a PE file: the PE signature is missing.");
|
|
20189
|
+
}
|
|
20190
|
+
const coff = reader.read(peOffset + 4, 20);
|
|
20191
|
+
if (!coff) throw new Error("The COFF header is truncated.");
|
|
20192
|
+
const machine = coff.readUInt16LE(0);
|
|
20193
|
+
const sectionCount = coff.readUInt16LE(2);
|
|
20194
|
+
const timeDateStamp = coff.readUInt32LE(4);
|
|
20195
|
+
const optionalSize = coff.readUInt16LE(16);
|
|
20196
|
+
const characteristics = coff.readUInt16LE(18);
|
|
20197
|
+
const optionalOffset = peOffset + 24;
|
|
20198
|
+
const optional = reader.read(optionalOffset, Math.min(optionalSize, 240));
|
|
20199
|
+
if (!optional || optional.length < 96) {
|
|
20200
|
+
throw new Error("The optional header is truncated.");
|
|
20201
|
+
}
|
|
20202
|
+
const magic = optional.readUInt16LE(0);
|
|
20203
|
+
if (magic !== 267 && magic !== 523) {
|
|
20204
|
+
throw new Error(`Unsupported optional header magic ${hex(magic)}.`);
|
|
20205
|
+
}
|
|
20206
|
+
const plus = magic === 523;
|
|
20207
|
+
const directoriesOffset = plus ? 112 : 96;
|
|
20208
|
+
const entryPointRva = optional.readUInt32LE(16);
|
|
20209
|
+
const imageBase = plus ? optional.readBigUInt64LE(24) : BigInt(optional.readUInt32LE(28));
|
|
20210
|
+
const linker = `${optional[2]}.${optional[3]}`;
|
|
20211
|
+
const minimumOs = `${optional.readUInt16LE(40)}.${optional.readUInt16LE(42)}`;
|
|
20212
|
+
const subsystemId = optional.readUInt16LE(68);
|
|
20213
|
+
const dllCharacteristics = optional.readUInt16LE(70);
|
|
20214
|
+
const declaredDirectories = optional.length >= directoriesOffset ? optional.readUInt32LE(directoriesOffset - 4) : 0;
|
|
20215
|
+
const directory = (index) => {
|
|
20216
|
+
if (index >= declaredDirectories || index >= 16) return void 0;
|
|
20217
|
+
const at = directoriesOffset + index * 8;
|
|
20218
|
+
if (at + 8 > optional.length) return void 0;
|
|
20219
|
+
const rva = optional.readUInt32LE(at);
|
|
20220
|
+
const sizeValue = optional.readUInt32LE(at + 4);
|
|
20221
|
+
return rva === 0 && sizeValue === 0 ? void 0 : { rva, size: sizeValue };
|
|
20222
|
+
};
|
|
20223
|
+
const sections = [];
|
|
20224
|
+
const sectionTableOffset = optionalOffset + optionalSize;
|
|
20225
|
+
const boundedCount = Math.min(sectionCount, MAX_SECTIONS);
|
|
20226
|
+
if (sectionCount > MAX_SECTIONS) {
|
|
20227
|
+
warnings.push(`The file declares ${sectionCount} sections; only the first ${MAX_SECTIONS} were read.`);
|
|
20228
|
+
}
|
|
20229
|
+
for (let index = 0; index < boundedCount; index += 1) {
|
|
20230
|
+
const raw = reader.read(sectionTableOffset + index * 40, 40);
|
|
20231
|
+
if (!raw) {
|
|
20232
|
+
warnings.push("The section table is truncated.");
|
|
20233
|
+
break;
|
|
20234
|
+
}
|
|
20235
|
+
const sectionCharacteristics = raw.readUInt32LE(36);
|
|
20236
|
+
const rawSize = raw.readUInt32LE(16);
|
|
20237
|
+
const rawOffset = raw.readUInt32LE(20);
|
|
20238
|
+
const section = {
|
|
20239
|
+
name: cString(raw, 0, 8),
|
|
20240
|
+
virtualSize: raw.readUInt32LE(8),
|
|
20241
|
+
rawSize,
|
|
20242
|
+
rawOffset,
|
|
20243
|
+
characteristics: sectionCharacteristics,
|
|
20244
|
+
executable: (sectionCharacteristics & 536870912) !== 0,
|
|
20245
|
+
writable: (sectionCharacteristics & 2147483648) !== 0
|
|
20246
|
+
};
|
|
20247
|
+
if (rawSize > 0 && rawOffset > 0) {
|
|
20248
|
+
const sample = reader.read(rawOffset, Math.min(rawSize, ENTROPY_SAMPLE_BYTES));
|
|
20249
|
+
if (sample) section.entropy = entropyOf(sample);
|
|
20250
|
+
}
|
|
20251
|
+
sections.push(section);
|
|
20252
|
+
}
|
|
20253
|
+
const sectionRva = raws(sectionTableOffset, reader, boundedCount);
|
|
20254
|
+
const rvaToOffset = (rva) => {
|
|
20255
|
+
for (const entry of sectionRva) {
|
|
20256
|
+
const span = Math.max(entry.virtualSize, entry.rawSize);
|
|
20257
|
+
if (rva >= entry.rva && rva < entry.rva + span) {
|
|
20258
|
+
const offset = entry.rawOffset + (rva - entry.rva);
|
|
20259
|
+
return offset < reader.size ? offset : void 0;
|
|
20260
|
+
}
|
|
20261
|
+
}
|
|
20262
|
+
return rva < reader.size && sectionRva.every((entry) => rva < entry.rva) ? rva : void 0;
|
|
20263
|
+
};
|
|
20264
|
+
const readRva = (rva, length) => {
|
|
20265
|
+
const offset = rvaToOffset(rva);
|
|
20266
|
+
return offset === void 0 ? void 0 : reader.read(offset, length);
|
|
20267
|
+
};
|
|
20268
|
+
const stringAtRva = (rva, max = 256) => {
|
|
20269
|
+
const offset = rvaToOffset(rva);
|
|
20270
|
+
if (offset === void 0) return void 0;
|
|
20271
|
+
const chunk = reader.read(offset, Math.min(max, reader.size - offset));
|
|
20272
|
+
return chunk ? cString(chunk) : void 0;
|
|
20273
|
+
};
|
|
20274
|
+
const imports = { libraryCount: 0, libraries: [], truncated: false };
|
|
20275
|
+
const importDirectory = directory(1);
|
|
20276
|
+
if (importDirectory) {
|
|
20277
|
+
const thunkSize = plus ? 8 : 4;
|
|
20278
|
+
const collectThunks = (lookupRva, library) => {
|
|
20279
|
+
if (!lookupRva) return;
|
|
20280
|
+
for (let i = 0; i < MAX_THUNKS_PER_LIBRARY; i += 1) {
|
|
20281
|
+
const thunk = readRva(lookupRva + i * thunkSize, thunkSize);
|
|
20282
|
+
if (!thunk) break;
|
|
20283
|
+
const value = plus ? thunk.readBigUInt64LE(0) : BigInt(thunk.readUInt32LE(0));
|
|
20284
|
+
if (value === 0n) break;
|
|
20285
|
+
library.functionCount += 1;
|
|
20286
|
+
if (library.functions.length >= MAX_FUNCTIONS_PER_LIBRARY) continue;
|
|
20287
|
+
const ordinalFlag = plus ? 1n << 63n : 1n << 31n;
|
|
20288
|
+
if (value & ordinalFlag) {
|
|
20289
|
+
library.functions.push(`#${Number(value & 0xffffn)}`);
|
|
20290
|
+
} else {
|
|
20291
|
+
const name = stringAtRva(Number(value & 0x7fffffffn) + 2, 256);
|
|
20292
|
+
if (name) library.functions.push(name);
|
|
20293
|
+
}
|
|
20294
|
+
}
|
|
20295
|
+
};
|
|
20296
|
+
for (let index = 0; index < 4096; index += 1) {
|
|
20297
|
+
const descriptor = readRva(importDirectory.rva + index * 20, 20);
|
|
20298
|
+
if (!descriptor) {
|
|
20299
|
+
if (index === 0) warnings.push("The import table could not be read.");
|
|
20300
|
+
break;
|
|
20301
|
+
}
|
|
20302
|
+
const nameRva = descriptor.readUInt32LE(12);
|
|
20303
|
+
const lookupRva = descriptor.readUInt32LE(0) || descriptor.readUInt32LE(16);
|
|
20304
|
+
if (nameRva === 0 && lookupRva === 0) break;
|
|
20305
|
+
imports.libraryCount += 1;
|
|
20306
|
+
if (imports.libraries.length >= MAX_IMPORT_LIBRARIES) {
|
|
20307
|
+
imports.truncated = true;
|
|
20308
|
+
continue;
|
|
20309
|
+
}
|
|
20310
|
+
const library = {
|
|
20311
|
+
name: stringAtRva(nameRva, 260) ?? "?",
|
|
20312
|
+
functionCount: 0,
|
|
20313
|
+
functions: [],
|
|
20314
|
+
delayLoaded: false
|
|
20315
|
+
};
|
|
20316
|
+
collectThunks(lookupRva, library);
|
|
20317
|
+
imports.libraries.push(library);
|
|
20318
|
+
}
|
|
20319
|
+
}
|
|
20320
|
+
const delayDirectory = directory(13);
|
|
20321
|
+
if (delayDirectory) {
|
|
20322
|
+
for (let index = 0; index < 64; index += 1) {
|
|
20323
|
+
const descriptor = readRva(delayDirectory.rva + index * 32, 32);
|
|
20324
|
+
if (!descriptor) break;
|
|
20325
|
+
const attributes = descriptor.readUInt32LE(0);
|
|
20326
|
+
const nameField = descriptor.readUInt32LE(4);
|
|
20327
|
+
if (nameField === 0) break;
|
|
20328
|
+
const nameRva = attributes & 1 ? nameField : nameField - Number(imageBase & 0xffffffffn);
|
|
20329
|
+
const name = stringAtRva(nameRva, 260);
|
|
20330
|
+
if (!name) continue;
|
|
20331
|
+
imports.libraryCount += 1;
|
|
20332
|
+
if (imports.libraries.length < MAX_IMPORT_LIBRARIES) {
|
|
20333
|
+
imports.libraries.push({ name, functionCount: 0, functions: [], delayLoaded: true });
|
|
20334
|
+
} else {
|
|
20335
|
+
imports.truncated = true;
|
|
20336
|
+
}
|
|
20337
|
+
}
|
|
20338
|
+
}
|
|
20339
|
+
const exportsInfo = { count: 0, namedCount: 0 };
|
|
20340
|
+
const exportDirectory = directory(0);
|
|
20341
|
+
if (exportDirectory) {
|
|
20342
|
+
const table = readRva(exportDirectory.rva, 40);
|
|
20343
|
+
if (table) {
|
|
20344
|
+
exportsInfo.count = table.readUInt32LE(20);
|
|
20345
|
+
exportsInfo.namedCount = table.readUInt32LE(24);
|
|
20346
|
+
exportsInfo.dllName = stringAtRva(table.readUInt32LE(12), 260);
|
|
20347
|
+
}
|
|
20348
|
+
}
|
|
20349
|
+
let dotnet;
|
|
20350
|
+
const clrDirectory = directory(14);
|
|
20351
|
+
if (clrDirectory) {
|
|
20352
|
+
const header = readRva(clrDirectory.rva, 72);
|
|
20353
|
+
if (header) {
|
|
20354
|
+
const flags = header.readUInt32LE(16);
|
|
20355
|
+
dotnet = {
|
|
20356
|
+
runtimeVersion: `${header.readUInt16LE(4)}.${header.readUInt16LE(6)}`,
|
|
20357
|
+
ilOnly: (flags & 1) !== 0,
|
|
20358
|
+
requires32Bit: (flags & 2) !== 0,
|
|
20359
|
+
strongNameSigned: (flags & 8) !== 0,
|
|
20360
|
+
nativeEntryPoint: (flags & 16) !== 0
|
|
20361
|
+
};
|
|
20362
|
+
const metadataRva = header.readUInt32LE(8);
|
|
20363
|
+
const root = metadataRva ? readRva(metadataRva, 32) : void 0;
|
|
20364
|
+
if (root && root.readUInt32LE(0) === 1112167234) {
|
|
20365
|
+
const versionLength = root.readUInt32LE(12);
|
|
20366
|
+
if (versionLength > 0 && versionLength <= 255) {
|
|
20367
|
+
const version2 = readRva(metadataRva + 16, versionLength);
|
|
20368
|
+
if (version2) dotnet.metadataVersion = cString(version2);
|
|
20369
|
+
}
|
|
20370
|
+
}
|
|
20371
|
+
}
|
|
20372
|
+
}
|
|
20373
|
+
const version = readVersionInfo(directory(2), readRva, rvaToOffset, reader, warnings);
|
|
20374
|
+
let debug;
|
|
20375
|
+
const debugDirectory = directory(6);
|
|
20376
|
+
if (debugDirectory) {
|
|
20377
|
+
const entries = Math.min(Math.floor(debugDirectory.size / 28), 16);
|
|
20378
|
+
for (let index = 0; index < entries && !debug; index += 1) {
|
|
20379
|
+
const entry = readRva(debugDirectory.rva + index * 28, 28);
|
|
20380
|
+
if (!entry || entry.readUInt32LE(12) !== 2) continue;
|
|
20381
|
+
const dataSize = entry.readUInt32LE(16);
|
|
20382
|
+
const pointer = entry.readUInt32LE(24);
|
|
20383
|
+
const data = pointer && dataSize >= 24 && dataSize < 4096 ? reader.read(pointer, dataSize) : void 0;
|
|
20384
|
+
if (data && data.toString("latin1", 0, 4) === "RSDS") {
|
|
20385
|
+
debug = {
|
|
20386
|
+
guid: guidString(data.subarray(4, 20)),
|
|
20387
|
+
age: data.readUInt32LE(20),
|
|
20388
|
+
pdbPath: cString(data, 24)
|
|
20389
|
+
};
|
|
20390
|
+
}
|
|
20391
|
+
}
|
|
20392
|
+
}
|
|
20393
|
+
const certificate = { present: false, sizeBytes: 0 };
|
|
20394
|
+
const certificateEntry = directory(4);
|
|
20395
|
+
if (certificateEntry) {
|
|
20396
|
+
certificate.sizeBytes = certificateEntry.size;
|
|
20397
|
+
const header = reader.read(certificateEntry.rva, 8);
|
|
20398
|
+
if (header) {
|
|
20399
|
+
certificate.present = true;
|
|
20400
|
+
certificate.type = CERTIFICATE_TYPES[header.readUInt16LE(6)] ?? `type-${header.readUInt16LE(6)}`;
|
|
20401
|
+
} else {
|
|
20402
|
+
warnings.push("The certificate table points outside the file.");
|
|
20403
|
+
}
|
|
20404
|
+
}
|
|
20405
|
+
let endOfImage = 0;
|
|
20406
|
+
for (const section of sections) {
|
|
20407
|
+
endOfImage = Math.max(endOfImage, section.rawOffset + section.rawSize);
|
|
20408
|
+
}
|
|
20409
|
+
const certificateStart = certificate.present ? certificateEntry.rva : reader.size;
|
|
20410
|
+
const overlayEnd = Math.min(reader.size, certificateStart);
|
|
20411
|
+
const overlaySizeBytes = Math.max(0, overlayEnd - endOfImage);
|
|
20412
|
+
const mitigations = [];
|
|
20413
|
+
if (dllCharacteristics & 64) mitigations.push("ASLR");
|
|
20414
|
+
if (dllCharacteristics & 32) mitigations.push("high-entropy-ASLR");
|
|
20415
|
+
if (dllCharacteristics & 256) mitigations.push("DEP");
|
|
20416
|
+
if (dllCharacteristics & 16384) mitigations.push("CFG");
|
|
20417
|
+
if (dllCharacteristics & 1024) mitigations.push("no-SEH");
|
|
20418
|
+
if (dllCharacteristics & 4096) mitigations.push("app-container");
|
|
20419
|
+
const machineName = MACHINES[machine] ?? `machine-${hex(machine)}`;
|
|
20420
|
+
const reproducible = debugHasReproFlag(debugDirectory, readRva) || timeDateStamp === 0;
|
|
20421
|
+
const timestamp = timeDateStamp !== 0 && !reproducible ? new Date(timeDateStamp * 1e3).toISOString() : void 0;
|
|
20422
|
+
return {
|
|
20423
|
+
kind: "pe",
|
|
20424
|
+
sizeBytes: reader.size,
|
|
20425
|
+
format: plus ? "PE32+" : "PE32",
|
|
20426
|
+
architecture: machineName,
|
|
20427
|
+
isDll: (characteristics & 8192) !== 0,
|
|
20428
|
+
subsystem: SUBSYSTEMS[subsystemId] ?? `subsystem-${subsystemId}`,
|
|
20429
|
+
timestamp,
|
|
20430
|
+
timestampIsReproducible: reproducible,
|
|
20431
|
+
entryPointRva,
|
|
20432
|
+
imageBase: hex(imageBase),
|
|
20433
|
+
linkerVersion: linker,
|
|
20434
|
+
minimumOsVersion: minimumOs,
|
|
20435
|
+
mitigations,
|
|
20436
|
+
sections,
|
|
20437
|
+
imports,
|
|
20438
|
+
exports: exportsInfo,
|
|
20439
|
+
dotnet,
|
|
20440
|
+
version,
|
|
20441
|
+
debug,
|
|
20442
|
+
certificate,
|
|
20443
|
+
overlaySizeBytes,
|
|
20444
|
+
warnings
|
|
20445
|
+
};
|
|
20446
|
+
}
|
|
20447
|
+
function raws(tableOffset, reader, count) {
|
|
20448
|
+
const result = [];
|
|
20449
|
+
for (let index = 0; index < count; index += 1) {
|
|
20450
|
+
const raw = reader.read(tableOffset + index * 40, 40);
|
|
20451
|
+
if (!raw) break;
|
|
20452
|
+
result.push({
|
|
20453
|
+
virtualSize: raw.readUInt32LE(8),
|
|
20454
|
+
rva: raw.readUInt32LE(12),
|
|
20455
|
+
rawSize: raw.readUInt32LE(16),
|
|
20456
|
+
rawOffset: raw.readUInt32LE(20)
|
|
20457
|
+
});
|
|
20458
|
+
}
|
|
20459
|
+
return result;
|
|
20460
|
+
}
|
|
20461
|
+
function guidString(bytes) {
|
|
20462
|
+
const part = (start, end) => [...bytes.subarray(start, end)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
20463
|
+
const reverse = (start, end) => [...bytes.subarray(start, end)].reverse().map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
20464
|
+
return `${reverse(0, 4)}-${reverse(4, 6)}-${reverse(6, 8)}-${part(8, 10)}-${part(10, 16)}`;
|
|
20465
|
+
}
|
|
20466
|
+
function debugHasReproFlag(directory, readRva) {
|
|
20467
|
+
if (!directory) return false;
|
|
20468
|
+
const entries = Math.min(Math.floor(directory.size / 28), 16);
|
|
20469
|
+
for (let index = 0; index < entries; index += 1) {
|
|
20470
|
+
const entry = readRva(directory.rva + index * 28, 28);
|
|
20471
|
+
if (entry && entry.readUInt32LE(12) === 16) return true;
|
|
20472
|
+
}
|
|
20473
|
+
return false;
|
|
20474
|
+
}
|
|
20475
|
+
function readVersionInfo(resources, readRva, rvaToOffset, reader, warnings) {
|
|
20476
|
+
if (!resources) return void 0;
|
|
20477
|
+
const base = resources.rva;
|
|
20478
|
+
const readDirectory = (offset2) => {
|
|
20479
|
+
const header = readRva(base + offset2, 16);
|
|
20480
|
+
if (!header) return void 0;
|
|
20481
|
+
const named = header.readUInt16LE(12);
|
|
20482
|
+
const ids = header.readUInt16LE(14);
|
|
20483
|
+
const count = Math.min(named + ids, 512);
|
|
20484
|
+
const table = count ? readRva(base + offset2 + 16, count * 8) : Buffer.alloc(0);
|
|
20485
|
+
if (!table) return void 0;
|
|
20486
|
+
const entries = [];
|
|
20487
|
+
for (let index = 0; index < count; index += 1) {
|
|
20488
|
+
const nameOrId = table.readUInt32LE(index * 8);
|
|
20489
|
+
const target = table.readUInt32LE(index * 8 + 4);
|
|
20490
|
+
entries.push({
|
|
20491
|
+
id: nameOrId & 2147483647,
|
|
20492
|
+
named: (nameOrId & 2147483648) !== 0,
|
|
20493
|
+
isDirectory: (target & 2147483648) !== 0,
|
|
20494
|
+
target: target & 2147483647
|
|
20495
|
+
});
|
|
20496
|
+
}
|
|
20497
|
+
return entries;
|
|
20498
|
+
};
|
|
20499
|
+
const root = readDirectory(0);
|
|
20500
|
+
const typeEntry = root?.find((entry) => !entry.named && entry.id === 16 && entry.isDirectory);
|
|
20501
|
+
if (!typeEntry) return void 0;
|
|
20502
|
+
let level = readDirectory(typeEntry.target);
|
|
20503
|
+
let depth = 1;
|
|
20504
|
+
let leaf;
|
|
20505
|
+
while (level && level.length > 0 && depth <= RESOURCE_MAX_DEPTH) {
|
|
20506
|
+
const entry = level[0];
|
|
20507
|
+
if (!entry.isDirectory) {
|
|
20508
|
+
leaf = entry;
|
|
20509
|
+
break;
|
|
20510
|
+
}
|
|
20511
|
+
level = readDirectory(entry.target);
|
|
20512
|
+
depth += 1;
|
|
20513
|
+
}
|
|
20514
|
+
if (!leaf) return void 0;
|
|
20515
|
+
const dataEntry = readRva(base + leaf.target, 16);
|
|
20516
|
+
if (!dataEntry) return void 0;
|
|
20517
|
+
const dataRva = dataEntry.readUInt32LE(0);
|
|
20518
|
+
const dataSize = dataEntry.readUInt32LE(4);
|
|
20519
|
+
if (dataSize < 8 || dataSize > 256 * 1024) {
|
|
20520
|
+
warnings.push("The version resource has an unexpected size and was skipped.");
|
|
20521
|
+
return void 0;
|
|
20522
|
+
}
|
|
20523
|
+
const offset = rvaToOffset(dataRva);
|
|
20524
|
+
const data = offset === void 0 ? void 0 : reader.read(offset, dataSize);
|
|
20525
|
+
if (!data) return void 0;
|
|
20526
|
+
return parseVersionResource(data);
|
|
20527
|
+
}
|
|
20528
|
+
function align4(value) {
|
|
20529
|
+
return value + 3 & ~3;
|
|
20530
|
+
}
|
|
20531
|
+
function readVersionBlock(buffer, offset, limit) {
|
|
20532
|
+
if (offset + 6 > limit) return void 0;
|
|
20533
|
+
const length = buffer.readUInt16LE(offset);
|
|
20534
|
+
const valueLength = buffer.readUInt16LE(offset + 2);
|
|
20535
|
+
const type = buffer.readUInt16LE(offset + 4);
|
|
20536
|
+
if (length < 6 || offset + length > limit) return void 0;
|
|
20537
|
+
let cursor = offset + 6;
|
|
20538
|
+
let key = "";
|
|
20539
|
+
while (cursor + 2 <= offset + length) {
|
|
20540
|
+
const unit = buffer.readUInt16LE(cursor);
|
|
20541
|
+
cursor += 2;
|
|
20542
|
+
if (unit === 0) break;
|
|
20543
|
+
key += String.fromCharCode(unit);
|
|
20544
|
+
if (key.length > 64) return void 0;
|
|
20545
|
+
}
|
|
20546
|
+
const valueOffset = align4(cursor);
|
|
20547
|
+
const valueBytes = type === 1 ? valueLength * 2 : valueLength;
|
|
20548
|
+
return {
|
|
20549
|
+
key,
|
|
20550
|
+
valueOffset,
|
|
20551
|
+
valueLength,
|
|
20552
|
+
type,
|
|
20553
|
+
childrenOffset: align4(valueOffset + valueBytes),
|
|
20554
|
+
end: offset + length
|
|
20555
|
+
};
|
|
20556
|
+
}
|
|
20557
|
+
var VERSION_FIELDS = {
|
|
20558
|
+
CompanyName: "companyName",
|
|
20559
|
+
FileDescription: "fileDescription",
|
|
20560
|
+
FileVersion: "fileVersion",
|
|
20561
|
+
InternalName: "internalName",
|
|
20562
|
+
LegalCopyright: "legalCopyright",
|
|
20563
|
+
OriginalFilename: "originalFilename",
|
|
20564
|
+
ProductName: "productName",
|
|
20565
|
+
ProductVersion: "productVersion"
|
|
20566
|
+
};
|
|
20567
|
+
function parseVersionResource(data) {
|
|
20568
|
+
const root = readVersionBlock(data, 0, data.length);
|
|
20569
|
+
if (!root || root.key !== "VS_VERSION_INFO") return void 0;
|
|
20570
|
+
const result = {};
|
|
20571
|
+
let cursor = root.childrenOffset;
|
|
20572
|
+
while (cursor < root.end) {
|
|
20573
|
+
const group = readVersionBlock(data, cursor, root.end);
|
|
20574
|
+
if (!group) break;
|
|
20575
|
+
if (group.key === "StringFileInfo") {
|
|
20576
|
+
let tableCursor = group.childrenOffset;
|
|
20577
|
+
const table = readVersionBlock(data, tableCursor, group.end);
|
|
20578
|
+
if (table) {
|
|
20579
|
+
tableCursor = table.childrenOffset;
|
|
20580
|
+
while (tableCursor < table.end) {
|
|
20581
|
+
const entry = readVersionBlock(data, tableCursor, table.end);
|
|
20582
|
+
if (!entry) break;
|
|
20583
|
+
const field = VERSION_FIELDS[entry.key];
|
|
20584
|
+
if (field && entry.type === 1 && entry.valueLength > 0) {
|
|
20585
|
+
const raw = data.toString(
|
|
20586
|
+
"utf16le",
|
|
20587
|
+
entry.valueOffset,
|
|
20588
|
+
Math.min(entry.valueOffset + entry.valueLength * 2, entry.end)
|
|
20589
|
+
);
|
|
20590
|
+
const value = raw.replace(/\0+$/, "").trim();
|
|
20591
|
+
if (value) result[field] = value.slice(0, 256);
|
|
20592
|
+
}
|
|
20593
|
+
tableCursor = align4(entry.end);
|
|
20594
|
+
}
|
|
20595
|
+
}
|
|
20596
|
+
}
|
|
20597
|
+
cursor = align4(group.end);
|
|
20598
|
+
}
|
|
20599
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
20600
|
+
}
|
|
20601
|
+
|
|
20602
|
+
// src/inspectors/windowsApp.ts
|
|
20603
|
+
var execFileAsync2 = (0, import_node_util2.promisify)(import_node_child_process2.execFile);
|
|
20604
|
+
var WALK_ENTRY_LIMIT = 2e4;
|
|
20605
|
+
var WALK_DEPTH_LIMIT = 4;
|
|
20606
|
+
var HELPER_EXECUTABLE = /^(unins\d*|uninstall.*|update|squirrel|setup.*|install.*|vc_?redist.*|vcredist.*|dxsetup|crashpad_handler|crashreporter.*|crashsender.*|notification_helper|elevate|createdump|.*helper.*|.*updater.*)\.exe$/i;
|
|
20607
|
+
function normalizeName(value) {
|
|
20608
|
+
return value.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
20609
|
+
}
|
|
20610
|
+
function relative(root, target) {
|
|
20611
|
+
const value = import_node_path4.default.relative(root, target).split(import_node_path4.default.sep).join("/");
|
|
20612
|
+
return value === "" ? "." : value;
|
|
20613
|
+
}
|
|
20614
|
+
function walk(root) {
|
|
20615
|
+
const files = [];
|
|
20616
|
+
const directories = [];
|
|
20617
|
+
let seen = 0;
|
|
20618
|
+
let truncated = false;
|
|
20619
|
+
const visit = (dir, depth) => {
|
|
20620
|
+
let entries;
|
|
20621
|
+
try {
|
|
20622
|
+
entries = import_node_fs6.default.readdirSync(dir, { withFileTypes: true });
|
|
20623
|
+
} catch {
|
|
20624
|
+
return;
|
|
20625
|
+
}
|
|
20626
|
+
for (const entry of entries) {
|
|
20627
|
+
if (seen >= WALK_ENTRY_LIMIT) {
|
|
20628
|
+
truncated = true;
|
|
20629
|
+
return;
|
|
20630
|
+
}
|
|
20631
|
+
seen += 1;
|
|
20632
|
+
if (entry.isSymbolicLink()) continue;
|
|
20633
|
+
const full = import_node_path4.default.join(dir, entry.name);
|
|
20634
|
+
const rel = relative(root, full);
|
|
20635
|
+
if (entry.isDirectory()) {
|
|
20636
|
+
directories.push(rel.toLowerCase());
|
|
20637
|
+
if (depth < WALK_DEPTH_LIMIT) visit(full, depth + 1);
|
|
20638
|
+
} else if (entry.isFile()) {
|
|
20639
|
+
let size = 0;
|
|
20640
|
+
try {
|
|
20641
|
+
size = import_node_fs6.default.statSync(full).size;
|
|
20642
|
+
} catch {
|
|
20643
|
+
}
|
|
20644
|
+
files.push({ relative: rel, lower: rel.toLowerCase(), size });
|
|
20645
|
+
}
|
|
20646
|
+
}
|
|
20647
|
+
};
|
|
20648
|
+
visit(root, 0);
|
|
20649
|
+
return { files, directories, truncated };
|
|
20650
|
+
}
|
|
20651
|
+
function topLevelExecutables(dir) {
|
|
20652
|
+
try {
|
|
20653
|
+
return import_node_fs6.default.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isFile() && /\.exe$/i.test(entry.name)).map((entry) => entry.name);
|
|
20654
|
+
} catch {
|
|
20655
|
+
return [];
|
|
20656
|
+
}
|
|
20657
|
+
}
|
|
20658
|
+
function newestSquirrelVersion(dir) {
|
|
20659
|
+
try {
|
|
20660
|
+
const versions = import_node_fs6.default.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && /^app-\d/i.test(entry.name)).map((entry) => entry.name);
|
|
20661
|
+
versions.sort(
|
|
20662
|
+
(a, b) => a.localeCompare(b, void 0, { numeric: true, sensitivity: "base" })
|
|
20663
|
+
);
|
|
20664
|
+
return versions.length ? versions[versions.length - 1] : void 0;
|
|
20665
|
+
} catch {
|
|
20666
|
+
return void 0;
|
|
20667
|
+
}
|
|
20668
|
+
}
|
|
20669
|
+
function findWindowsElectronRoot(appPath) {
|
|
20670
|
+
let base = appPath;
|
|
20671
|
+
try {
|
|
20672
|
+
if (import_node_fs6.default.statSync(appPath).isFile()) base = import_node_path4.default.dirname(appPath);
|
|
20673
|
+
} catch {
|
|
20674
|
+
return void 0;
|
|
20675
|
+
}
|
|
20676
|
+
const roots = [base];
|
|
20677
|
+
const squirrel = newestSquirrelVersion(base);
|
|
20678
|
+
if (squirrel) roots.push(import_node_path4.default.join(base, squirrel));
|
|
20679
|
+
for (const root of roots) {
|
|
20680
|
+
const asar = import_node_path4.default.join(root, "resources", "app.asar");
|
|
20681
|
+
if (import_node_fs6.default.existsSync(asar)) return asar;
|
|
20682
|
+
const unpacked = import_node_path4.default.join(root, "resources", "app");
|
|
20683
|
+
if (import_node_fs6.default.existsSync(import_node_path4.default.join(unpacked, "package.json"))) return unpacked;
|
|
20684
|
+
}
|
|
20685
|
+
return void 0;
|
|
20686
|
+
}
|
|
20687
|
+
function rankExecutables(root, names, appName) {
|
|
20688
|
+
const wanted = normalizeName(appName);
|
|
20689
|
+
const scored = names.map((name) => {
|
|
20690
|
+
let size = 0;
|
|
20691
|
+
try {
|
|
20692
|
+
size = import_node_fs6.default.statSync(import_node_path4.default.join(root, name)).size;
|
|
20693
|
+
} catch {
|
|
20694
|
+
}
|
|
20695
|
+
let score = Math.log10(size + 1);
|
|
20696
|
+
if (HELPER_EXECUTABLE.test(name)) score -= 100;
|
|
20697
|
+
if (wanted && normalizeName(name.replace(/\.exe$/i, "")) === wanted) score += 50;
|
|
20698
|
+
return { name, score };
|
|
20699
|
+
});
|
|
20700
|
+
scored.sort((a, b) => b.score - a.score || a.name.localeCompare(b.name));
|
|
20701
|
+
return scored.map((item) => item.name);
|
|
20702
|
+
}
|
|
20703
|
+
var FILE_MARKERS = [
|
|
20704
|
+
{ hint: "CEF (Chromium Embedded Framework)", test: (f) => /(^|\/)libcef\.dll$/.test(f), basis: "libcef.dll" },
|
|
20705
|
+
{ hint: "WebView2", test: (f) => /(^|\/)webview2loader\.dll$/.test(f), basis: "WebView2Loader.dll" },
|
|
20706
|
+
{ hint: "Qt", test: (f) => /(^|\/)qt\d?core(d)?\.dll$/.test(f), basis: "QtCore library" },
|
|
20707
|
+
{ hint: "Flutter", test: (f) => /(^|\/)flutter_windows\.dll$/.test(f), basis: "flutter_windows.dll" },
|
|
20708
|
+
{ hint: "Unity", test: (f) => /(^|\/)unityplayer\.dll$/.test(f), basis: "UnityPlayer.dll" },
|
|
20709
|
+
{ hint: "NW.js", test: (f) => /(^|\/)nw(_elf)?\.dll$/.test(f), basis: "nw.dll" },
|
|
20710
|
+
{ hint: "Java", test: (f) => /(^|\/)jvm\.dll$/.test(f), basis: "bundled jvm.dll" },
|
|
20711
|
+
{ hint: "Python", test: (f) => /(^|\/)python3\d*\.dll$/.test(f), basis: "python3 runtime DLL" },
|
|
20712
|
+
{ hint: "PyInstaller", test: (f) => /(^|\/)base_library\.zip$/.test(f), basis: "base_library.zip" },
|
|
20713
|
+
{ hint: ".NET (modern runtime)", test: (f) => /(^|\/)(hostfxr|coreclr)\.dll$/.test(f) || /\.runtimeconfig\.json$/.test(f), basis: "hostfxr/coreclr or runtimeconfig.json" },
|
|
20714
|
+
{ hint: "WPF", test: (f) => /(^|\/)presentationframework\.dll$/.test(f), basis: "PresentationFramework.dll" },
|
|
20715
|
+
{ hint: "Windows Forms", test: (f) => /(^|\/)system\.windows\.forms\.dll$/.test(f), basis: "System.Windows.Forms.dll" },
|
|
20716
|
+
{ hint: "WinUI", test: (f) => /(^|\/)microsoft\.ui\.xaml\.dll$/.test(f), basis: "Microsoft.UI.Xaml.dll" },
|
|
20717
|
+
{ hint: "Squirrel installer", test: (f) => f === "update.exe", basis: "Update.exe" },
|
|
20718
|
+
{ hint: "Inno Setup uninstaller", test: (f) => /(^|\/)unins\d+\.exe$/.test(f), basis: "unins000.exe" },
|
|
20719
|
+
{ hint: "NSIS uninstaller", test: (f) => /(^|\/)uninstall[^/]*\.exe$/.test(f), basis: "Uninstall*.exe" }
|
|
20720
|
+
];
|
|
20721
|
+
var BINARY_MARKERS = [
|
|
20722
|
+
{ hint: "Go", needle: Buffer.from("Go buildinf:", "latin1"), basis: "Go build info in the executable" },
|
|
20723
|
+
{ hint: "Rust", needle: Buffer.from("/rustc/", "latin1"), basis: "rustc source paths in the executable" },
|
|
20724
|
+
{ hint: "Tauri", needle: Buffer.from("tauri-runtime", "latin1"), basis: "tauri-runtime crate paths in the executable" },
|
|
20725
|
+
{ hint: "Wails", needle: Buffer.from("wailsapp/wails", "latin1"), basis: "wails module path in the executable" },
|
|
20726
|
+
{ hint: "PyInstaller", needle: Buffer.from("PyInstaller", "latin1"), basis: "PyInstaller bootloader strings" },
|
|
20727
|
+
{ hint: "Electron", needle: Buffer.from("dL7pKGdnNz796PbbjQWNKmHXBZaB9tsX", "latin1"), basis: "Electron fuse sentinel in the executable" }
|
|
20728
|
+
];
|
|
20729
|
+
var BINARY_SCAN_LIMIT = 192 * 1024 * 1024;
|
|
20730
|
+
var BINARY_SCAN_CHUNK = 8 * 1024 * 1024;
|
|
20731
|
+
function scanBinaryMarkers(filePath) {
|
|
20732
|
+
const found = [];
|
|
20733
|
+
let fd;
|
|
20734
|
+
try {
|
|
20735
|
+
fd = import_node_fs6.default.openSync(filePath, "r");
|
|
20736
|
+
const size = import_node_fs6.default.fstatSync(fd).size;
|
|
20737
|
+
const overlap = Math.max(...BINARY_MARKERS.map((marker) => marker.needle.length));
|
|
20738
|
+
const buffer = Buffer.alloc(BINARY_SCAN_CHUNK + overlap);
|
|
20739
|
+
let position = 0;
|
|
20740
|
+
let carried = 0;
|
|
20741
|
+
while (position < Math.min(size, BINARY_SCAN_LIMIT)) {
|
|
20742
|
+
const read = import_node_fs6.default.readSync(fd, buffer, carried, BINARY_SCAN_CHUNK, position);
|
|
20743
|
+
if (read <= 0) break;
|
|
20744
|
+
const view = buffer.subarray(0, carried + read);
|
|
20745
|
+
for (const marker of BINARY_MARKERS) {
|
|
20746
|
+
if (!found.some((item) => item.hint === marker.hint) && view.includes(marker.needle)) {
|
|
20747
|
+
found.push({ hint: marker.hint, basis: marker.basis });
|
|
20748
|
+
}
|
|
20749
|
+
}
|
|
20750
|
+
position += read;
|
|
20751
|
+
carried = Math.min(overlap - 1, view.length);
|
|
20752
|
+
view.copy(buffer, 0, view.length - carried);
|
|
20753
|
+
}
|
|
20754
|
+
} catch {
|
|
20755
|
+
} finally {
|
|
20756
|
+
if (fd !== void 0) import_node_fs6.default.closeSync(fd);
|
|
20757
|
+
}
|
|
20758
|
+
return found;
|
|
20759
|
+
}
|
|
20760
|
+
function detectTechnology(files, directories, electron, pe, mainPath) {
|
|
20761
|
+
const evidence = [];
|
|
20762
|
+
const add = (hint, basis) => {
|
|
20763
|
+
if (!evidence.some((item) => item.hint === hint)) evidence.push({ hint, basis });
|
|
20764
|
+
};
|
|
20765
|
+
if (electron.asar) add("Electron", "resources/app.asar");
|
|
20766
|
+
else if (electron.unpackedApp) add("Electron", "resources/app");
|
|
20767
|
+
const lower = files.map((file) => file.lower);
|
|
20768
|
+
if (!electron.asar && !electron.unpackedApp && lower.includes("chrome_100_percent.pak") && lower.includes("resources.pak")) {
|
|
20769
|
+
add("Chromium-based", "chrome_100_percent.pak and resources.pak");
|
|
20770
|
+
}
|
|
20771
|
+
for (const marker of FILE_MARKERS) {
|
|
20772
|
+
if (lower.some(marker.test)) add(marker.hint, marker.basis);
|
|
20773
|
+
}
|
|
20774
|
+
if (directories.some((dir) => /(^|\/)[^/]+_data$/.test(dir)) && lower.some((f) => /(^|\/)unityplayer\.dll$/.test(f))) {
|
|
20775
|
+
add("Unity", "*_Data directory");
|
|
20776
|
+
}
|
|
20777
|
+
if (pe?.dotnet) {
|
|
20778
|
+
add(".NET", pe.dotnet.metadataVersion ? `CLR header (${pe.dotnet.metadataVersion})` : "CLR header");
|
|
20779
|
+
}
|
|
20780
|
+
if (pe?.imports.libraries.some((library) => /^mscoree\.dll$/i.test(library.name))) {
|
|
20781
|
+
add(".NET Framework", "imports mscoree.dll");
|
|
20782
|
+
}
|
|
20783
|
+
const libraries = pe?.imports.libraries.map((library) => library.name.toLowerCase()) ?? [];
|
|
20784
|
+
if (libraries.some((name) => /^mfc\d+/.test(name))) add("MFC", "imports mfc*.dll");
|
|
20785
|
+
if (libraries.some((name) => /^qt\d?(core|gui|widgets)/.test(name))) add("Qt", "imports Qt libraries");
|
|
20786
|
+
if (libraries.some((name) => /^webview2loader\.dll$/.test(name))) add("WebView2", "imports WebView2Loader.dll");
|
|
20787
|
+
if (mainPath) {
|
|
20788
|
+
for (const found of scanBinaryMarkers(mainPath)) add(found.hint, found.basis);
|
|
20789
|
+
}
|
|
20790
|
+
return { hints: evidence.map((item) => item.hint), evidence };
|
|
20791
|
+
}
|
|
20792
|
+
function powershellPath() {
|
|
20793
|
+
const root = process.env.SystemRoot || "C:\\Windows";
|
|
20794
|
+
return import_node_path4.default.join(root, "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
|
|
20795
|
+
}
|
|
20796
|
+
async function verifyAuthenticode(filePath, warnings) {
|
|
20797
|
+
if (process.platform !== "win32") {
|
|
20798
|
+
warnings.push("Authenticode verification requires a Windows host; only the embedded signature block was read.");
|
|
20799
|
+
return void 0;
|
|
20800
|
+
}
|
|
20801
|
+
const script = "$s = Get-AuthenticodeSignature -LiteralPath $env:APPTRACE_SIGNATURE_PATH; [pscustomobject]@{ Status = [string]$s.Status; Message = $s.StatusMessage; Subject = $s.SignerCertificate.Subject; Issuer = $s.SignerCertificate.Issuer; Thumbprint = $s.SignerCertificate.Thumbprint; NotAfter = if ($s.SignerCertificate) { $s.SignerCertificate.NotAfter.ToString('o') } else { $null }; Timestamper = $s.TimeStamperCertificate.Subject } | ConvertTo-Json -Compress";
|
|
20802
|
+
try {
|
|
20803
|
+
const { stdout } = await execFileAsync2(
|
|
20804
|
+
powershellPath(),
|
|
20805
|
+
["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", script],
|
|
20806
|
+
{
|
|
20807
|
+
timeout: 45e3,
|
|
20808
|
+
maxBuffer: 1024 * 1024,
|
|
20809
|
+
windowsHide: true,
|
|
20810
|
+
env: { ...process.env, APPTRACE_SIGNATURE_PATH: filePath }
|
|
20811
|
+
}
|
|
20812
|
+
);
|
|
20813
|
+
const parsed = JSON.parse(stdout.trim());
|
|
20814
|
+
const text2 = (value) => typeof value === "string" && value ? value : void 0;
|
|
20815
|
+
const status = text2(parsed.Status);
|
|
20816
|
+
return {
|
|
20817
|
+
verified: status === "Valid",
|
|
20818
|
+
status,
|
|
20819
|
+
signer: text2(parsed.Subject),
|
|
20820
|
+
issuer: text2(parsed.Issuer),
|
|
20821
|
+
thumbprint: text2(parsed.Thumbprint),
|
|
20822
|
+
notAfter: text2(parsed.NotAfter),
|
|
20823
|
+
timestamper: text2(parsed.Timestamper)
|
|
20824
|
+
};
|
|
20825
|
+
} catch (error) {
|
|
20826
|
+
warnings.push(
|
|
20827
|
+
`Authenticode verification did not complete: ${error instanceof Error ? error.message.split("\n")[0] : String(error)}`
|
|
20828
|
+
);
|
|
20829
|
+
return void 0;
|
|
20830
|
+
}
|
|
20831
|
+
}
|
|
20832
|
+
function packerWarnings(pe, warnings) {
|
|
20833
|
+
const packerNames = /^(upx\d|\.upx|\.aspack|\.adata|\.themida|\.vmp\d|\.petite|\.mpress|\.enigma\d|\.nsp\d|\.packed)/i;
|
|
20834
|
+
for (const section of pe.sections) {
|
|
20835
|
+
if (packerNames.test(section.name)) {
|
|
20836
|
+
warnings.push(`Section "${section.name}" is typical of a packer; static results may cover only the unpacker stub.`);
|
|
20837
|
+
return;
|
|
20838
|
+
}
|
|
20839
|
+
}
|
|
20840
|
+
const dense = pe.sections.filter((section) => section.executable && (section.entropy ?? 0) >= 7.4 && section.rawSize > 32 * 1024);
|
|
20841
|
+
if (dense.length > 0) {
|
|
20842
|
+
warnings.push(
|
|
20843
|
+
`Executable section "${dense[0].name}" has entropy ${dense[0].entropy}; the code may be packed or encrypted.`
|
|
20844
|
+
);
|
|
20845
|
+
}
|
|
20846
|
+
}
|
|
20847
|
+
async function inspectWindowsApp(appPath) {
|
|
20848
|
+
const warnings = [];
|
|
20849
|
+
const stat = import_node_fs6.default.statSync(appPath);
|
|
20850
|
+
let appRoot = stat.isDirectory() ? appPath : import_node_path4.default.dirname(appPath);
|
|
20851
|
+
const requestedExecutable = stat.isFile() ? import_node_path4.default.basename(appPath) : void 0;
|
|
20852
|
+
const appName = import_node_path4.default.basename(appRoot);
|
|
20853
|
+
let executables = requestedExecutable ? [requestedExecutable] : topLevelExecutables(appRoot);
|
|
20854
|
+
let ranked = rankExecutables(appRoot, executables, appName);
|
|
20855
|
+
const onlyHelpers = ranked.length > 0 && ranked.every((name) => HELPER_EXECUTABLE.test(name));
|
|
20856
|
+
if (!requestedExecutable && (ranked.length === 0 || onlyHelpers)) {
|
|
20857
|
+
const version2 = newestSquirrelVersion(appRoot);
|
|
20858
|
+
if (version2) {
|
|
20859
|
+
const nested = import_node_path4.default.join(appRoot, version2);
|
|
20860
|
+
const nestedRanked = rankExecutables(nested, topLevelExecutables(nested), appName);
|
|
20861
|
+
if (nestedRanked.length > 0) {
|
|
20862
|
+
warnings.push(`The program lives in "${version2}"; the newest versioned folder was inspected.`);
|
|
20863
|
+
appRoot = nested;
|
|
20864
|
+
executables = topLevelExecutables(nested);
|
|
20865
|
+
ranked = nestedRanked;
|
|
20866
|
+
}
|
|
20867
|
+
}
|
|
20868
|
+
}
|
|
20869
|
+
if (ranked.length === 0) {
|
|
20870
|
+
throw new Error("No Windows executable (.exe) was found at the top level of the application directory.");
|
|
20871
|
+
}
|
|
20872
|
+
const mainName = ranked[0];
|
|
20873
|
+
const mainPath = import_node_path4.default.join(appRoot, mainName);
|
|
20874
|
+
if (!isPeFile(mainPath)) {
|
|
20875
|
+
throw new Error(`"${mainName}" is not a Windows executable (the MZ signature is missing).`);
|
|
20876
|
+
}
|
|
20877
|
+
if (HELPER_EXECUTABLE.test(mainName)) {
|
|
20878
|
+
warnings.push(`Only helper-style executables were found; "${mainName}" was inspected but may not be the application itself.`);
|
|
20879
|
+
}
|
|
20880
|
+
if (ranked.length > 1) {
|
|
20881
|
+
const second = ranked[1];
|
|
20882
|
+
if (!HELPER_EXECUTABLE.test(second) && !requestedExecutable) {
|
|
20883
|
+
warnings.push(`Several executables could be the main program (${ranked.slice(0, 4).join(", ")}); "${mainName}" was chosen.`);
|
|
20884
|
+
}
|
|
20885
|
+
}
|
|
20886
|
+
const pe = inspectPeFile(mainPath);
|
|
20887
|
+
warnings.push(...pe.warnings);
|
|
20888
|
+
packerWarnings(pe, warnings);
|
|
20889
|
+
const walked = walk(appRoot);
|
|
20890
|
+
const asar = import_node_path4.default.join(appRoot, "resources", "app.asar");
|
|
20891
|
+
const unpacked = import_node_path4.default.join(appRoot, "resources", "app");
|
|
20892
|
+
let asarSize;
|
|
20893
|
+
try {
|
|
20894
|
+
asarSize = import_node_fs6.default.statSync(asar).size;
|
|
20895
|
+
} catch {
|
|
20896
|
+
}
|
|
20897
|
+
const electron = {
|
|
20898
|
+
asar: asarSize !== void 0,
|
|
20899
|
+
unpackedApp: import_node_fs6.default.existsSync(import_node_path4.default.join(unpacked, "package.json")),
|
|
20900
|
+
...asarSize !== void 0 ? { asarSizeBytes: asarSize } : {}
|
|
20901
|
+
};
|
|
20902
|
+
let topLevel = [];
|
|
20903
|
+
try {
|
|
20904
|
+
topLevel = import_node_fs6.default.readdirSync(appRoot).sort((a, b) => a.localeCompare(b)).slice(0, 200);
|
|
20905
|
+
} catch {
|
|
20906
|
+
}
|
|
20907
|
+
const signature = { embedded: pe.certificate.present };
|
|
20908
|
+
const verified = await verifyAuthenticode(mainPath, warnings);
|
|
20909
|
+
if (verified) Object.assign(signature, verified);
|
|
20910
|
+
if (!pe.certificate.present && (!verified || verified.status === "NotSigned")) {
|
|
20911
|
+
warnings.push("The main executable carries no Authenticode signature.");
|
|
20912
|
+
}
|
|
20913
|
+
const version = pe.version?.productVersion ?? pe.version?.fileVersion;
|
|
20914
|
+
return {
|
|
20915
|
+
kind: "windows-app",
|
|
20916
|
+
app: {
|
|
20917
|
+
path: appPath,
|
|
20918
|
+
name: pe.version?.productName ?? pe.version?.fileDescription ?? appName,
|
|
20919
|
+
version,
|
|
20920
|
+
publisher: pe.version?.companyName
|
|
20921
|
+
},
|
|
20922
|
+
layout: {
|
|
20923
|
+
root: relative(stat.isDirectory() ? appPath : import_node_path4.default.dirname(appPath), appRoot),
|
|
20924
|
+
topLevel,
|
|
20925
|
+
executableCount: walked.files.filter((file) => file.lower.endsWith(".exe")).length,
|
|
20926
|
+
dllCount: walked.files.filter((file) => file.lower.endsWith(".dll")).length,
|
|
20927
|
+
fileCount: walked.files.length,
|
|
20928
|
+
truncated: walked.truncated,
|
|
20929
|
+
electron
|
|
20930
|
+
},
|
|
20931
|
+
mainExecutable: {
|
|
20932
|
+
path: relative(stat.isDirectory() ? appPath : import_node_path4.default.dirname(appPath), mainPath),
|
|
20933
|
+
sizeBytes: pe.sizeBytes,
|
|
20934
|
+
candidates: ranked.slice(0, 8),
|
|
20935
|
+
pe
|
|
20936
|
+
},
|
|
20937
|
+
technology: detectTechnology(walked.files, walked.directories, electron, pe, mainPath),
|
|
20938
|
+
signature,
|
|
20939
|
+
warnings
|
|
20940
|
+
};
|
|
20941
|
+
}
|
|
20942
|
+
|
|
20077
20943
|
// src/inspectors/report.ts
|
|
20078
20944
|
function cell(value) {
|
|
20079
20945
|
return String(value ?? "\u2014").replace(/\|/g, "\\|").replace(/\r?\n/g, " ");
|
|
@@ -20128,6 +20994,42 @@ function renderMacAppInspection(lines, inspection, warnings) {
|
|
|
20128
20994
|
lines.push("");
|
|
20129
20995
|
}
|
|
20130
20996
|
}
|
|
20997
|
+
function renderWindowsAppInspection(lines, inspection, warnings) {
|
|
20998
|
+
const app = record(inspection.app) ?? {};
|
|
20999
|
+
const layout = record(inspection.layout) ?? {};
|
|
21000
|
+
const electron = record(layout.electron);
|
|
21001
|
+
const main = record(inspection.mainExecutable);
|
|
21002
|
+
const pe = record(main?.pe);
|
|
21003
|
+
const imports = record(pe?.imports);
|
|
21004
|
+
const dotnet = record(pe?.dotnet);
|
|
21005
|
+
const signature = record(inspection.signature);
|
|
21006
|
+
const technology = record(inspection.technology);
|
|
21007
|
+
const hints = Array.isArray(technology?.hints) ? technology.hints.map(String) : [];
|
|
21008
|
+
lines.push(
|
|
21009
|
+
"| Field | Value |",
|
|
21010
|
+
"| --- | --- |",
|
|
21011
|
+
`| Name | ${cell(app.name)} |`,
|
|
21012
|
+
`| Version | ${cell(app.version)} |`,
|
|
21013
|
+
`| Publisher | ${cell(app.publisher)} |`,
|
|
21014
|
+
`| Main executable | ${cell(main?.path)} |`,
|
|
21015
|
+
`| Architecture | ${cell(pe?.architecture)} (${cell(pe?.format)}) |`,
|
|
21016
|
+
`| Subsystem | ${cell(pe?.subsystem)} |`,
|
|
21017
|
+
`| Imported libraries | ${cell(imports?.libraryCount)} |`,
|
|
21018
|
+
`| .NET runtime | ${cell(dotnet ? dotnet.runtimeVersion ?? "managed" : void 0)} |`,
|
|
21019
|
+
`| Executables / DLLs | ${cell(layout.executableCount)} / ${cell(layout.dllCount)} |`,
|
|
21020
|
+
`| Electron bundle | ${cell(electron?.asar ? "app.asar" : electron?.unpackedApp ? "unpacked app" : "none")} |`,
|
|
21021
|
+
`| Technology hints | ${cell(hints.length > 0 ? hints.join(", ") : void 0)} |`,
|
|
21022
|
+
`| Signature embedded | ${cell(signature?.embedded)} |`,
|
|
21023
|
+
`| Signature verified | ${cell(signature?.verified ?? "not checked on this host")} |`,
|
|
21024
|
+
`| Signer | ${cell(signature?.signer)} |`,
|
|
21025
|
+
""
|
|
21026
|
+
);
|
|
21027
|
+
if (warnings.length > 0) {
|
|
21028
|
+
lines.push("### Inspection warnings", "");
|
|
21029
|
+
for (const warning of warnings) lines.push(`- ${warning}`);
|
|
21030
|
+
lines.push("");
|
|
21031
|
+
}
|
|
21032
|
+
}
|
|
20131
21033
|
function renderIpaInspection(lines, inspection, warnings) {
|
|
20132
21034
|
lines.push(
|
|
20133
21035
|
"| Field | Value |",
|
|
@@ -20219,6 +21121,8 @@ function renderReportMarkdown(input2) {
|
|
|
20219
21121
|
lines.push(`Inspection failed: ${cell(inspection.error)}`, "");
|
|
20220
21122
|
} else if (inspection.kind === "mac-app") {
|
|
20221
21123
|
renderMacAppInspection(lines, inspection, warnings);
|
|
21124
|
+
} else if (inspection.kind === "windows-app") {
|
|
21125
|
+
renderWindowsAppInspection(lines, inspection, warnings);
|
|
20222
21126
|
} else {
|
|
20223
21127
|
renderIpaInspection(lines, inspection, warnings);
|
|
20224
21128
|
}
|
|
@@ -20311,8 +21215,8 @@ function describeResult(result) {
|
|
|
20311
21215
|
}
|
|
20312
21216
|
|
|
20313
21217
|
// src/process/run.ts
|
|
20314
|
-
var
|
|
20315
|
-
var
|
|
21218
|
+
var import_node_child_process3 = require("child_process");
|
|
21219
|
+
var import_node_path5 = __toESM(require("path"));
|
|
20316
21220
|
function quoteWindowsArgument(value) {
|
|
20317
21221
|
if (value.length === 0) return '""';
|
|
20318
21222
|
if (!/[\s"&|<>^()%!]/.test(value)) return value;
|
|
@@ -20322,7 +21226,7 @@ function windowsCommandLine(command, args) {
|
|
|
20322
21226
|
return [command, ...args].map(quoteWindowsArgument).join(" ");
|
|
20323
21227
|
}
|
|
20324
21228
|
function windowsSpawnSpec(command, args) {
|
|
20325
|
-
const extension =
|
|
21229
|
+
const extension = import_node_path5.default.extname(command).toLowerCase();
|
|
20326
21230
|
if (extension !== ".cmd" && extension !== ".bat") {
|
|
20327
21231
|
return { command, args, verbatim: false };
|
|
20328
21232
|
}
|
|
@@ -20340,7 +21244,7 @@ function runCommand(command, args, options = {}) {
|
|
|
20340
21244
|
let child;
|
|
20341
21245
|
try {
|
|
20342
21246
|
const spec = process.platform === "win32" ? windowsSpawnSpec(command, args) : { command, args, verbatim: false };
|
|
20343
|
-
child = (0,
|
|
21247
|
+
child = (0, import_node_child_process3.spawn)(spec.command, spec.args, {
|
|
20344
21248
|
env: options.env ?? process.env,
|
|
20345
21249
|
cwd: options.cwd,
|
|
20346
21250
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -20464,7 +21368,7 @@ function uniqueStrings(values2, limit) {
|
|
|
20464
21368
|
function isNetworkEndpointValue(value) {
|
|
20465
21369
|
return /^(?:https?|wss?):\/\//i.test(value) || /^\/\//.test(value) || /^[a-z0-9.-]+\.[a-z]{2,}(?:[/:?#]|$)/i.test(value) || /^\/[a-z0-9._~!$&'()*+,;=:@%/-]+$/i.test(value);
|
|
20466
21370
|
}
|
|
20467
|
-
function
|
|
21371
|
+
function detectTechnology2(subjectName, modulePaths, assetPaths) {
|
|
20468
21372
|
const text2 = [subjectName, ...modulePaths, ...assetPaths].filter((value) => typeof value === "string").join("\n").toLowerCase();
|
|
20469
21373
|
const hints = [];
|
|
20470
21374
|
if (/__uni__|uni-app|uni-jsframework|hbuilder/.test(text2)) {
|
|
@@ -20516,7 +21420,7 @@ function javascriptFindings(job, artifact, evidenceId, parsed, summary) {
|
|
|
20516
21420
|
}
|
|
20517
21421
|
}
|
|
20518
21422
|
}
|
|
20519
|
-
if (artifact.packageFormat === "app") {
|
|
21423
|
+
if (artifact.packageFormat === "app" || artifact.packageFormat === "exe") {
|
|
20520
21424
|
push("technology", "Electron", {
|
|
20521
21425
|
group: "technology",
|
|
20522
21426
|
confidence: "high",
|
|
@@ -20613,10 +21517,10 @@ function javascriptFindings(job, artifact, evidenceId, parsed, summary) {
|
|
|
20613
21517
|
return findings;
|
|
20614
21518
|
}
|
|
20615
21519
|
function findElectronJavascriptRoot(appPath) {
|
|
20616
|
-
const asar =
|
|
20617
|
-
if (
|
|
20618
|
-
const unpacked =
|
|
20619
|
-
if (
|
|
21520
|
+
const asar = import_node_path6.default.join(appPath, "Contents", "Resources", "app.asar");
|
|
21521
|
+
if (import_node_fs7.default.existsSync(asar)) return asar;
|
|
21522
|
+
const unpacked = import_node_path6.default.join(appPath, "Contents", "Resources", "app");
|
|
21523
|
+
if (import_node_fs7.default.existsSync(unpacked) && import_node_fs7.default.statSync(unpacked).isDirectory()) {
|
|
20620
21524
|
return unpacked;
|
|
20621
21525
|
}
|
|
20622
21526
|
fail(
|
|
@@ -20712,7 +21616,7 @@ function summarizeRea(parsed) {
|
|
|
20712
21616
|
operation: parsed.operation,
|
|
20713
21617
|
technology: {
|
|
20714
21618
|
confidence: "static-hint",
|
|
20715
|
-
hints:
|
|
21619
|
+
hints: detectTechnology2(subject?.name, modulePaths, assetPaths)
|
|
20716
21620
|
},
|
|
20717
21621
|
subject: subject ? {
|
|
20718
21622
|
name: subject.name,
|
|
@@ -20885,6 +21789,9 @@ var CapabilityJobRunner = class {
|
|
|
20885
21789
|
case "inspect:mac-app":
|
|
20886
21790
|
await this.runInspectMacApp(job, target, artifact);
|
|
20887
21791
|
return;
|
|
21792
|
+
case "inspect:pe":
|
|
21793
|
+
await this.runInspectPe(job, target, artifact);
|
|
21794
|
+
return;
|
|
20888
21795
|
case "extract:app":
|
|
20889
21796
|
await this.runExtract(job, target, artifact);
|
|
20890
21797
|
return;
|
|
@@ -20975,10 +21882,36 @@ var CapabilityJobRunner = class {
|
|
|
20975
21882
|
warnings: inspection.warnings.length
|
|
20976
21883
|
});
|
|
20977
21884
|
}
|
|
21885
|
+
async runInspectPe(job, target, artifact) {
|
|
21886
|
+
if (!artifact) fail("ARTIFACT_REQUIRED", "Acquire an artifact first.");
|
|
21887
|
+
this.log(job, "info", "Inspecting the Windows application.");
|
|
21888
|
+
this.patch(job, { progress: 25 });
|
|
21889
|
+
const inspection = await inspectWindowsApp(
|
|
21890
|
+
this.workspace.resolveStoredPath(artifact.path)
|
|
21891
|
+
);
|
|
21892
|
+
const pe = inspection.mainExecutable?.pe;
|
|
21893
|
+
const updated = {
|
|
21894
|
+
...artifact,
|
|
21895
|
+
version: inspection.app.version ?? artifact.version,
|
|
21896
|
+
architecture: pe?.architecture ?? artifact.architecture,
|
|
21897
|
+
inspection
|
|
21898
|
+
};
|
|
21899
|
+
this.store.insertArtifact(updated);
|
|
21900
|
+
this.workspace.saveArtifactMetadata(target, updated);
|
|
21901
|
+
this.patch(job, {
|
|
21902
|
+
status: "analyzed",
|
|
21903
|
+
progress: 100,
|
|
21904
|
+
result: { inspection },
|
|
21905
|
+
finishedAt: now()
|
|
21906
|
+
});
|
|
21907
|
+
this.log(job, "info", "Windows application inspection complete.", {
|
|
21908
|
+
warnings: inspection.warnings.length
|
|
21909
|
+
});
|
|
21910
|
+
}
|
|
20978
21911
|
async runExtract(job, target, artifact) {
|
|
20979
21912
|
if (!artifact) fail("ARTIFACT_REQUIRED", "\u8BF7\u5148\u83B7\u53D6 artifact\u3002");
|
|
20980
21913
|
this.log(job, "info", "\u5F00\u59CB\u89E3\u5305 .app\u3002");
|
|
20981
|
-
const outputDir =
|
|
21914
|
+
const outputDir = import_node_path6.default.join(
|
|
20982
21915
|
this.workspace.analysisDir(target),
|
|
20983
21916
|
"extract-app",
|
|
20984
21917
|
job.id
|
|
@@ -20992,7 +21925,7 @@ var CapabilityJobRunner = class {
|
|
|
20992
21925
|
progress: 100,
|
|
20993
21926
|
result: {
|
|
20994
21927
|
...extracted,
|
|
20995
|
-
outputDir:
|
|
21928
|
+
outputDir: import_node_path6.default.relative(this.workspace.root, extracted.outputDir)
|
|
20996
21929
|
},
|
|
20997
21930
|
finishedAt: now()
|
|
20998
21931
|
});
|
|
@@ -21003,19 +21936,34 @@ var CapabilityJobRunner = class {
|
|
|
21003
21936
|
async runReaJavascript(job, target, artifact) {
|
|
21004
21937
|
if (!artifact) fail("ARTIFACT_REQUIRED", "\u8BF7\u5148\u83B7\u53D6 artifact\u3002");
|
|
21005
21938
|
let javascriptRoot;
|
|
21006
|
-
if (artifact.packageFormat === "
|
|
21939
|
+
if (artifact.packageFormat === "exe") {
|
|
21940
|
+
const found = findWindowsElectronRoot(
|
|
21941
|
+
this.workspace.resolveStoredPath(artifact.path)
|
|
21942
|
+
);
|
|
21943
|
+
if (!found) {
|
|
21944
|
+
fail(
|
|
21945
|
+
"CAPABILITY_UNAVAILABLE",
|
|
21946
|
+
"No Electron JavaScript bundle (resources/app.asar or resources/app) was found in this application."
|
|
21947
|
+
);
|
|
21948
|
+
}
|
|
21949
|
+
javascriptRoot = found;
|
|
21950
|
+
this.log(job, "info", "Running static analysis on the Windows JavaScript bundle.", {
|
|
21951
|
+
path: import_node_path6.default.relative(this.workspace.root, javascriptRoot)
|
|
21952
|
+
});
|
|
21953
|
+
this.patch(job, { progress: 25 });
|
|
21954
|
+
} else if (artifact.packageFormat === "app") {
|
|
21007
21955
|
javascriptRoot = findElectronJavascriptRoot(
|
|
21008
21956
|
this.workspace.resolveStoredPath(artifact.path)
|
|
21009
21957
|
);
|
|
21010
21958
|
this.log(job, "info", "Running static analysis on the macOS JavaScript bundle.", {
|
|
21011
|
-
path:
|
|
21959
|
+
path: import_node_path6.default.relative(this.workspace.root, javascriptRoot)
|
|
21012
21960
|
});
|
|
21013
21961
|
this.patch(job, { progress: 25 });
|
|
21014
21962
|
} else {
|
|
21015
21963
|
const extraction = await this.ensureExtraction(job, target, artifact);
|
|
21016
21964
|
javascriptRoot = extraction.appRoot;
|
|
21017
21965
|
this.log(job, "info", "\u5BF9\u89E3\u5305\u76EE\u5F55\u8FD0\u884C JavaScript \u9759\u6001\u5206\u6790\u3002", {
|
|
21018
|
-
path:
|
|
21966
|
+
path: import_node_path6.default.relative(this.workspace.root, javascriptRoot)
|
|
21019
21967
|
});
|
|
21020
21968
|
this.patch(job, { progress: 25 });
|
|
21021
21969
|
}
|
|
@@ -21027,7 +21975,11 @@ var CapabilityJobRunner = class {
|
|
|
21027
21975
|
"analyze-javascript-application",
|
|
21028
21976
|
javascriptRoot,
|
|
21029
21977
|
"--analysis-timeout-ms",
|
|
21030
|
-
"300000"
|
|
21978
|
+
"300000",
|
|
21979
|
+
// Signed Electron apps often carry unpacked native binaries whose
|
|
21980
|
+
// bytes no longer match the integrity recorded in app.asar. Keep
|
|
21981
|
+
// going and let REA mark those files untrusted rather than abort.
|
|
21982
|
+
...artifact.packageFormat === "ipa" ? [] : ["--integrity-policy", "record-and-continue"]
|
|
21031
21983
|
],
|
|
21032
21984
|
// Electron bundles are much larger than iOS web bundles; a 136 MB asar
|
|
21033
21985
|
// takes several minutes on its own.
|
|
@@ -21035,7 +21987,7 @@ var CapabilityJobRunner = class {
|
|
|
21035
21987
|
);
|
|
21036
21988
|
const evidenceId = this.reaEvidenceId(rea);
|
|
21037
21989
|
const summary = { ...rea.summary };
|
|
21038
|
-
if (artifact.packageFormat === "app") {
|
|
21990
|
+
if (artifact.packageFormat === "app" || artifact.packageFormat === "exe") {
|
|
21039
21991
|
const technology = record2(summary.technology) ?? {};
|
|
21040
21992
|
const hints = Array.isArray(technology.hints) ? [...technology.hints] : [];
|
|
21041
21993
|
if (!hints.includes("Electron")) hints.unshift("Electron");
|
|
@@ -21064,7 +22016,7 @@ var CapabilityJobRunner = class {
|
|
|
21064
22016
|
result: {
|
|
21065
22017
|
...summary,
|
|
21066
22018
|
evidenceId,
|
|
21067
|
-
outputDir:
|
|
22019
|
+
outputDir: import_node_path6.default.relative(this.workspace.root, javascriptRoot),
|
|
21068
22020
|
evidencePath: rea.rawPath
|
|
21069
22021
|
},
|
|
21070
22022
|
finishedAt: now()
|
|
@@ -21108,7 +22060,7 @@ var CapabilityJobRunner = class {
|
|
|
21108
22060
|
result: {
|
|
21109
22061
|
...rea.summary,
|
|
21110
22062
|
evidenceId,
|
|
21111
|
-
binaryPath:
|
|
22063
|
+
binaryPath: import_node_path6.default.relative(this.workspace.root, prepared.binaryPath),
|
|
21112
22064
|
evidencePath: rea.rawPath,
|
|
21113
22065
|
encryption,
|
|
21114
22066
|
warning: encryption.encryptedSlices > 0 ? "\u4E3B\u7A0B\u5E8F\u5305\u542B\u58F0\u660E\u52A0\u5BC6\u7684\u5207\u7247\uFF1B\u4E0D\u8981\u628A\u52A0\u5BC6\u533A\u53CD\u7F16\u8BD1\u7ED3\u679C\u5F53\u4F5C\u53EF\u4FE1\u539F\u59CB\u5B9E\u73B0\u3002" : void 0
|
|
@@ -21171,7 +22123,7 @@ var CapabilityJobRunner = class {
|
|
|
21171
22123
|
mode,
|
|
21172
22124
|
matchCount: rawMatches.length,
|
|
21173
22125
|
matches,
|
|
21174
|
-
binaryPath:
|
|
22126
|
+
binaryPath: import_node_path6.default.relative(this.workspace.root, prepared.binaryPath),
|
|
21175
22127
|
evidencePath: rea.rawPath,
|
|
21176
22128
|
limitations: Array.isArray(rea.parsed.limitations) ? rea.parsed.limitations : [],
|
|
21177
22129
|
encryption: encryptionState(prepared.inspection)
|
|
@@ -21216,7 +22168,7 @@ var CapabilityJobRunner = class {
|
|
|
21216
22168
|
result: {
|
|
21217
22169
|
...rea.summary,
|
|
21218
22170
|
functionName: input2.functionName,
|
|
21219
|
-
binaryPath:
|
|
22171
|
+
binaryPath: import_node_path6.default.relative(this.workspace.root, prepared.binaryPath),
|
|
21220
22172
|
evidencePath: rea.rawPath,
|
|
21221
22173
|
encryption
|
|
21222
22174
|
},
|
|
@@ -21231,7 +22183,7 @@ var CapabilityJobRunner = class {
|
|
|
21231
22183
|
this.log(job, "info", "\u6C47\u603B Target\u3001artifact \u548C\u4EFB\u52A1\u8BC1\u636E\u3002");
|
|
21232
22184
|
this.patch(job, { progress: 35 });
|
|
21233
22185
|
const generatedAt = now();
|
|
21234
|
-
const reportPath =
|
|
22186
|
+
const reportPath = import_node_path6.default.join(
|
|
21235
22187
|
this.workspace.targetDir(target),
|
|
21236
22188
|
"reports",
|
|
21237
22189
|
`${job.id}.md`
|
|
@@ -21268,7 +22220,7 @@ var CapabilityJobRunner = class {
|
|
|
21268
22220
|
generatedAt
|
|
21269
22221
|
});
|
|
21270
22222
|
this.workspace.writeText(reportPath, markdown);
|
|
21271
|
-
const relativePath =
|
|
22223
|
+
const relativePath = import_node_path6.default.relative(this.workspace.root, reportPath);
|
|
21272
22224
|
this.patch(job, {
|
|
21273
22225
|
status: "analyzed",
|
|
21274
22226
|
progress: 100,
|
|
@@ -21300,12 +22252,12 @@ var CapabilityJobRunner = class {
|
|
|
21300
22252
|
fail("MAIN_EXECUTABLE_NOT_FOUND", "IPA \u68C0\u67E5\u7ED3\u679C\u4E2D\u6CA1\u6709\u4E3B\u7A0B\u5E8F\u8DEF\u5F84\u3002");
|
|
21301
22253
|
}
|
|
21302
22254
|
const extraction = await this.ensureExtraction(job, target, current);
|
|
21303
|
-
const binaryPath =
|
|
21304
|
-
const root =
|
|
21305
|
-
if (binaryPath !== root && !binaryPath.startsWith(root +
|
|
22255
|
+
const binaryPath = import_node_path6.default.resolve(extraction.outputDir, executable);
|
|
22256
|
+
const root = import_node_path6.default.resolve(extraction.outputDir);
|
|
22257
|
+
if (binaryPath !== root && !binaryPath.startsWith(root + import_node_path6.default.sep)) {
|
|
21306
22258
|
fail("UNSAFE_ARTIFACT_PATH", "\u4E3B\u7A0B\u5E8F\u8DEF\u5F84\u8D8A\u51FA\u89E3\u5305\u76EE\u5F55\uFF0C\u5DF2\u62D2\u7EDD\u6267\u884C\u3002");
|
|
21307
22259
|
}
|
|
21308
|
-
if (!
|
|
22260
|
+
if (!import_node_fs7.default.existsSync(binaryPath)) {
|
|
21309
22261
|
fail("MAIN_EXECUTABLE_NOT_FOUND", `\u89E3\u5305\u76EE\u5F55\u4E2D\u6CA1\u6709\u4E3B\u7A0B\u5E8F\uFF1A${executable}`);
|
|
21310
22262
|
}
|
|
21311
22263
|
return { inspection, binaryPath };
|
|
@@ -21314,24 +22266,24 @@ var CapabilityJobRunner = class {
|
|
|
21314
22266
|
const previous = this.findExtraction(target.id, artifact.id);
|
|
21315
22267
|
if (previous) {
|
|
21316
22268
|
this.log(job, "info", "\u590D\u7528\u5DF2\u6709\u89E3\u5305\u76EE\u5F55\u3002", {
|
|
21317
|
-
path:
|
|
22269
|
+
path: import_node_path6.default.relative(this.workspace.root, previous.outputDir)
|
|
21318
22270
|
});
|
|
21319
22271
|
return previous;
|
|
21320
22272
|
}
|
|
21321
|
-
const outputDir =
|
|
22273
|
+
const outputDir = import_node_path6.default.join(
|
|
21322
22274
|
this.workspace.analysisDir(target),
|
|
21323
22275
|
"extracted",
|
|
21324
22276
|
artifact.id
|
|
21325
22277
|
);
|
|
21326
|
-
const existingApp =
|
|
22278
|
+
const existingApp = import_node_fs7.default.existsSync(outputDir) ? findAppPrefix(outputDir) : void 0;
|
|
21327
22279
|
if (existingApp) {
|
|
21328
22280
|
const location = {
|
|
21329
22281
|
outputDir,
|
|
21330
22282
|
prefix: existingApp,
|
|
21331
|
-
appRoot:
|
|
22283
|
+
appRoot: import_node_path6.default.join(outputDir, existingApp)
|
|
21332
22284
|
};
|
|
21333
22285
|
this.log(job, "info", "\u590D\u7528\u6309 artifact \u54C8\u5E0C\u5F52\u6863\u7684\u89E3\u5305\u76EE\u5F55\u3002", {
|
|
21334
|
-
path:
|
|
22286
|
+
path: import_node_path6.default.relative(this.workspace.root, outputDir)
|
|
21335
22287
|
});
|
|
21336
22288
|
return location;
|
|
21337
22289
|
}
|
|
@@ -21341,8 +22293,8 @@ var CapabilityJobRunner = class {
|
|
|
21341
22293
|
this.workspace.resolveStoredPath(artifact.path),
|
|
21342
22294
|
outputDir
|
|
21343
22295
|
);
|
|
21344
|
-
const appRoot =
|
|
21345
|
-
if (appRoot !== outputDir && !appRoot.startsWith(outputDir +
|
|
22296
|
+
const appRoot = import_node_path6.default.resolve(outputDir, extracted.prefix);
|
|
22297
|
+
if (appRoot !== outputDir && !appRoot.startsWith(outputDir + import_node_path6.default.sep)) {
|
|
21346
22298
|
fail("UNSAFE_ARTIFACT_PATH", "\u89E3\u5305\u524D\u7F00\u8D8A\u51FA\u8F93\u51FA\u76EE\u5F55\uFF0C\u5DF2\u62D2\u7EDD\u7EE7\u7EED\u3002");
|
|
21347
22299
|
}
|
|
21348
22300
|
return { outputDir, appRoot, prefix: extracted.prefix };
|
|
@@ -21357,8 +22309,8 @@ var CapabilityJobRunner = class {
|
|
|
21357
22309
|
if (typeof output !== "string" || typeof prefix !== "string") continue;
|
|
21358
22310
|
try {
|
|
21359
22311
|
const outputDir = this.workspace.resolveStoredPath(output);
|
|
21360
|
-
const appRoot =
|
|
21361
|
-
if (
|
|
22312
|
+
const appRoot = import_node_path6.default.resolve(outputDir, prefix);
|
|
22313
|
+
if (import_node_fs7.default.existsSync(appRoot) && (appRoot === outputDir || appRoot.startsWith(outputDir + import_node_path6.default.sep))) {
|
|
21362
22314
|
return { outputDir, appRoot, prefix };
|
|
21363
22315
|
}
|
|
21364
22316
|
} catch {
|
|
@@ -21367,11 +22319,11 @@ var CapabilityJobRunner = class {
|
|
|
21367
22319
|
return void 0;
|
|
21368
22320
|
}
|
|
21369
22321
|
async runReaJson(job, target, folder, args, timeoutMs) {
|
|
21370
|
-
const dir =
|
|
21371
|
-
const rawPath =
|
|
21372
|
-
const stderrPath =
|
|
21373
|
-
const commandPath =
|
|
21374
|
-
|
|
22322
|
+
const dir = import_node_path6.default.join(this.workspace.analysisDir(target), folder, job.id);
|
|
22323
|
+
const rawPath = import_node_path6.default.join(dir, "result.json");
|
|
22324
|
+
const stderrPath = import_node_path6.default.join(dir, "stderr.log");
|
|
22325
|
+
const commandPath = import_node_path6.default.join(dir, "command.json");
|
|
22326
|
+
import_node_fs7.default.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
21375
22327
|
this.workspace.writeJson(commandPath, {
|
|
21376
22328
|
command: this.config.reaPath,
|
|
21377
22329
|
args,
|
|
@@ -21390,7 +22342,7 @@ var CapabilityJobRunner = class {
|
|
|
21390
22342
|
});
|
|
21391
22343
|
this.workspace.writeText(rawPath, result.stdout);
|
|
21392
22344
|
this.workspace.writeText(stderrPath, result.stderr);
|
|
21393
|
-
this.workspace.writeJson(
|
|
22345
|
+
this.workspace.writeJson(import_node_path6.default.join(dir, "status.json"), {
|
|
21394
22346
|
code: result.code,
|
|
21395
22347
|
timedOut: result.timedOut,
|
|
21396
22348
|
finishedAt: now()
|
|
@@ -21404,7 +22356,7 @@ var CapabilityJobRunner = class {
|
|
|
21404
22356
|
if (result.code !== 0) {
|
|
21405
22357
|
const parsed2 = parseJsonDocument(result.stdout);
|
|
21406
22358
|
this.log(job, "warn", "\u5206\u6790\u5F15\u64CE\u672A\u6210\u529F\u5B8C\u6210\uFF0C\u539F\u59CB\u8F93\u51FA\u5DF2\u4FDD\u7559\u3002", {
|
|
21407
|
-
evidencePath:
|
|
22359
|
+
evidencePath: import_node_path6.default.relative(this.workspace.root, rawPath)
|
|
21408
22360
|
});
|
|
21409
22361
|
const message = reaErrorMessage(parsed2) ?? shortFailure(result.stderr, result.stdout);
|
|
21410
22362
|
fail("ENGINE_FAILED", message);
|
|
@@ -21418,7 +22370,7 @@ var CapabilityJobRunner = class {
|
|
|
21418
22370
|
}
|
|
21419
22371
|
return {
|
|
21420
22372
|
parsed,
|
|
21421
|
-
rawPath:
|
|
22373
|
+
rawPath: import_node_path6.default.relative(this.workspace.root, rawPath),
|
|
21422
22374
|
sha256: hashText(result.stdout),
|
|
21423
22375
|
summary: summarizeRea(parsed)
|
|
21424
22376
|
};
|
|
@@ -21466,10 +22418,10 @@ var CapabilityJobRunner = class {
|
|
|
21466
22418
|
}
|
|
21467
22419
|
};
|
|
21468
22420
|
function findAppPrefix(outputDir) {
|
|
21469
|
-
const payload =
|
|
21470
|
-
if (!
|
|
22421
|
+
const payload = import_node_path6.default.join(outputDir, "Payload");
|
|
22422
|
+
if (!import_node_fs7.default.existsSync(payload)) return void 0;
|
|
21471
22423
|
try {
|
|
21472
|
-
const app =
|
|
22424
|
+
const app = import_node_fs7.default.readdirSync(payload, { withFileTypes: true }).find((entry) => entry.isDirectory() && entry.name.endsWith(".app"));
|
|
21473
22425
|
return app ? `Payload/${app.name}/` : void 0;
|
|
21474
22426
|
} catch {
|
|
21475
22427
|
return void 0;
|
|
@@ -21477,11 +22429,12 @@ function findAppPrefix(outputDir) {
|
|
|
21477
22429
|
}
|
|
21478
22430
|
|
|
21479
22431
|
// src/license.ts
|
|
21480
|
-
var
|
|
22432
|
+
var import_node_fs8 = __toESM(require("fs"));
|
|
21481
22433
|
var import_node_crypto2 = __toESM(require("crypto"));
|
|
21482
22434
|
var FREE_FEATURES = /* @__PURE__ */ new Set([
|
|
21483
22435
|
"inspect:ipa",
|
|
21484
22436
|
"inspect:mac-app",
|
|
22437
|
+
"inspect:pe",
|
|
21485
22438
|
"extract:app",
|
|
21486
22439
|
"javascript:analyze",
|
|
21487
22440
|
"generate:report"
|
|
@@ -21497,7 +22450,7 @@ function verifyLicense(token) {
|
|
|
21497
22450
|
if (!encodedPayload || !encodedSignature) {
|
|
21498
22451
|
throw new Error("Invalid license token.");
|
|
21499
22452
|
}
|
|
21500
|
-
const publicKeyValue = process.env.APPTRACE_LICENSE_PUBLIC_KEY || (process.env.APPTRACE_LICENSE_PUBLIC_KEY_FILE ?
|
|
22453
|
+
const publicKeyValue = process.env.APPTRACE_LICENSE_PUBLIC_KEY || (process.env.APPTRACE_LICENSE_PUBLIC_KEY_FILE ? import_node_fs8.default.readFileSync(process.env.APPTRACE_LICENSE_PUBLIC_KEY_FILE, "utf8") : void 0);
|
|
21501
22454
|
if (!publicKeyValue) {
|
|
21502
22455
|
throw new Error("APPTRACE_LICENSE_PUBLIC_KEY is not configured.");
|
|
21503
22456
|
}
|
|
@@ -21554,8 +22507,8 @@ function compareVersionData(input2) {
|
|
|
21554
22507
|
}
|
|
21555
22508
|
|
|
21556
22509
|
// src/store.ts
|
|
21557
|
-
var
|
|
21558
|
-
var
|
|
22510
|
+
var import_node_fs9 = __toESM(require("fs"));
|
|
22511
|
+
var import_node_path7 = __toESM(require("path"));
|
|
21559
22512
|
var import_node_module = require("module");
|
|
21560
22513
|
var import_meta = {};
|
|
21561
22514
|
function openSqlite(databasePath) {
|
|
@@ -21808,7 +22761,7 @@ var SqliteStore = class {
|
|
|
21808
22761
|
kind = "sqlite";
|
|
21809
22762
|
db;
|
|
21810
22763
|
constructor(databasePath) {
|
|
21811
|
-
|
|
22764
|
+
import_node_fs9.default.mkdirSync(import_node_path7.default.dirname(databasePath), { recursive: true, mode: 448 });
|
|
21812
22765
|
const db = openSqlite(databasePath);
|
|
21813
22766
|
if (!db) throw new Error("node:sqlite is not available");
|
|
21814
22767
|
this.db = db;
|
|
@@ -22141,11 +23094,11 @@ var JsonStore = class {
|
|
|
22141
23094
|
data;
|
|
22142
23095
|
constructor(databasePath) {
|
|
22143
23096
|
this.filePath = databasePath.replace(/\.sqlite$/i, "") + ".json";
|
|
22144
|
-
|
|
23097
|
+
import_node_fs9.default.mkdirSync(import_node_path7.default.dirname(this.filePath), { recursive: true, mode: 448 });
|
|
22145
23098
|
this.data = this.load();
|
|
22146
23099
|
}
|
|
22147
23100
|
load() {
|
|
22148
|
-
if (!
|
|
23101
|
+
if (!import_node_fs9.default.existsSync(this.filePath)) {
|
|
22149
23102
|
return {
|
|
22150
23103
|
targets: [],
|
|
22151
23104
|
artifacts: [],
|
|
@@ -22157,7 +23110,7 @@ var JsonStore = class {
|
|
|
22157
23110
|
};
|
|
22158
23111
|
}
|
|
22159
23112
|
try {
|
|
22160
|
-
const parsed = JSON.parse(
|
|
23113
|
+
const parsed = JSON.parse(import_node_fs9.default.readFileSync(this.filePath, "utf8"));
|
|
22161
23114
|
return {
|
|
22162
23115
|
targets: parsed.targets ?? [],
|
|
22163
23116
|
artifacts: parsed.artifacts ?? [],
|
|
@@ -22181,8 +23134,8 @@ var JsonStore = class {
|
|
|
22181
23134
|
}
|
|
22182
23135
|
save() {
|
|
22183
23136
|
const temp = `${this.filePath}.${process.pid}.tmp`;
|
|
22184
|
-
|
|
22185
|
-
|
|
23137
|
+
import_node_fs9.default.writeFileSync(temp, JSON.stringify(this.data, null, 2), { mode: 384 });
|
|
23138
|
+
import_node_fs9.default.renameSync(temp, this.filePath);
|
|
22186
23139
|
}
|
|
22187
23140
|
listTargets() {
|
|
22188
23141
|
return [...this.data.targets].sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
@@ -22323,44 +23276,44 @@ function createStore(databasePath) {
|
|
|
22323
23276
|
}
|
|
22324
23277
|
|
|
22325
23278
|
// src/workspace.ts
|
|
22326
|
-
var
|
|
22327
|
-
var
|
|
23279
|
+
var import_node_fs10 = __toESM(require("fs"));
|
|
23280
|
+
var import_node_path8 = __toESM(require("path"));
|
|
22328
23281
|
var WorkspaceService = class {
|
|
22329
23282
|
root;
|
|
22330
23283
|
constructor(root) {
|
|
22331
|
-
this.root =
|
|
22332
|
-
|
|
22333
|
-
|
|
22334
|
-
|
|
23284
|
+
this.root = import_node_path8.default.resolve(root);
|
|
23285
|
+
import_node_fs10.default.mkdirSync(this.root, { recursive: true, mode: 448 });
|
|
23286
|
+
import_node_fs10.default.mkdirSync(import_node_path8.default.join(this.root, "targets"), { recursive: true, mode: 448 });
|
|
23287
|
+
import_node_fs10.default.mkdirSync(import_node_path8.default.join(this.root, "tmp"), { recursive: true, mode: 448 });
|
|
22335
23288
|
}
|
|
22336
23289
|
targetDir(target) {
|
|
22337
|
-
return
|
|
23290
|
+
return import_node_path8.default.join(
|
|
22338
23291
|
this.root,
|
|
22339
23292
|
"targets",
|
|
22340
23293
|
`${target.targetRef.platform}-${target.targetRef.sourceId}-${target.slug}-${target.id.slice(0, 8)}`
|
|
22341
23294
|
);
|
|
22342
23295
|
}
|
|
22343
23296
|
artifactDir(target) {
|
|
22344
|
-
return
|
|
23297
|
+
return import_node_path8.default.join(this.targetDir(target), "artifacts");
|
|
22345
23298
|
}
|
|
22346
23299
|
analysisDir(target) {
|
|
22347
|
-
return
|
|
23300
|
+
return import_node_path8.default.join(this.targetDir(target), "analysis");
|
|
22348
23301
|
}
|
|
22349
23302
|
ensureTarget(target) {
|
|
22350
23303
|
const dir = this.targetDir(target);
|
|
22351
23304
|
for (const subdir of ["", "artifacts", "analysis", "reports"]) {
|
|
22352
|
-
|
|
23305
|
+
import_node_fs10.default.mkdirSync(import_node_path8.default.join(dir, subdir), { recursive: true, mode: 448 });
|
|
22353
23306
|
}
|
|
22354
23307
|
}
|
|
22355
23308
|
saveResolvedTarget(target, rawSource) {
|
|
22356
23309
|
this.ensureTarget(target);
|
|
22357
23310
|
const dir = this.targetDir(target);
|
|
22358
|
-
this.writeJson(
|
|
22359
|
-
this.writeJson(
|
|
23311
|
+
this.writeJson(import_node_path8.default.join(dir, "meta.json"), target);
|
|
23312
|
+
this.writeJson(import_node_path8.default.join(dir, "source.json"), rawSource);
|
|
22360
23313
|
}
|
|
22361
23314
|
saveArtifactMetadata(target, artifact, extra = {}) {
|
|
22362
23315
|
const dir = this.targetDir(target);
|
|
22363
|
-
this.writeJson(
|
|
23316
|
+
this.writeJson(import_node_path8.default.join(dir, "artifact.json"), {
|
|
22364
23317
|
schemaVersion: 1,
|
|
22365
23318
|
target: {
|
|
22366
23319
|
id: target.id,
|
|
@@ -22384,7 +23337,7 @@ var WorkspaceService = class {
|
|
|
22384
23337
|
},
|
|
22385
23338
|
...extra
|
|
22386
23339
|
});
|
|
22387
|
-
this.writeJson(
|
|
23340
|
+
this.writeJson(import_node_path8.default.join(dir, "current.json"), {
|
|
22388
23341
|
artifactId: artifact.id,
|
|
22389
23342
|
path: artifact.path,
|
|
22390
23343
|
sha256: artifact.sha256,
|
|
@@ -22392,11 +23345,11 @@ var WorkspaceService = class {
|
|
|
22392
23345
|
});
|
|
22393
23346
|
}
|
|
22394
23347
|
artifactRelativePath(target, filePath) {
|
|
22395
|
-
return
|
|
23348
|
+
return import_node_path8.default.relative(this.root, filePath);
|
|
22396
23349
|
}
|
|
22397
23350
|
resolveStoredPath(relativePath) {
|
|
22398
|
-
const resolved =
|
|
22399
|
-
if (resolved !== this.root && !resolved.startsWith(this.root +
|
|
23351
|
+
const resolved = import_node_path8.default.resolve(this.root, relativePath);
|
|
23352
|
+
if (resolved !== this.root && !resolved.startsWith(this.root + import_node_path8.default.sep)) {
|
|
22400
23353
|
throw new Error(`Path escapes workspace: ${relativePath}`);
|
|
22401
23354
|
}
|
|
22402
23355
|
return resolved;
|
|
@@ -22406,10 +23359,10 @@ var WorkspaceService = class {
|
|
|
22406
23359
|
`);
|
|
22407
23360
|
}
|
|
22408
23361
|
writeText(filePath, value) {
|
|
22409
|
-
|
|
23362
|
+
import_node_fs10.default.mkdirSync(import_node_path8.default.dirname(filePath), { recursive: true, mode: 448 });
|
|
22410
23363
|
const temp = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
22411
|
-
|
|
22412
|
-
|
|
23364
|
+
import_node_fs10.default.writeFileSync(temp, value, { mode: 384 });
|
|
23365
|
+
import_node_fs10.default.renameSync(temp, filePath);
|
|
22413
23366
|
}
|
|
22414
23367
|
};
|
|
22415
23368
|
|
|
@@ -22448,6 +23401,8 @@ async function handle(line) {
|
|
|
22448
23401
|
ok: true,
|
|
22449
23402
|
result: { protocolVersion: PROTOCOL_VERSION, capabilities: [
|
|
22450
23403
|
"inspect:ipa",
|
|
23404
|
+
"inspect:mac-app",
|
|
23405
|
+
"inspect:pe",
|
|
22451
23406
|
"extract:app",
|
|
22452
23407
|
"javascript:analyze",
|
|
22453
23408
|
"native:inspect",
|
|
@@ -22475,12 +23430,12 @@ async function handle(line) {
|
|
|
22475
23430
|
});
|
|
22476
23431
|
return;
|
|
22477
23432
|
}
|
|
22478
|
-
const workspaceRoot =
|
|
22479
|
-
if (!
|
|
23433
|
+
const workspaceRoot = import_node_path9.default.resolve(request.workspacePath);
|
|
23434
|
+
if (!import_node_fs11.default.existsSync(workspaceRoot)) {
|
|
22480
23435
|
send({ id: request.id, ok: false, error: { code: "WORKSPACE_NOT_FOUND", message: workspaceRoot } });
|
|
22481
23436
|
return;
|
|
22482
23437
|
}
|
|
22483
|
-
const store = createStore(
|
|
23438
|
+
const store = createStore(import_node_path9.default.join(workspaceRoot, "workbench.sqlite"));
|
|
22484
23439
|
try {
|
|
22485
23440
|
if (request.capability === "compare:versions") {
|
|
22486
23441
|
send({
|