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