bun-memory 1.1.42 → 1.1.44
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/package.json +6 -3
- package/structs/Memory.ts +219 -132
- package/types/Memory.ts +31 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bun-memory",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.44",
|
|
4
4
|
"author": "Stev Peifer <stev@bell.net>",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"@types/bun": "latest"
|
|
13
13
|
},
|
|
14
14
|
"peerDependencies": {
|
|
15
|
-
"typescript": "^5"
|
|
15
|
+
"typescript": "^5.9.2"
|
|
16
16
|
},
|
|
17
17
|
"exports": {
|
|
18
18
|
".": "./index.ts"
|
|
@@ -50,5 +50,8 @@
|
|
|
50
50
|
"run:benchmark": "bun ./example/benchmark.ts",
|
|
51
51
|
"run:trigger-bot": "bun ./example/trigger-bot.ts"
|
|
52
52
|
},
|
|
53
|
-
"type": "module"
|
|
53
|
+
"type": "module",
|
|
54
|
+
"dependencies": {
|
|
55
|
+
"bun-kernel32": "^1.0.9"
|
|
56
|
+
}
|
|
54
57
|
}
|
package/structs/Memory.ts
CHANGED
|
@@ -1,25 +1,11 @@
|
|
|
1
|
-
import
|
|
1
|
+
import '../runtime/extensions';
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
|
|
3
|
+
import { CString, type Pointer, ptr } from 'bun:ffi';
|
|
4
|
+
|
|
5
|
+
import Kernel32, { INVALID_HANDLE_VALUE } from 'bun-kernel32';
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
} = dlopen('kernel32.dll', {
|
|
9
|
-
CloseHandle: { args: [FFIType.u64], returns: FFIType.bool },
|
|
10
|
-
CreateToolhelp32Snapshot: { args: [FFIType.u32, FFIType.u32], returns: FFIType.u64 },
|
|
11
|
-
GetCurrentProcess: { args: [], returns: FFIType.ptr },
|
|
12
|
-
GetLastError: { returns: FFIType.u32 },
|
|
13
|
-
Module32FirstW: { args: [FFIType.u64, FFIType.ptr], returns: FFIType.bool },
|
|
14
|
-
Module32NextW: { args: [FFIType.u64, FFIType.ptr], returns: FFIType.bool },
|
|
15
|
-
OpenProcess: { args: [FFIType.u32, FFIType.bool, FFIType.u32], returns: FFIType.u64 },
|
|
16
|
-
Process32FirstW: { args: [FFIType.u64, FFIType.ptr], returns: FFIType.bool },
|
|
17
|
-
Process32NextW: { args: [FFIType.u64, FFIType.ptr], returns: FFIType.bool },
|
|
18
|
-
ReadProcessMemory: { args: [FFIType.u64, FFIType.u64, FFIType.ptr, FFIType.u64, FFIType.ptr], returns: FFIType.bool },
|
|
19
|
-
VirtualProtectEx: { args: [FFIType.u64, FFIType.u64, FFIType.u64, FFIType.u32, FFIType.ptr], returns: FFIType.bool },
|
|
20
|
-
VirtualQueryEx: { args: [FFIType.u64, FFIType.u64, FFIType.ptr, FFIType.u64], returns: FFIType.u64 },
|
|
21
|
-
WriteProcessMemory: { args: [FFIType.u64, FFIType.u64, FFIType.ptr, FFIType.u64, FFIType.ptr], returns: FFIType.bool },
|
|
22
|
-
});
|
|
7
|
+
import type { Module, Point, QAngle, Quaternion, RGB, RGBA, Scratch, UPtr, UPtrArray, Vector2, Vector3, Vector4 } from '../types/Memory';
|
|
8
|
+
import Win32Error from './Win32Error';
|
|
23
9
|
|
|
24
10
|
/**
|
|
25
11
|
* Provides cross-process memory manipulation for native applications.
|
|
@@ -28,13 +14,12 @@ const {
|
|
|
28
14
|
*
|
|
29
15
|
* Many scalar reads utilize `TypedArray` scratches to avoid a second FFI hop, such as calling `bun:ffi.read.*`.
|
|
30
16
|
*
|
|
31
|
-
* @todo Add call method for calling functions in remote process.
|
|
32
17
|
* @todo Add support for 32 or 64-bit processes using IsWow64Process2 (Windows 10+).
|
|
33
18
|
* @todo When adding 32-bit support, several u64 will need changed to u64_fast.
|
|
34
19
|
*
|
|
35
20
|
* @example
|
|
36
21
|
* ```ts
|
|
37
|
-
* import Memory from '
|
|
22
|
+
* import Memory from 'bun-memory';
|
|
38
23
|
* const cs2 = new Memory('cs2.exe');
|
|
39
24
|
* const myFloat = cs2.f32(0x12345678n);
|
|
40
25
|
* cs2.close();
|
|
@@ -51,29 +36,33 @@ class Memory {
|
|
|
51
36
|
* ```
|
|
52
37
|
*/
|
|
53
38
|
constructor(identifier: number | string) {
|
|
39
|
+
const { Patterns: { ReplaceTrailingNull } } = Memory; // prettier-ignore
|
|
40
|
+
|
|
54
41
|
const dwFlags = 0x00000002; /* TH32CS_SNAPPROCESS */
|
|
55
42
|
const th32ProcessID = 0;
|
|
56
43
|
|
|
57
|
-
const hSnapshot = CreateToolhelp32Snapshot(dwFlags, th32ProcessID);
|
|
44
|
+
const hSnapshot = Kernel32.CreateToolhelp32Snapshot(dwFlags, th32ProcessID);
|
|
58
45
|
|
|
59
46
|
if (hSnapshot === -1n) {
|
|
60
|
-
throw new Win32Error('CreateToolhelp32Snapshot', GetLastError());
|
|
47
|
+
throw new Win32Error('CreateToolhelp32Snapshot', Kernel32.GetLastError());
|
|
61
48
|
}
|
|
62
49
|
|
|
63
|
-
const
|
|
64
|
-
/* */
|
|
50
|
+
const lppeBuffer = Buffer.allocUnsafe(0x238 /* sizeof(PROCESSENTRY32) */);
|
|
51
|
+
/* */ lppeBuffer.writeUInt32LE(0x238 /* sizeof(PROCESSENTRY32) */);
|
|
65
52
|
|
|
66
|
-
const
|
|
53
|
+
const lppe = lppeBuffer.ptr;
|
|
54
|
+
|
|
55
|
+
const bProcess32FirstW = Kernel32.Process32FirstW(hSnapshot, lppe);
|
|
67
56
|
|
|
68
57
|
if (!bProcess32FirstW) {
|
|
69
|
-
CloseHandle(hSnapshot);
|
|
58
|
+
Kernel32.CloseHandle(hSnapshot);
|
|
70
59
|
|
|
71
|
-
throw new Win32Error('Process32FirstW', GetLastError());
|
|
60
|
+
throw new Win32Error('Process32FirstW', Kernel32.GetLastError());
|
|
72
61
|
}
|
|
73
62
|
|
|
74
63
|
do {
|
|
75
|
-
const szExeFile =
|
|
76
|
-
const th32ProcessID =
|
|
64
|
+
const szExeFile = lppeBuffer.toString('utf16le', 0x2c, 0x234).replace(ReplaceTrailingNull, '');
|
|
65
|
+
const th32ProcessID = lppeBuffer.readUInt32LE(0x08);
|
|
77
66
|
|
|
78
67
|
if (
|
|
79
68
|
(typeof identifier === 'number' && identifier !== th32ProcessID) || //
|
|
@@ -83,43 +72,33 @@ class Memory {
|
|
|
83
72
|
}
|
|
84
73
|
|
|
85
74
|
const desiredAccess = 0x001f0fff; /* PROCESS_ALL_ACCESS */
|
|
86
|
-
const inheritHandle =
|
|
75
|
+
const inheritHandle = 0;
|
|
87
76
|
|
|
88
|
-
const hProcess = OpenProcess(desiredAccess, inheritHandle, th32ProcessID);
|
|
77
|
+
const hProcess = Kernel32.OpenProcess(desiredAccess, inheritHandle, th32ProcessID);
|
|
89
78
|
|
|
90
79
|
if (hProcess === 0n) {
|
|
91
|
-
CloseHandle(hSnapshot);
|
|
80
|
+
Kernel32.CloseHandle(hSnapshot);
|
|
92
81
|
|
|
93
|
-
throw new Win32Error('OpenProcess', GetLastError());
|
|
82
|
+
throw new Win32Error('OpenProcess', Kernel32.GetLastError());
|
|
94
83
|
}
|
|
95
84
|
|
|
96
|
-
this.
|
|
85
|
+
this.__modules = {};
|
|
97
86
|
|
|
98
87
|
this.hProcess = hProcess;
|
|
99
88
|
this.th32ProcessID = th32ProcessID;
|
|
100
89
|
|
|
101
90
|
this.refresh();
|
|
102
91
|
|
|
103
|
-
CloseHandle(hSnapshot);
|
|
92
|
+
Kernel32.CloseHandle(hSnapshot);
|
|
104
93
|
|
|
105
94
|
return;
|
|
106
|
-
} while (Process32NextW(hSnapshot, lppe));
|
|
95
|
+
} while (Kernel32.Process32NextW(hSnapshot, lppe));
|
|
107
96
|
|
|
108
|
-
CloseHandle(hSnapshot);
|
|
97
|
+
Kernel32.CloseHandle(hSnapshot);
|
|
109
98
|
|
|
110
|
-
throw new Error(`Process not found: ${identifier}
|
|
99
|
+
throw new Error(`Process not found: ${identifier}.`);
|
|
111
100
|
}
|
|
112
101
|
|
|
113
|
-
/**
|
|
114
|
-
* Regex patterns for matching hex strings and wildcards in memory scans.
|
|
115
|
-
* Used by the pattern method.
|
|
116
|
-
*/
|
|
117
|
-
private static readonly Patterns = {
|
|
118
|
-
MatchAll: /(?:[0-9A-Fa-f]{2})+/g,
|
|
119
|
-
Test: /^(?=.*[0-9A-Fa-f]{2})(?:\*{2}|\?{2}|[0-9A-Fa-f]{2})+$/,
|
|
120
|
-
// Test: /^(?:\*{2}|[0-9A-Fa-f]{2}|\?{2})+$/,
|
|
121
|
-
};
|
|
122
|
-
|
|
123
102
|
/**
|
|
124
103
|
* Map of loaded modules in the process, keyed by module name.
|
|
125
104
|
* @example
|
|
@@ -128,7 +107,17 @@ class Memory {
|
|
|
128
107
|
* const mainModule = cs2.modules['cs2.exe'];
|
|
129
108
|
* ```
|
|
130
109
|
*/
|
|
131
|
-
private
|
|
110
|
+
private __modules: { [key: string]: Readonly<Module> };
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Regex patterns for matching hex strings and wildcards in memory scans.
|
|
114
|
+
* Used by the pattern method.
|
|
115
|
+
*/
|
|
116
|
+
private static readonly Patterns = {
|
|
117
|
+
PatternMatchAll: /(?:[0-9A-Fa-f]{2})+/g,
|
|
118
|
+
PatternTest: /^(?=.*[0-9A-Fa-f]{2})(?:\*{2}|\?{2}|[0-9A-Fa-f]{2})+$/,
|
|
119
|
+
ReplaceTrailingNull: /\0+$/,
|
|
120
|
+
};
|
|
132
121
|
|
|
133
122
|
/**
|
|
134
123
|
* Scratch buffers and typed views for temporary FFI reads/writes.
|
|
@@ -160,7 +149,14 @@ class Memory {
|
|
|
160
149
|
private readonly Scratch16 = new Uint8Array(0x10);
|
|
161
150
|
private readonly Scratch16Float32Array = new Float32Array(this.Scratch16.buffer, this.Scratch16.byteOffset, 0x04);
|
|
162
151
|
|
|
163
|
-
private readonly
|
|
152
|
+
private readonly Scratch48 = new Uint8Array([
|
|
153
|
+
0x48, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x83, 0xec, 0x28, 0xff, 0xd0, 0x48, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x83, 0xc4,
|
|
154
|
+
0x28, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
155
|
+
]);
|
|
156
|
+
|
|
157
|
+
private readonly Scratch112 = new Uint8Array(0x70);
|
|
158
|
+
|
|
159
|
+
private readonly Scratch1080 = Buffer.allocUnsafe(0x438 /* sizeof(MODULEENTRY32W) */);
|
|
164
160
|
|
|
165
161
|
private static TextDecoderUTF8 = new TextDecoder('utf-8');
|
|
166
162
|
|
|
@@ -171,29 +167,15 @@ class Memory {
|
|
|
171
167
|
|
|
172
168
|
/**
|
|
173
169
|
* Gets all loaded modules in the process.
|
|
174
|
-
* @returns Map of module name to module info.
|
|
170
|
+
* @returns Map of module name to module info with base-relative helpers.
|
|
175
171
|
* @example
|
|
176
172
|
* ```ts
|
|
177
173
|
* const cs2 = new Memory('cs2.exe');
|
|
178
174
|
* const client = cs2.modules['client.dll'];
|
|
179
175
|
* ```
|
|
180
176
|
*/
|
|
181
|
-
public get modules(): Memory['
|
|
182
|
-
return this.
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Closes the process handle.
|
|
187
|
-
* @example
|
|
188
|
-
* ```ts
|
|
189
|
-
* const cs2 = new Memory('cs2.exe');
|
|
190
|
-
* cs2.close();
|
|
191
|
-
* ```
|
|
192
|
-
*/
|
|
193
|
-
public close(): void {
|
|
194
|
-
CloseHandle(this.hProcess);
|
|
195
|
-
|
|
196
|
-
return;
|
|
177
|
+
public get modules(): Memory['__modules'] {
|
|
178
|
+
return this.__modules;
|
|
197
179
|
}
|
|
198
180
|
|
|
199
181
|
/**
|
|
@@ -227,49 +209,106 @@ class Memory {
|
|
|
227
209
|
}
|
|
228
210
|
|
|
229
211
|
/**
|
|
230
|
-
*
|
|
212
|
+
* Allocates memory in the remote process.
|
|
213
|
+
* @param length Bytes to allocate.
|
|
214
|
+
* @param protect Page protection (defaults to PAGE_READWRITE).
|
|
215
|
+
* @returns Base address of the allocation.
|
|
231
216
|
* @example
|
|
232
217
|
* ```ts
|
|
233
|
-
* const
|
|
234
|
-
* cs2.refresh();
|
|
218
|
+
* const ptr = cs2.alloc(0x100);
|
|
235
219
|
* ```
|
|
236
220
|
*/
|
|
237
|
-
public
|
|
238
|
-
const
|
|
221
|
+
public alloc(length: number, protect: number = 0x04): bigint {
|
|
222
|
+
const { hProcess } = this;
|
|
239
223
|
|
|
240
|
-
|
|
224
|
+
if (length <= 0) {
|
|
225
|
+
throw new RangeError('length must be greater than 0.');
|
|
226
|
+
}
|
|
241
227
|
|
|
242
|
-
|
|
243
|
-
|
|
228
|
+
const dwSize = BigInt(length);
|
|
229
|
+
const flAllocationType = 0x3000; /* MEM_COMMIT | MEM_RESERVE */
|
|
230
|
+
const flProtect = protect;
|
|
231
|
+
const lpAddress = 0n;
|
|
232
|
+
|
|
233
|
+
const lpBaseAddress = Kernel32.VirtualAllocEx(hProcess, lpAddress, dwSize, flAllocationType, flProtect);
|
|
234
|
+
|
|
235
|
+
if (lpBaseAddress === 0n) {
|
|
236
|
+
throw new Win32Error('VirtualAllocEx', Kernel32.GetLastError());
|
|
244
237
|
}
|
|
245
238
|
|
|
246
|
-
|
|
239
|
+
return lpBaseAddress;
|
|
240
|
+
}
|
|
247
241
|
|
|
248
|
-
|
|
242
|
+
/**
|
|
243
|
+
* Closes the process handle.
|
|
244
|
+
* @example
|
|
245
|
+
* ```ts
|
|
246
|
+
* const cs2 = new Memory('cs2.exe');
|
|
247
|
+
* cs2.close();
|
|
248
|
+
* ```
|
|
249
|
+
*/
|
|
250
|
+
public close(): void {
|
|
251
|
+
Kernel32.CloseHandle(this.hProcess);
|
|
249
252
|
|
|
250
|
-
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
251
255
|
|
|
252
|
-
|
|
253
|
-
|
|
256
|
+
/**
|
|
257
|
+
* Frees memory allocated in the remote process.
|
|
258
|
+
* @param address Allocation base address.
|
|
259
|
+
* @example
|
|
260
|
+
* ```ts
|
|
261
|
+
* const ptr = cs2.alloc(0x100);
|
|
262
|
+
* cs2.free(ptr);
|
|
263
|
+
* ```
|
|
264
|
+
*/
|
|
265
|
+
public free(address: bigint): void {
|
|
266
|
+
const { hProcess } = this;
|
|
254
267
|
|
|
255
|
-
|
|
268
|
+
const dwFreeType = 0x8000; /* MEM_RELEASE */
|
|
269
|
+
const dwSize = 0x00n;
|
|
270
|
+
const lpAddress = address;
|
|
271
|
+
|
|
272
|
+
const bVirtualFreeEx = !!Kernel32.VirtualFreeEx(hProcess, lpAddress, dwSize, dwFreeType);
|
|
273
|
+
|
|
274
|
+
if (!bVirtualFreeEx) {
|
|
275
|
+
throw new Win32Error('VirtualFreeEx', Kernel32.GetLastError());
|
|
256
276
|
}
|
|
257
277
|
|
|
258
|
-
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
259
280
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
281
|
+
/**
|
|
282
|
+
* Changes the page protection of a region.
|
|
283
|
+
* @param address Base address to protect.
|
|
284
|
+
* @param length Bytes to protect.
|
|
285
|
+
* @param protect New protection flags.
|
|
286
|
+
* @returns Previous protection flags.
|
|
287
|
+
* @example
|
|
288
|
+
* ```ts
|
|
289
|
+
* const cs2 = new Memory('cs2.exe');
|
|
290
|
+
* const previous = cs2.protection(0x12345678n, 0x1000, 0x40);
|
|
291
|
+
* ```
|
|
292
|
+
*/
|
|
293
|
+
public protection(address: bigint, length: number, protect: number): number {
|
|
294
|
+
const { Scratch4Uint32Array, hProcess } = this;
|
|
264
295
|
|
|
265
|
-
|
|
266
|
-
|
|
296
|
+
if (length <= 0) {
|
|
297
|
+
throw new RangeError('length must be greater than 0.');
|
|
298
|
+
}
|
|
267
299
|
|
|
268
|
-
|
|
300
|
+
const dwSize = BigInt(length);
|
|
301
|
+
const flNewProtect = protect;
|
|
302
|
+
const lpAddress = address;
|
|
303
|
+
const lpflOldProtect = Scratch4Uint32Array.ptr;
|
|
269
304
|
|
|
270
|
-
|
|
305
|
+
const bVirtualProtectEx = Kernel32.VirtualProtectEx(hProcess, lpAddress, dwSize, flNewProtect, lpflOldProtect);
|
|
271
306
|
|
|
272
|
-
|
|
307
|
+
if (!bVirtualProtectEx) {
|
|
308
|
+
throw new Win32Error('VirtualProtectEx', Kernel32.GetLastError());
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return Scratch4Uint32Array[0x00];
|
|
273
312
|
}
|
|
274
313
|
|
|
275
314
|
/**
|
|
@@ -285,20 +324,70 @@ class Memory {
|
|
|
285
324
|
* ```
|
|
286
325
|
*/
|
|
287
326
|
public read<T extends Scratch>(address: bigint, scratch: T): T {
|
|
327
|
+
const { hProcess } = this;
|
|
328
|
+
|
|
288
329
|
const lpBaseAddress = address;
|
|
289
330
|
const lpBuffer = ptr(scratch);
|
|
290
331
|
const nSize = BigInt(scratch.byteLength);
|
|
291
332
|
const numberOfBytesRead = 0x00n;
|
|
292
333
|
|
|
293
|
-
const bReadProcessMemory = ReadProcessMemory(
|
|
334
|
+
const bReadProcessMemory = Kernel32.ReadProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, numberOfBytesRead);
|
|
294
335
|
|
|
295
336
|
if (!bReadProcessMemory) {
|
|
296
|
-
throw new Win32Error('ReadProcessMemory', GetLastError());
|
|
337
|
+
throw new Win32Error('ReadProcessMemory', Kernel32.GetLastError());
|
|
297
338
|
}
|
|
298
339
|
|
|
299
340
|
return scratch;
|
|
300
341
|
}
|
|
301
342
|
|
|
343
|
+
/**
|
|
344
|
+
* Refreshes the module list for the process.
|
|
345
|
+
* @example
|
|
346
|
+
* ```ts
|
|
347
|
+
* const cs2 = new Memory('cs2.exe');
|
|
348
|
+
* cs2.refresh();
|
|
349
|
+
* ```
|
|
350
|
+
*/
|
|
351
|
+
public refresh(): void {
|
|
352
|
+
const { Patterns: { ReplaceTrailingNull } } = Memory; // prettier-ignore
|
|
353
|
+
const { th32ProcessID } = this;
|
|
354
|
+
|
|
355
|
+
const dwFlags = 0x00000018; /* TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32 */
|
|
356
|
+
|
|
357
|
+
const hSnapshot = Kernel32.CreateToolhelp32Snapshot(dwFlags, th32ProcessID)!;
|
|
358
|
+
|
|
359
|
+
if (hSnapshot === INVALID_HANDLE_VALUE) {
|
|
360
|
+
throw new Win32Error('CreateToolhelp32Snapshot', Kernel32.GetLastError());
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const { Scratch1080: lpme } = this;
|
|
364
|
+
/* */ lpme.writeUInt32LE(0x438 /* sizeof(MODULEENTRY32W) */);
|
|
365
|
+
|
|
366
|
+
const bModule32FirstW = Kernel32.Module32FirstW(hSnapshot, ptr(lpme));
|
|
367
|
+
|
|
368
|
+
if (!bModule32FirstW) {
|
|
369
|
+
Kernel32.CloseHandle(hSnapshot);
|
|
370
|
+
|
|
371
|
+
throw new Win32Error('Module32FirstW', Kernel32.GetLastError());
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const modules: Memory['__modules'] = {};
|
|
375
|
+
|
|
376
|
+
do {
|
|
377
|
+
const modBaseAddr = lpme.readBigUInt64LE(0x18);
|
|
378
|
+
const modBaseSize = lpme.readUInt32LE(0x20);
|
|
379
|
+
const szModule = lpme.toString('utf16le', 0x30, 0x230).replace(ReplaceTrailingNull, '');
|
|
380
|
+
|
|
381
|
+
modules[szModule] = Object.freeze({ base: modBaseAddr, name: szModule, size: modBaseSize });
|
|
382
|
+
} while (Kernel32.Module32NextW(hSnapshot, ptr(lpme)));
|
|
383
|
+
|
|
384
|
+
Kernel32.CloseHandle(hSnapshot);
|
|
385
|
+
|
|
386
|
+
this.__modules = Object.freeze(modules);
|
|
387
|
+
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
|
|
302
391
|
/**
|
|
303
392
|
* Writes a buffer to memory.
|
|
304
393
|
* @param address Address to write to.
|
|
@@ -314,16 +403,18 @@ class Memory {
|
|
|
314
403
|
* ```
|
|
315
404
|
*/
|
|
316
405
|
public write(address: bigint, scratch: Scratch, force: boolean = false): this {
|
|
406
|
+
const { hProcess } = this;
|
|
407
|
+
|
|
317
408
|
const lpBaseAddress = address;
|
|
318
409
|
const lpBuffer = scratch.ptr;
|
|
319
410
|
const nSize = BigInt(scratch.byteLength);
|
|
320
411
|
const numberOfBytesWritten = 0x00n;
|
|
321
412
|
|
|
322
413
|
if (!force) {
|
|
323
|
-
const bWriteProcessMemory = WriteProcessMemory(
|
|
414
|
+
const bWriteProcessMemory = Kernel32.WriteProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, numberOfBytesWritten);
|
|
324
415
|
|
|
325
416
|
if (!bWriteProcessMemory) {
|
|
326
|
-
throw new Win32Error('WriteProcessMemory', GetLastError());
|
|
417
|
+
throw new Win32Error('WriteProcessMemory', Kernel32.GetLastError());
|
|
327
418
|
}
|
|
328
419
|
|
|
329
420
|
return this;
|
|
@@ -333,26 +424,26 @@ class Memory {
|
|
|
333
424
|
const flNewProtect = 0x40; /* PAGE_EXECUTE_READWRITE */
|
|
334
425
|
const lpflOldProtect = Buffer.allocUnsafe(0x04);
|
|
335
426
|
|
|
336
|
-
const bVirtualProtectEx = VirtualProtectEx(
|
|
427
|
+
const bVirtualProtectEx = Kernel32.VirtualProtectEx(hProcess, lpBaseAddress, dwSize, flNewProtect, lpflOldProtect.ptr);
|
|
337
428
|
|
|
338
429
|
if (!bVirtualProtectEx) {
|
|
339
|
-
throw new Win32Error('VirtualProtectEx', GetLastError());
|
|
430
|
+
throw new Win32Error('VirtualProtectEx', Kernel32.GetLastError());
|
|
340
431
|
}
|
|
341
432
|
|
|
342
433
|
try {
|
|
343
|
-
const bWriteProcessMemory = WriteProcessMemory(
|
|
434
|
+
const bWriteProcessMemory = Kernel32.WriteProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, numberOfBytesWritten);
|
|
344
435
|
|
|
345
436
|
if (!bWriteProcessMemory) {
|
|
346
|
-
throw new Win32Error('WriteProcessMemory', GetLastError());
|
|
437
|
+
throw new Win32Error('WriteProcessMemory', Kernel32.GetLastError());
|
|
347
438
|
}
|
|
348
439
|
} finally {
|
|
349
440
|
const flNewProtect2 = lpflOldProtect.readUInt32LE(0x00);
|
|
350
441
|
const lpflOldProtect2 = Buffer.allocUnsafe(0x04);
|
|
351
442
|
|
|
352
|
-
const bVirtualProtectEx2 = VirtualProtectEx(
|
|
443
|
+
const bVirtualProtectEx2 = Kernel32.VirtualProtectEx(hProcess, lpBaseAddress, dwSize, flNewProtect2, lpflOldProtect2.ptr);
|
|
353
444
|
|
|
354
445
|
if (!bVirtualProtectEx2) {
|
|
355
|
-
throw new Win32Error('VirtualProtectEx', GetLastError());
|
|
446
|
+
throw new Win32Error('VirtualProtectEx', Kernel32.GetLastError());
|
|
356
447
|
}
|
|
357
448
|
}
|
|
358
449
|
|
|
@@ -375,13 +466,13 @@ class Memory {
|
|
|
375
466
|
public bool(address: bigint): boolean;
|
|
376
467
|
public bool(address: bigint, value: boolean, force?: boolean): this;
|
|
377
468
|
public bool(address: bigint, value?: boolean, force?: boolean): boolean | this {
|
|
378
|
-
const Scratch1 = this
|
|
469
|
+
const { Scratch1 } = this;
|
|
379
470
|
|
|
380
471
|
if (value === undefined) {
|
|
381
472
|
return this.read(address, Scratch1)[0x00] !== 0;
|
|
382
473
|
}
|
|
383
474
|
|
|
384
|
-
Scratch1[0x00] = value ?
|
|
475
|
+
Scratch1[0x00] = value ? 0x01 : 0x00;
|
|
385
476
|
|
|
386
477
|
void this.write(address, Scratch1, force);
|
|
387
478
|
|
|
@@ -471,7 +562,7 @@ class Memory {
|
|
|
471
562
|
public f32(address: bigint): number;
|
|
472
563
|
public f32(address: bigint, value: number, force?: boolean): this;
|
|
473
564
|
public f32(address: bigint, value?: number, force?: boolean): number | this {
|
|
474
|
-
const Scratch4Float32Array = this
|
|
565
|
+
const { Scratch4Float32Array } = this; // prettier-ignore
|
|
475
566
|
|
|
476
567
|
if (value === undefined) {
|
|
477
568
|
return this.read(address, Scratch4Float32Array)[0x00];
|
|
@@ -532,7 +623,7 @@ class Memory {
|
|
|
532
623
|
public f64(address: bigint): number;
|
|
533
624
|
public f64(address: bigint, value: number, force?: boolean): this;
|
|
534
625
|
public f64(address: bigint, value?: number, force?: boolean): number | this {
|
|
535
|
-
const Scratch8Float64Array = this
|
|
626
|
+
const { Scratch8Float64Array } = this; // prettier-ignore
|
|
536
627
|
|
|
537
628
|
if (value === undefined) {
|
|
538
629
|
return this.read(address, Scratch8Float64Array)[0x00];
|
|
@@ -593,7 +684,7 @@ class Memory {
|
|
|
593
684
|
public i16(address: bigint): number;
|
|
594
685
|
public i16(address: bigint, value: number, force?: boolean): this;
|
|
595
686
|
public i16(address: bigint, value?: number, force?: boolean): number | this {
|
|
596
|
-
const Scratch2Int16Array = this
|
|
687
|
+
const { Scratch2Int16Array } = this; // prettier-ignore
|
|
597
688
|
|
|
598
689
|
if (value === undefined) {
|
|
599
690
|
return this.read(address, Scratch2Int16Array)[0x00];
|
|
@@ -654,7 +745,7 @@ class Memory {
|
|
|
654
745
|
public i32(address: bigint): number;
|
|
655
746
|
public i32(address: bigint, value: number, force?: boolean): this;
|
|
656
747
|
public i32(address: bigint, value?: number, force?: boolean): number | this {
|
|
657
|
-
const Scratch4Int32Array = this
|
|
748
|
+
const { Scratch4Int32Array } = this;
|
|
658
749
|
|
|
659
750
|
if (value === undefined) {
|
|
660
751
|
return this.read(address, Scratch4Int32Array)[0x00];
|
|
@@ -715,7 +806,7 @@ class Memory {
|
|
|
715
806
|
public i64(address: bigint): bigint;
|
|
716
807
|
public i64(address: bigint, value: bigint, force?: boolean): this;
|
|
717
808
|
public i64(address: bigint, value?: bigint, force?: boolean): bigint | this {
|
|
718
|
-
const Scratch8BigInt64Array = this
|
|
809
|
+
const { Scratch8BigInt64Array } = this;
|
|
719
810
|
|
|
720
811
|
if (value === undefined) {
|
|
721
812
|
return this.read(address, Scratch8BigInt64Array)[0x00];
|
|
@@ -776,7 +867,7 @@ class Memory {
|
|
|
776
867
|
public i8(address: bigint): number;
|
|
777
868
|
public i8(address: bigint, value: number, force?: boolean): this;
|
|
778
869
|
public i8(address: bigint, value?: number, force?: boolean): number | this {
|
|
779
|
-
const Scratch1Int8Array = this
|
|
870
|
+
const { Scratch1Int8Array } = this;
|
|
780
871
|
|
|
781
872
|
if (value === undefined) {
|
|
782
873
|
return this.read(address, Scratch1Int8Array)[0x00];
|
|
@@ -936,7 +1027,7 @@ class Memory {
|
|
|
936
1027
|
public point(address: bigint): Point;
|
|
937
1028
|
public point(address: bigint, value: Point, force?: boolean): this;
|
|
938
1029
|
public point(address: bigint, value?: Point, force?: boolean): Point | this {
|
|
939
|
-
const Scratch8Float32Array = this
|
|
1030
|
+
const { Scratch8Float32Array } = this;
|
|
940
1031
|
|
|
941
1032
|
if (value === undefined) {
|
|
942
1033
|
void this.read(address, Scratch8Float32Array);
|
|
@@ -980,8 +1071,8 @@ class Memory {
|
|
|
980
1071
|
const result = new Array<Vector2>(length);
|
|
981
1072
|
|
|
982
1073
|
for (let i = 0, j = 0; i < length; i++, j += 0x02) {
|
|
983
|
-
const x = scratch[j]
|
|
984
|
-
|
|
1074
|
+
const x = scratch[j],
|
|
1075
|
+
y = scratch[j + 0x01]; // prettier-ignore
|
|
985
1076
|
|
|
986
1077
|
result[i] = { x, y };
|
|
987
1078
|
}
|
|
@@ -1048,7 +1139,7 @@ class Memory {
|
|
|
1048
1139
|
public qAngle(address: bigint): QAngle;
|
|
1049
1140
|
public qAngle(address: bigint, value: QAngle, force?: boolean): this;
|
|
1050
1141
|
public qAngle(address: bigint, value?: QAngle, force?: boolean): QAngle | this {
|
|
1051
|
-
const Scratch12Float32Array = this
|
|
1142
|
+
const { Scratch12Float32Array } = this;
|
|
1052
1143
|
|
|
1053
1144
|
if (value === undefined) {
|
|
1054
1145
|
void this.read(address, Scratch12Float32Array);
|
|
@@ -1093,9 +1184,9 @@ class Memory {
|
|
|
1093
1184
|
const result = new Array<QAngle>(length);
|
|
1094
1185
|
|
|
1095
1186
|
for (let i = 0, j = 0; i < length; i++, j += 0x03) {
|
|
1096
|
-
const pitch = scratch[j]
|
|
1097
|
-
|
|
1098
|
-
|
|
1187
|
+
const pitch = scratch[j],
|
|
1188
|
+
yaw = scratch[j + 0x01],
|
|
1189
|
+
roll = scratch[j + 0x02]; // prettier-ignore
|
|
1099
1190
|
|
|
1100
1191
|
result[i] = { pitch, yaw, roll };
|
|
1101
1192
|
}
|
|
@@ -1164,7 +1255,7 @@ class Memory {
|
|
|
1164
1255
|
public quaternion(address: bigint): Quaternion;
|
|
1165
1256
|
public quaternion(address: bigint, value: Quaternion, force?: boolean): this;
|
|
1166
1257
|
public quaternion(address: bigint, value?: Quaternion, force?: boolean): Quaternion | this {
|
|
1167
|
-
const Scratch16Float32Array = this
|
|
1258
|
+
const { Scratch16Float32Array } = this;
|
|
1168
1259
|
|
|
1169
1260
|
if (value === undefined) {
|
|
1170
1261
|
void this.read(address, Scratch16Float32Array);
|
|
@@ -1285,7 +1376,7 @@ class Memory {
|
|
|
1285
1376
|
public rgb(address: bigint): RGB;
|
|
1286
1377
|
public rgb(address: bigint, value: RGB, force?: boolean): this;
|
|
1287
1378
|
public rgb(address: bigint, value?: RGB, force?: boolean): RGB | this {
|
|
1288
|
-
const Scratch3 = this
|
|
1379
|
+
const { Scratch3 } = this;
|
|
1289
1380
|
|
|
1290
1381
|
if (value === undefined) {
|
|
1291
1382
|
void this.read(address, Scratch3);
|
|
@@ -1320,7 +1411,6 @@ class Memory {
|
|
|
1320
1411
|
* ```
|
|
1321
1412
|
*/
|
|
1322
1413
|
public rgbRaw(address: bigint): Uint8Array;
|
|
1323
|
-
public rgbRaw(address: bigint): Uint8Array;
|
|
1324
1414
|
public rgbRaw(address: bigint, values: Buffer | Uint8Array | Uint8ClampedArray, force?: boolean): this;
|
|
1325
1415
|
public rgbRaw(address: bigint, values?: Buffer | Uint8Array | Uint8ClampedArray, force?: boolean): Uint8Array | this {
|
|
1326
1416
|
if (values === undefined) {
|
|
@@ -1352,7 +1442,7 @@ class Memory {
|
|
|
1352
1442
|
public rgba(address: bigint): RGBA;
|
|
1353
1443
|
public rgba(address: bigint, value: RGBA, force?: boolean): this;
|
|
1354
1444
|
public rgba(address: bigint, value?: RGBA, force?: boolean): RGBA | this {
|
|
1355
|
-
const Scratch4 = this
|
|
1445
|
+
const { Scratch4 } = this;
|
|
1356
1446
|
|
|
1357
1447
|
if (value === undefined) {
|
|
1358
1448
|
void this.read(address, Scratch4);
|
|
@@ -1389,7 +1479,6 @@ class Memory {
|
|
|
1389
1479
|
* ```
|
|
1390
1480
|
*/
|
|
1391
1481
|
public rgbaRaw(address: bigint): Uint8Array;
|
|
1392
|
-
public rgbaRaw(address: bigint): Uint8Array;
|
|
1393
1482
|
public rgbaRaw(address: bigint, values: Buffer | Uint8Array | Uint8ClampedArray, force?: boolean): this;
|
|
1394
1483
|
public rgbaRaw(address: bigint, values?: Buffer | Uint8Array | Uint8ClampedArray, force?: boolean): Uint8Array | this {
|
|
1395
1484
|
if (values === undefined) {
|
|
@@ -1421,7 +1510,6 @@ class Memory {
|
|
|
1421
1510
|
* ```
|
|
1422
1511
|
*/
|
|
1423
1512
|
public string(address: bigint, length: number): string;
|
|
1424
|
-
public string(address: bigint, length: number): string;
|
|
1425
1513
|
public string(address: bigint, value: string, force?: boolean): this;
|
|
1426
1514
|
public string(address: bigint, lengthOrValue: number | string, force?: boolean): string | this {
|
|
1427
1515
|
if (typeof lengthOrValue === 'number') {
|
|
@@ -1461,7 +1549,7 @@ class Memory {
|
|
|
1461
1549
|
public u16(address: bigint): number;
|
|
1462
1550
|
public u16(address: bigint, value: number, force?: boolean): this;
|
|
1463
1551
|
public u16(address: bigint, value?: number, force?: boolean): number | this {
|
|
1464
|
-
const Scratch2Uint16Array = this
|
|
1552
|
+
const { Scratch2Uint16Array } = this;
|
|
1465
1553
|
|
|
1466
1554
|
if (value === undefined) {
|
|
1467
1555
|
return this.read(address, Scratch2Uint16Array)[0x00];
|
|
@@ -1522,7 +1610,7 @@ class Memory {
|
|
|
1522
1610
|
public u32(address: bigint): number;
|
|
1523
1611
|
public u32(address: bigint, value: number, force?: boolean): this;
|
|
1524
1612
|
public u32(address: bigint, value?: number, force?: boolean): number | this {
|
|
1525
|
-
const Scratch4Uint32Array = this
|
|
1613
|
+
const { Scratch4Uint32Array } = this;
|
|
1526
1614
|
|
|
1527
1615
|
if (value === undefined) {
|
|
1528
1616
|
return this.read(address, Scratch4Uint32Array)[0x00];
|
|
@@ -1583,7 +1671,7 @@ class Memory {
|
|
|
1583
1671
|
public u64(address: bigint): bigint;
|
|
1584
1672
|
public u64(address: bigint, value: bigint, force?: boolean): this;
|
|
1585
1673
|
public u64(address: bigint, value?: bigint, force?: boolean): bigint | this {
|
|
1586
|
-
const Scratch8BigUint64Array = this
|
|
1674
|
+
const { Scratch8BigUint64Array } = this;
|
|
1587
1675
|
|
|
1588
1676
|
if (value === undefined) {
|
|
1589
1677
|
return this.read(address, Scratch8BigUint64Array)[0x00];
|
|
@@ -1644,7 +1732,7 @@ class Memory {
|
|
|
1644
1732
|
public u8(address: bigint): number;
|
|
1645
1733
|
public u8(address: bigint, value: number, force?: boolean): this;
|
|
1646
1734
|
public u8(address: bigint, value?: number, force?: boolean): number | this {
|
|
1647
|
-
const Scratch1 = this
|
|
1735
|
+
const { Scratch1 } = this;
|
|
1648
1736
|
|
|
1649
1737
|
if (value === undefined) {
|
|
1650
1738
|
return this.read(address, Scratch1)[0x00];
|
|
@@ -1998,7 +2086,7 @@ class Memory {
|
|
|
1998
2086
|
public vector3(address: bigint): Vector3;
|
|
1999
2087
|
public vector3(address: bigint, value: Vector3, force?: boolean): this;
|
|
2000
2088
|
public vector3(address: bigint, value?: Vector3, force?: boolean): Vector3 | this {
|
|
2001
|
-
const Scratch12Float32Array = this
|
|
2089
|
+
const { Scratch12Float32Array } = this;
|
|
2002
2090
|
|
|
2003
2091
|
if (value === undefined) {
|
|
2004
2092
|
void this.read(address, Scratch12Float32Array);
|
|
@@ -2147,7 +2235,6 @@ class Memory {
|
|
|
2147
2235
|
return this.quaternionArray(address, lengthOrValues, force);
|
|
2148
2236
|
}
|
|
2149
2237
|
|
|
2150
|
-
public vector4Raw(address: bigint): Float32Array;
|
|
2151
2238
|
/**
|
|
2152
2239
|
* Reads or writes a raw Vector4 as a Float32Array.
|
|
2153
2240
|
* @param address Address to access.
|
|
@@ -2277,7 +2364,7 @@ class Memory {
|
|
|
2277
2364
|
public pattern(needle: string, address: bigint, length: number, all: false): bigint;
|
|
2278
2365
|
public pattern(needle: string, address: bigint, length: number, all: true): bigint[];
|
|
2279
2366
|
public pattern(needle: string, address: bigint, length: number, all: boolean = false): bigint | bigint[] {
|
|
2280
|
-
const test = Memory.Patterns.
|
|
2367
|
+
const test = Memory.Patterns.PatternTest.test(needle);
|
|
2281
2368
|
|
|
2282
2369
|
if (!test) {
|
|
2283
2370
|
return !all ? -1n : [];
|
|
@@ -2285,7 +2372,7 @@ class Memory {
|
|
|
2285
2372
|
|
|
2286
2373
|
// The RegExp test ensures that we have at least one token…
|
|
2287
2374
|
|
|
2288
|
-
const tokens = [...needle.matchAll(Memory.Patterns.
|
|
2375
|
+
const tokens = [...needle.matchAll(Memory.Patterns.PatternMatchAll)]
|
|
2289
2376
|
.map((match) => ({ buffer: Buffer.from(match[0], 'hex'), index: match.index >>> 1, length: match[0].length >>> 1 })) //
|
|
2290
2377
|
.sort(({ length: a }, { length: b }) => b - a);
|
|
2291
2378
|
|
package/types/Memory.ts
CHANGED
|
@@ -1,3 +1,34 @@
|
|
|
1
|
+
import type { FFIType, Pointer } from 'bun:ffi';
|
|
2
|
+
|
|
3
|
+
export type CallResult<R extends FFIType> = R extends typeof FFIType.bool
|
|
4
|
+
? boolean
|
|
5
|
+
: R extends typeof FFIType.f32 | typeof FFIType.f64 | typeof FFIType.i8 | typeof FFIType.i16 | typeof FFIType.i32 | typeof FFIType.u8 | typeof FFIType.u16 | typeof FFIType.u32
|
|
6
|
+
? number
|
|
7
|
+
: R extends typeof FFIType.i64 | typeof FFIType.u64
|
|
8
|
+
? bigint
|
|
9
|
+
: R extends typeof FFIType.cstring | typeof FFIType.ptr
|
|
10
|
+
? bigint | Pointer
|
|
11
|
+
: void;
|
|
12
|
+
|
|
13
|
+
export type CallSignature = {
|
|
14
|
+
args: (
|
|
15
|
+
| { type: typeof FFIType.bool; value: boolean }
|
|
16
|
+
| { type: typeof FFIType.cstring; value: ArrayBuffer | ArrayBufferView | bigint | Buffer | null | number | string }
|
|
17
|
+
| { type: typeof FFIType.f32; value: number }
|
|
18
|
+
| { type: typeof FFIType.f64; value: number }
|
|
19
|
+
| { type: typeof FFIType.i8; value: number }
|
|
20
|
+
| { type: typeof FFIType.i16; value: number }
|
|
21
|
+
| { type: typeof FFIType.i32; value: number }
|
|
22
|
+
| { type: typeof FFIType.i64; value: bigint }
|
|
23
|
+
| { type: typeof FFIType.ptr; value: ArrayBuffer | ArrayBufferView | bigint | Buffer | null | number | Pointer }
|
|
24
|
+
| { type: typeof FFIType.u8; value: number }
|
|
25
|
+
| { type: typeof FFIType.u16; value: number }
|
|
26
|
+
| { type: typeof FFIType.u32; value: number }
|
|
27
|
+
| { type: typeof FFIType.u64; value: bigint }
|
|
28
|
+
)[];
|
|
29
|
+
returns: FFIType;
|
|
30
|
+
};
|
|
31
|
+
|
|
1
32
|
/**
|
|
2
33
|
* Represents a loaded module in a process.
|
|
3
34
|
* @property base Base address of the module.
|