exoproc-accessors 0.1.0
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 +235 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1660 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware-accessor.d.ts +346 -0
- package/dist/middleware-accessor.d.ts.map +1 -0
- package/dist/middleware-accessor.js +1870 -0
- package/dist/middleware-accessor.js.map +1 -0
- package/package.json +50 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1660 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/accessors/src/middleware-accessor.ts
|
|
3
|
+
import { randomUUID } from "crypto";
|
|
4
|
+
import * as fs from "fs";
|
|
5
|
+
import * as path from "path";
|
|
6
|
+
import * as os from "os";
|
|
7
|
+
import {
|
|
8
|
+
NativeMemory,
|
|
9
|
+
normalizeType,
|
|
10
|
+
resolveAddress,
|
|
11
|
+
alignUp,
|
|
12
|
+
computeNearAllocRange,
|
|
13
|
+
freeRegionCandidate,
|
|
14
|
+
AllocNearRangeError,
|
|
15
|
+
Kernel32Impl,
|
|
16
|
+
MsvcrtImpl,
|
|
17
|
+
MsvcrtLibrary,
|
|
18
|
+
MemoryBasicInformation,
|
|
19
|
+
PROCESSENTRY32W_SIZE,
|
|
20
|
+
MemoryState,
|
|
21
|
+
MemoryProtection,
|
|
22
|
+
MemoryFreeType,
|
|
23
|
+
ToolhelpSnapshotFlag,
|
|
24
|
+
ProcessAccess,
|
|
25
|
+
DEFAULT_MACHINECODE_ALIGNMENT,
|
|
26
|
+
memmem,
|
|
27
|
+
memmem1,
|
|
28
|
+
memmem2,
|
|
29
|
+
memmem4,
|
|
30
|
+
memmem8,
|
|
31
|
+
memmemWithoutBuffer,
|
|
32
|
+
Pattern,
|
|
33
|
+
Scanner,
|
|
34
|
+
verifyCoreModules,
|
|
35
|
+
verifyCoreModulesSync,
|
|
36
|
+
isModuleLoadedInProcess,
|
|
37
|
+
isModuleLoadedInProcessSync,
|
|
38
|
+
MiddlewareAccessor,
|
|
39
|
+
InittableMiddlewareAccessor,
|
|
40
|
+
isMiddlewareAccessor
|
|
41
|
+
} from "bun-xffi";
|
|
42
|
+
|
|
43
|
+
class MsvcrtDependentMiddlewareAccessor extends InittableMiddlewareAccessor {
|
|
44
|
+
async onInit() {
|
|
45
|
+
if (this.isLocal)
|
|
46
|
+
return;
|
|
47
|
+
const msvcrtBase = MsvcrtLibrary.baseAddress;
|
|
48
|
+
const isMsvcrtLoaded = await isModuleLoadedInProcess(this.root, msvcrtBase);
|
|
49
|
+
if (!isMsvcrtLoaded) {
|
|
50
|
+
throw new Error("Target process does not have msvcrt.dll loaded.");
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
onInitSync() {
|
|
54
|
+
if (this.isLocal)
|
|
55
|
+
return;
|
|
56
|
+
const msvcrtBase = MsvcrtLibrary.baseAddress;
|
|
57
|
+
const isMsvcrtLoaded = isModuleLoadedInProcessSync(this.root, msvcrtBase);
|
|
58
|
+
if (!isMsvcrtLoaded) {
|
|
59
|
+
throw new Error("Target process does not have msvcrt.dll loaded.");
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
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
|
+
class ThrowingHostAccessor extends HostAccessor {
|
|
86
|
+
constructor(processId, root) {
|
|
87
|
+
super(new ThrowingMemoryAccessor(processId), root);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
class RedirectorHostAccessor extends HostAccessor {
|
|
92
|
+
_target;
|
|
93
|
+
constructor(processId, root) {
|
|
94
|
+
super(new ThrowingMemoryAccessor(processId), root);
|
|
95
|
+
this._target = new ThrowingHostAccessor(processId, this.root);
|
|
96
|
+
}
|
|
97
|
+
get target() {
|
|
98
|
+
return this._target;
|
|
99
|
+
}
|
|
100
|
+
set target(value) {
|
|
101
|
+
this._target = value;
|
|
102
|
+
this._processId = value.processId;
|
|
103
|
+
}
|
|
104
|
+
getTarget() {
|
|
105
|
+
return this._target;
|
|
106
|
+
}
|
|
107
|
+
async read(address, size, offset = 0) {
|
|
108
|
+
const t = this.getTarget();
|
|
109
|
+
if (t === this) {
|
|
110
|
+
return super.read(address, size, offset);
|
|
111
|
+
}
|
|
112
|
+
return t.read(address, size, offset);
|
|
113
|
+
}
|
|
114
|
+
async write(address, data, offset = 0) {
|
|
115
|
+
const t = this.getTarget();
|
|
116
|
+
if (t === this) {
|
|
117
|
+
return super.write(address, data, offset);
|
|
118
|
+
}
|
|
119
|
+
return t.write(address, data, offset);
|
|
120
|
+
}
|
|
121
|
+
async alloc(size, address = null, protection, allocationType) {
|
|
122
|
+
const t = this.getTarget();
|
|
123
|
+
if (t === this) {
|
|
124
|
+
return super.alloc(size, address, protection, allocationType);
|
|
125
|
+
}
|
|
126
|
+
return t.alloc(size, address, protection, allocationType);
|
|
127
|
+
}
|
|
128
|
+
async free(address, size = 0, freeType) {
|
|
129
|
+
const t = this.getTarget();
|
|
130
|
+
if (t === this) {
|
|
131
|
+
return super.free(address, size, freeType);
|
|
132
|
+
}
|
|
133
|
+
return t.free(address, size, freeType);
|
|
134
|
+
}
|
|
135
|
+
async protect(address, size, newProtect) {
|
|
136
|
+
const t = this.getTarget();
|
|
137
|
+
if (t === this) {
|
|
138
|
+
return super.protect(address, size, newProtect);
|
|
139
|
+
}
|
|
140
|
+
return t.protect(address, size, newProtect);
|
|
141
|
+
}
|
|
142
|
+
async query(address) {
|
|
143
|
+
const t = this.getTarget();
|
|
144
|
+
if (t === this) {
|
|
145
|
+
return super.query(address);
|
|
146
|
+
}
|
|
147
|
+
return t.query(address);
|
|
148
|
+
}
|
|
149
|
+
async call(func, ...args) {
|
|
150
|
+
const t = this.getTarget();
|
|
151
|
+
if (t === this) {
|
|
152
|
+
return super.call(func, ...args);
|
|
153
|
+
}
|
|
154
|
+
return t.call(func, ...args);
|
|
155
|
+
}
|
|
156
|
+
async machineCode(machineCode) {
|
|
157
|
+
const t = this.getTarget();
|
|
158
|
+
if (t === this) {
|
|
159
|
+
return super.machineCode(machineCode);
|
|
160
|
+
}
|
|
161
|
+
return t.machineCode(machineCode);
|
|
162
|
+
}
|
|
163
|
+
async* scan(address, size, pattern) {
|
|
164
|
+
const t = this.getTarget();
|
|
165
|
+
if (t === this) {
|
|
166
|
+
yield* super.scan(address, size, pattern);
|
|
167
|
+
} else {
|
|
168
|
+
yield* t.scan(address, size, pattern);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
readSync(address, size, offset = 0) {
|
|
172
|
+
const t = this.getTarget();
|
|
173
|
+
if (t === this) {
|
|
174
|
+
return super.readSync(address, size, offset);
|
|
175
|
+
}
|
|
176
|
+
return t.readSync(address, size, offset);
|
|
177
|
+
}
|
|
178
|
+
writeSync(address, data, offset = 0) {
|
|
179
|
+
const t = this.getTarget();
|
|
180
|
+
if (t === this) {
|
|
181
|
+
return super.writeSync(address, data, offset);
|
|
182
|
+
}
|
|
183
|
+
return t.writeSync(address, data, offset);
|
|
184
|
+
}
|
|
185
|
+
allocSync(size, address = null, protection, allocationType) {
|
|
186
|
+
const t = this.getTarget();
|
|
187
|
+
if (t === this) {
|
|
188
|
+
return super.allocSync(size, address, protection, allocationType);
|
|
189
|
+
}
|
|
190
|
+
return t.allocSync(size, address, protection, allocationType);
|
|
191
|
+
}
|
|
192
|
+
freeSync(address, size = 0, freeType) {
|
|
193
|
+
const t = this.getTarget();
|
|
194
|
+
if (t === this) {
|
|
195
|
+
return super.freeSync(address, size, freeType);
|
|
196
|
+
}
|
|
197
|
+
return t.freeSync(address, size, freeType);
|
|
198
|
+
}
|
|
199
|
+
protectSync(address, size, newProtect) {
|
|
200
|
+
const t = this.getTarget();
|
|
201
|
+
if (t === this) {
|
|
202
|
+
return super.protectSync(address, size, newProtect);
|
|
203
|
+
}
|
|
204
|
+
return t.protectSync(address, size, newProtect);
|
|
205
|
+
}
|
|
206
|
+
querySync(address) {
|
|
207
|
+
const t = this.getTarget();
|
|
208
|
+
if (t === this) {
|
|
209
|
+
return super.querySync(address);
|
|
210
|
+
}
|
|
211
|
+
return t.querySync(address);
|
|
212
|
+
}
|
|
213
|
+
callSync(func, ...args) {
|
|
214
|
+
const t = this.getTarget();
|
|
215
|
+
if (t === this) {
|
|
216
|
+
return super.callSync(func, ...args);
|
|
217
|
+
}
|
|
218
|
+
return t.callSync(func, ...args);
|
|
219
|
+
}
|
|
220
|
+
machineCodeSync(machineCode) {
|
|
221
|
+
const t = this.getTarget();
|
|
222
|
+
if (t === this) {
|
|
223
|
+
return super.machineCodeSync(machineCode);
|
|
224
|
+
}
|
|
225
|
+
return t.machineCodeSync(machineCode);
|
|
226
|
+
}
|
|
227
|
+
*scanSync(address, size, pattern) {
|
|
228
|
+
const t = this.getTarget();
|
|
229
|
+
if (t === this) {
|
|
230
|
+
yield* super.scanSync(address, size, pattern);
|
|
231
|
+
} else {
|
|
232
|
+
yield* t.scanSync(address, size, pattern);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
close() {
|
|
236
|
+
if (this._target && this._target !== this) {
|
|
237
|
+
this._target.close();
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
async onInit() {}
|
|
241
|
+
onInitSync() {}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
class BootstrapHostAccessor extends RedirectorHostAccessor {
|
|
245
|
+
constructor(processId, root) {
|
|
246
|
+
super(processId, root);
|
|
247
|
+
this.target = this;
|
|
248
|
+
}
|
|
249
|
+
async onInit() {
|
|
250
|
+
await this.initNext();
|
|
251
|
+
this.target = this.root;
|
|
252
|
+
}
|
|
253
|
+
onInitSync() {
|
|
254
|
+
this.initNextSync();
|
|
255
|
+
this.target = this.root;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
class CallRedirectorAccessor extends MiddlewareAccessor {
|
|
260
|
+
async alloc(size, address = null, protection = MemoryProtection.READWRITE, allocationType = MemoryState.COMMIT | MemoryState.RESERVE) {
|
|
261
|
+
const addressVal = address ? resolveAddress(address) : null;
|
|
262
|
+
const result = await this.root.call(Kernel32Impl.VirtualAlloc, addressVal, size, allocationType, protection);
|
|
263
|
+
if (!result || Number(result) === 0) {
|
|
264
|
+
throw new Error(`VirtualAlloc failed in remote process for size ${size}`);
|
|
265
|
+
}
|
|
266
|
+
return result;
|
|
267
|
+
}
|
|
268
|
+
allocSync(size, address = null, protection = MemoryProtection.READWRITE, allocationType = MemoryState.COMMIT | MemoryState.RESERVE) {
|
|
269
|
+
const addressVal = address ? resolveAddress(address) : null;
|
|
270
|
+
const result = this.root.callSync(Kernel32Impl.VirtualAlloc, addressVal, size, allocationType, protection);
|
|
271
|
+
if (!result || Number(result) === 0) {
|
|
272
|
+
throw new Error(`VirtualAlloc failed in remote process for size ${size}`);
|
|
273
|
+
}
|
|
274
|
+
return result;
|
|
275
|
+
}
|
|
276
|
+
async free(address, size = 0, freeType = MemoryFreeType.RELEASE) {
|
|
277
|
+
const success = await this.root.call(Kernel32Impl.VirtualFree, resolveAddress(address), size, freeType);
|
|
278
|
+
return success !== 0 && success !== false;
|
|
279
|
+
}
|
|
280
|
+
freeSync(address, size = 0, freeType = MemoryFreeType.RELEASE) {
|
|
281
|
+
const success = this.root.callSync(Kernel32Impl.VirtualFree, resolveAddress(address), size, freeType);
|
|
282
|
+
return success !== 0 && success !== false;
|
|
283
|
+
}
|
|
284
|
+
async protect(address, size, newProtect) {
|
|
285
|
+
const tempAddr = await this.root.alloc(4);
|
|
286
|
+
try {
|
|
287
|
+
const success = await this.root.call(Kernel32Impl.VirtualProtect, resolveAddress(address), size, newProtect, tempAddr);
|
|
288
|
+
if (!success || success === 0) {
|
|
289
|
+
throw new Error(`VirtualProtect failed in remote process at address ${resolveAddress(address)}`);
|
|
290
|
+
}
|
|
291
|
+
const oldProtect = await this.root.readUInt32(tempAddr);
|
|
292
|
+
return oldProtect;
|
|
293
|
+
} finally {
|
|
294
|
+
await this.root.free(tempAddr);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
protectSync(address, size, newProtect) {
|
|
298
|
+
const tempAddr = this.root.allocSync(4);
|
|
299
|
+
try {
|
|
300
|
+
const success = this.root.callSync(Kernel32Impl.VirtualProtect, resolveAddress(address), size, newProtect, tempAddr);
|
|
301
|
+
if (!success || success === 0) {
|
|
302
|
+
throw new Error(`VirtualProtect failed in remote process at address ${resolveAddress(address)}`);
|
|
303
|
+
}
|
|
304
|
+
return this.root.readUInt32Sync(tempAddr);
|
|
305
|
+
} finally {
|
|
306
|
+
this.root.freeSync(tempAddr);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
async query(address) {
|
|
310
|
+
const info = new MemoryBasicInformation;
|
|
311
|
+
const tempAddr = await this.root.alloc(info.size);
|
|
312
|
+
try {
|
|
313
|
+
const bytesReturned = await this.root.call(Kernel32Impl.VirtualQuery, resolveAddress(address), tempAddr, info.size);
|
|
314
|
+
if (!bytesReturned || Number(bytesReturned) === 0) {
|
|
315
|
+
throw new Error(`VirtualQuery failed in remote process at address ${resolveAddress(address)}`);
|
|
316
|
+
}
|
|
317
|
+
const buffer = await this.root.read(tempAddr, info.size);
|
|
318
|
+
info.writeSync(buffer, 0);
|
|
319
|
+
return info;
|
|
320
|
+
} finally {
|
|
321
|
+
await this.root.free(tempAddr);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
querySync(address) {
|
|
325
|
+
const info = new MemoryBasicInformation;
|
|
326
|
+
const tempAddr = this.root.allocSync(info.size);
|
|
327
|
+
try {
|
|
328
|
+
const bytesReturned = this.root.callSync(Kernel32Impl.VirtualQuery, resolveAddress(address), tempAddr, info.size);
|
|
329
|
+
if (!bytesReturned || Number(bytesReturned) === 0) {
|
|
330
|
+
throw new Error(`VirtualQuery failed in remote process at address ${resolveAddress(address)}`);
|
|
331
|
+
}
|
|
332
|
+
const buffer = this.root.readSync(tempAddr, info.size);
|
|
333
|
+
info.writeSync(buffer, 0);
|
|
334
|
+
return info;
|
|
335
|
+
} finally {
|
|
336
|
+
this.root.freeSync(tempAddr);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
async allocNear(target, size, options = {}) {
|
|
340
|
+
const range = computeNearAllocRange(target, options);
|
|
341
|
+
const protection = options.protection ?? MemoryProtection.EXECUTE_READWRITE;
|
|
342
|
+
const allocationType = MemoryState.COMMIT | MemoryState.RESERVE;
|
|
343
|
+
const tryRegion = async (info) => {
|
|
344
|
+
const candidate = freeRegionCandidate(info, size, range);
|
|
345
|
+
if (candidate === null)
|
|
346
|
+
return null;
|
|
347
|
+
try {
|
|
348
|
+
return await this.alloc(size, candidate, protection, allocationType);
|
|
349
|
+
} catch {
|
|
350
|
+
return null;
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
const first = await this.query(range.target);
|
|
354
|
+
const firstHit = await tryRegion(first);
|
|
355
|
+
if (firstHit !== null)
|
|
356
|
+
return firstHit;
|
|
357
|
+
const firstBase = BigInt(resolveAddress(first.BaseAddress));
|
|
358
|
+
const firstSize = BigInt(first.RegionSize) || 0x10000n;
|
|
359
|
+
let cursor = firstBase - 1n;
|
|
360
|
+
while (cursor >= range.minAddr) {
|
|
361
|
+
const info = await this.query(cursor);
|
|
362
|
+
const hit = await tryRegion(info);
|
|
363
|
+
if (hit !== null)
|
|
364
|
+
return hit;
|
|
365
|
+
const base = BigInt(resolveAddress(info.BaseAddress));
|
|
366
|
+
const next = base - 1n;
|
|
367
|
+
if (next >= cursor)
|
|
368
|
+
break;
|
|
369
|
+
cursor = next;
|
|
370
|
+
}
|
|
371
|
+
cursor = firstBase + firstSize;
|
|
372
|
+
while (cursor <= range.maxAddr) {
|
|
373
|
+
const info = await this.query(cursor);
|
|
374
|
+
const hit = await tryRegion(info);
|
|
375
|
+
if (hit !== null)
|
|
376
|
+
return hit;
|
|
377
|
+
const base = BigInt(resolveAddress(info.BaseAddress));
|
|
378
|
+
const regionSize = BigInt(info.RegionSize) || 0x10000n;
|
|
379
|
+
const next = base + regionSize;
|
|
380
|
+
if (next <= cursor)
|
|
381
|
+
break;
|
|
382
|
+
cursor = next;
|
|
383
|
+
}
|
|
384
|
+
throw new AllocNearRangeError(range.target, range.maxDistance);
|
|
385
|
+
}
|
|
386
|
+
allocNearSync(target, size, options = {}) {
|
|
387
|
+
const range = computeNearAllocRange(target, options);
|
|
388
|
+
const protection = options.protection ?? MemoryProtection.EXECUTE_READWRITE;
|
|
389
|
+
const allocationType = MemoryState.COMMIT | MemoryState.RESERVE;
|
|
390
|
+
const tryRegion = (info) => {
|
|
391
|
+
const candidate = freeRegionCandidate(info, size, range);
|
|
392
|
+
if (candidate === null)
|
|
393
|
+
return null;
|
|
394
|
+
try {
|
|
395
|
+
return this.allocSync(size, candidate, protection, allocationType);
|
|
396
|
+
} catch {
|
|
397
|
+
return null;
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
const first = this.querySync(range.target);
|
|
401
|
+
const firstHit = tryRegion(first);
|
|
402
|
+
if (firstHit !== null)
|
|
403
|
+
return firstHit;
|
|
404
|
+
const firstBase = BigInt(resolveAddress(first.BaseAddress));
|
|
405
|
+
const firstSize = BigInt(first.RegionSize) || 0x10000n;
|
|
406
|
+
let cursor = firstBase - 1n;
|
|
407
|
+
while (cursor >= range.minAddr) {
|
|
408
|
+
const info = this.querySync(cursor);
|
|
409
|
+
const hit = tryRegion(info);
|
|
410
|
+
if (hit !== null)
|
|
411
|
+
return hit;
|
|
412
|
+
const base = BigInt(resolveAddress(info.BaseAddress));
|
|
413
|
+
const next = base - 1n;
|
|
414
|
+
if (next >= cursor)
|
|
415
|
+
break;
|
|
416
|
+
cursor = next;
|
|
417
|
+
}
|
|
418
|
+
cursor = firstBase + firstSize;
|
|
419
|
+
while (cursor <= range.maxAddr) {
|
|
420
|
+
const info = this.querySync(cursor);
|
|
421
|
+
const hit = tryRegion(info);
|
|
422
|
+
if (hit !== null)
|
|
423
|
+
return hit;
|
|
424
|
+
const base = BigInt(resolveAddress(info.BaseAddress));
|
|
425
|
+
const regionSize = BigInt(info.RegionSize) || 0x10000n;
|
|
426
|
+
const next = base + regionSize;
|
|
427
|
+
if (next <= cursor)
|
|
428
|
+
break;
|
|
429
|
+
cursor = next;
|
|
430
|
+
}
|
|
431
|
+
throw new AllocNearRangeError(range.target, range.maxDistance);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
class IndirectCallRedirectorAccessor extends CallRedirectorAccessor {
|
|
436
|
+
mallocs = new Map;
|
|
437
|
+
findMallocBlock(address) {
|
|
438
|
+
for (const [base, size] of this.mallocs.entries()) {
|
|
439
|
+
if (address >= base && address < base + size) {
|
|
440
|
+
return { base, size };
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
return null;
|
|
444
|
+
}
|
|
445
|
+
buildMallocBlockInfo(block) {
|
|
446
|
+
const info = new MemoryBasicInformation;
|
|
447
|
+
info.assign({
|
|
448
|
+
BaseAddress: block.base,
|
|
449
|
+
AllocationBase: block.base,
|
|
450
|
+
AllocationProtect: MemoryProtection.READWRITE,
|
|
451
|
+
PartitionId: 0,
|
|
452
|
+
RegionSize: BigInt(block.size),
|
|
453
|
+
State: MemoryState.COMMIT,
|
|
454
|
+
Protect: MemoryProtection.READWRITE,
|
|
455
|
+
Type: 131072
|
|
456
|
+
});
|
|
457
|
+
return info;
|
|
458
|
+
}
|
|
459
|
+
async alloc(size, address = null, protection = MemoryProtection.READWRITE, allocationType = MemoryState.COMMIT | MemoryState.RESERVE) {
|
|
460
|
+
if (protection === MemoryProtection.READWRITE && (address === null || address === undefined)) {
|
|
461
|
+
const result = await this.root.call(MsvcrtImpl.malloc, size);
|
|
462
|
+
if (!result || Number(result) === 0) {
|
|
463
|
+
throw new Error(`malloc failed in remote process for size ${size}`);
|
|
464
|
+
}
|
|
465
|
+
const allocatedAddr = Number(result);
|
|
466
|
+
this.mallocs.set(allocatedAddr, size);
|
|
467
|
+
return allocatedAddr;
|
|
468
|
+
}
|
|
469
|
+
return super.alloc(size, address, protection, allocationType);
|
|
470
|
+
}
|
|
471
|
+
allocSync(size, address = null, protection = MemoryProtection.READWRITE, allocationType = MemoryState.COMMIT | MemoryState.RESERVE) {
|
|
472
|
+
if (protection === MemoryProtection.READWRITE && (address === null || address === undefined)) {
|
|
473
|
+
const result = this.root.callSync(MsvcrtImpl.malloc, size);
|
|
474
|
+
if (!result || Number(result) === 0) {
|
|
475
|
+
throw new Error(`malloc failed in remote process for size ${size}`);
|
|
476
|
+
}
|
|
477
|
+
const allocatedAddr = Number(result);
|
|
478
|
+
this.mallocs.set(allocatedAddr, size);
|
|
479
|
+
return allocatedAddr;
|
|
480
|
+
}
|
|
481
|
+
return super.allocSync(size, address, protection, allocationType);
|
|
482
|
+
}
|
|
483
|
+
async free(address, size = 0, freeType = MemoryFreeType.RELEASE) {
|
|
484
|
+
const addrVal = Number(resolveAddress(address));
|
|
485
|
+
if (this.mallocs.has(addrVal)) {
|
|
486
|
+
await this.root.call(MsvcrtImpl.free, addrVal);
|
|
487
|
+
this.mallocs.delete(addrVal);
|
|
488
|
+
return true;
|
|
489
|
+
}
|
|
490
|
+
return super.free(address, size, freeType);
|
|
491
|
+
}
|
|
492
|
+
freeSync(address, size = 0, freeType = MemoryFreeType.RELEASE) {
|
|
493
|
+
const addrVal = Number(resolveAddress(address));
|
|
494
|
+
if (this.mallocs.has(addrVal)) {
|
|
495
|
+
this.root.callSync(MsvcrtImpl.free, addrVal);
|
|
496
|
+
this.mallocs.delete(addrVal);
|
|
497
|
+
return true;
|
|
498
|
+
}
|
|
499
|
+
return super.freeSync(address, size, freeType);
|
|
500
|
+
}
|
|
501
|
+
async protect(address, size, newProtect) {
|
|
502
|
+
const addrVal = Number(resolveAddress(address));
|
|
503
|
+
const block = this.findMallocBlock(addrVal);
|
|
504
|
+
if (block) {
|
|
505
|
+
if (newProtect !== MemoryProtection.READWRITE) {
|
|
506
|
+
throw new Error(`Cannot change protection of indirect heap block at 0x${addrVal.toString(16)} to ${newProtect}. Only READWRITE is allowed.`);
|
|
507
|
+
}
|
|
508
|
+
return MemoryProtection.READWRITE;
|
|
509
|
+
}
|
|
510
|
+
return super.protect(address, size, newProtect);
|
|
511
|
+
}
|
|
512
|
+
protectSync(address, size, newProtect) {
|
|
513
|
+
const addrVal = Number(resolveAddress(address));
|
|
514
|
+
const block = this.findMallocBlock(addrVal);
|
|
515
|
+
if (block) {
|
|
516
|
+
if (newProtect !== MemoryProtection.READWRITE) {
|
|
517
|
+
throw new Error(`Cannot change protection of indirect heap block at 0x${addrVal.toString(16)} to ${newProtect}. Only READWRITE is allowed.`);
|
|
518
|
+
}
|
|
519
|
+
return MemoryProtection.READWRITE;
|
|
520
|
+
}
|
|
521
|
+
return super.protectSync(address, size, newProtect);
|
|
522
|
+
}
|
|
523
|
+
async query(address) {
|
|
524
|
+
const addrVal = Number(resolveAddress(address));
|
|
525
|
+
const block = this.findMallocBlock(addrVal);
|
|
526
|
+
if (block) {
|
|
527
|
+
return this.buildMallocBlockInfo(block);
|
|
528
|
+
}
|
|
529
|
+
return super.query(address);
|
|
530
|
+
}
|
|
531
|
+
querySync(address) {
|
|
532
|
+
const addrVal = Number(resolveAddress(address));
|
|
533
|
+
const block = this.findMallocBlock(addrVal);
|
|
534
|
+
if (block) {
|
|
535
|
+
return this.buildMallocBlockInfo(block);
|
|
536
|
+
}
|
|
537
|
+
return super.querySync(address);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
class MarshallingCallableAccessor extends MiddlewareAccessor {
|
|
542
|
+
static bufferToMarshal(type, val) {
|
|
543
|
+
const normType = normalizeType(type);
|
|
544
|
+
if (normType === "cstring" && typeof val === "string") {
|
|
545
|
+
return Buffer.from(val + "\x00", "utf8");
|
|
546
|
+
} else if (normType === "cwstring" && typeof val === "string") {
|
|
547
|
+
return Buffer.from(val + "\x00", "utf16le");
|
|
548
|
+
} else if (val && typeof val === "object" && Buffer.isBuffer(val)) {
|
|
549
|
+
return val;
|
|
550
|
+
}
|
|
551
|
+
return null;
|
|
552
|
+
}
|
|
553
|
+
async call(func, ...args) {
|
|
554
|
+
const modifiedArgs = [...args];
|
|
555
|
+
const tempAllocations = [];
|
|
556
|
+
try {
|
|
557
|
+
const signatureArgs = func.args || [];
|
|
558
|
+
for (let i = 0;i < signatureArgs.length; i++) {
|
|
559
|
+
const buf = MarshallingCallableAccessor.bufferToMarshal(signatureArgs[i], args[i]);
|
|
560
|
+
if (!buf)
|
|
561
|
+
continue;
|
|
562
|
+
const remoteAddr = Number(await this.root.alloc(buf.length));
|
|
563
|
+
await this.root.write(remoteAddr, buf);
|
|
564
|
+
tempAllocations.push({ address: remoteAddr, size: buf.length });
|
|
565
|
+
modifiedArgs[i] = remoteAddr;
|
|
566
|
+
}
|
|
567
|
+
return await this.backend.call(func, ...modifiedArgs);
|
|
568
|
+
} finally {
|
|
569
|
+
for (const alloc of tempAllocations) {
|
|
570
|
+
try {
|
|
571
|
+
await this.root.free(alloc.address, alloc.size);
|
|
572
|
+
} catch {}
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
callSync(func, ...args) {
|
|
577
|
+
const modifiedArgs = [...args];
|
|
578
|
+
const tempAllocations = [];
|
|
579
|
+
try {
|
|
580
|
+
const signatureArgs = func.args || [];
|
|
581
|
+
for (let i = 0;i < signatureArgs.length; i++) {
|
|
582
|
+
const buf = MarshallingCallableAccessor.bufferToMarshal(signatureArgs[i], args[i]);
|
|
583
|
+
if (!buf)
|
|
584
|
+
continue;
|
|
585
|
+
const remoteAddr = Number(this.root.allocSync(buf.length));
|
|
586
|
+
this.root.writeSync(remoteAddr, buf);
|
|
587
|
+
tempAllocations.push({ address: remoteAddr, size: buf.length });
|
|
588
|
+
modifiedArgs[i] = remoteAddr;
|
|
589
|
+
}
|
|
590
|
+
return this.backend.callSync(func, ...modifiedArgs);
|
|
591
|
+
} finally {
|
|
592
|
+
for (const alloc of tempAllocations) {
|
|
593
|
+
try {
|
|
594
|
+
this.root.freeSync(alloc.address, alloc.size);
|
|
595
|
+
} catch {}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
class MemsetWriteAccessor extends MsvcrtDependentMiddlewareAccessor {
|
|
602
|
+
_isExecuting = false;
|
|
603
|
+
static computeRuns(buffer) {
|
|
604
|
+
const runs = [];
|
|
605
|
+
let i = 0;
|
|
606
|
+
while (i < buffer.length) {
|
|
607
|
+
const val = buffer[i];
|
|
608
|
+
if (val === undefined)
|
|
609
|
+
break;
|
|
610
|
+
let runLength = 1;
|
|
611
|
+
while (i + runLength < buffer.length && buffer[i + runLength] === val) {
|
|
612
|
+
runLength++;
|
|
613
|
+
}
|
|
614
|
+
runs.push({ startOffset: i, value: val, count: runLength });
|
|
615
|
+
i += runLength;
|
|
616
|
+
}
|
|
617
|
+
return runs;
|
|
618
|
+
}
|
|
619
|
+
async writeAfterInit(address, data, offset = 0) {
|
|
620
|
+
if (this.isInitializing || this._isExecuting) {
|
|
621
|
+
return super.writeAfterInit(address, data, offset);
|
|
622
|
+
}
|
|
623
|
+
this._isExecuting = true;
|
|
624
|
+
try {
|
|
625
|
+
const baseAddress = resolveAddress(address) + offset;
|
|
626
|
+
const buffer = data instanceof Buffer ? data : Buffer.from(data);
|
|
627
|
+
const runs = MemsetWriteAccessor.computeRuns(buffer);
|
|
628
|
+
let totalWritten = 0;
|
|
629
|
+
for (const run of runs) {
|
|
630
|
+
const targetAddr = baseAddress + run.startOffset;
|
|
631
|
+
await this.root.call(MsvcrtImpl.memset, targetAddr, run.value, run.count);
|
|
632
|
+
totalWritten += run.count;
|
|
633
|
+
}
|
|
634
|
+
return totalWritten;
|
|
635
|
+
} finally {
|
|
636
|
+
this._isExecuting = false;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
writeAfterInitSync(address, data, offset = 0) {
|
|
640
|
+
if (this.isInitializingSync || this._isExecuting) {
|
|
641
|
+
return super.writeAfterInitSync(address, data, offset);
|
|
642
|
+
}
|
|
643
|
+
this._isExecuting = true;
|
|
644
|
+
try {
|
|
645
|
+
const baseAddress = resolveAddress(address) + offset;
|
|
646
|
+
const buffer = data instanceof Buffer ? data : Buffer.from(data);
|
|
647
|
+
const runs = MemsetWriteAccessor.computeRuns(buffer);
|
|
648
|
+
let totalWritten = 0;
|
|
649
|
+
for (const run of runs) {
|
|
650
|
+
const targetAddr = baseAddress + run.startOffset;
|
|
651
|
+
this.root.callSync(MsvcrtImpl.memset, targetAddr, run.value, run.count);
|
|
652
|
+
totalWritten += run.count;
|
|
653
|
+
}
|
|
654
|
+
return totalWritten;
|
|
655
|
+
} finally {
|
|
656
|
+
this._isExecuting = false;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
class MemcmpReadAccessor extends MsvcrtDependentMiddlewareAccessor {
|
|
662
|
+
_isExecuting = false;
|
|
663
|
+
async readAfterInit(address, size, offset = 0) {
|
|
664
|
+
if (this.isInitializing || this._isExecuting) {
|
|
665
|
+
return super.readAfterInit(address, size, offset);
|
|
666
|
+
}
|
|
667
|
+
this._isExecuting = true;
|
|
668
|
+
const baseAddress = resolveAddress(address) + offset;
|
|
669
|
+
const candidateAddr = Number(resolveAddress(await this.root.alloc(1)));
|
|
670
|
+
const candidateBuf = Buffer.alloc(1);
|
|
671
|
+
try {
|
|
672
|
+
const result = Buffer.alloc(size);
|
|
673
|
+
for (let i = 0;i < size; i++) {
|
|
674
|
+
let low = 0;
|
|
675
|
+
let high = 255;
|
|
676
|
+
let foundByte = 0;
|
|
677
|
+
while (low <= high) {
|
|
678
|
+
const mid = low + high >> 1;
|
|
679
|
+
candidateBuf[0] = mid;
|
|
680
|
+
await this.root.write(candidateAddr, candidateBuf);
|
|
681
|
+
const cmp = await this.root.call(MsvcrtImpl.memcmp, baseAddress + i, candidateAddr, 1);
|
|
682
|
+
const cmpVal = Number(cmp);
|
|
683
|
+
if (cmpVal === 0) {
|
|
684
|
+
foundByte = mid;
|
|
685
|
+
break;
|
|
686
|
+
} else if (cmpVal < 0) {
|
|
687
|
+
high = mid - 1;
|
|
688
|
+
} else {
|
|
689
|
+
low = mid + 1;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
result[i] = foundByte;
|
|
693
|
+
}
|
|
694
|
+
return result;
|
|
695
|
+
} finally {
|
|
696
|
+
this._isExecuting = false;
|
|
697
|
+
await this.root.free(candidateAddr).catch(() => {});
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
readAfterInitSync(address, size, offset = 0) {
|
|
701
|
+
if (this.isInitializingSync || this._isExecuting) {
|
|
702
|
+
return super.readAfterInitSync(address, size, offset);
|
|
703
|
+
}
|
|
704
|
+
this._isExecuting = true;
|
|
705
|
+
const baseAddress = resolveAddress(address) + offset;
|
|
706
|
+
const candidateAddr = Number(resolveAddress(this.root.allocSync(1)));
|
|
707
|
+
const candidateBuf = Buffer.alloc(1);
|
|
708
|
+
try {
|
|
709
|
+
const result = Buffer.alloc(size);
|
|
710
|
+
for (let i = 0;i < size; i++) {
|
|
711
|
+
let low = 0;
|
|
712
|
+
let high = 255;
|
|
713
|
+
let foundByte = 0;
|
|
714
|
+
while (low <= high) {
|
|
715
|
+
const mid = low + high >> 1;
|
|
716
|
+
candidateBuf[0] = mid;
|
|
717
|
+
this.root.writeSync(candidateAddr, candidateBuf);
|
|
718
|
+
const cmp = this.root.callSync(MsvcrtImpl.memcmp, baseAddress + i, candidateAddr, 1);
|
|
719
|
+
const cmpVal = Number(cmp);
|
|
720
|
+
if (cmpVal === 0) {
|
|
721
|
+
foundByte = mid;
|
|
722
|
+
break;
|
|
723
|
+
} else if (cmpVal < 0) {
|
|
724
|
+
high = mid - 1;
|
|
725
|
+
} else {
|
|
726
|
+
low = mid + 1;
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
result[i] = foundByte;
|
|
730
|
+
}
|
|
731
|
+
return result;
|
|
732
|
+
} finally {
|
|
733
|
+
this._isExecuting = false;
|
|
734
|
+
try {
|
|
735
|
+
this.root.freeSync(candidateAddr);
|
|
736
|
+
} catch {}
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
class ThrowingMemoryAccessor {
|
|
742
|
+
isLocal = false;
|
|
743
|
+
processId;
|
|
744
|
+
constructor(processId) {
|
|
745
|
+
this.processId = processId;
|
|
746
|
+
}
|
|
747
|
+
throwError(method) {
|
|
748
|
+
throw new Error(`ThrowingMemoryAccessor: '${method}' is not implemented or permitted.`);
|
|
749
|
+
}
|
|
750
|
+
enableDebug() {}
|
|
751
|
+
disableDebug() {}
|
|
752
|
+
async read(_address, _size, _offset = 0) {
|
|
753
|
+
this.throwError("read");
|
|
754
|
+
}
|
|
755
|
+
readSync(_address, _size, _offset = 0) {
|
|
756
|
+
this.throwError("readSync");
|
|
757
|
+
}
|
|
758
|
+
async write(_address, _data, _offset = 0) {
|
|
759
|
+
this.throwError("write");
|
|
760
|
+
}
|
|
761
|
+
writeSync(_address, _data, _offset = 0) {
|
|
762
|
+
this.throwError("writeSync");
|
|
763
|
+
}
|
|
764
|
+
async alloc(_size, _address = null, _protection, _allocationType) {
|
|
765
|
+
this.throwError("alloc");
|
|
766
|
+
}
|
|
767
|
+
allocSync(_size, _address = null, _protection, _allocationType) {
|
|
768
|
+
this.throwError("allocSync");
|
|
769
|
+
}
|
|
770
|
+
async allocNear(_target, _size, _options) {
|
|
771
|
+
this.throwError("allocNear");
|
|
772
|
+
}
|
|
773
|
+
allocNearSync(_target, _size, _options) {
|
|
774
|
+
this.throwError("allocNearSync");
|
|
775
|
+
}
|
|
776
|
+
async free(_address, _size = 0, _freeType) {
|
|
777
|
+
this.throwError("free");
|
|
778
|
+
}
|
|
779
|
+
freeSync(_address, _size = 0, _freeType) {
|
|
780
|
+
this.throwError("freeSync");
|
|
781
|
+
}
|
|
782
|
+
async protect(_address, _size, _newProtect) {
|
|
783
|
+
this.throwError("protect");
|
|
784
|
+
}
|
|
785
|
+
protectSync(_address, _size, _newProtect) {
|
|
786
|
+
this.throwError("protectSync");
|
|
787
|
+
}
|
|
788
|
+
async query(_address) {
|
|
789
|
+
this.throwError("query");
|
|
790
|
+
}
|
|
791
|
+
querySync(_address) {
|
|
792
|
+
this.throwError("querySync");
|
|
793
|
+
}
|
|
794
|
+
async call(_func, ..._args) {
|
|
795
|
+
this.throwError("call");
|
|
796
|
+
}
|
|
797
|
+
callSync(_func, ..._args) {
|
|
798
|
+
this.throwError("callSync");
|
|
799
|
+
}
|
|
800
|
+
async* scan(_address, _size, _pattern) {
|
|
801
|
+
this.throwError("scan");
|
|
802
|
+
}
|
|
803
|
+
*scanSync(_address, _size, _pattern) {
|
|
804
|
+
this.throwError("scanSync");
|
|
805
|
+
}
|
|
806
|
+
async readInt8(_address, _offset = 0) {
|
|
807
|
+
this.throwError("readInt8");
|
|
808
|
+
}
|
|
809
|
+
readInt8Sync(_address, _offset = 0) {
|
|
810
|
+
this.throwError("readInt8Sync");
|
|
811
|
+
}
|
|
812
|
+
async readUInt8(_address, _offset = 0) {
|
|
813
|
+
this.throwError("readUInt8");
|
|
814
|
+
}
|
|
815
|
+
readUInt8Sync(_address, _offset = 0) {
|
|
816
|
+
this.throwError("readUInt8Sync");
|
|
817
|
+
}
|
|
818
|
+
async readInt16(_address, _offset = 0) {
|
|
819
|
+
this.throwError("readInt16");
|
|
820
|
+
}
|
|
821
|
+
readInt16Sync(_address, _offset = 0) {
|
|
822
|
+
this.throwError("readInt16Sync");
|
|
823
|
+
}
|
|
824
|
+
async readUInt16(_address, _offset = 0) {
|
|
825
|
+
this.throwError("readUInt16");
|
|
826
|
+
}
|
|
827
|
+
readUInt16Sync(_address, _offset = 0) {
|
|
828
|
+
this.throwError("readUInt16Sync");
|
|
829
|
+
}
|
|
830
|
+
async readInt32(_address, _offset = 0) {
|
|
831
|
+
this.throwError("readInt32");
|
|
832
|
+
}
|
|
833
|
+
readInt32Sync(_address, _offset = 0) {
|
|
834
|
+
this.throwError("readInt32Sync");
|
|
835
|
+
}
|
|
836
|
+
async readUInt32(_address, _offset = 0) {
|
|
837
|
+
this.throwError("readUInt32");
|
|
838
|
+
}
|
|
839
|
+
readUInt32Sync(_address, _offset = 0) {
|
|
840
|
+
this.throwError("readUInt32Sync");
|
|
841
|
+
}
|
|
842
|
+
async readInt64(_address, _offset = 0) {
|
|
843
|
+
this.throwError("readInt64");
|
|
844
|
+
}
|
|
845
|
+
readInt64Sync(_address, _offset = 0) {
|
|
846
|
+
this.throwError("readInt64Sync");
|
|
847
|
+
}
|
|
848
|
+
async readUInt64(_address, _offset = 0) {
|
|
849
|
+
this.throwError("readUInt64");
|
|
850
|
+
}
|
|
851
|
+
readUInt64Sync(_address, _offset = 0) {
|
|
852
|
+
this.throwError("readUInt64Sync");
|
|
853
|
+
}
|
|
854
|
+
async readFloat(_address, _offset = 0) {
|
|
855
|
+
this.throwError("readFloat");
|
|
856
|
+
}
|
|
857
|
+
readFloatSync(_address, _offset = 0) {
|
|
858
|
+
this.throwError("readFloatSync");
|
|
859
|
+
}
|
|
860
|
+
async readDouble(_address, _offset = 0) {
|
|
861
|
+
this.throwError("readDouble");
|
|
862
|
+
}
|
|
863
|
+
readDoubleSync(_address, _offset = 0) {
|
|
864
|
+
this.throwError("readDoubleSync");
|
|
865
|
+
}
|
|
866
|
+
async readPointer(_address, _offset = 0) {
|
|
867
|
+
this.throwError("readPointer");
|
|
868
|
+
}
|
|
869
|
+
readPointerSync(_address, _offset = 0) {
|
|
870
|
+
this.throwError("readPointerSync");
|
|
871
|
+
}
|
|
872
|
+
async writeInt8(_address, _value, _offset = 0) {
|
|
873
|
+
this.throwError("writeInt8");
|
|
874
|
+
}
|
|
875
|
+
writeInt8Sync(_address, _value, _offset = 0) {
|
|
876
|
+
this.throwError("writeInt8Sync");
|
|
877
|
+
}
|
|
878
|
+
async writeUInt8(_address, _value, _offset = 0) {
|
|
879
|
+
this.throwError("writeUInt8");
|
|
880
|
+
}
|
|
881
|
+
writeUInt8Sync(_address, _value, _offset = 0) {
|
|
882
|
+
this.throwError("writeUInt8Sync");
|
|
883
|
+
}
|
|
884
|
+
async writeInt16(_address, _value, _offset = 0) {
|
|
885
|
+
this.throwError("writeInt16");
|
|
886
|
+
}
|
|
887
|
+
writeInt16Sync(_address, _value, _offset = 0) {
|
|
888
|
+
this.throwError("writeInt16Sync");
|
|
889
|
+
}
|
|
890
|
+
async writeUInt16(_address, _value, _offset = 0) {
|
|
891
|
+
this.throwError("writeUInt16");
|
|
892
|
+
}
|
|
893
|
+
writeUInt16Sync(_address, _value, _offset = 0) {
|
|
894
|
+
this.throwError("writeUInt16Sync");
|
|
895
|
+
}
|
|
896
|
+
async writeInt32(_address, _value, _offset = 0) {
|
|
897
|
+
this.throwError("writeInt32");
|
|
898
|
+
}
|
|
899
|
+
writeInt32Sync(_address, _value, _offset = 0) {
|
|
900
|
+
this.throwError("writeInt32Sync");
|
|
901
|
+
}
|
|
902
|
+
async writeUInt32(_address, _value, _offset = 0) {
|
|
903
|
+
this.throwError("writeUInt32");
|
|
904
|
+
}
|
|
905
|
+
writeUInt32Sync(_address, _value, _offset = 0) {
|
|
906
|
+
this.throwError("writeUInt32Sync");
|
|
907
|
+
}
|
|
908
|
+
async writeInt64(_address, _value, _offset = 0) {
|
|
909
|
+
this.throwError("writeInt64");
|
|
910
|
+
}
|
|
911
|
+
writeInt64Sync(_address, _value, _offset = 0) {
|
|
912
|
+
this.throwError("writeInt64Sync");
|
|
913
|
+
}
|
|
914
|
+
async writeUInt64(_address, _value, _offset = 0) {
|
|
915
|
+
this.throwError("writeUInt64");
|
|
916
|
+
}
|
|
917
|
+
writeUInt64Sync(_address, _value, _offset = 0) {
|
|
918
|
+
this.throwError("writeUInt64Sync");
|
|
919
|
+
}
|
|
920
|
+
async writeFloat(_address, _value, _offset = 0) {
|
|
921
|
+
this.throwError("writeFloat");
|
|
922
|
+
}
|
|
923
|
+
writeFloatSync(_address, _value, _offset = 0) {
|
|
924
|
+
this.throwError("writeFloatSync");
|
|
925
|
+
}
|
|
926
|
+
async writeDouble(_address, _value, _offset = 0) {
|
|
927
|
+
this.throwError("writeDouble");
|
|
928
|
+
}
|
|
929
|
+
writeDoubleSync(_address, _value, _offset = 0) {
|
|
930
|
+
this.throwError("writeDoubleSync");
|
|
931
|
+
}
|
|
932
|
+
async writePointer(_address, _value, _offset = 0) {
|
|
933
|
+
this.throwError("writePointer");
|
|
934
|
+
}
|
|
935
|
+
writePointerSync(_address, _value, _offset = 0) {
|
|
936
|
+
this.throwError("writePointerSync");
|
|
937
|
+
}
|
|
938
|
+
async machineCode(_machineCode) {
|
|
939
|
+
this.throwError("machineCode");
|
|
940
|
+
}
|
|
941
|
+
machineCodeSync(_machineCode) {
|
|
942
|
+
this.throwError("machineCodeSync");
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
class FileTransferReadAccessor extends MsvcrtDependentMiddlewareAccessor {
|
|
947
|
+
hFile;
|
|
948
|
+
tempFilePath;
|
|
949
|
+
async onInit() {
|
|
950
|
+
await super.onInit();
|
|
951
|
+
this.tempFilePath = path.join(os.tmpdir(), `exoproc-read-${randomUUID()}.tmp`);
|
|
952
|
+
}
|
|
953
|
+
onInitSync() {
|
|
954
|
+
super.onInitSync();
|
|
955
|
+
this.tempFilePath = path.join(os.tmpdir(), `exoproc-read-${randomUUID()}.tmp`);
|
|
956
|
+
}
|
|
957
|
+
async ensureOpen() {
|
|
958
|
+
if (this.hFile)
|
|
959
|
+
return;
|
|
960
|
+
const pathBuf = Buffer.from(this.tempFilePath + "\x00", "utf8");
|
|
961
|
+
const modeBuf = Buffer.from("wb\x00", "utf8");
|
|
962
|
+
const pathPtr = await this.backend.alloc(pathBuf.length);
|
|
963
|
+
const modePtr = await this.backend.alloc(modeBuf.length);
|
|
964
|
+
await this.backend.write(pathPtr, pathBuf);
|
|
965
|
+
await this.backend.write(modePtr, modeBuf);
|
|
966
|
+
this.hFile = await this.backend.call(MsvcrtImpl.fopen, pathPtr, modePtr);
|
|
967
|
+
await this.backend.free(pathPtr).catch(() => {});
|
|
968
|
+
await this.backend.free(modePtr).catch(() => {});
|
|
969
|
+
if (!this.hFile || Number(this.hFile) === 0) {
|
|
970
|
+
this.hFile = undefined;
|
|
971
|
+
throw new Error(`fopen failed to open temp file in write mode: ${this.tempFilePath}`);
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
ensureOpenSync() {
|
|
975
|
+
if (this.hFile)
|
|
976
|
+
return;
|
|
977
|
+
const pathBuf = Buffer.from(this.tempFilePath + "\x00", "utf8");
|
|
978
|
+
const modeBuf = Buffer.from("wb\x00", "utf8");
|
|
979
|
+
const pathPtr = this.backend.allocSync(pathBuf.length);
|
|
980
|
+
const modePtr = this.backend.allocSync(modeBuf.length);
|
|
981
|
+
this.backend.writeSync(pathPtr, pathBuf);
|
|
982
|
+
this.backend.writeSync(modePtr, modeBuf);
|
|
983
|
+
this.hFile = this.backend.callSync(MsvcrtImpl.fopen, pathPtr, modePtr);
|
|
984
|
+
try {
|
|
985
|
+
this.backend.freeSync(pathPtr);
|
|
986
|
+
} catch {}
|
|
987
|
+
try {
|
|
988
|
+
this.backend.freeSync(modePtr);
|
|
989
|
+
} catch {}
|
|
990
|
+
if (!this.hFile || Number(this.hFile) === 0) {
|
|
991
|
+
this.hFile = undefined;
|
|
992
|
+
throw new Error(`fopen failed to open temp file in write mode: ${this.tempFilePath}`);
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
async onDeinit() {
|
|
996
|
+
if (this.hFile) {
|
|
997
|
+
await this.backend.call(MsvcrtImpl.fclose, this.hFile).catch(() => {});
|
|
998
|
+
this.hFile = undefined;
|
|
999
|
+
}
|
|
1000
|
+
if (this.tempFilePath) {
|
|
1001
|
+
try {
|
|
1002
|
+
fs.unlinkSync(this.tempFilePath);
|
|
1003
|
+
} catch {}
|
|
1004
|
+
this.tempFilePath = undefined;
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
onDeinitSync() {
|
|
1008
|
+
if (this.hFile) {
|
|
1009
|
+
try {
|
|
1010
|
+
this.backend.callSync(MsvcrtImpl.fclose, this.hFile);
|
|
1011
|
+
} catch {}
|
|
1012
|
+
this.hFile = undefined;
|
|
1013
|
+
}
|
|
1014
|
+
if (this.tempFilePath) {
|
|
1015
|
+
try {
|
|
1016
|
+
fs.unlinkSync(this.tempFilePath);
|
|
1017
|
+
} catch {}
|
|
1018
|
+
this.tempFilePath = undefined;
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
async readAfterInit(address, size, offset = 0) {
|
|
1022
|
+
if (this.isInitializing || !this.tempFilePath) {
|
|
1023
|
+
return super.readAfterInit(address, size, offset);
|
|
1024
|
+
}
|
|
1025
|
+
await this.ensureOpen();
|
|
1026
|
+
const targetAddr = resolveAddress(address) + offset;
|
|
1027
|
+
await this.backend.call(MsvcrtImpl.rewind, this.hFile);
|
|
1028
|
+
const written = await this.backend.call(MsvcrtImpl.fwrite, targetAddr, 1, size, this.hFile);
|
|
1029
|
+
if (Number(written) !== size) {
|
|
1030
|
+
throw new Error(`fwrite failed to write entire memory block of size ${size} (wrote ${written})`);
|
|
1031
|
+
}
|
|
1032
|
+
await this.backend.call(MsvcrtImpl.fflush, this.hFile);
|
|
1033
|
+
return fs.readFileSync(this.tempFilePath).subarray(0, size);
|
|
1034
|
+
}
|
|
1035
|
+
readAfterInitSync(address, size, offset = 0) {
|
|
1036
|
+
if (this.isInitializingSync || !this.tempFilePath) {
|
|
1037
|
+
return super.readAfterInitSync(address, size, offset);
|
|
1038
|
+
}
|
|
1039
|
+
this.ensureOpenSync();
|
|
1040
|
+
const targetAddr = resolveAddress(address) + offset;
|
|
1041
|
+
this.backend.callSync(MsvcrtImpl.rewind, this.hFile);
|
|
1042
|
+
const written = this.backend.callSync(MsvcrtImpl.fwrite, targetAddr, 1, size, this.hFile);
|
|
1043
|
+
if (Number(written) !== size) {
|
|
1044
|
+
throw new Error(`fwrite failed to write entire memory block of size ${size} (wrote ${written})`);
|
|
1045
|
+
}
|
|
1046
|
+
this.backend.callSync(MsvcrtImpl.fflush, this.hFile);
|
|
1047
|
+
return fs.readFileSync(this.tempFilePath).subarray(0, size);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
class FileTransferWriteAccessor extends MsvcrtDependentMiddlewareAccessor {
|
|
1052
|
+
hFile;
|
|
1053
|
+
tempFilePath;
|
|
1054
|
+
async onInit() {
|
|
1055
|
+
await super.onInit();
|
|
1056
|
+
this.tempFilePath = path.join(os.tmpdir(), `exoproc-write-${randomUUID()}.tmp`);
|
|
1057
|
+
}
|
|
1058
|
+
onInitSync() {
|
|
1059
|
+
super.onInitSync();
|
|
1060
|
+
this.tempFilePath = path.join(os.tmpdir(), `exoproc-write-${randomUUID()}.tmp`);
|
|
1061
|
+
}
|
|
1062
|
+
async ensureOpen() {
|
|
1063
|
+
if (this.hFile)
|
|
1064
|
+
return;
|
|
1065
|
+
const pathBuf = Buffer.from(this.tempFilePath + "\x00", "utf8");
|
|
1066
|
+
const modeBuf = Buffer.from("rb\x00", "utf8");
|
|
1067
|
+
const pathPtr = await this.backend.alloc(pathBuf.length);
|
|
1068
|
+
const modePtr = await this.backend.alloc(modeBuf.length);
|
|
1069
|
+
await this.backend.write(pathPtr, pathBuf);
|
|
1070
|
+
await this.backend.write(modePtr, modeBuf);
|
|
1071
|
+
this.hFile = await this.backend.call(MsvcrtImpl.fopen, pathPtr, modePtr);
|
|
1072
|
+
await this.backend.free(pathPtr).catch(() => {});
|
|
1073
|
+
await this.backend.free(modePtr).catch(() => {});
|
|
1074
|
+
if (!this.hFile || Number(this.hFile) === 0) {
|
|
1075
|
+
this.hFile = undefined;
|
|
1076
|
+
throw new Error(`fopen failed to open temp file in read mode: ${this.tempFilePath}`);
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
ensureOpenSync() {
|
|
1080
|
+
if (this.hFile)
|
|
1081
|
+
return;
|
|
1082
|
+
const pathBuf = Buffer.from(this.tempFilePath + "\x00", "utf8");
|
|
1083
|
+
const modeBuf = Buffer.from("rb\x00", "utf8");
|
|
1084
|
+
const pathPtr = this.backend.allocSync(pathBuf.length);
|
|
1085
|
+
const modePtr = this.backend.allocSync(modeBuf.length);
|
|
1086
|
+
this.backend.writeSync(pathPtr, pathBuf);
|
|
1087
|
+
this.backend.writeSync(modePtr, modeBuf);
|
|
1088
|
+
this.hFile = this.backend.callSync(MsvcrtImpl.fopen, pathPtr, modePtr);
|
|
1089
|
+
try {
|
|
1090
|
+
this.backend.freeSync(pathPtr);
|
|
1091
|
+
} catch {}
|
|
1092
|
+
try {
|
|
1093
|
+
this.backend.freeSync(modePtr);
|
|
1094
|
+
} catch {}
|
|
1095
|
+
if (!this.hFile || Number(this.hFile) === 0) {
|
|
1096
|
+
this.hFile = undefined;
|
|
1097
|
+
throw new Error(`fopen failed to open temp file in read mode: ${this.tempFilePath}`);
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
async onDeinit() {
|
|
1101
|
+
if (this.hFile) {
|
|
1102
|
+
await this.backend.call(MsvcrtImpl.fclose, this.hFile).catch(() => {});
|
|
1103
|
+
this.hFile = undefined;
|
|
1104
|
+
}
|
|
1105
|
+
if (this.tempFilePath) {
|
|
1106
|
+
try {
|
|
1107
|
+
fs.unlinkSync(this.tempFilePath);
|
|
1108
|
+
} catch {}
|
|
1109
|
+
this.tempFilePath = undefined;
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
onDeinitSync() {
|
|
1113
|
+
if (this.hFile) {
|
|
1114
|
+
try {
|
|
1115
|
+
this.backend.callSync(MsvcrtImpl.fclose, this.hFile);
|
|
1116
|
+
} catch {}
|
|
1117
|
+
this.hFile = undefined;
|
|
1118
|
+
}
|
|
1119
|
+
if (this.tempFilePath) {
|
|
1120
|
+
try {
|
|
1121
|
+
fs.unlinkSync(this.tempFilePath);
|
|
1122
|
+
} catch {}
|
|
1123
|
+
this.tempFilePath = undefined;
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
async writeAfterInit(address, data, offset = 0) {
|
|
1127
|
+
if (this.isInitializing || !this.tempFilePath) {
|
|
1128
|
+
return super.writeAfterInit(address, data, offset);
|
|
1129
|
+
}
|
|
1130
|
+
const targetAddr = resolveAddress(address) + offset;
|
|
1131
|
+
const buffer = data instanceof Buffer ? data : Buffer.from(data);
|
|
1132
|
+
fs.writeFileSync(this.tempFilePath, buffer);
|
|
1133
|
+
await this.ensureOpen();
|
|
1134
|
+
await this.backend.call(MsvcrtImpl.rewind, this.hFile);
|
|
1135
|
+
const readBytes = await this.backend.call(MsvcrtImpl.fread, targetAddr, 1, buffer.length, this.hFile);
|
|
1136
|
+
if (Number(readBytes) !== buffer.length) {
|
|
1137
|
+
throw new Error(`fread failed to read entire file block of size ${buffer.length} (read ${readBytes})`);
|
|
1138
|
+
}
|
|
1139
|
+
return Number(readBytes);
|
|
1140
|
+
}
|
|
1141
|
+
writeAfterInitSync(address, data, offset = 0) {
|
|
1142
|
+
if (this.isInitializingSync || !this.tempFilePath) {
|
|
1143
|
+
return super.writeAfterInitSync(address, data, offset);
|
|
1144
|
+
}
|
|
1145
|
+
const targetAddr = resolveAddress(address) + offset;
|
|
1146
|
+
const buffer = data instanceof Buffer ? data : Buffer.from(data);
|
|
1147
|
+
fs.writeFileSync(this.tempFilePath, buffer);
|
|
1148
|
+
this.ensureOpenSync();
|
|
1149
|
+
this.backend.callSync(MsvcrtImpl.rewind, this.hFile);
|
|
1150
|
+
const readBytes = this.backend.callSync(MsvcrtImpl.fread, targetAddr, 1, buffer.length, this.hFile);
|
|
1151
|
+
if (Number(readBytes) !== buffer.length) {
|
|
1152
|
+
throw new Error(`fread failed to read entire file block of size ${buffer.length} (read ${readBytes})`);
|
|
1153
|
+
}
|
|
1154
|
+
return Number(readBytes);
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
class ProcessCacheAccessor extends InittableMiddlewareAccessor {
|
|
1159
|
+
static TARGET_MODULE_NAMES = new Set([
|
|
1160
|
+
"msvcrt",
|
|
1161
|
+
"msvcrt.dll",
|
|
1162
|
+
"user32",
|
|
1163
|
+
"user32.dll"
|
|
1164
|
+
]);
|
|
1165
|
+
static extractModuleName(buf) {
|
|
1166
|
+
let len = buf.indexOf(0);
|
|
1167
|
+
if (len === -1)
|
|
1168
|
+
len = buf.length;
|
|
1169
|
+
return buf.subarray(0, len).toString("utf8").toLowerCase();
|
|
1170
|
+
}
|
|
1171
|
+
static resolveTrackedAddrs() {
|
|
1172
|
+
return {
|
|
1173
|
+
getModuleHandleExAAddr: resolveAddress(Kernel32Impl.GetModuleHandleExA.ptr),
|
|
1174
|
+
getModuleHandleAAddr: resolveAddress(Kernel32Impl.GetModuleHandleA.ptr),
|
|
1175
|
+
getCurrentProcessAddr: resolveAddress(Kernel32Impl.GetCurrentProcess.ptr),
|
|
1176
|
+
getCurrentProcessIdAddr: resolveAddress(Kernel32Impl.GetCurrentProcessId.ptr)
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
1179
|
+
static findOpenHandleInChain(backend) {
|
|
1180
|
+
let curr = backend;
|
|
1181
|
+
while (curr) {
|
|
1182
|
+
if (curr.handle !== undefined && curr.handle !== null && Number(curr.handle) !== 0) {
|
|
1183
|
+
return curr.handle;
|
|
1184
|
+
}
|
|
1185
|
+
curr = curr.backend;
|
|
1186
|
+
}
|
|
1187
|
+
return null;
|
|
1188
|
+
}
|
|
1189
|
+
writeMemoryCache = new Map;
|
|
1190
|
+
moduleHandleCache = new Map;
|
|
1191
|
+
cachedIs64Bit = null;
|
|
1192
|
+
cachedProcessName = null;
|
|
1193
|
+
cachedCoreModules = null;
|
|
1194
|
+
async onInit() {}
|
|
1195
|
+
onInitSync() {}
|
|
1196
|
+
getIs64BitCore() {
|
|
1197
|
+
if (this.cachedIs64Bit !== null) {
|
|
1198
|
+
return this.cachedIs64Bit;
|
|
1199
|
+
}
|
|
1200
|
+
const handle = ProcessCacheAccessor.findOpenHandleInChain(this.backend);
|
|
1201
|
+
const hProcess = handle || Kernel32Impl.OpenProcess(ProcessAccess.QUERY_INFORMATION | ProcessAccess.VM_READ, 0, this.processId);
|
|
1202
|
+
const ownsHandle = !handle && hProcess && Number(hProcess) !== 0;
|
|
1203
|
+
if (hProcess && Number(hProcess) !== 0) {
|
|
1204
|
+
try {
|
|
1205
|
+
const wow64Buf = Buffer.alloc(4);
|
|
1206
|
+
const success = Kernel32Impl.IsWow64Process(hProcess, wow64Buf);
|
|
1207
|
+
if (Number(success) !== 0) {
|
|
1208
|
+
const is32Bit = wow64Buf.readUInt32LE(0) !== 0;
|
|
1209
|
+
this.cachedIs64Bit = !is32Bit;
|
|
1210
|
+
} else {
|
|
1211
|
+
this.cachedIs64Bit = true;
|
|
1212
|
+
}
|
|
1213
|
+
} finally {
|
|
1214
|
+
if (ownsHandle) {
|
|
1215
|
+
Kernel32Impl.CloseHandle(hProcess);
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
} else {
|
|
1219
|
+
this.cachedIs64Bit = true;
|
|
1220
|
+
}
|
|
1221
|
+
return this.cachedIs64Bit;
|
|
1222
|
+
}
|
|
1223
|
+
async getIs64Bit() {
|
|
1224
|
+
await this.init();
|
|
1225
|
+
return this.getIs64BitCore();
|
|
1226
|
+
}
|
|
1227
|
+
getIs64BitSync() {
|
|
1228
|
+
if (!this.isInitializingSync) {
|
|
1229
|
+
this.initSync();
|
|
1230
|
+
}
|
|
1231
|
+
return this.getIs64BitCore();
|
|
1232
|
+
}
|
|
1233
|
+
getProcessNameCore() {
|
|
1234
|
+
if (this.cachedProcessName !== null) {
|
|
1235
|
+
return this.cachedProcessName;
|
|
1236
|
+
}
|
|
1237
|
+
const hSnapshot = Kernel32Impl.CreateToolhelp32Snapshot(ToolhelpSnapshotFlag.PROCESS, 0);
|
|
1238
|
+
if (hSnapshot && Number(hSnapshot) !== 0 && Number(hSnapshot) !== -1) {
|
|
1239
|
+
try {
|
|
1240
|
+
const size = PROCESSENTRY32W_SIZE;
|
|
1241
|
+
const buf = Buffer.alloc(size);
|
|
1242
|
+
buf.writeUInt32LE(size, 0);
|
|
1243
|
+
let ok = Kernel32Impl.Process32FirstW(hSnapshot, buf);
|
|
1244
|
+
while (Number(ok) !== 0) {
|
|
1245
|
+
const pid = buf.readUInt32LE(8);
|
|
1246
|
+
if (pid === this.processId) {
|
|
1247
|
+
const nameBuf = buf.subarray(44, 44 + 520);
|
|
1248
|
+
let len = 0;
|
|
1249
|
+
for (let i = 0;i < nameBuf.length; i += 2) {
|
|
1250
|
+
if (nameBuf[i] === 0 && nameBuf[i + 1] === 0) {
|
|
1251
|
+
len = i;
|
|
1252
|
+
break;
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
this.cachedProcessName = nameBuf.subarray(0, len).toString("utf16le");
|
|
1256
|
+
break;
|
|
1257
|
+
}
|
|
1258
|
+
buf.writeUInt32LE(size, 0);
|
|
1259
|
+
ok = Kernel32Impl.Process32NextW(hSnapshot, buf);
|
|
1260
|
+
}
|
|
1261
|
+
} finally {
|
|
1262
|
+
Kernel32Impl.CloseHandle(hSnapshot);
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
return this.cachedProcessName ?? "";
|
|
1266
|
+
}
|
|
1267
|
+
async getProcessName() {
|
|
1268
|
+
await this.init();
|
|
1269
|
+
return this.getProcessNameCore();
|
|
1270
|
+
}
|
|
1271
|
+
getProcessNameSync() {
|
|
1272
|
+
if (!this.isInitializingSync) {
|
|
1273
|
+
this.initSync();
|
|
1274
|
+
}
|
|
1275
|
+
return this.getProcessNameCore();
|
|
1276
|
+
}
|
|
1277
|
+
async getCoreModules() {
|
|
1278
|
+
await this.init();
|
|
1279
|
+
if (this.cachedCoreModules !== null) {
|
|
1280
|
+
return this.cachedCoreModules;
|
|
1281
|
+
}
|
|
1282
|
+
this.cachedCoreModules = await verifyCoreModules(this);
|
|
1283
|
+
return this.cachedCoreModules;
|
|
1284
|
+
}
|
|
1285
|
+
getCoreModulesSync() {
|
|
1286
|
+
if (!this.isInitializingSync) {
|
|
1287
|
+
this.initSync();
|
|
1288
|
+
}
|
|
1289
|
+
if (this.cachedCoreModules !== null) {
|
|
1290
|
+
return this.cachedCoreModules;
|
|
1291
|
+
}
|
|
1292
|
+
this.cachedCoreModules = verifyCoreModulesSync(this);
|
|
1293
|
+
return this.cachedCoreModules;
|
|
1294
|
+
}
|
|
1295
|
+
async writeAfterInit(address, data, offset = 0) {
|
|
1296
|
+
const addrVal = resolveAddress(address) + offset;
|
|
1297
|
+
const buf = data instanceof Buffer ? data : Buffer.from(data);
|
|
1298
|
+
this.writeMemoryCache.set(addrVal, buf);
|
|
1299
|
+
return super.writeAfterInit(address, data, offset);
|
|
1300
|
+
}
|
|
1301
|
+
writeAfterInitSync(address, data, offset = 0) {
|
|
1302
|
+
const addrVal = resolveAddress(address) + offset;
|
|
1303
|
+
const buf = data instanceof Buffer ? data : Buffer.from(data);
|
|
1304
|
+
this.writeMemoryCache.set(addrVal, buf);
|
|
1305
|
+
return super.writeAfterInitSync(address, data, offset);
|
|
1306
|
+
}
|
|
1307
|
+
cachedModuleName(namePtr) {
|
|
1308
|
+
const writtenBuf = this.writeMemoryCache.get(namePtr);
|
|
1309
|
+
return writtenBuf ? ProcessCacheAccessor.extractModuleName(writtenBuf) : null;
|
|
1310
|
+
}
|
|
1311
|
+
async resolveModuleName(namePtr) {
|
|
1312
|
+
const cached = this.cachedModuleName(namePtr);
|
|
1313
|
+
if (cached !== null)
|
|
1314
|
+
return cached;
|
|
1315
|
+
try {
|
|
1316
|
+
const buf = await this.root.read(namePtr, 256);
|
|
1317
|
+
return ProcessCacheAccessor.extractModuleName(buf);
|
|
1318
|
+
} catch {
|
|
1319
|
+
return "";
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
resolveModuleNameSync(namePtr) {
|
|
1323
|
+
const cached = this.cachedModuleName(namePtr);
|
|
1324
|
+
if (cached !== null)
|
|
1325
|
+
return cached;
|
|
1326
|
+
try {
|
|
1327
|
+
const buf = this.root.readSync(namePtr, 256);
|
|
1328
|
+
return ProcessCacheAccessor.extractModuleName(buf);
|
|
1329
|
+
} catch {
|
|
1330
|
+
return "";
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
async callAfterInit(func, args) {
|
|
1334
|
+
const funcAddr = resolveAddress(func.ptr);
|
|
1335
|
+
const {
|
|
1336
|
+
getModuleHandleExAAddr,
|
|
1337
|
+
getModuleHandleAAddr,
|
|
1338
|
+
getCurrentProcessAddr,
|
|
1339
|
+
getCurrentProcessIdAddr
|
|
1340
|
+
} = ProcessCacheAccessor.resolveTrackedAddrs();
|
|
1341
|
+
if (funcAddr === getCurrentProcessAddr) {
|
|
1342
|
+
return 0xffffffffffffffffn;
|
|
1343
|
+
} else if (funcAddr === getCurrentProcessIdAddr) {
|
|
1344
|
+
return this.processId;
|
|
1345
|
+
}
|
|
1346
|
+
if (funcAddr === getModuleHandleExAAddr && args.length >= 3) {
|
|
1347
|
+
const flags = Number(args[0]);
|
|
1348
|
+
const namePtr = resolveAddress(args[1]);
|
|
1349
|
+
const outPtr = resolveAddress(args[2]);
|
|
1350
|
+
const isFromAddress = (flags & 4) !== 0;
|
|
1351
|
+
if (!isFromAddress && BigInt(namePtr) !== 0n) {
|
|
1352
|
+
const moduleName = await this.resolveModuleName(namePtr);
|
|
1353
|
+
const isTargetModule = ProcessCacheAccessor.TARGET_MODULE_NAMES.has(moduleName);
|
|
1354
|
+
if (isTargetModule && this.moduleHandleCache.has(moduleName)) {
|
|
1355
|
+
const cachedHModule = this.moduleHandleCache.get(moduleName);
|
|
1356
|
+
const hModuleBuf = Buffer.alloc(8);
|
|
1357
|
+
hModuleBuf.writeBigUInt64LE(cachedHModule, 0);
|
|
1358
|
+
await this.root.write(outPtr, hModuleBuf);
|
|
1359
|
+
return 1;
|
|
1360
|
+
}
|
|
1361
|
+
const result = await this.backend.call(func, args);
|
|
1362
|
+
if (isTargetModule && Number(result) !== 0) {
|
|
1363
|
+
const hModuleBuf = await this.root.read(outPtr, 8);
|
|
1364
|
+
const hModule = hModuleBuf.readBigUInt64LE(0);
|
|
1365
|
+
this.moduleHandleCache.set(moduleName, hModule);
|
|
1366
|
+
}
|
|
1367
|
+
return result;
|
|
1368
|
+
}
|
|
1369
|
+
} else if (funcAddr === getModuleHandleAAddr && args.length >= 1) {
|
|
1370
|
+
const namePtr = resolveAddress(args[0]);
|
|
1371
|
+
if (BigInt(namePtr) !== 0n) {
|
|
1372
|
+
const moduleName = await this.resolveModuleName(namePtr);
|
|
1373
|
+
const isTargetModule = ProcessCacheAccessor.TARGET_MODULE_NAMES.has(moduleName);
|
|
1374
|
+
if (isTargetModule && this.moduleHandleCache.has(moduleName)) {
|
|
1375
|
+
return this.moduleHandleCache.get(moduleName);
|
|
1376
|
+
}
|
|
1377
|
+
const result = await this.backend.call(func, args);
|
|
1378
|
+
if (isTargetModule && result && Number(result) !== 0) {
|
|
1379
|
+
this.moduleHandleCache.set(moduleName, BigInt(result));
|
|
1380
|
+
}
|
|
1381
|
+
return result;
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
return super.callAfterInit(func, args);
|
|
1385
|
+
}
|
|
1386
|
+
callAfterInitSync(func, args) {
|
|
1387
|
+
const funcAddr = resolveAddress(func.ptr);
|
|
1388
|
+
const {
|
|
1389
|
+
getModuleHandleExAAddr,
|
|
1390
|
+
getModuleHandleAAddr,
|
|
1391
|
+
getCurrentProcessAddr,
|
|
1392
|
+
getCurrentProcessIdAddr
|
|
1393
|
+
} = ProcessCacheAccessor.resolveTrackedAddrs();
|
|
1394
|
+
if (funcAddr === getCurrentProcessAddr) {
|
|
1395
|
+
return 0xffffffffffffffffn;
|
|
1396
|
+
} else if (funcAddr === getCurrentProcessIdAddr) {
|
|
1397
|
+
return this.processId;
|
|
1398
|
+
}
|
|
1399
|
+
if (funcAddr === getModuleHandleExAAddr && args.length >= 3) {
|
|
1400
|
+
const flags = Number(args[0]);
|
|
1401
|
+
const namePtr = resolveAddress(args[1]);
|
|
1402
|
+
const outPtr = resolveAddress(args[2]);
|
|
1403
|
+
const isFromAddress = (flags & 4) !== 0;
|
|
1404
|
+
if (!isFromAddress && BigInt(namePtr) !== 0n) {
|
|
1405
|
+
const moduleName = this.resolveModuleNameSync(namePtr);
|
|
1406
|
+
const isTargetModule = ProcessCacheAccessor.TARGET_MODULE_NAMES.has(moduleName);
|
|
1407
|
+
if (isTargetModule && this.moduleHandleCache.has(moduleName)) {
|
|
1408
|
+
const cachedHModule = this.moduleHandleCache.get(moduleName);
|
|
1409
|
+
const hModuleBuf = Buffer.alloc(8);
|
|
1410
|
+
hModuleBuf.writeBigUInt64LE(cachedHModule, 0);
|
|
1411
|
+
this.root.writeSync(outPtr, hModuleBuf);
|
|
1412
|
+
return 1;
|
|
1413
|
+
}
|
|
1414
|
+
const result = this.backend.callSync(func, args);
|
|
1415
|
+
if (isTargetModule && Number(result) !== 0) {
|
|
1416
|
+
const hModuleBuf = this.root.readSync(outPtr, 8);
|
|
1417
|
+
const hModule = hModuleBuf.readBigUInt64LE(0);
|
|
1418
|
+
this.moduleHandleCache.set(moduleName, hModule);
|
|
1419
|
+
}
|
|
1420
|
+
return result;
|
|
1421
|
+
}
|
|
1422
|
+
} else if (funcAddr === getModuleHandleAAddr && args.length >= 1) {
|
|
1423
|
+
const namePtr = resolveAddress(args[0]);
|
|
1424
|
+
if (BigInt(namePtr) !== 0n) {
|
|
1425
|
+
const moduleName = this.resolveModuleNameSync(namePtr);
|
|
1426
|
+
const isTargetModule = ProcessCacheAccessor.TARGET_MODULE_NAMES.has(moduleName);
|
|
1427
|
+
if (isTargetModule && this.moduleHandleCache.has(moduleName)) {
|
|
1428
|
+
return this.moduleHandleCache.get(moduleName);
|
|
1429
|
+
}
|
|
1430
|
+
const result = this.backend.callSync(func, args);
|
|
1431
|
+
if (isTargetModule && result && Number(result) !== 0) {
|
|
1432
|
+
this.moduleHandleCache.set(moduleName, BigInt(result));
|
|
1433
|
+
}
|
|
1434
|
+
return result;
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
return super.callAfterInitSync(func, args);
|
|
1438
|
+
}
|
|
1439
|
+
async freeAfterInit(address, size, freeType) {
|
|
1440
|
+
const addrVal = resolveAddress(address);
|
|
1441
|
+
this.writeMemoryCache.delete(addrVal);
|
|
1442
|
+
return super.freeAfterInit(address, size, freeType);
|
|
1443
|
+
}
|
|
1444
|
+
freeAfterInitSync(address, size, freeType) {
|
|
1445
|
+
const addrVal = resolveAddress(address);
|
|
1446
|
+
this.writeMemoryCache.delete(addrVal);
|
|
1447
|
+
return super.freeAfterInitSync(address, size, freeType);
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
class MachineCodePoolMiddleware extends MiddlewareAccessor {
|
|
1452
|
+
blocks = [];
|
|
1453
|
+
alignment = DEFAULT_MACHINECODE_ALIGNMENT;
|
|
1454
|
+
defaultBlockSize = 4096;
|
|
1455
|
+
findBlockWithSpace(needed) {
|
|
1456
|
+
for (let i = 0;i < this.blocks.length; i++) {
|
|
1457
|
+
const block = this.blocks[i];
|
|
1458
|
+
const alignedUsed = alignUp(block.used, this.alignment);
|
|
1459
|
+
if (block.size - alignedUsed >= needed) {
|
|
1460
|
+
return { address: block.address, offset: alignedUsed, blockIdx: i };
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
return null;
|
|
1464
|
+
}
|
|
1465
|
+
async getOrAllocBlock(needed) {
|
|
1466
|
+
const existing = this.findBlockWithSpace(needed);
|
|
1467
|
+
if (existing)
|
|
1468
|
+
return existing;
|
|
1469
|
+
const blockSize = Math.max(this.defaultBlockSize, needed);
|
|
1470
|
+
const blockAddr = await this.root.alloc(blockSize, null, MemoryProtection.EXECUTE_READWRITE);
|
|
1471
|
+
const address = Number(resolveAddress(blockAddr));
|
|
1472
|
+
this.blocks.push({ address, size: blockSize, used: 0 });
|
|
1473
|
+
return { address, offset: 0, blockIdx: this.blocks.length - 1 };
|
|
1474
|
+
}
|
|
1475
|
+
getOrAllocBlockSync(needed) {
|
|
1476
|
+
const existing = this.findBlockWithSpace(needed);
|
|
1477
|
+
if (existing)
|
|
1478
|
+
return existing;
|
|
1479
|
+
const blockSize = Math.max(this.defaultBlockSize, needed);
|
|
1480
|
+
const blockAddr = this.root.allocSync(blockSize, null, MemoryProtection.EXECUTE_READWRITE);
|
|
1481
|
+
const address = Number(resolveAddress(blockAddr));
|
|
1482
|
+
this.blocks.push({ address, size: blockSize, used: 0 });
|
|
1483
|
+
return { address, offset: 0, blockIdx: this.blocks.length - 1 };
|
|
1484
|
+
}
|
|
1485
|
+
async machineCode(machineCode) {
|
|
1486
|
+
const size = machineCode.size;
|
|
1487
|
+
const bytes = Array.isArray(machineCode.bytes) ? new Uint8Array(machineCode.bytes) : machineCode.bytes;
|
|
1488
|
+
const { address, offset, blockIdx } = await this.getOrAllocBlock(size);
|
|
1489
|
+
const targetAddr = address + offset;
|
|
1490
|
+
await this.root.write(targetAddr, bytes);
|
|
1491
|
+
this.blocks[blockIdx].used = offset + size;
|
|
1492
|
+
return targetAddr;
|
|
1493
|
+
}
|
|
1494
|
+
machineCodeSync(machineCode) {
|
|
1495
|
+
const size = machineCode.size;
|
|
1496
|
+
const bytes = Array.isArray(machineCode.bytes) ? new Uint8Array(machineCode.bytes) : machineCode.bytes;
|
|
1497
|
+
const { address, offset, blockIdx } = this.getOrAllocBlockSync(size);
|
|
1498
|
+
const targetAddr = address + offset;
|
|
1499
|
+
this.root.writeSync(targetAddr, bytes);
|
|
1500
|
+
this.blocks[blockIdx].used = offset + size;
|
|
1501
|
+
return targetAddr;
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
class ScannerMiddleware extends MiddlewareAccessor {
|
|
1506
|
+
static pickMemmemVariant(n, patBytes) {
|
|
1507
|
+
if (n === 1) {
|
|
1508
|
+
return {
|
|
1509
|
+
sc: memmem1,
|
|
1510
|
+
needleVal: BigInt(patBytes.readUInt8(0)),
|
|
1511
|
+
isWithoutBuffer: true
|
|
1512
|
+
};
|
|
1513
|
+
} else if (n === 2) {
|
|
1514
|
+
return {
|
|
1515
|
+
sc: memmem2,
|
|
1516
|
+
needleVal: BigInt(patBytes.readUInt16LE(0)),
|
|
1517
|
+
isWithoutBuffer: true
|
|
1518
|
+
};
|
|
1519
|
+
} else if (n === 3 || n === 5 || n === 6 || n === 7) {
|
|
1520
|
+
let val = 0n;
|
|
1521
|
+
for (let i = 0;i < n; i++) {
|
|
1522
|
+
val |= BigInt(patBytes[i]) << BigInt(i * 8);
|
|
1523
|
+
}
|
|
1524
|
+
return { sc: memmemWithoutBuffer, needleVal: val, isWithoutBuffer: true };
|
|
1525
|
+
} else if (n === 4) {
|
|
1526
|
+
return {
|
|
1527
|
+
sc: memmem4,
|
|
1528
|
+
needleVal: BigInt(patBytes.readUInt32LE(0)),
|
|
1529
|
+
isWithoutBuffer: true
|
|
1530
|
+
};
|
|
1531
|
+
} else if (n === 8) {
|
|
1532
|
+
return {
|
|
1533
|
+
sc: memmem8,
|
|
1534
|
+
needleVal: patBytes.readBigUInt64LE(0),
|
|
1535
|
+
isWithoutBuffer: true
|
|
1536
|
+
};
|
|
1537
|
+
}
|
|
1538
|
+
return { sc: memmem, needleVal: 0n, isWithoutBuffer: false };
|
|
1539
|
+
}
|
|
1540
|
+
async* scan(address, size, pattern) {
|
|
1541
|
+
const pat = typeof pattern === "string" ? new Pattern(pattern) : pattern;
|
|
1542
|
+
const startAddr = BigInt(resolveAddress(address));
|
|
1543
|
+
const end = startAddr + BigInt(size);
|
|
1544
|
+
let current = startAddr;
|
|
1545
|
+
while (current < end) {
|
|
1546
|
+
const mbi = await this.root.query(current);
|
|
1547
|
+
const regionBase = BigInt(resolveAddress(mbi.BaseAddress));
|
|
1548
|
+
const regionSize = BigInt(mbi.RegionSize);
|
|
1549
|
+
const regionEnd = regionBase + regionSize;
|
|
1550
|
+
const isReadable = mbi.State === MemoryState.COMMIT && !(mbi.Protect & MemoryProtection.GUARD) && !!(mbi.Protect & pat.protect);
|
|
1551
|
+
if (isReadable) {
|
|
1552
|
+
const scanStart = current > regionBase ? current : regionBase;
|
|
1553
|
+
const scanEnd = end < regionEnd ? end : regionEnd;
|
|
1554
|
+
const scanSize = Number(scanEnd - scanStart);
|
|
1555
|
+
if (scanSize >= pat.length) {
|
|
1556
|
+
const n = pat.length;
|
|
1557
|
+
const { sc, needleVal, isWithoutBuffer } = ScannerMiddleware.pickMemmemVariant(n, pat.bytes);
|
|
1558
|
+
const addr = await this.root.machineCode(sc);
|
|
1559
|
+
const memmemFnRemote = sc.cloneForAddress(addr);
|
|
1560
|
+
let needleRemoteAddr = 0n;
|
|
1561
|
+
if (!isWithoutBuffer) {
|
|
1562
|
+
needleRemoteAddr = await this.root.alloc(n);
|
|
1563
|
+
await this.root.write(needleRemoteAddr, pat.bytes);
|
|
1564
|
+
}
|
|
1565
|
+
try {
|
|
1566
|
+
const memmemFn = async (haystack, haystackLen) => {
|
|
1567
|
+
const callArgs = [
|
|
1568
|
+
haystack,
|
|
1569
|
+
haystackLen,
|
|
1570
|
+
isWithoutBuffer ? needleVal : needleRemoteAddr
|
|
1571
|
+
];
|
|
1572
|
+
if (n !== 1 && n !== 2 && n !== 4 && n !== 8) {
|
|
1573
|
+
callArgs.push(BigInt(n));
|
|
1574
|
+
}
|
|
1575
|
+
const res = await this.root.call(memmemFnRemote, ...callArgs);
|
|
1576
|
+
return BigInt(resolveAddress(res));
|
|
1577
|
+
};
|
|
1578
|
+
yield* Scanner.scan(new NativeMemory(scanStart, scanSize), pat, memmemFn);
|
|
1579
|
+
} finally {
|
|
1580
|
+
if (BigInt(resolveAddress(needleRemoteAddr)) !== 0n) {
|
|
1581
|
+
await this.root.free(needleRemoteAddr, n);
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
if (regionEnd <= current)
|
|
1587
|
+
break;
|
|
1588
|
+
current = regionEnd;
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
*scanSync(address, size, pattern) {
|
|
1592
|
+
const pat = typeof pattern === "string" ? new Pattern(pattern) : pattern;
|
|
1593
|
+
const startAddr = BigInt(resolveAddress(address));
|
|
1594
|
+
const end = startAddr + BigInt(size);
|
|
1595
|
+
let current = startAddr;
|
|
1596
|
+
while (current < end) {
|
|
1597
|
+
const mbi = this.root.querySync(current);
|
|
1598
|
+
const regionBase = BigInt(resolveAddress(mbi.BaseAddress));
|
|
1599
|
+
const regionSize = BigInt(mbi.RegionSize);
|
|
1600
|
+
const regionEnd = regionBase + regionSize;
|
|
1601
|
+
const isReadable = mbi.State === MemoryState.COMMIT && !(mbi.Protect & MemoryProtection.GUARD) && !!(mbi.Protect & pat.protect);
|
|
1602
|
+
if (isReadable) {
|
|
1603
|
+
const scanStart = current > regionBase ? current : regionBase;
|
|
1604
|
+
const scanEnd = end < regionEnd ? end : regionEnd;
|
|
1605
|
+
const scanSize = Number(scanEnd - scanStart);
|
|
1606
|
+
if (scanSize >= pat.length) {
|
|
1607
|
+
const n = pat.length;
|
|
1608
|
+
const { sc, needleVal, isWithoutBuffer } = ScannerMiddleware.pickMemmemVariant(n, pat.bytes);
|
|
1609
|
+
const addr = this.root.machineCodeSync(sc);
|
|
1610
|
+
const memmemFnRemote = sc.cloneForAddress(addr);
|
|
1611
|
+
let needleRemoteAddr = 0n;
|
|
1612
|
+
if (!isWithoutBuffer) {
|
|
1613
|
+
needleRemoteAddr = this.root.allocSync(n);
|
|
1614
|
+
this.root.writeSync(needleRemoteAddr, pat.bytes);
|
|
1615
|
+
}
|
|
1616
|
+
try {
|
|
1617
|
+
const memmemFn = (haystack, haystackLen) => {
|
|
1618
|
+
const callArgs = [
|
|
1619
|
+
haystack,
|
|
1620
|
+
haystackLen,
|
|
1621
|
+
isWithoutBuffer ? needleVal : needleRemoteAddr
|
|
1622
|
+
];
|
|
1623
|
+
if (n !== 1 && n !== 2 && n !== 4 && n !== 8) {
|
|
1624
|
+
callArgs.push(BigInt(n));
|
|
1625
|
+
}
|
|
1626
|
+
const res = this.root.callSync(memmemFnRemote, ...callArgs);
|
|
1627
|
+
return BigInt(resolveAddress(res));
|
|
1628
|
+
};
|
|
1629
|
+
yield* Scanner.scanSync(new NativeMemory(scanStart, scanSize), pat, memmemFn);
|
|
1630
|
+
} finally {
|
|
1631
|
+
if (BigInt(resolveAddress(needleRemoteAddr)) !== 0n) {
|
|
1632
|
+
this.root.freeSync(needleRemoteAddr, n);
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
if (regionEnd <= current)
|
|
1638
|
+
break;
|
|
1639
|
+
current = regionEnd;
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
export {
|
|
1644
|
+
ThrowingMemoryAccessor,
|
|
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,
|
|
1658
|
+
CallRedirectorAccessor,
|
|
1659
|
+
BootstrapHostAccessor
|
|
1660
|
+
};
|