exoproc-accessors 0.1.0 → 0.6.2
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/LICENSE +21 -235
- package/dist/accessors.d.ts +186 -0
- package/dist/accessors.d.ts.map +1 -0
- package/dist/accessors.js +363 -0
- package/dist/accessors.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +725 -109
- package/dist/index.js.map +1 -1
- package/dist/indirect-nthread-host-accessor.d.ts +127 -0
- package/dist/indirect-nthread-host-accessor.d.ts.map +1 -0
- package/dist/indirect-nthread-host-accessor.js +288 -0
- package/dist/indirect-nthread-host-accessor.js.map +1 -0
- package/dist/middleware-accessor.d.ts +82 -14
- package/dist/middleware-accessor.d.ts.map +1 -1
- package/dist/middleware-accessor.js +336 -108
- package/dist/middleware-accessor.js.map +1 -1
- package/dist/stealth-java.d.ts +21 -0
- package/dist/stealth-java.d.ts.map +1 -0
- package/dist/stealth-java.js +119 -0
- package/dist/stealth-java.js.map +1 -0
- package/package.json +20 -10
package/dist/index.js
CHANGED
|
@@ -37,9 +37,9 @@ import {
|
|
|
37
37
|
isModuleLoadedInProcessSync,
|
|
38
38
|
MiddlewareAccessor,
|
|
39
39
|
InittableMiddlewareAccessor,
|
|
40
|
-
|
|
40
|
+
isInittableAccessor,
|
|
41
|
+
HostAccessor
|
|
41
42
|
} from "bun-xffi";
|
|
42
|
-
|
|
43
43
|
class MsvcrtDependentMiddlewareAccessor extends InittableMiddlewareAccessor {
|
|
44
44
|
async onInit() {
|
|
45
45
|
if (this.isLocal)
|
|
@@ -61,27 +61,6 @@ class MsvcrtDependentMiddlewareAccessor extends InittableMiddlewareAccessor {
|
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
class HostAccessor extends InittableMiddlewareAccessor {
|
|
65
|
-
get processId() {
|
|
66
|
-
return this._processId;
|
|
67
|
-
}
|
|
68
|
-
constructor(backend, root) {
|
|
69
|
-
super(backend, root ?? null);
|
|
70
|
-
if (!root) {
|
|
71
|
-
this.root = this;
|
|
72
|
-
}
|
|
73
|
-
let b = backend;
|
|
74
|
-
while (isMiddlewareAccessor(b)) {
|
|
75
|
-
b = b.backend;
|
|
76
|
-
}
|
|
77
|
-
if (b) {
|
|
78
|
-
this._processId = b.processId;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
async onInit() {}
|
|
82
|
-
onInitSync() {}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
64
|
class ThrowingHostAccessor extends HostAccessor {
|
|
86
65
|
constructor(processId, root) {
|
|
87
66
|
super(new ThrowingMemoryAccessor(processId), root);
|
|
@@ -92,7 +71,7 @@ class RedirectorHostAccessor extends HostAccessor {
|
|
|
92
71
|
_target;
|
|
93
72
|
constructor(processId, root) {
|
|
94
73
|
super(new ThrowingMemoryAccessor(processId), root);
|
|
95
|
-
this._target = new ThrowingHostAccessor(processId
|
|
74
|
+
this._target = new ThrowingHostAccessor(processId);
|
|
96
75
|
}
|
|
97
76
|
get target() {
|
|
98
77
|
return this._target;
|
|
@@ -238,9 +217,51 @@ class RedirectorHostAccessor extends HostAccessor {
|
|
|
238
217
|
}
|
|
239
218
|
}
|
|
240
219
|
async onInit() {}
|
|
220
|
+
async onDeinit() {
|
|
221
|
+
const t = this.getTarget();
|
|
222
|
+
if (t !== this && t !== this.root && isInittableAccessor(t)) {
|
|
223
|
+
await t.deinit();
|
|
224
|
+
}
|
|
225
|
+
}
|
|
241
226
|
onInitSync() {}
|
|
242
227
|
}
|
|
243
228
|
|
|
229
|
+
class RacingHostAccessor extends RedirectorHostAccessor {
|
|
230
|
+
racers = [];
|
|
231
|
+
registerChild(child) {
|
|
232
|
+
if (!this.racers || !isInittableAccessor(child))
|
|
233
|
+
return;
|
|
234
|
+
this.racers.push(child);
|
|
235
|
+
}
|
|
236
|
+
startRacer(racer) {
|
|
237
|
+
return racer.init();
|
|
238
|
+
}
|
|
239
|
+
async race() {
|
|
240
|
+
if (this.racers.length === 0) {
|
|
241
|
+
throw new Error("RacingHostAccessor: no racers registered to race");
|
|
242
|
+
}
|
|
243
|
+
const attempts = this.racers.map((racer) => ({
|
|
244
|
+
racer,
|
|
245
|
+
settled: this.startRacer(racer)
|
|
246
|
+
}));
|
|
247
|
+
let winner;
|
|
248
|
+
try {
|
|
249
|
+
winner = await Promise.any(attempts.map((a) => a.settled.then(() => a.racer)));
|
|
250
|
+
} catch (err) {
|
|
251
|
+
throw new Error(`RacingHostAccessor: none of ${attempts.length} racer(s) could be initialized`, { cause: err });
|
|
252
|
+
}
|
|
253
|
+
for (const a of attempts) {
|
|
254
|
+
if (a.racer === winner)
|
|
255
|
+
continue;
|
|
256
|
+
this.releaseLoser(a.racer, a.settled);
|
|
257
|
+
}
|
|
258
|
+
return winner;
|
|
259
|
+
}
|
|
260
|
+
releaseLoser(racer, settled) {
|
|
261
|
+
settled.then(() => racer.deinit().catch(() => {})).catch(() => {});
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
244
265
|
class BootstrapHostAccessor extends RedirectorHostAccessor {
|
|
245
266
|
constructor(processId, root) {
|
|
246
267
|
super(processId, root);
|
|
@@ -258,7 +279,7 @@ class BootstrapHostAccessor extends RedirectorHostAccessor {
|
|
|
258
279
|
|
|
259
280
|
class CallRedirectorAccessor extends MiddlewareAccessor {
|
|
260
281
|
async alloc(size, address = null, protection = MemoryProtection.READWRITE, allocationType = MemoryState.COMMIT | MemoryState.RESERVE) {
|
|
261
|
-
const addressVal = address ? resolveAddress(address) :
|
|
282
|
+
const addressVal = address ? resolveAddress(address) : 0;
|
|
262
283
|
const result = await this.root.call(Kernel32Impl.VirtualAlloc, addressVal, size, allocationType, protection);
|
|
263
284
|
if (!result || Number(result) === 0) {
|
|
264
285
|
throw new Error(`VirtualAlloc failed in remote process for size ${size}`);
|
|
@@ -266,7 +287,7 @@ class CallRedirectorAccessor extends MiddlewareAccessor {
|
|
|
266
287
|
return result;
|
|
267
288
|
}
|
|
268
289
|
allocSync(size, address = null, protection = MemoryProtection.READWRITE, allocationType = MemoryState.COMMIT | MemoryState.RESERVE) {
|
|
269
|
-
const addressVal = address ? resolveAddress(address) :
|
|
290
|
+
const addressVal = address ? resolveAddress(address) : 0;
|
|
270
291
|
const result = this.root.callSync(Kernel32Impl.VirtualAlloc, addressVal, size, allocationType, protection);
|
|
271
292
|
if (!result || Number(result) === 0) {
|
|
272
293
|
throw new Error(`VirtualAlloc failed in remote process for size ${size}`);
|
|
@@ -456,27 +477,56 @@ class IndirectCallRedirectorAccessor extends CallRedirectorAccessor {
|
|
|
456
477
|
});
|
|
457
478
|
return info;
|
|
458
479
|
}
|
|
480
|
+
hasMsvcrt = null;
|
|
481
|
+
async checkMsvcrt() {
|
|
482
|
+
if (this.hasMsvcrt !== null)
|
|
483
|
+
return this.hasMsvcrt;
|
|
484
|
+
const msvcrtBase = MsvcrtLibrary.baseAddress;
|
|
485
|
+
if (msvcrtBase.isNull() || Number(msvcrtBase) === 0) {
|
|
486
|
+
this.hasMsvcrt = false;
|
|
487
|
+
return false;
|
|
488
|
+
}
|
|
489
|
+
this.hasMsvcrt = await isModuleLoadedInProcess(this.root, msvcrtBase);
|
|
490
|
+
return this.hasMsvcrt;
|
|
491
|
+
}
|
|
492
|
+
checkMsvcrtSync() {
|
|
493
|
+
if (this.hasMsvcrt !== null)
|
|
494
|
+
return this.hasMsvcrt;
|
|
495
|
+
const msvcrtBase = MsvcrtLibrary.baseAddress;
|
|
496
|
+
if (msvcrtBase.isNull() || Number(msvcrtBase) === 0) {
|
|
497
|
+
this.hasMsvcrt = false;
|
|
498
|
+
return false;
|
|
499
|
+
}
|
|
500
|
+
this.hasMsvcrt = isModuleLoadedInProcessSync(this.root, msvcrtBase);
|
|
501
|
+
return this.hasMsvcrt;
|
|
502
|
+
}
|
|
459
503
|
async alloc(size, address = null, protection = MemoryProtection.READWRITE, allocationType = MemoryState.COMMIT | MemoryState.RESERVE) {
|
|
460
504
|
if (protection === MemoryProtection.READWRITE && (address === null || address === undefined)) {
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
505
|
+
try {
|
|
506
|
+
if (await this.checkMsvcrt()) {
|
|
507
|
+
const result = await this.root.call(MsvcrtImpl.malloc, size);
|
|
508
|
+
if (result && Number(result) !== 0) {
|
|
509
|
+
const allocatedAddr = Number(result);
|
|
510
|
+
this.mallocs.set(allocatedAddr, size);
|
|
511
|
+
return allocatedAddr;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
} catch {}
|
|
468
515
|
}
|
|
469
516
|
return super.alloc(size, address, protection, allocationType);
|
|
470
517
|
}
|
|
471
518
|
allocSync(size, address = null, protection = MemoryProtection.READWRITE, allocationType = MemoryState.COMMIT | MemoryState.RESERVE) {
|
|
472
519
|
if (protection === MemoryProtection.READWRITE && (address === null || address === undefined)) {
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
520
|
+
try {
|
|
521
|
+
if (this.checkMsvcrtSync()) {
|
|
522
|
+
const result = this.root.callSync(MsvcrtImpl.malloc, size);
|
|
523
|
+
if (result && Number(result) !== 0) {
|
|
524
|
+
const allocatedAddr = Number(result);
|
|
525
|
+
this.mallocs.set(allocatedAddr, size);
|
|
526
|
+
return allocatedAddr;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
} catch {}
|
|
480
530
|
}
|
|
481
531
|
return super.allocSync(size, address, protection, allocationType);
|
|
482
532
|
}
|
|
@@ -539,14 +589,35 @@ class IndirectCallRedirectorAccessor extends CallRedirectorAccessor {
|
|
|
539
589
|
}
|
|
540
590
|
|
|
541
591
|
class MarshallingCallableAccessor extends MiddlewareAccessor {
|
|
542
|
-
static
|
|
592
|
+
static marshalInfo(funcName, type, val) {
|
|
543
593
|
const normType = normalizeType(type);
|
|
544
594
|
if (normType === "cstring" && typeof val === "string") {
|
|
545
|
-
return Buffer.from(val + "\x00", "utf8");
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
595
|
+
return { buf: Buffer.from(val + "\x00", "utf8") };
|
|
596
|
+
}
|
|
597
|
+
if (normType === "cwstring" && typeof val === "string") {
|
|
598
|
+
return { buf: Buffer.from(val + "\x00", "utf16le") };
|
|
599
|
+
}
|
|
600
|
+
if (typeof val === "string" && (normType === "ptr" || normType === "i64" || normType === "u64" || normType === "usize" || normType === "size_t")) {
|
|
601
|
+
const trimmed = val.trim();
|
|
602
|
+
if (!/^-?0x[0-9a-fA-F]+$/.test(trimmed)) {
|
|
603
|
+
const isAnsi = funcName ? funcName.endsWith("A") : false;
|
|
604
|
+
return {
|
|
605
|
+
buf: Buffer.from(val + "\x00", isAnsi ? "utf8" : "utf16le")
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
if (val && typeof val === "object") {
|
|
610
|
+
if (Buffer.isBuffer(val)) {
|
|
611
|
+
return { buf: val, writeBackTarget: val };
|
|
612
|
+
}
|
|
613
|
+
if (Buffer.isBuffer(val._backingBuffer)) {
|
|
614
|
+
const backing = val._backingBuffer;
|
|
615
|
+
return { buf: backing, writeBackTarget: backing };
|
|
616
|
+
}
|
|
617
|
+
if (ArrayBuffer.isView(val)) {
|
|
618
|
+
const viewBuf = Buffer.from(val.buffer, val.byteOffset, val.byteLength);
|
|
619
|
+
return { buf: viewBuf, writeBackTarget: viewBuf };
|
|
620
|
+
}
|
|
550
621
|
}
|
|
551
622
|
return null;
|
|
552
623
|
}
|
|
@@ -555,16 +626,39 @@ class MarshallingCallableAccessor extends MiddlewareAccessor {
|
|
|
555
626
|
const tempAllocations = [];
|
|
556
627
|
try {
|
|
557
628
|
const signatureArgs = func.args || [];
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
629
|
+
const totalArgs = Math.max(signatureArgs.length, args.length);
|
|
630
|
+
for (let i = 0;i < totalArgs; i++) {
|
|
631
|
+
const info = MarshallingCallableAccessor.marshalInfo(func.name, signatureArgs[i] || "", args[i]);
|
|
632
|
+
if (!info) {
|
|
633
|
+
if (typeof args[i] === "string" && /^-?0x[0-9a-fA-F]+$/.test(args[i].trim())) {
|
|
634
|
+
modifiedArgs[i] = Number(BigInt(args[i].trim()));
|
|
635
|
+
}
|
|
561
636
|
continue;
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
637
|
+
}
|
|
638
|
+
const allocLen = Math.max(info.buf.length, 1);
|
|
639
|
+
const remoteAddr = Number(await this.root.alloc(allocLen));
|
|
640
|
+
if (info.buf.length > 0) {
|
|
641
|
+
await this.root.write(remoteAddr, info.buf);
|
|
642
|
+
}
|
|
643
|
+
tempAllocations.push({
|
|
644
|
+
address: remoteAddr,
|
|
645
|
+
size: allocLen,
|
|
646
|
+
writeBackTarget: info.writeBackTarget
|
|
647
|
+
});
|
|
565
648
|
modifiedArgs[i] = remoteAddr;
|
|
566
649
|
}
|
|
567
|
-
|
|
650
|
+
const result = await this.backend.call(func, ...modifiedArgs);
|
|
651
|
+
for (const alloc of tempAllocations) {
|
|
652
|
+
if (alloc.writeBackTarget && alloc.writeBackTarget.length > 0) {
|
|
653
|
+
try {
|
|
654
|
+
const remoteBytes = await this.root.read(alloc.address, alloc.writeBackTarget.length);
|
|
655
|
+
if (remoteBytes && remoteBytes.length > 0) {
|
|
656
|
+
remoteBytes.copy(alloc.writeBackTarget, 0, 0, Math.min(remoteBytes.length, alloc.writeBackTarget.length));
|
|
657
|
+
}
|
|
658
|
+
} catch {}
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
return result;
|
|
568
662
|
} finally {
|
|
569
663
|
for (const alloc of tempAllocations) {
|
|
570
664
|
try {
|
|
@@ -578,16 +672,39 @@ class MarshallingCallableAccessor extends MiddlewareAccessor {
|
|
|
578
672
|
const tempAllocations = [];
|
|
579
673
|
try {
|
|
580
674
|
const signatureArgs = func.args || [];
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
675
|
+
const totalArgs = Math.max(signatureArgs.length, args.length);
|
|
676
|
+
for (let i = 0;i < totalArgs; i++) {
|
|
677
|
+
const info = MarshallingCallableAccessor.marshalInfo(func.name, signatureArgs[i] || "", args[i]);
|
|
678
|
+
if (!info) {
|
|
679
|
+
if (typeof args[i] === "string" && /^-?0x[0-9a-fA-F]+$/.test(args[i].trim())) {
|
|
680
|
+
modifiedArgs[i] = Number(BigInt(args[i].trim()));
|
|
681
|
+
}
|
|
584
682
|
continue;
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
683
|
+
}
|
|
684
|
+
const allocLen = Math.max(info.buf.length, 1);
|
|
685
|
+
const remoteAddr = Number(this.root.allocSync(allocLen));
|
|
686
|
+
if (info.buf.length > 0) {
|
|
687
|
+
this.root.writeSync(remoteAddr, info.buf);
|
|
688
|
+
}
|
|
689
|
+
tempAllocations.push({
|
|
690
|
+
address: remoteAddr,
|
|
691
|
+
size: allocLen,
|
|
692
|
+
writeBackTarget: info.writeBackTarget
|
|
693
|
+
});
|
|
588
694
|
modifiedArgs[i] = remoteAddr;
|
|
589
695
|
}
|
|
590
|
-
|
|
696
|
+
const result = this.backend.callSync(func, ...modifiedArgs);
|
|
697
|
+
for (const alloc of tempAllocations) {
|
|
698
|
+
if (alloc.writeBackTarget && alloc.writeBackTarget.length > 0) {
|
|
699
|
+
try {
|
|
700
|
+
const remoteBytes = this.root.readSync(alloc.address, alloc.writeBackTarget.length);
|
|
701
|
+
if (remoteBytes && remoteBytes.length > 0) {
|
|
702
|
+
remoteBytes.copy(alloc.writeBackTarget, 0, 0, Math.min(remoteBytes.length, alloc.writeBackTarget.length));
|
|
703
|
+
}
|
|
704
|
+
} catch {}
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
return result;
|
|
591
708
|
} finally {
|
|
592
709
|
for (const alloc of tempAllocations) {
|
|
593
710
|
try {
|
|
@@ -1022,29 +1139,33 @@ class FileTransferReadAccessor extends MsvcrtDependentMiddlewareAccessor {
|
|
|
1022
1139
|
if (this.isInitializing || !this.tempFilePath) {
|
|
1023
1140
|
return super.readAfterInit(address, size, offset);
|
|
1024
1141
|
}
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1142
|
+
try {
|
|
1143
|
+
await this.ensureOpen();
|
|
1144
|
+
const targetAddr = resolveAddress(address) + offset;
|
|
1145
|
+
await this.backend.call(MsvcrtImpl.rewind, this.hFile);
|
|
1146
|
+
const written = await this.backend.call(MsvcrtImpl.fwrite, targetAddr, 1, size, this.hFile);
|
|
1147
|
+
if (Number(written) === size) {
|
|
1148
|
+
await this.backend.call(MsvcrtImpl.fflush, this.hFile);
|
|
1149
|
+
return fs.readFileSync(this.tempFilePath).subarray(0, size);
|
|
1150
|
+
}
|
|
1151
|
+
} catch {}
|
|
1152
|
+
return super.readAfterInit(address, size, offset);
|
|
1034
1153
|
}
|
|
1035
1154
|
readAfterInitSync(address, size, offset = 0) {
|
|
1036
1155
|
if (this.isInitializingSync || !this.tempFilePath) {
|
|
1037
1156
|
return super.readAfterInitSync(address, size, offset);
|
|
1038
1157
|
}
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1158
|
+
try {
|
|
1159
|
+
this.ensureOpenSync();
|
|
1160
|
+
const targetAddr = resolveAddress(address) + offset;
|
|
1161
|
+
this.backend.callSync(MsvcrtImpl.rewind, this.hFile);
|
|
1162
|
+
const written = this.backend.callSync(MsvcrtImpl.fwrite, targetAddr, 1, size, this.hFile);
|
|
1163
|
+
if (Number(written) === size) {
|
|
1164
|
+
this.backend.callSync(MsvcrtImpl.fflush, this.hFile);
|
|
1165
|
+
return fs.readFileSync(this.tempFilePath).subarray(0, size);
|
|
1166
|
+
}
|
|
1167
|
+
} catch {}
|
|
1168
|
+
return super.readAfterInitSync(address, size, offset);
|
|
1048
1169
|
}
|
|
1049
1170
|
}
|
|
1050
1171
|
|
|
@@ -1129,14 +1250,16 @@ class FileTransferWriteAccessor extends MsvcrtDependentMiddlewareAccessor {
|
|
|
1129
1250
|
}
|
|
1130
1251
|
const targetAddr = resolveAddress(address) + offset;
|
|
1131
1252
|
const buffer = data instanceof Buffer ? data : Buffer.from(data);
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1253
|
+
try {
|
|
1254
|
+
fs.writeFileSync(this.tempFilePath, buffer);
|
|
1255
|
+
await this.ensureOpen();
|
|
1256
|
+
await this.backend.call(MsvcrtImpl.rewind, this.hFile);
|
|
1257
|
+
const readBytes = await this.backend.call(MsvcrtImpl.fread, targetAddr, 1, buffer.length, this.hFile);
|
|
1258
|
+
if (Number(readBytes) === buffer.length) {
|
|
1259
|
+
return buffer.length;
|
|
1260
|
+
}
|
|
1261
|
+
} catch {}
|
|
1262
|
+
return super.writeAfterInit(address, data, offset);
|
|
1140
1263
|
}
|
|
1141
1264
|
writeAfterInitSync(address, data, offset = 0) {
|
|
1142
1265
|
if (this.isInitializingSync || !this.tempFilePath) {
|
|
@@ -1144,14 +1267,16 @@ class FileTransferWriteAccessor extends MsvcrtDependentMiddlewareAccessor {
|
|
|
1144
1267
|
}
|
|
1145
1268
|
const targetAddr = resolveAddress(address) + offset;
|
|
1146
1269
|
const buffer = data instanceof Buffer ? data : Buffer.from(data);
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1270
|
+
try {
|
|
1271
|
+
fs.writeFileSync(this.tempFilePath, buffer);
|
|
1272
|
+
this.ensureOpenSync();
|
|
1273
|
+
this.backend.callSync(MsvcrtImpl.rewind, this.hFile);
|
|
1274
|
+
const readBytes = this.backend.callSync(MsvcrtImpl.fread, targetAddr, 1, buffer.length, this.hFile);
|
|
1275
|
+
if (Number(readBytes) === buffer.length) {
|
|
1276
|
+
return buffer.length;
|
|
1277
|
+
}
|
|
1278
|
+
} catch {}
|
|
1279
|
+
return super.writeAfterInitSync(address, data, offset);
|
|
1155
1280
|
}
|
|
1156
1281
|
}
|
|
1157
1282
|
|
|
@@ -1640,21 +1765,512 @@ class ScannerMiddleware extends MiddlewareAccessor {
|
|
|
1640
1765
|
}
|
|
1641
1766
|
}
|
|
1642
1767
|
}
|
|
1768
|
+
// packages/accessors/src/indirect-nthread-host-accessor.ts
|
|
1769
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
1770
|
+
import {
|
|
1771
|
+
isMiddlewareAccessor
|
|
1772
|
+
} from "bun-xffi";
|
|
1773
|
+
import { Thread } from "bun-winapi";
|
|
1774
|
+
import { NThread } from "bun-nthread";
|
|
1775
|
+
class IndirectNThreadHostAccessor extends HostAccessor {
|
|
1776
|
+
nthreadRef;
|
|
1777
|
+
bootstrapRoot;
|
|
1778
|
+
get nthread() {
|
|
1779
|
+
return this.nthreadRef instanceof NThreadRaceAccessor ? this.nthreadRef.target : this.nthreadRef;
|
|
1780
|
+
}
|
|
1781
|
+
constructor(backendOrPid, threadId, options = {}) {
|
|
1782
|
+
let nthread;
|
|
1783
|
+
let nthreadRoot;
|
|
1784
|
+
if (typeof backendOrPid === "number") {
|
|
1785
|
+
nthreadRoot = new RedirectorHostAccessor(backendOrPid);
|
|
1786
|
+
nthread = new NThread(backendOrPid, threadId, options, nthreadRoot);
|
|
1787
|
+
} else {
|
|
1788
|
+
nthread = backendOrPid;
|
|
1789
|
+
}
|
|
1790
|
+
const pid = nthread.processId;
|
|
1791
|
+
super(new ThrowingMemoryAccessor(pid));
|
|
1792
|
+
const bootstrap = new BootstrapHostAccessor(pid, this);
|
|
1793
|
+
this.bootstrapRoot = bootstrap;
|
|
1794
|
+
bootstrap.backend = nthread;
|
|
1795
|
+
const redirector = new IndirectCallRedirectorAccessor(nthread, bootstrap);
|
|
1796
|
+
const machineCodePool = new MachineCodePoolMiddleware(redirector, bootstrap);
|
|
1797
|
+
const memsetWrite = new MemsetWriteAccessor(machineCodePool, bootstrap);
|
|
1798
|
+
const memcmpRead = new MemcmpReadAccessor(memsetWrite, bootstrap);
|
|
1799
|
+
const fileWriter = new FileTransferWriteAccessor(memcmpRead, bootstrap);
|
|
1800
|
+
const fileReader = new FileTransferReadAccessor(fileWriter, bootstrap);
|
|
1801
|
+
const scanner = new ScannerMiddleware(fileReader, bootstrap);
|
|
1802
|
+
const marshalling = new MarshallingCallableAccessor(scanner, bootstrap);
|
|
1803
|
+
this.backend = marshalling;
|
|
1804
|
+
let b = marshalling;
|
|
1805
|
+
while (isMiddlewareAccessor(b)) {
|
|
1806
|
+
b = b.backend;
|
|
1807
|
+
}
|
|
1808
|
+
if (b) {
|
|
1809
|
+
this._processId = b.processId;
|
|
1810
|
+
}
|
|
1811
|
+
if (nthreadRoot) {
|
|
1812
|
+
nthreadRoot.target = this;
|
|
1813
|
+
}
|
|
1814
|
+
this.nthreadRef = nthread;
|
|
1815
|
+
}
|
|
1816
|
+
async onInit() {
|
|
1817
|
+
await this.bootstrapRoot.init();
|
|
1818
|
+
}
|
|
1819
|
+
onInitSync() {
|
|
1820
|
+
this.bootstrapRoot.initSync();
|
|
1821
|
+
}
|
|
1822
|
+
async onDeinit() {
|
|
1823
|
+
try {
|
|
1824
|
+
await this.bootstrapRoot.deinit();
|
|
1825
|
+
} catch {}
|
|
1826
|
+
try {
|
|
1827
|
+
const nt = this.nthread;
|
|
1828
|
+
if (nt && typeof nt.deinit === "function") {
|
|
1829
|
+
await nt.deinit();
|
|
1830
|
+
}
|
|
1831
|
+
} catch {}
|
|
1832
|
+
}
|
|
1833
|
+
onDeinitSync() {
|
|
1834
|
+
try {
|
|
1835
|
+
this.bootstrapRoot.deinitSync();
|
|
1836
|
+
} catch {}
|
|
1837
|
+
try {
|
|
1838
|
+
const nt = this.nthread;
|
|
1839
|
+
if (nt && typeof nt.deinitSync === "function") {
|
|
1840
|
+
nt.deinitSync();
|
|
1841
|
+
}
|
|
1842
|
+
} catch {}
|
|
1843
|
+
}
|
|
1844
|
+
toJSON() {
|
|
1845
|
+
const session = super.toJSON();
|
|
1846
|
+
session.accessorType = "IndirectNThreadHostAccessor";
|
|
1847
|
+
try {
|
|
1848
|
+
const nt = this.nthread;
|
|
1849
|
+
if (nt && typeof nt.threadId === "number") {
|
|
1850
|
+
session.threadId = nt.threadId;
|
|
1851
|
+
}
|
|
1852
|
+
if (nt && nt.stubs) {
|
|
1853
|
+
const stubs = {};
|
|
1854
|
+
for (const [k, v] of Object.entries(nt.stubs)) {
|
|
1855
|
+
if (v && typeof v.address === "bigint") {
|
|
1856
|
+
stubs[k] = `0x${v.address.toString(16)}`;
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
if (Object.keys(stubs).length > 0) {
|
|
1860
|
+
session.stubs = stubs;
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
} catch {}
|
|
1864
|
+
return session;
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
var currentCandidate = new AsyncLocalStorage;
|
|
1868
|
+
|
|
1869
|
+
class NThreadRaceAccessor extends RacingHostAccessor {
|
|
1870
|
+
aborts = new Map;
|
|
1871
|
+
constructor(pid, nthreadOptions = {}, root) {
|
|
1872
|
+
super(pid, root);
|
|
1873
|
+
this.target = this;
|
|
1874
|
+
const threads = Thread.getThreads(pid);
|
|
1875
|
+
if (threads.length === 0) {
|
|
1876
|
+
throw new Error(`createAccessor: process ${pid} has no threads to redirect`);
|
|
1877
|
+
}
|
|
1878
|
+
const callerSignal = nthreadOptions.signal;
|
|
1879
|
+
for (const t of threads) {
|
|
1880
|
+
const controller = new AbortController;
|
|
1881
|
+
if (callerSignal) {
|
|
1882
|
+
if (callerSignal.aborted)
|
|
1883
|
+
controller.abort(callerSignal.reason);
|
|
1884
|
+
else
|
|
1885
|
+
callerSignal.addEventListener("abort", () => controller.abort(callerSignal.reason), { once: true });
|
|
1886
|
+
}
|
|
1887
|
+
const nthread = new NThread(pid, t.tid, { ...nthreadOptions, signal: controller.signal }, this);
|
|
1888
|
+
this.aborts.set(nthread, () => controller.abort());
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
startRacer(racer) {
|
|
1892
|
+
return currentCandidate.run(racer, () => racer.init());
|
|
1893
|
+
}
|
|
1894
|
+
async call(func, ...args) {
|
|
1895
|
+
if (this.target === this) {
|
|
1896
|
+
const caller = currentCandidate.getStore();
|
|
1897
|
+
if (caller)
|
|
1898
|
+
return caller.call(func, ...args);
|
|
1899
|
+
}
|
|
1900
|
+
return super.call(func, ...args);
|
|
1901
|
+
}
|
|
1902
|
+
releaseLoser(racer, settled) {
|
|
1903
|
+
this.aborts.get(racer)?.();
|
|
1904
|
+
super.releaseLoser(racer, settled);
|
|
1905
|
+
}
|
|
1906
|
+
async onInit() {
|
|
1907
|
+
const winner = await this.race();
|
|
1908
|
+
this.target = winner;
|
|
1909
|
+
}
|
|
1910
|
+
onInitSync() {
|
|
1911
|
+
throw new Error("NThreadRaceAccessor does not support initSync()");
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
// packages/accessors/src/accessors.ts
|
|
1915
|
+
import { Thread as Thread3, Module } from "bun-winapi";
|
|
1916
|
+
import { Kernel32Impl as Kernel32Impl2 } from "bun-xffi";
|
|
1917
|
+
import { CallThreadDiedError, NThread as NThread2 } from "bun-nthread";
|
|
1918
|
+
import { NShm } from "bun-nshm";
|
|
1919
|
+
import {
|
|
1920
|
+
attachNJava as attachNJava2,
|
|
1921
|
+
findNJava,
|
|
1922
|
+
getNJava,
|
|
1923
|
+
getReflection,
|
|
1924
|
+
findReflection,
|
|
1925
|
+
getJavaClass
|
|
1926
|
+
} from "bun-njava";
|
|
1927
|
+
|
|
1928
|
+
// packages/accessors/src/stealth-java.ts
|
|
1929
|
+
import { Thread as Thread2 } from "bun-winapi";
|
|
1930
|
+
import { attachNJava } from "bun-njava";
|
|
1931
|
+
async function spawnStealthWorkerThread(bootstrapHost) {
|
|
1932
|
+
const pid = bootstrapHost.processId;
|
|
1933
|
+
const beforeThreads = new Set(Thread2.getThreads(pid).map((t) => t.tid));
|
|
1934
|
+
const njava = await attachNJava(bootstrapHost);
|
|
1935
|
+
const reflection = await njava.getReflection();
|
|
1936
|
+
const functions = await njava.getFunctions();
|
|
1937
|
+
const executorsClass = await reflection.getClass("java.util.concurrent.Executors");
|
|
1938
|
+
const dtfMethod = await executorsClass.getMethod("defaultThreadFactory");
|
|
1939
|
+
const threadFactory = await dtfMethod.call(0n);
|
|
1940
|
+
const allExecMethods = await reflection.getAllReflectedMethods(executorsClass.classObj);
|
|
1941
|
+
const newPoolMethod = allExecMethods.find((m) => m.name === "newSingleThreadScheduledExecutor" && m.paramTypes.length === 1) ?? allExecMethods.find((m) => m.name === "newSingleThreadScheduledExecutor");
|
|
1942
|
+
const pool = newPoolMethod?.paramTypes.length === 1 ? await newPoolMethod.call(0n, threadFactory) : await newPoolMethod.call(0n);
|
|
1943
|
+
const cdlClass = await reflection.getClass("java.util.concurrent.CountDownLatch");
|
|
1944
|
+
const cdlCtor = await reflection.findConstructor(cdlClass.classObj, (p) => p.length === 1 && p[0] === "int");
|
|
1945
|
+
const cdlInstance = await reflection.construct(cdlCtor, [0]);
|
|
1946
|
+
const eventHandlerClass = await reflection.getClass("java.beans.EventHandler");
|
|
1947
|
+
const ehCtor = await reflection.findConstructor(eventHandlerClass.classObj, (p) => p.length === 4 && p[0] === "java.lang.Object" && p[1] === "java.lang.String" && p[2] === "java.lang.String" && p[3] === "java.lang.String");
|
|
1948
|
+
const handler = await reflection.construct(ehCtor, [
|
|
1949
|
+
cdlInstance,
|
|
1950
|
+
"countDown",
|
|
1951
|
+
0n,
|
|
1952
|
+
"run"
|
|
1953
|
+
]);
|
|
1954
|
+
const proxyClass = await reflection.getClass("java.lang.reflect.Proxy");
|
|
1955
|
+
const newProxyInstanceMethod = await proxyClass.getMethod("newProxyInstance");
|
|
1956
|
+
const classLoaderClass = await reflection.getClass("java.lang.ClassLoader");
|
|
1957
|
+
const getSysLoaderMethod = await classLoaderClass.getMethod("getSystemClassLoader");
|
|
1958
|
+
const sysLoader = await getSysLoaderMethod.call(0n);
|
|
1959
|
+
const classClass = await reflection.getClass("java.lang.Class");
|
|
1960
|
+
const runnableClass = await reflection.getClass("java.lang.Runnable");
|
|
1961
|
+
const interfacesArray = BigInt(await functions.NewObjectArray(1, classClass.classObj, runnableClass.classObj));
|
|
1962
|
+
const runnable = await newProxyInstanceMethod.invokeRaw(0n, [
|
|
1963
|
+
sysLoader,
|
|
1964
|
+
interfacesArray,
|
|
1965
|
+
handler
|
|
1966
|
+
]);
|
|
1967
|
+
const timeUnitClass = await reflection.getClass("java.util.concurrent.TimeUnit");
|
|
1968
|
+
const msField = await timeUnitClass.getField("MILLISECONDS");
|
|
1969
|
+
const msUnit = await msField.get();
|
|
1970
|
+
const poolClass = await reflection.getClass("java.util.concurrent.ScheduledExecutorService");
|
|
1971
|
+
const scheduleMethod = await poolClass.getMethod("scheduleWithFixedDelay");
|
|
1972
|
+
await scheduleMethod.invokeRaw(pool, [
|
|
1973
|
+
runnable,
|
|
1974
|
+
0n,
|
|
1975
|
+
50n,
|
|
1976
|
+
msUnit
|
|
1977
|
+
]);
|
|
1978
|
+
const spawnDeadline = Date.now() + 5000;
|
|
1979
|
+
let newTid;
|
|
1980
|
+
while (Date.now() < spawnDeadline && !newTid) {
|
|
1981
|
+
const afterThreads = Thread2.getThreads(pid);
|
|
1982
|
+
const found = afterThreads.find((t) => !beforeThreads.has(t.tid));
|
|
1983
|
+
if (found) {
|
|
1984
|
+
newTid = found.tid;
|
|
1985
|
+
break;
|
|
1986
|
+
}
|
|
1987
|
+
await Bun.sleep(20);
|
|
1988
|
+
}
|
|
1989
|
+
if (!newTid) {
|
|
1990
|
+
throw new Error("spawnStealthWorkerThread: spawned worker thread did not appear in target process thread list");
|
|
1991
|
+
}
|
|
1992
|
+
return { osTid: newTid };
|
|
1993
|
+
}
|
|
1994
|
+
async function createStealthJavaAccessor(pid, options = {}) {
|
|
1995
|
+
const { njava: _njavaOpt, ...passThroughOptions } = options;
|
|
1996
|
+
const bootstrapHost = await createAccessor(pid, {
|
|
1997
|
+
...passThroughOptions,
|
|
1998
|
+
njava: true
|
|
1999
|
+
});
|
|
2000
|
+
try {
|
|
2001
|
+
const { osTid: workerTid } = await spawnStealthWorkerThread(bootstrapHost);
|
|
2002
|
+
await bootstrapHost.deinit();
|
|
2003
|
+
return await createAccessor(workerTid, {
|
|
2004
|
+
...passThroughOptions,
|
|
2005
|
+
idType: "thread",
|
|
2006
|
+
njava: true
|
|
2007
|
+
});
|
|
2008
|
+
} catch (err) {
|
|
2009
|
+
await bootstrapHost.deinit().catch(() => {});
|
|
2010
|
+
throw err;
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
|
|
2014
|
+
// packages/accessors/src/accessors.ts
|
|
2015
|
+
var DEFAULT_ID_TYPE = "processAllThreadIds";
|
|
2016
|
+
function resolveFromThreadId(id) {
|
|
2017
|
+
const entry = Thread3.getThreads(0).find((t) => t.tid === id);
|
|
2018
|
+
if (!entry) {
|
|
2019
|
+
throw new Error(`createAccessor: no thread with id ${id} found`);
|
|
2020
|
+
}
|
|
2021
|
+
return { pid: entry.ownerPid, threadId: id };
|
|
2022
|
+
}
|
|
2023
|
+
function resolveFromProcessId(id) {
|
|
2024
|
+
const threadId = Thread3.getThreads(id)[0]?.tid;
|
|
2025
|
+
if (threadId === undefined) {
|
|
2026
|
+
throw new Error(`createAccessor: process ${id} has no threads to redirect`);
|
|
2027
|
+
}
|
|
2028
|
+
return { pid: id, threadId };
|
|
2029
|
+
}
|
|
2030
|
+
function resolveBaseAccessor(id, options) {
|
|
2031
|
+
if (options.backend) {
|
|
2032
|
+
return options.backend;
|
|
2033
|
+
}
|
|
2034
|
+
const Host = options.host ?? IndirectNThreadHostAccessor;
|
|
2035
|
+
const idType = options.idType ?? DEFAULT_ID_TYPE;
|
|
2036
|
+
if (idType === "processAllThreadIds") {
|
|
2037
|
+
if (Host !== IndirectNThreadHostAccessor) {
|
|
2038
|
+
throw new Error('createAccessor: idType "processAllThreadIds" is only supported ' + "with the default host (IndirectNThreadHostAccessor) -- there's " + "no generic way to race across an arbitrary host class's threads.");
|
|
2039
|
+
}
|
|
2040
|
+
return new IndirectNThreadHostAccessor(new NThreadRaceAccessor(id, options.hostOptions));
|
|
2041
|
+
}
|
|
2042
|
+
const { pid, threadId } = idType === "thread" ? resolveFromThreadId(id) : resolveFromProcessId(id);
|
|
2043
|
+
return new Host(pid, threadId, options.hostOptions);
|
|
2044
|
+
}
|
|
2045
|
+
function createAccessorWithoutInit(id, options = {}) {
|
|
2046
|
+
const base = resolveBaseAccessor(id, options);
|
|
2047
|
+
base._options = options;
|
|
2048
|
+
if (!options.sharedMemory) {
|
|
2049
|
+
return base;
|
|
2050
|
+
}
|
|
2051
|
+
base.backend = new (options.sharedMemoryMiddleware ?? NShm)(base.backend, base, options.sharedMemoryOptions);
|
|
2052
|
+
return base;
|
|
2053
|
+
}
|
|
2054
|
+
async function remoteGetModuleHandleA(accessor, name) {
|
|
2055
|
+
const buf = Buffer.from(`${name}\x00`);
|
|
2056
|
+
const ptr = await accessor.alloc(buf.length);
|
|
2057
|
+
try {
|
|
2058
|
+
await accessor.write(ptr, buf);
|
|
2059
|
+
return BigInt(await accessor.call(Kernel32Impl2.GetModuleHandleA, ptr));
|
|
2060
|
+
} finally {
|
|
2061
|
+
await accessor.free(ptr).catch(() => {});
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
function isThreadDiedError(err) {
|
|
2065
|
+
if (!(err instanceof Error))
|
|
2066
|
+
return false;
|
|
2067
|
+
return err instanceof CallThreadDiedError || err.name === "CallThreadDiedError" || err.constructor?.name === "CallThreadDiedError" || /Thread died/i.test(err.message);
|
|
2068
|
+
}
|
|
2069
|
+
async function createAccessor(idOrPath, options = {}) {
|
|
2070
|
+
if (typeof idOrPath === "string") {
|
|
2071
|
+
return await HostAccessor.load(idOrPath, options);
|
|
2072
|
+
}
|
|
2073
|
+
const id = idOrPath;
|
|
2074
|
+
const initOne = async (targetId, opt) => {
|
|
2075
|
+
const accessor = createAccessorWithoutInit(targetId, opt);
|
|
2076
|
+
accessor._options = opt;
|
|
2077
|
+
try {
|
|
2078
|
+
await accessor.init();
|
|
2079
|
+
const njavaOpt = opt.njava ?? "auto";
|
|
2080
|
+
if (njavaOpt === true) {
|
|
2081
|
+
await attachNJava2(accessor);
|
|
2082
|
+
} else if (njavaOpt === "auto") {
|
|
2083
|
+
try {
|
|
2084
|
+
let hJvm = await remoteGetModuleHandleA(accessor, "jvm.dll");
|
|
2085
|
+
if (hJvm === 0n) {
|
|
2086
|
+
const modules = Module.getModules(accessor.processId);
|
|
2087
|
+
const isJavaLauncher = modules.some((m) => /java(\.exe|w\.exe|\.dll)/i.test(m.name));
|
|
2088
|
+
if (isJavaLauncher) {
|
|
2089
|
+
const deadline = Date.now() + 1e4;
|
|
2090
|
+
while (hJvm === 0n && Date.now() < deadline) {
|
|
2091
|
+
await Bun.sleep(100);
|
|
2092
|
+
hJvm = await remoteGetModuleHandleA(accessor, "jvm.dll");
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2096
|
+
if (hJvm !== 0n) {
|
|
2097
|
+
await attachNJava2(accessor);
|
|
2098
|
+
}
|
|
2099
|
+
} catch (err) {
|
|
2100
|
+
if (isThreadDiedError(err)) {
|
|
2101
|
+
accessor.close();
|
|
2102
|
+
throw err;
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2106
|
+
return accessor;
|
|
2107
|
+
} catch (err) {
|
|
2108
|
+
accessor.close();
|
|
2109
|
+
throw err;
|
|
2110
|
+
}
|
|
2111
|
+
};
|
|
2112
|
+
if (options.njava === "stealth") {
|
|
2113
|
+
return await createStealthJavaAccessor(id, options);
|
|
2114
|
+
}
|
|
2115
|
+
if (options.backend || options.idType === "thread") {
|
|
2116
|
+
return await initOne(id, options);
|
|
2117
|
+
}
|
|
2118
|
+
if (!options.idType) {
|
|
2119
|
+
try {
|
|
2120
|
+
const modules = Module.getModules(id);
|
|
2121
|
+
const isJava = modules.some((m) => /java(\.exe|w\.exe|\.dll)/i.test(m.name));
|
|
2122
|
+
if (isJava) {
|
|
2123
|
+
const threads = Thread3.getThreads(id);
|
|
2124
|
+
const candidateThreads = threads.length > 1 ? [...threads.slice(1), threads[0]] : threads;
|
|
2125
|
+
for (const t of candidateThreads) {
|
|
2126
|
+
try {
|
|
2127
|
+
return await initOne(t.tid, {
|
|
2128
|
+
...options,
|
|
2129
|
+
idType: "thread"
|
|
2130
|
+
});
|
|
2131
|
+
} catch {}
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2134
|
+
} catch {}
|
|
2135
|
+
}
|
|
2136
|
+
try {
|
|
2137
|
+
return await initOne(id, options);
|
|
2138
|
+
} catch (err) {
|
|
2139
|
+
if (isThreadDiedError(err)) {
|
|
2140
|
+
const threads = Thread3.getThreads(id);
|
|
2141
|
+
for (const t of threads) {
|
|
2142
|
+
try {
|
|
2143
|
+
return await initOne(t.tid, {
|
|
2144
|
+
...options,
|
|
2145
|
+
idType: "thread"
|
|
2146
|
+
});
|
|
2147
|
+
} catch {}
|
|
2148
|
+
}
|
|
2149
|
+
}
|
|
2150
|
+
throw err;
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
var AGGRESSIVENESS_PRESETS = {
|
|
2154
|
+
1: {
|
|
2155
|
+
hostOptions: { timeoutMs: 20000, pollIntervalMs: 2 },
|
|
2156
|
+
sharedMemory: false
|
|
2157
|
+
},
|
|
2158
|
+
2: {
|
|
2159
|
+
hostOptions: { timeoutMs: 5000, pollIntervalMs: 2 },
|
|
2160
|
+
sharedMemory: true
|
|
2161
|
+
}
|
|
2162
|
+
};
|
|
2163
|
+
function createAccessorOptions(aggressiveness = 1) {
|
|
2164
|
+
const preset = AGGRESSIVENESS_PRESETS[aggressiveness];
|
|
2165
|
+
return {
|
|
2166
|
+
hostOptions: { ...preset.hostOptions },
|
|
2167
|
+
sharedMemory: preset.sharedMemory
|
|
2168
|
+
};
|
|
2169
|
+
}
|
|
2170
|
+
async function restoreAccessorFromSession(session, overrideOptions = {}) {
|
|
2171
|
+
const baseOptions = {
|
|
2172
|
+
...session.options,
|
|
2173
|
+
...overrideOptions
|
|
2174
|
+
};
|
|
2175
|
+
const nthreadLayer = session.pipeline?.find((p) => p.type === "NThread");
|
|
2176
|
+
if (nthreadLayer?.state && nthreadLayer.state.threadId) {
|
|
2177
|
+
try {
|
|
2178
|
+
const testThread = Thread3.open(nthreadLayer.state.threadId);
|
|
2179
|
+
const isAlive = testThread.isValid() && testThread.getExitCode() === 259;
|
|
2180
|
+
testThread.close();
|
|
2181
|
+
if (isAlive) {
|
|
2182
|
+
const redirector = new RedirectorHostAccessor(session.processId);
|
|
2183
|
+
const nthread = new NThread2(session.processId, nthreadLayer.state.threadId, baseOptions.hostOptions ?? {}, redirector);
|
|
2184
|
+
nthread.resumeFromHibernation(nthreadLayer.state);
|
|
2185
|
+
const accessor = new IndirectNThreadHostAccessor(nthread);
|
|
2186
|
+
redirector.target = accessor;
|
|
2187
|
+
const hasNShm = session.pipeline?.some((p) => p.type === "NShm") || baseOptions.sharedMemory;
|
|
2188
|
+
if (hasNShm) {
|
|
2189
|
+
accessor.backend = new (baseOptions.sharedMemoryMiddleware ?? NShm)(accessor.backend, accessor, baseOptions.sharedMemoryOptions);
|
|
2190
|
+
}
|
|
2191
|
+
await accessor.init();
|
|
2192
|
+
const hasNJava = session.pipeline?.some((p) => p.type === "NJavaAccessor") || Boolean(baseOptions.njava);
|
|
2193
|
+
if (hasNJava) {
|
|
2194
|
+
await attachNJava2(accessor);
|
|
2195
|
+
}
|
|
2196
|
+
accessor._options = baseOptions;
|
|
2197
|
+
if (session.metadata) {
|
|
2198
|
+
for (const [key, value] of Object.entries(session.metadata)) {
|
|
2199
|
+
accessor.setSessionMetadata(key, value);
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
return accessor;
|
|
2203
|
+
}
|
|
2204
|
+
} catch {}
|
|
2205
|
+
}
|
|
2206
|
+
let targetId = session.processId;
|
|
2207
|
+
let useThread = false;
|
|
2208
|
+
try {
|
|
2209
|
+
const threads = Thread3.getThreads(session.processId);
|
|
2210
|
+
if (threads.length === 0) {
|
|
2211
|
+
throw new Error(`Cannot restore accessor: process ${session.processId} is not running (no threads found).`);
|
|
2212
|
+
}
|
|
2213
|
+
if (session.threadId !== undefined && threads.some((t) => t.tid === session.threadId)) {
|
|
2214
|
+
targetId = session.threadId;
|
|
2215
|
+
useThread = true;
|
|
2216
|
+
}
|
|
2217
|
+
} catch (err) {
|
|
2218
|
+
if (err.message?.includes("Cannot restore accessor:")) {
|
|
2219
|
+
throw err;
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
const finalOptions = {
|
|
2223
|
+
...baseOptions,
|
|
2224
|
+
idType: useThread ? "thread" : baseOptions.idType ?? "processAllThreadIds"
|
|
2225
|
+
};
|
|
2226
|
+
const accessor = await createAccessor(targetId, finalOptions);
|
|
2227
|
+
if (session.metadata) {
|
|
2228
|
+
for (const [key, value] of Object.entries(session.metadata)) {
|
|
2229
|
+
accessor.setSessionMetadata(key, value);
|
|
2230
|
+
}
|
|
2231
|
+
}
|
|
2232
|
+
return accessor;
|
|
2233
|
+
}
|
|
2234
|
+
async function createAccessorFromFile(filePath, overrideOptions) {
|
|
2235
|
+
return await HostAccessor.restoreFromFile(filePath, overrideOptions);
|
|
2236
|
+
}
|
|
2237
|
+
HostAccessor.registerRestorer("IndirectNThreadHostAccessor", async (session, overrideOptions) => {
|
|
2238
|
+
return await restoreAccessorFromSession(session, overrideOptions);
|
|
2239
|
+
});
|
|
2240
|
+
HostAccessor.registerRestorer("default", async (session, overrideOptions) => {
|
|
2241
|
+
return await restoreAccessorFromSession(session, overrideOptions);
|
|
2242
|
+
});
|
|
1643
2243
|
export {
|
|
1644
|
-
|
|
1645
|
-
ThrowingHostAccessor,
|
|
1646
|
-
ScannerMiddleware,
|
|
1647
|
-
RedirectorHostAccessor,
|
|
1648
|
-
ProcessCacheAccessor,
|
|
1649
|
-
MsvcrtDependentMiddlewareAccessor,
|
|
1650
|
-
MemsetWriteAccessor,
|
|
1651
|
-
MemcmpReadAccessor,
|
|
1652
|
-
MarshallingCallableAccessor,
|
|
1653
|
-
MachineCodePoolMiddleware,
|
|
1654
|
-
IndirectCallRedirectorAccessor,
|
|
1655
|
-
HostAccessor,
|
|
1656
|
-
FileTransferWriteAccessor,
|
|
1657
|
-
FileTransferReadAccessor,
|
|
2244
|
+
BootstrapHostAccessor,
|
|
1658
2245
|
CallRedirectorAccessor,
|
|
1659
|
-
|
|
2246
|
+
FileTransferReadAccessor,
|
|
2247
|
+
FileTransferWriteAccessor,
|
|
2248
|
+
HostAccessor,
|
|
2249
|
+
IndirectCallRedirectorAccessor,
|
|
2250
|
+
IndirectNThreadHostAccessor,
|
|
2251
|
+
MachineCodePoolMiddleware,
|
|
2252
|
+
MarshallingCallableAccessor,
|
|
2253
|
+
MemcmpReadAccessor,
|
|
2254
|
+
MemsetWriteAccessor,
|
|
2255
|
+
MsvcrtDependentMiddlewareAccessor,
|
|
2256
|
+
NThreadRaceAccessor,
|
|
2257
|
+
ProcessCacheAccessor,
|
|
2258
|
+
RacingHostAccessor,
|
|
2259
|
+
RedirectorHostAccessor,
|
|
2260
|
+
ScannerMiddleware,
|
|
2261
|
+
ThrowingHostAccessor,
|
|
2262
|
+
ThrowingMemoryAccessor,
|
|
2263
|
+
attachNJava2 as attachNJava,
|
|
2264
|
+
createAccessor,
|
|
2265
|
+
createAccessorFromFile,
|
|
2266
|
+
createAccessorOptions,
|
|
2267
|
+
createAccessorWithoutInit,
|
|
2268
|
+
createStealthJavaAccessor,
|
|
2269
|
+
findNJava,
|
|
2270
|
+
findReflection,
|
|
2271
|
+
getJavaClass,
|
|
2272
|
+
getNJava,
|
|
2273
|
+
getReflection,
|
|
2274
|
+
restoreAccessorFromSession,
|
|
2275
|
+
spawnStealthWorkerThread
|
|
1660
2276
|
};
|