bun-memory 1.0.2 → 1.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/README.md +42 -39
- package/index.ts +1 -0
- package/package.json +1 -1
- package/structs/Memory.ts +1056 -880
- package/structs/Win32Error.ts +261 -24
package/structs/Memory.ts
CHANGED
|
@@ -1,25 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* This module exposes a {@link Memory} class that can attach to a running process by name
|
|
5
|
-
* and perform high-performance reads and writes directly against the target process'
|
|
6
|
-
* virtual address space. It wraps selected Kernel32 functions via FFI and provides
|
|
7
|
-
* strongly-typed helpers for common primitives and patterns.
|
|
8
|
-
*
|
|
9
|
-
* @remarks
|
|
10
|
-
* - Requires Windows (uses `kernel32.dll`).
|
|
11
|
-
* - Runs under Bun (uses `bun:ffi`).
|
|
12
|
-
* - Use with appropriate privileges; many operations require administrator rights.
|
|
13
|
-
*/
|
|
1
|
+
// TODO: Reintroduce findPattern(…)…
|
|
2
|
+
// TODO: Reintroduce indexOf(…)…
|
|
3
|
+
// TODO: String methods…
|
|
14
4
|
|
|
15
|
-
import { dlopen,
|
|
5
|
+
import { FFIType, dlopen, read } from 'bun:ffi';
|
|
16
6
|
|
|
7
|
+
import type { Module, Quaternion, Region, Scratch, Vector2, Vector3 } from '../types/Memory';
|
|
17
8
|
import Win32Error from './Win32Error';
|
|
18
9
|
|
|
10
|
+
const { f32, f64, i16, i32, i64, i8, u16, u32, u64, u8 } = read;
|
|
11
|
+
|
|
19
12
|
/**
|
|
20
|
-
*
|
|
13
|
+
* Kernel32 Windows API functions imported via Foreign Function Interface (FFI).
|
|
14
|
+
* These functions provide low-level access to process and memory management operations.
|
|
21
15
|
*/
|
|
22
|
-
|
|
23
16
|
const { symbols: Kernel32 } = dlopen('kernel32.dll', {
|
|
24
17
|
CloseHandle: { args: [FFIType.u64], returns: FFIType.bool },
|
|
25
18
|
CreateToolhelp32Snapshot: { args: [FFIType.u32, FFIType.u32], returns: FFIType.u64 },
|
|
@@ -29,83 +22,53 @@ const { symbols: Kernel32 } = dlopen('kernel32.dll', {
|
|
|
29
22
|
OpenProcess: { args: [FFIType.u32, FFIType.bool, FFIType.u32], returns: FFIType.u64 },
|
|
30
23
|
Process32FirstW: { args: [FFIType.u64, FFIType.ptr], returns: FFIType.bool },
|
|
31
24
|
Process32NextW: { args: [FFIType.u64, FFIType.ptr], returns: FFIType.bool },
|
|
32
|
-
ReadProcessMemory: { args: [FFIType.u64, FFIType.u64, FFIType.ptr, FFIType.
|
|
25
|
+
ReadProcessMemory: { args: [FFIType.u64, FFIType.u64, FFIType.ptr, FFIType.u64_fast, FFIType.ptr], returns: FFIType.bool },
|
|
33
26
|
VirtualProtectEx: { args: [FFIType.u64, FFIType.u64, FFIType.u64, FFIType.u32, FFIType.ptr], returns: FFIType.bool },
|
|
34
27
|
VirtualQueryEx: { args: [FFIType.u64, FFIType.u64, FFIType.ptr, FFIType.u64], returns: FFIType.u64 },
|
|
35
|
-
WriteProcessMemory: { args: [FFIType.u64, FFIType.u64, FFIType.ptr, FFIType.
|
|
28
|
+
WriteProcessMemory: { args: [FFIType.u64, FFIType.u64, FFIType.ptr, FFIType.u64_fast, FFIType.ptr], returns: FFIType.bool },
|
|
36
29
|
});
|
|
37
30
|
|
|
38
31
|
/**
|
|
39
|
-
*
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
type Module = {
|
|
43
|
-
modBaseAddr: bigint;
|
|
44
|
-
modBaseSize: number;
|
|
45
|
-
szModule: string;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Three-dimensional vector laid out as three 32‑bit floats (x, y, z).
|
|
50
|
-
*/
|
|
51
|
-
|
|
52
|
-
type Vector3 = {
|
|
53
|
-
x: number;
|
|
54
|
-
y: number;
|
|
55
|
-
z: number;
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Memory region information derived from `MEMORY_BASIC_INFORMATION`.
|
|
60
|
-
*/
|
|
61
|
-
|
|
62
|
-
type Region = {
|
|
63
|
-
base: bigint;
|
|
64
|
-
protect: number;
|
|
65
|
-
size: bigint;
|
|
66
|
-
state: number;
|
|
67
|
-
type: number;
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Attaches to a Windows process and provides safe, typed memory accessors.
|
|
32
|
+
* Memory class provides cross-process memory manipulation capabilities on Windows systems.
|
|
33
|
+
* This class allows reading from and writing to memory addresses in external processes,
|
|
34
|
+
* supporting various data types including primitives, arrays, and custom structures like vectors and quaternions.
|
|
72
35
|
*
|
|
73
36
|
* @example
|
|
74
|
-
* ```
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
* const clientDLL = memory.modules['client.dll'];
|
|
37
|
+
* ```typescript
|
|
38
|
+
* // Connect to a process by name
|
|
39
|
+
* const memory = new Memory('notepad.exe');
|
|
78
40
|
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
* }
|
|
41
|
+
* // Connect to a process by ID
|
|
42
|
+
* const memory = new Memory(1234);
|
|
82
43
|
*
|
|
83
|
-
* //
|
|
84
|
-
* const
|
|
85
|
-
* memory.writeUInt32(clientDLL.modBaseAddr + ammoOffset, 9_999);
|
|
44
|
+
* // Read a 32-bit integer from memory
|
|
45
|
+
* const value = memory.i32(0x12345678n);
|
|
86
46
|
*
|
|
87
|
-
* //
|
|
88
|
-
*
|
|
89
|
-
* const health = memory.readUInt32LE(clientDLL.modBaseAddr + healthOffset);
|
|
90
|
-
* console.log('You have %d health...', health);
|
|
91
|
-
*
|
|
92
|
-
* // Find an offset by pattern...
|
|
93
|
-
* const otherOffset = memory.findPattern('aa??bbccdd??ff', clientDLL.modBaseAddr, clientDLL.modBaseSize);
|
|
94
|
-
* const otherValue = memory.readBoolean(otherOffset + 0x1234n);
|
|
47
|
+
* // Write a float to memory
|
|
48
|
+
* memory.f32(0x12345678n, 3.14159);
|
|
95
49
|
*
|
|
50
|
+
* // Clean up when done
|
|
96
51
|
* memory.close();
|
|
97
52
|
* ```
|
|
98
53
|
*/
|
|
99
|
-
|
|
100
54
|
class Memory {
|
|
101
55
|
/**
|
|
102
|
-
*
|
|
56
|
+
* Creates a new Memory instance and attaches to the specified process.
|
|
57
|
+
*
|
|
58
|
+
* @param identifier - Either a process ID (number) or process name (string)
|
|
59
|
+
* @throws {Win32Error} When process operations fail
|
|
60
|
+
* @throws {Error} When the specified process is not found
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* // Attach to process by name
|
|
65
|
+
* const memory = new Memory('calculator.exe');
|
|
103
66
|
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
67
|
+
* // Attach to process by ID
|
|
68
|
+
* const memory = new Memory(5432);
|
|
69
|
+
* ```
|
|
107
70
|
*/
|
|
108
|
-
constructor(
|
|
71
|
+
constructor(identifier: number | string) {
|
|
109
72
|
const dwFlags = 0x00000002; /* TH32CS_SNAPPROCESS */
|
|
110
73
|
const th32ProcessID = 0;
|
|
111
74
|
|
|
@@ -128,112 +91,135 @@ class Memory {
|
|
|
128
91
|
|
|
129
92
|
do {
|
|
130
93
|
const szExeFile = lppe.toString('utf16le', 0x2c, 0x234).replace(/\0+$/, '');
|
|
94
|
+
const th32ProcessID = lppe.readUInt32LE(0x08);
|
|
131
95
|
|
|
132
|
-
if (
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
96
|
+
if (
|
|
97
|
+
(typeof identifier === 'number' && identifier !== th32ProcessID) || //
|
|
98
|
+
(typeof identifier === 'string' && identifier !== szExeFile)
|
|
99
|
+
) {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
136
102
|
|
|
137
|
-
|
|
103
|
+
const desiredAccess = 0x001f0fff; /* PROCESS_ALL_ACCESS */
|
|
104
|
+
const inheritHandle = false;
|
|
138
105
|
|
|
139
|
-
|
|
140
|
-
Kernel32.CloseHandle(hSnapshot);
|
|
106
|
+
const hProcess = Kernel32.OpenProcess(desiredAccess, inheritHandle, th32ProcessID);
|
|
141
107
|
|
|
142
|
-
|
|
143
|
-
|
|
108
|
+
if (hProcess === 0n) {
|
|
109
|
+
Kernel32.CloseHandle(hSnapshot);
|
|
144
110
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
this.th32ProcessID = th32ProcessID;
|
|
111
|
+
throw new Win32Error('OpenProcess', Kernel32.GetLastError());
|
|
112
|
+
}
|
|
148
113
|
|
|
149
|
-
|
|
114
|
+
this._modules = {};
|
|
115
|
+
this.hProcess = hProcess;
|
|
116
|
+
this.th32ProcessID = th32ProcessID;
|
|
150
117
|
|
|
151
|
-
|
|
118
|
+
this.refresh();
|
|
152
119
|
|
|
153
|
-
|
|
154
|
-
|
|
120
|
+
Kernel32.CloseHandle(hSnapshot);
|
|
121
|
+
|
|
122
|
+
return;
|
|
155
123
|
} while (Kernel32.Process32NextW(hSnapshot, lppe));
|
|
156
124
|
|
|
157
125
|
Kernel32.CloseHandle(hSnapshot);
|
|
158
126
|
|
|
159
|
-
throw new Error(`Process not found: ${
|
|
127
|
+
throw new Error(`Process not found: ${identifier}…`);
|
|
160
128
|
}
|
|
161
129
|
|
|
162
|
-
|
|
163
|
-
|
|
130
|
+
/**
|
|
131
|
+
* Memory protection constants used for determining safe memory regions.
|
|
132
|
+
* Safe regions can be read from or written to, while unsafe regions should be avoided.
|
|
133
|
+
*/
|
|
164
134
|
private static readonly MemoryProtections = {
|
|
165
135
|
Safe: 0x10 /* PAGE_EXECUTE */ | 0x20 /* PAGE_EXECUTE_READ */ | 0x40 /* PAGE_EXECUTE_READWRITE */ | 0x80 /* PAGE_EXECUTE_WRITECOPY */ | 0x02 /* PAGE_READONLY */ | 0x04 /* PAGE_READWRITE */ | 0x08 /* PAGE_WRITECOPY */,
|
|
166
136
|
Unsafe: 0x100 /* PAGE_GUARD */ | 0x01 /* PAGE_NOACCESS */,
|
|
167
137
|
};
|
|
168
138
|
|
|
169
|
-
|
|
170
|
-
|
|
139
|
+
/**
|
|
140
|
+
* Internal storage for loaded modules information.
|
|
141
|
+
*/
|
|
142
|
+
private _modules: { [key: string]: Module };
|
|
171
143
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
private
|
|
177
|
-
private
|
|
144
|
+
/**
|
|
145
|
+
* Pre-allocated scratch buffers for memory operations to avoid repeated allocations.
|
|
146
|
+
* These buffers are reused across multiple read/write operations for performance.
|
|
147
|
+
*/
|
|
148
|
+
private readonly Scratch1 = new Uint8Array(0x01);
|
|
149
|
+
private readonly Scratch2 = new Uint8Array(0x02);
|
|
150
|
+
private readonly Scratch4 = new Uint8Array(0x04);
|
|
151
|
+
private readonly Scratch8 = new Uint8Array(0x08);
|
|
152
|
+
private readonly Scratch12 = new Uint8Array(0x0c);
|
|
153
|
+
private readonly Scratch16 = new Uint8Array(0x10);
|
|
178
154
|
|
|
155
|
+
/**
|
|
156
|
+
* Buffer views of the scratch arrays for easier data manipulation.
|
|
157
|
+
*/
|
|
158
|
+
private readonly Scratch1Buffer = Buffer.from(this.Scratch1.buffer, this.Scratch1.byteOffset, this.Scratch1.byteLength);
|
|
159
|
+
private readonly Scratch2Buffer = Buffer.from(this.Scratch2.buffer, this.Scratch2.byteOffset, this.Scratch2.byteLength);
|
|
160
|
+
private readonly Scratch4Buffer = Buffer.from(this.Scratch4.buffer, this.Scratch4.byteOffset, this.Scratch4.byteLength);
|
|
161
|
+
private readonly Scratch8Buffer = Buffer.from(this.Scratch8.buffer, this.Scratch8.byteOffset, this.Scratch8.byteLength);
|
|
162
|
+
private readonly Scratch12Buffer = Buffer.from(this.Scratch12.buffer, this.Scratch12.byteOffset, this.Scratch12.byteLength);
|
|
163
|
+
private readonly Scratch16Buffer = Buffer.from(this.Scratch16.buffer, this.Scratch16.byteOffset, this.Scratch16.byteLength);
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Scratch buffers for Windows API structures.
|
|
167
|
+
*/
|
|
179
168
|
private readonly ScratchMemoryBasicInformation = Buffer.allocUnsafe(0x30 /* sizeof(MEMORY_BASIC_INFORMATION) */);
|
|
180
169
|
private readonly ScratchModuleEntry32W = Buffer.allocUnsafe(0x438 /* sizeof(MODULEENTRY32W) */);
|
|
181
170
|
|
|
182
|
-
private _modules!: { [key: string]: Module };
|
|
183
|
-
|
|
184
171
|
/**
|
|
185
|
-
*
|
|
172
|
+
* Handle to the target process.
|
|
186
173
|
*/
|
|
187
|
-
|
|
188
|
-
public readonly hProcess: bigint;
|
|
189
|
-
public readonly szModule: string;
|
|
174
|
+
private readonly hProcess: bigint;
|
|
190
175
|
|
|
191
176
|
/**
|
|
192
|
-
*
|
|
177
|
+
* Process ID of the target process.
|
|
193
178
|
*/
|
|
194
|
-
|
|
195
|
-
public readonly th32ProcessID: number;
|
|
196
|
-
|
|
197
|
-
public get modBaseAddr(): Memory['_modules'][string]['modBaseAddr'] {
|
|
198
|
-
return this.modules[this.szModule].modBaseAddr;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
public get modBaseSize(): Memory['_modules'][string]['modBaseSize'] {
|
|
202
|
-
return this.modules[this.szModule].modBaseSize;
|
|
203
|
-
}
|
|
179
|
+
private readonly th32ProcessID: number;
|
|
204
180
|
|
|
205
181
|
/**
|
|
206
|
-
*
|
|
182
|
+
* Gets the loaded modules for the target process.
|
|
183
|
+
*
|
|
184
|
+
* @returns A frozen object containing module information indexed by module name
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* ```typescript
|
|
188
|
+
* const memory = new Memory('notepad.exe');
|
|
189
|
+
* const modules = memory.modules;
|
|
207
190
|
*
|
|
208
|
-
*
|
|
191
|
+
* // Access a specific module
|
|
192
|
+
* const mainModule = modules['notepad.exe'];
|
|
193
|
+
* console.log(`Base address: 0x${mainModule.base.toString(16)}`);
|
|
194
|
+
* console.log(`Size: ${mainModule.size} bytes`);
|
|
195
|
+
* ```
|
|
209
196
|
*/
|
|
210
|
-
|
|
211
197
|
public get modules(): Memory['_modules'] {
|
|
212
198
|
return this._modules;
|
|
213
199
|
}
|
|
214
200
|
|
|
215
|
-
//
|
|
201
|
+
// Internal methods
|
|
216
202
|
|
|
217
203
|
/**
|
|
218
|
-
*
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Enumerate committed, readable/executable memory regions within the given range.
|
|
204
|
+
* Retrieves memory region information for a specified address range.
|
|
205
|
+
* This method queries the virtual memory layout to identify safe regions for memory operations.
|
|
206
|
+
*
|
|
207
|
+
* @param address - Starting memory address
|
|
208
|
+
* @param length - Length of the memory range to query
|
|
209
|
+
* @returns Array of Region objects describing the memory layout
|
|
210
|
+
* @throws {Win32Error} When memory query operations fail
|
|
229
211
|
*
|
|
230
|
-
* @
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
212
|
+
* @example
|
|
213
|
+
* ```typescript
|
|
214
|
+
* const memory = new Memory('notepad.exe');
|
|
215
|
+
* const regions = memory.regions(0x10000000n, 0x1000n);
|
|
216
|
+
*
|
|
217
|
+
* regions.forEach(region => {
|
|
218
|
+
* console.log(`Region: 0x${region.base.toString(16)} - Size: ${region.size}`);
|
|
219
|
+
* });
|
|
220
|
+
* ```
|
|
234
221
|
*/
|
|
235
|
-
|
|
236
|
-
private regions(address: bigint | number, length: bigint | number): Region[] {
|
|
222
|
+
private regions(address: bigint, length: bigint | number): Region[] {
|
|
237
223
|
const dwLength = 0x30; /* sizeof(MEMORY_BASIC_INFORMATION) */
|
|
238
224
|
let lpAddress = BigInt(address); // prettier-ignore
|
|
239
225
|
const lpBuffer = this.ScratchMemoryBasicInformation;
|
|
@@ -264,1035 +250,1225 @@ class Memory {
|
|
|
264
250
|
return result;
|
|
265
251
|
}
|
|
266
252
|
|
|
253
|
+
// Core memory operations
|
|
254
|
+
|
|
267
255
|
/**
|
|
268
|
-
*
|
|
256
|
+
* Reads data from the target process memory into a scratch buffer.
|
|
257
|
+
* This is a low-level method used internally by the typed read methods.
|
|
269
258
|
*
|
|
270
|
-
* @
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
259
|
+
* @param address - Memory address to read from
|
|
260
|
+
* @param scratch - Buffer to store the read data
|
|
261
|
+
* @returns This Memory instance for method chaining
|
|
262
|
+
* @throws {Win32Error} When the read operation fails
|
|
263
|
+
*
|
|
264
|
+
* @example
|
|
265
|
+
* ```typescript
|
|
266
|
+
* const memory = new Memory('notepad.exe');
|
|
267
|
+
* const buffer = new Uint8Array(4);
|
|
268
|
+
* memory.read(0x12345678n, buffer);
|
|
269
|
+
* ```
|
|
270
|
+
*/
|
|
271
|
+
public read(address: bigint, scratch: Scratch): this {
|
|
272
|
+
const lpBaseAddress = address;
|
|
273
|
+
const lpBuffer = scratch.ptr;
|
|
274
|
+
const nSize = scratch.byteLength;
|
|
275
|
+
const numberOfBytesRead = 0x00n;
|
|
275
276
|
|
|
276
|
-
const
|
|
277
|
+
const bReadProcessMemory = Kernel32.ReadProcessMemory(this.hProcess, lpBaseAddress, lpBuffer, nSize, numberOfBytesRead);
|
|
277
278
|
|
|
278
|
-
if (
|
|
279
|
-
throw new Win32Error('
|
|
279
|
+
if (!bReadProcessMemory) {
|
|
280
|
+
throw new Win32Error('ReadProcessMemory', Kernel32.GetLastError());
|
|
280
281
|
}
|
|
281
282
|
|
|
282
|
-
|
|
283
|
-
|
|
283
|
+
return this;
|
|
284
|
+
}
|
|
284
285
|
|
|
285
|
-
|
|
286
|
+
/**
|
|
287
|
+
* Writes data from a scratch buffer to the target process memory.
|
|
288
|
+
* This is a low-level method used internally by the typed write methods.
|
|
289
|
+
*
|
|
290
|
+
* @param address - Memory address to write to
|
|
291
|
+
* @param scratch - Buffer containing the data to write
|
|
292
|
+
* @throws {Win32Error} When the write operation fails
|
|
293
|
+
*
|
|
294
|
+
* @example
|
|
295
|
+
* ```typescript
|
|
296
|
+
* const memory = new Memory('notepad.exe');
|
|
297
|
+
* const buffer = new Uint8Array([0x41, 0x42, 0x43, 0x44]);
|
|
298
|
+
* memory.write(0x12345678n, buffer);
|
|
299
|
+
* ```
|
|
300
|
+
*/
|
|
301
|
+
private write(address: bigint, scratch: Scratch): void {
|
|
302
|
+
const lpBaseAddress = address;
|
|
303
|
+
const lpBuffer = scratch.ptr;
|
|
304
|
+
const nSize = scratch.byteLength;
|
|
305
|
+
const numberOfBytesWritten = 0x00n;
|
|
286
306
|
|
|
287
|
-
|
|
288
|
-
Kernel32.CloseHandle(hSnapshot);
|
|
307
|
+
const WriteProcessMemory = Kernel32.WriteProcessMemory(this.hProcess, lpBaseAddress, lpBuffer, nSize, numberOfBytesWritten);
|
|
289
308
|
|
|
290
|
-
|
|
309
|
+
if (!WriteProcessMemory) {
|
|
310
|
+
throw new Win32Error('WriteProcessMemory', Kernel32.GetLastError());
|
|
291
311
|
}
|
|
292
312
|
|
|
293
|
-
const modules: Memory['_modules'] = {};
|
|
294
|
-
|
|
295
|
-
do {
|
|
296
|
-
const modBaseAddr = lpme.readBigUInt64LE(0x18);
|
|
297
|
-
const modBaseSize = lpme.readUInt32LE(0x20);
|
|
298
|
-
const szModule = lpme.toString('utf16le', 0x30, 0x230).replace(/\0+$/, '');
|
|
299
|
-
|
|
300
|
-
modules[szModule] = { modBaseAddr, modBaseSize, szModule };
|
|
301
|
-
} while (Kernel32.Module32NextW(hSnapshot, lpme));
|
|
302
|
-
|
|
303
|
-
Kernel32.CloseHandle(hSnapshot);
|
|
304
|
-
|
|
305
|
-
this._modules = Object.freeze(modules);
|
|
306
|
-
|
|
307
313
|
return;
|
|
308
314
|
}
|
|
309
315
|
|
|
310
|
-
//
|
|
311
|
-
|
|
312
|
-
// QoL methods…
|
|
316
|
+
// Public utility methods
|
|
313
317
|
|
|
314
318
|
/**
|
|
315
|
-
*
|
|
319
|
+
* Closes the handle to the target process and releases resources.
|
|
320
|
+
* This method should be called when the Memory instance is no longer needed.
|
|
316
321
|
*
|
|
317
|
-
* @
|
|
318
|
-
*
|
|
319
|
-
*
|
|
320
|
-
*
|
|
321
|
-
*
|
|
322
|
+
* @example
|
|
323
|
+
* ```typescript
|
|
324
|
+
* const memory = new Memory('notepad.exe');
|
|
325
|
+
* // ... perform memory operations
|
|
326
|
+
* memory.close(); // Clean up resources
|
|
327
|
+
* ```
|
|
322
328
|
*/
|
|
329
|
+
public close(): void {
|
|
330
|
+
Kernel32.CloseHandle(this.hProcess);
|
|
323
331
|
|
|
324
|
-
|
|
325
|
-
const { PatternMatchAll, PatternTest } = Memory;
|
|
326
|
-
|
|
327
|
-
address = BigInt(address);
|
|
328
|
-
length = BigInt(length);
|
|
329
|
-
|
|
330
|
-
const test = PatternTest.test(needle);
|
|
331
|
-
|
|
332
|
-
if (!test) {
|
|
333
|
-
return -1n;
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
const actualEnd = address + length;
|
|
337
|
-
const actualStart = address;
|
|
338
|
-
|
|
339
|
-
const needleLength = needle.length >>> 1;
|
|
340
|
-
|
|
341
|
-
const [anchor, ...tokens] = [...needle.matchAll(PatternMatchAll)] //
|
|
342
|
-
.map((match) => ({ buffer: Buffer.from(match[0], 'hex'), index: match.index >>> 1, length: match[0].length >>> 1 }))
|
|
343
|
-
.sort(({ buffer: { length: a } }, { buffer: { length: b } }) => b - a);
|
|
344
|
-
|
|
345
|
-
const regions = this.regions(address, length);
|
|
346
|
-
|
|
347
|
-
for (const region of regions) {
|
|
348
|
-
const regionEnd = region.base + region.size;
|
|
349
|
-
const regionStart = region.base;
|
|
350
|
-
|
|
351
|
-
const scanEnd = regionEnd < actualEnd ? regionEnd : actualEnd;
|
|
352
|
-
const scanStart = regionStart > actualStart ? regionStart : actualStart;
|
|
353
|
-
|
|
354
|
-
const scanLength = scanEnd - scanStart;
|
|
355
|
-
|
|
356
|
-
if (needleLength > scanLength) {
|
|
357
|
-
continue;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
const haystack = this.readBuffer(scanStart, scanLength);
|
|
361
|
-
|
|
362
|
-
let indexOf = haystack.indexOf(anchor.buffer, anchor.index);
|
|
363
|
-
|
|
364
|
-
if (indexOf === -1) {
|
|
365
|
-
continue;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
const lastStart = scanLength - BigInt(needleLength);
|
|
369
|
-
|
|
370
|
-
outer: do {
|
|
371
|
-
const matchStart = indexOf - anchor.index;
|
|
372
|
-
|
|
373
|
-
if (lastStart < matchStart) {
|
|
374
|
-
break;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
for (const token of tokens) {
|
|
378
|
-
const sourceEnd = matchStart + token.index + token.length;
|
|
379
|
-
const sourceStart = matchStart + token.index;
|
|
380
|
-
|
|
381
|
-
const targetEnd = token.length;
|
|
382
|
-
const targetStart = 0;
|
|
383
|
-
|
|
384
|
-
const compare = haystack.compare(token.buffer, targetStart, targetEnd, sourceStart, sourceEnd);
|
|
385
|
-
|
|
386
|
-
if (compare !== 0) {
|
|
387
|
-
indexOf = haystack.indexOf(anchor.buffer, indexOf + 1);
|
|
388
|
-
continue outer;
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
return scanStart + BigInt(matchStart);
|
|
393
|
-
} while (indexOf !== -1);
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
return -1n;
|
|
332
|
+
return;
|
|
397
333
|
}
|
|
398
334
|
|
|
399
335
|
/**
|
|
400
|
-
*
|
|
336
|
+
* Refreshes the list of modules loaded in the target process.
|
|
337
|
+
* This method should be called if modules are loaded or unloaded during runtime.
|
|
401
338
|
*
|
|
402
|
-
* @
|
|
403
|
-
*
|
|
404
|
-
* @
|
|
405
|
-
*
|
|
406
|
-
*
|
|
339
|
+
* @throws {Win32Error} When module enumeration fails
|
|
340
|
+
*
|
|
341
|
+
* @example
|
|
342
|
+
* ```typescript
|
|
343
|
+
* const memory = new Memory('notepad.exe');
|
|
344
|
+
*
|
|
345
|
+
* // Initial modules
|
|
346
|
+
* console.log('Initial modules:', Object.keys(memory.modules));
|
|
347
|
+
*
|
|
348
|
+
* // After some time, refresh to get updated module list
|
|
349
|
+
* memory.refresh();
|
|
350
|
+
* console.log('Updated modules:', Object.keys(memory.modules));
|
|
351
|
+
* ```
|
|
407
352
|
*/
|
|
353
|
+
public refresh(): void {
|
|
354
|
+
const dwFlags = 0x00000008 /* TH32CS_SNAPMODULE */ | 0x00000010; /* TH32CS_SNAPMODULE32 */
|
|
408
355
|
|
|
409
|
-
|
|
410
|
-
address = BigInt(address);
|
|
411
|
-
length = BigInt(length);
|
|
412
|
-
|
|
413
|
-
const regions = this.regions(address, length);
|
|
356
|
+
const hSnapshot = Kernel32.CreateToolhelp32Snapshot(dwFlags, this.th32ProcessID)!;
|
|
414
357
|
|
|
415
|
-
|
|
416
|
-
|
|
358
|
+
if (hSnapshot === -1n) {
|
|
359
|
+
throw new Win32Error('CreateToolhelp32Snapshot', Kernel32.GetLastError());
|
|
360
|
+
}
|
|
417
361
|
|
|
418
|
-
|
|
419
|
-
const indexOf = haystack.indexOf(needle, 0, encoding);
|
|
362
|
+
this.ScratchModuleEntry32W.writeUInt32LE(0x438 /* sizeof(MODULEENTRY32W) */);
|
|
420
363
|
|
|
421
|
-
|
|
422
|
-
continue;
|
|
423
|
-
}
|
|
364
|
+
const lpme = this.ScratchModuleEntry32W.ptr;
|
|
424
365
|
|
|
425
|
-
|
|
426
|
-
}
|
|
366
|
+
const bModule32FirstW = Kernel32.Module32FirstW(hSnapshot, lpme);
|
|
427
367
|
|
|
428
|
-
|
|
429
|
-
|
|
368
|
+
if (!bModule32FirstW) {
|
|
369
|
+
Kernel32.CloseHandle(hSnapshot);
|
|
430
370
|
|
|
431
|
-
|
|
371
|
+
throw new Win32Error('Module32FirstW', Kernel32.GetLastError());
|
|
372
|
+
}
|
|
432
373
|
|
|
433
|
-
|
|
434
|
-
* Read a contiguous block as a `BigInt64Array`.
|
|
435
|
-
*
|
|
436
|
-
* @param address Source address.
|
|
437
|
-
* @param length Element count (not bytes).
|
|
438
|
-
* @param scratch Optional destination buffer to avoid allocations.
|
|
439
|
-
* @returns View over the backing buffer as `BigInt64Array`.
|
|
440
|
-
*/
|
|
374
|
+
const modules: Memory['_modules'] = {};
|
|
441
375
|
|
|
442
|
-
|
|
443
|
-
|
|
376
|
+
do {
|
|
377
|
+
const modBaseAddr = this.ScratchModuleEntry32W.readBigUInt64LE(0x18);
|
|
378
|
+
const modBaseSize = this.ScratchModuleEntry32W.readUInt32LE(0x20);
|
|
379
|
+
const szModule = this.ScratchModuleEntry32W.toString('utf16le', 0x30, 0x230).replace(/\0+$/, '');
|
|
444
380
|
|
|
445
|
-
|
|
381
|
+
modules[szModule] = Object.freeze({ base: modBaseAddr, name: szModule, size: modBaseSize });
|
|
382
|
+
} while (Kernel32.Module32NextW(hSnapshot, lpme));
|
|
446
383
|
|
|
447
|
-
|
|
448
|
-
}
|
|
384
|
+
Kernel32.CloseHandle(hSnapshot);
|
|
449
385
|
|
|
450
|
-
|
|
451
|
-
* Read a signed 64‑bit big-endian integer.
|
|
452
|
-
* @param address Source address.
|
|
453
|
-
*/
|
|
386
|
+
this._modules = Object.freeze(modules);
|
|
454
387
|
|
|
455
|
-
|
|
456
|
-
return this.readBuffer(address, 8).readBigInt64BE();
|
|
388
|
+
return;
|
|
457
389
|
}
|
|
458
390
|
|
|
459
|
-
|
|
460
|
-
* Read a signed 64‑bit little-endian integer.
|
|
461
|
-
* @param address Source address.
|
|
462
|
-
*/
|
|
463
|
-
|
|
464
|
-
public readBigInt64LE(address: bigint | number): bigint {
|
|
465
|
-
return this.readBuffer(address, 8).readBigInt64LE();
|
|
466
|
-
}
|
|
391
|
+
// Typed read/write methods
|
|
467
392
|
|
|
468
393
|
/**
|
|
469
|
-
*
|
|
394
|
+
* Reads a boolean value from memory or writes a boolean value to memory.
|
|
470
395
|
*
|
|
471
|
-
* @param address
|
|
472
|
-
* @param
|
|
473
|
-
* @
|
|
474
|
-
*
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
* Read an unsigned 64‑bit big-endian integer.
|
|
487
|
-
* @param address Source address.
|
|
396
|
+
* @param address - Memory address to read from or write to
|
|
397
|
+
* @param value - Optional value to write. If omitted, performs a read operation
|
|
398
|
+
* @returns The boolean value when reading, or this Memory instance when writing
|
|
399
|
+
*
|
|
400
|
+
* @example
|
|
401
|
+
* ```typescript
|
|
402
|
+
* const memory = new Memory('game.exe');
|
|
403
|
+
*
|
|
404
|
+
* // Read a boolean value
|
|
405
|
+
* const isAlive = memory.bool(0x12345678n);
|
|
406
|
+
* console.log('Player is alive:', isAlive);
|
|
407
|
+
*
|
|
408
|
+
* // Write a boolean value
|
|
409
|
+
* memory.bool(0x12345678n, true);
|
|
410
|
+
* ```
|
|
488
411
|
*/
|
|
412
|
+
public bool(address: bigint): boolean;
|
|
413
|
+
public bool(address: bigint, value: boolean): this;
|
|
414
|
+
public bool(address: bigint, value?: boolean): boolean | this {
|
|
415
|
+
if (value === undefined) {
|
|
416
|
+
this.read(address, this.Scratch1);
|
|
489
417
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
}
|
|
418
|
+
return u8(this.Scratch1.ptr) !== 0;
|
|
419
|
+
}
|
|
493
420
|
|
|
494
|
-
|
|
495
|
-
* Read an unsigned 64‑bit little-endian integer.
|
|
496
|
-
* @param address Source address.
|
|
497
|
-
*/
|
|
421
|
+
this.Scratch1Buffer.writeUInt8(+value);
|
|
498
422
|
|
|
499
|
-
|
|
500
|
-
return this.readBuffer(address, 0x08, Memory.Scratch8).readBigUInt64LE();
|
|
501
|
-
}
|
|
423
|
+
this.write(address, this.Scratch1);
|
|
502
424
|
|
|
503
|
-
|
|
504
|
-
* Read a boolean value (non-zero -> `true`).
|
|
505
|
-
* @param address Source address.
|
|
506
|
-
*/
|
|
507
|
-
|
|
508
|
-
public readBoolean(address: bigint | number): boolean {
|
|
509
|
-
return Boolean(this.readUInt8(address));
|
|
425
|
+
return this;
|
|
510
426
|
}
|
|
511
427
|
|
|
512
428
|
/**
|
|
513
|
-
*
|
|
429
|
+
* Reads a 32-bit floating-point value from memory or writes a 32-bit floating-point value to memory.
|
|
430
|
+
*
|
|
431
|
+
* @param address - Memory address to read from or write to
|
|
432
|
+
* @param value - Optional value to write. If omitted, performs a read operation
|
|
433
|
+
* @returns The float value when reading, or this Memory instance when writing
|
|
514
434
|
*
|
|
515
|
-
* @
|
|
516
|
-
*
|
|
517
|
-
*
|
|
518
|
-
*
|
|
519
|
-
*
|
|
435
|
+
* @example
|
|
436
|
+
* ```typescript
|
|
437
|
+
* const memory = new Memory('game.exe');
|
|
438
|
+
*
|
|
439
|
+
* // Read a float value
|
|
440
|
+
* const playerHealth = memory.f32(0x12345678n);
|
|
441
|
+
* console.log('Player health:', playerHealth);
|
|
442
|
+
*
|
|
443
|
+
* // Write a float value
|
|
444
|
+
* memory.f32(0x12345678n, 100.0);
|
|
445
|
+
* ```
|
|
520
446
|
*/
|
|
447
|
+
public f32(address: bigint): number;
|
|
448
|
+
public f32(address: bigint, value: number): this;
|
|
449
|
+
public f32(address: bigint, value?: number): number | this {
|
|
450
|
+
if (value === undefined) {
|
|
451
|
+
this.read(address, this.Scratch4);
|
|
521
452
|
|
|
522
|
-
|
|
523
|
-
const { hProcess } = this;
|
|
524
|
-
|
|
525
|
-
address = BigInt(address);
|
|
526
|
-
length = BigInt(length);
|
|
527
|
-
|
|
528
|
-
const lpBaseAddress = address;
|
|
529
|
-
const lpBuffer = scratch ?? Buffer.allocUnsafe(Number(length));
|
|
530
|
-
const nSize = length;
|
|
531
|
-
const numberOfBytesRead = 0x00n;
|
|
532
|
-
|
|
533
|
-
const bReadProcessMemory = Kernel32.ReadProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, numberOfBytesRead);
|
|
534
|
-
|
|
535
|
-
if (!bReadProcessMemory) {
|
|
536
|
-
throw new Win32Error('ReadProcessMemory', Kernel32.GetLastError());
|
|
453
|
+
return f32(this.Scratch4.ptr);
|
|
537
454
|
}
|
|
538
455
|
|
|
539
|
-
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
/**
|
|
543
|
-
* Read a 64‑bit big-endian IEEE-754 float.
|
|
544
|
-
* @param address Source address.
|
|
545
|
-
*/
|
|
456
|
+
this.Scratch4Buffer.writeFloatLE(value);
|
|
546
457
|
|
|
547
|
-
|
|
548
|
-
return this.readBuffer(address, 0x08, Memory.Scratch8).readDoubleBE();
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
/**
|
|
552
|
-
* Read a 64‑bit little-endian IEEE-754 float.
|
|
553
|
-
* @param address Source address.
|
|
554
|
-
*/
|
|
458
|
+
this.write(address, this.Scratch4);
|
|
555
459
|
|
|
556
|
-
|
|
557
|
-
return this.readBuffer(address, 0x08, Memory.Scratch8).readDoubleLE();
|
|
460
|
+
return this;
|
|
558
461
|
}
|
|
559
462
|
|
|
560
|
-
// + TODO: Implement scratch…
|
|
561
|
-
|
|
562
463
|
/**
|
|
563
|
-
*
|
|
464
|
+
* Reads an array of 32-bit floating-point values from memory or writes an array of 32-bit floating-point values to memory.
|
|
465
|
+
*
|
|
466
|
+
* @param address - Memory address to read from or write to
|
|
467
|
+
* @param lengthOrValues - Length of array to read, or array of values to write
|
|
468
|
+
* @returns Float32Array when reading, or this Memory instance when writing
|
|
564
469
|
*
|
|
565
|
-
* @
|
|
566
|
-
*
|
|
567
|
-
*
|
|
568
|
-
*
|
|
470
|
+
* @example
|
|
471
|
+
* ```typescript
|
|
472
|
+
* const memory = new Memory('game.exe');
|
|
473
|
+
*
|
|
474
|
+
* // Read an array of 10 float values
|
|
475
|
+
* const coordinates = memory.f32Array(0x12345678n, 10);
|
|
476
|
+
* console.log('Coordinates:', coordinates);
|
|
477
|
+
*
|
|
478
|
+
* // Write an array of float values
|
|
479
|
+
* const newCoordinates = new Float32Array([1.0, 2.5, 3.14, 4.2]);
|
|
480
|
+
* memory.f32Array(0x12345678n, newCoordinates);
|
|
481
|
+
* ```
|
|
569
482
|
*/
|
|
483
|
+
public f32Array(address: bigint, length: number): Float32Array;
|
|
484
|
+
public f32Array(address: bigint, values: Float32Array): this;
|
|
485
|
+
public f32Array(address: bigint, lengthOrValues: Float32Array | number): Float32Array | this {
|
|
486
|
+
if (typeof lengthOrValues === 'number') {
|
|
487
|
+
const length = lengthOrValues;
|
|
488
|
+
const scratch = new Float32Array(length);
|
|
570
489
|
|
|
571
|
-
|
|
572
|
-
const buffer = this.readBuffer(address, length * 0x04, scratch);
|
|
490
|
+
this.read(address, scratch);
|
|
573
491
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
return float32Array;
|
|
577
|
-
}
|
|
492
|
+
return scratch;
|
|
493
|
+
}
|
|
578
494
|
|
|
579
|
-
|
|
580
|
-
* Read a 32‑bit big-endian IEEE-754 float.
|
|
581
|
-
* @param address Source address.
|
|
582
|
-
*/
|
|
495
|
+
const values = lengthOrValues;
|
|
583
496
|
|
|
584
|
-
|
|
585
|
-
return this.readBuffer(address, 0x04, Memory.Scratch4).readFloatBE();
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
/**
|
|
589
|
-
* Read a 32‑bit little-endian IEEE-754 float.
|
|
590
|
-
* @param address Source address.
|
|
591
|
-
*/
|
|
497
|
+
this.write(address, values);
|
|
592
498
|
|
|
593
|
-
|
|
594
|
-
return this.readBuffer(address, 0x04, Memory.Scratch4).readFloatLE();
|
|
499
|
+
return this;
|
|
595
500
|
}
|
|
596
501
|
|
|
597
502
|
/**
|
|
598
|
-
*
|
|
599
|
-
*
|
|
503
|
+
* Reads a 64-bit floating-point value from memory or writes a 64-bit floating-point value to memory.
|
|
504
|
+
*
|
|
505
|
+
* @param address - Memory address to read from or write to
|
|
506
|
+
* @param value - Optional value to write. If omitted, performs a read operation
|
|
507
|
+
* @returns The double value when reading, or this Memory instance when writing
|
|
508
|
+
*
|
|
509
|
+
* @example
|
|
510
|
+
* ```typescript
|
|
511
|
+
* const memory = new Memory('scientific_app.exe');
|
|
512
|
+
*
|
|
513
|
+
* // Read a double precision value
|
|
514
|
+
* const preciseValue = memory.f64(0x12345678n);
|
|
515
|
+
* console.log('Precise value:', preciseValue);
|
|
516
|
+
*
|
|
517
|
+
* // Write a double precision value
|
|
518
|
+
* memory.f64(0x12345678n, 3.141592653589793);
|
|
519
|
+
* ```
|
|
600
520
|
*/
|
|
521
|
+
public f64(address: bigint): number;
|
|
522
|
+
public f64(address: bigint, value: number): this;
|
|
523
|
+
public f64(address: bigint, value?: number): number | this {
|
|
524
|
+
if (value === undefined) {
|
|
525
|
+
this.read(address, this.Scratch8);
|
|
601
526
|
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
/**
|
|
607
|
-
* Read a 16‑bit little-endian signed integer.
|
|
608
|
-
* @param address Source address.
|
|
609
|
-
*/
|
|
527
|
+
return f64(this.Scratch8.ptr);
|
|
528
|
+
}
|
|
610
529
|
|
|
611
|
-
|
|
612
|
-
return this.readBuffer(address, 0x02, Memory.Scratch2).readInt16LE();
|
|
613
|
-
}
|
|
530
|
+
this.Scratch8Buffer.writeDoubleLE(value);
|
|
614
531
|
|
|
615
|
-
|
|
616
|
-
* Read a 32‑bit big-endian signed integer.
|
|
617
|
-
* @param address Source address.
|
|
618
|
-
*/
|
|
532
|
+
this.write(address, this.Scratch8);
|
|
619
533
|
|
|
620
|
-
|
|
621
|
-
return this.readBuffer(address, 0x04, Memory.Scratch4).readInt32BE();
|
|
534
|
+
return this;
|
|
622
535
|
}
|
|
623
536
|
|
|
624
537
|
/**
|
|
625
|
-
*
|
|
626
|
-
*
|
|
538
|
+
* Reads an array of 64-bit floating-point values from memory or writes an array of 64-bit floating-point values to memory.
|
|
539
|
+
*
|
|
540
|
+
* @param address - Memory address to read from or write to
|
|
541
|
+
* @param lengthOrValues - Length of array to read, or array of values to write
|
|
542
|
+
* @returns Float64Array when reading, or this Memory instance when writing
|
|
543
|
+
*
|
|
544
|
+
* @example
|
|
545
|
+
* ```typescript
|
|
546
|
+
* const memory = new Memory('scientific_app.exe');
|
|
547
|
+
*
|
|
548
|
+
* // Read an array of 5 double precision values
|
|
549
|
+
* const preciseData = memory.f64Array(0x12345678n, 5);
|
|
550
|
+
* console.log('Precise data:', preciseData);
|
|
551
|
+
*
|
|
552
|
+
* // Write an array of double precision values
|
|
553
|
+
* const newData = new Float64Array([1.1, 2.2, 3.3, 4.4, 5.5]);
|
|
554
|
+
* memory.f64Array(0x12345678n, newData);
|
|
555
|
+
* ```
|
|
627
556
|
*/
|
|
557
|
+
public f64Array(address: bigint, length: number): Float64Array;
|
|
558
|
+
public f64Array(address: bigint, values: Float64Array): this;
|
|
559
|
+
public f64Array(address: bigint, lengthOrValues: Float64Array | number): Float64Array | this {
|
|
560
|
+
if (typeof lengthOrValues === 'number') {
|
|
561
|
+
const length = lengthOrValues;
|
|
562
|
+
const scratch = new Float64Array(length);
|
|
628
563
|
|
|
629
|
-
|
|
630
|
-
return this.readBuffer(address, 0x04, Memory.Scratch4).readInt32LE();
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
/**
|
|
634
|
-
* Read an 8‑bit signed integer.
|
|
635
|
-
* @param address Source address.
|
|
636
|
-
*/
|
|
564
|
+
this.read(address, scratch);
|
|
637
565
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
}
|
|
566
|
+
return scratch;
|
|
567
|
+
}
|
|
641
568
|
|
|
642
|
-
|
|
569
|
+
const values = lengthOrValues;
|
|
643
570
|
|
|
644
|
-
|
|
645
|
-
* Read a big-endian signed integer of arbitrary byte length.
|
|
646
|
-
* @param address Source address.
|
|
647
|
-
* @param byteLength Number of bytes (1–6).
|
|
648
|
-
* @param scratch Optional Buffer to reuse for improved performance.
|
|
649
|
-
*/
|
|
571
|
+
this.write(address, values);
|
|
650
572
|
|
|
651
|
-
|
|
652
|
-
return this.readBuffer(address, byteLength, scratch).readIntBE(0, byteLength);
|
|
573
|
+
return this;
|
|
653
574
|
}
|
|
654
575
|
|
|
655
576
|
/**
|
|
656
|
-
*
|
|
657
|
-
*
|
|
658
|
-
* @param
|
|
659
|
-
* @param
|
|
577
|
+
* Reads a signed 16-bit integer value from memory or writes a signed 16-bit integer value to memory.
|
|
578
|
+
*
|
|
579
|
+
* @param address - Memory address to read from or write to
|
|
580
|
+
* @param value - Optional value to write. If omitted, performs a read operation
|
|
581
|
+
* @returns The int16 value when reading, or this Memory instance when writing
|
|
582
|
+
*
|
|
583
|
+
* @example
|
|
584
|
+
* ```typescript
|
|
585
|
+
* const memory = new Memory('game.exe');
|
|
586
|
+
*
|
|
587
|
+
* // Read a signed 16-bit integer
|
|
588
|
+
* const temperature = memory.i16(0x12345678n);
|
|
589
|
+
* console.log('Temperature:', temperature);
|
|
590
|
+
*
|
|
591
|
+
* // Write a signed 16-bit integer
|
|
592
|
+
* memory.i16(0x12345678n, -273);
|
|
593
|
+
* ```
|
|
660
594
|
*/
|
|
595
|
+
public i16(address: bigint): number;
|
|
596
|
+
public i16(address: bigint, value: number): this;
|
|
597
|
+
public i16(address: bigint, value?: number): number | this {
|
|
598
|
+
if (value === undefined) {
|
|
599
|
+
this.read(address, this.Scratch2);
|
|
661
600
|
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
// ? …
|
|
601
|
+
return i16(this.Scratch2.ptr);
|
|
602
|
+
}
|
|
667
603
|
|
|
668
|
-
|
|
669
|
-
* Read bytes directly into the provided scratch buffer.
|
|
670
|
-
* @param address Source address.
|
|
671
|
-
* @param scratch Destination buffer; its length determines the read size.
|
|
672
|
-
* @returns The same scratch buffer for chaining.
|
|
673
|
-
*/
|
|
604
|
+
this.Scratch2Buffer.writeInt16LE(value);
|
|
674
605
|
|
|
675
|
-
|
|
676
|
-
this.readBuffer(address, scratch.byteLength, scratch);
|
|
606
|
+
this.write(address, this.Scratch2);
|
|
677
607
|
|
|
678
|
-
return
|
|
608
|
+
return this;
|
|
679
609
|
}
|
|
680
610
|
|
|
681
|
-
// + TODO: Implement scratch…
|
|
682
|
-
|
|
683
611
|
/**
|
|
684
|
-
*
|
|
612
|
+
* Reads an array of signed 16-bit integer values from memory or writes an array of signed 16-bit integer values to memory.
|
|
613
|
+
*
|
|
614
|
+
* @param address - Memory address to read from or write to
|
|
615
|
+
* @param lengthOrValues - Length of array to read, or array of values to write
|
|
616
|
+
* @returns Int16Array when reading, or this Memory instance when writing
|
|
617
|
+
*
|
|
618
|
+
* @example
|
|
619
|
+
* ```typescript
|
|
620
|
+
* const memory = new Memory('audio_app.exe');
|
|
685
621
|
*
|
|
686
|
-
*
|
|
687
|
-
*
|
|
688
|
-
*
|
|
689
|
-
*
|
|
690
|
-
*
|
|
622
|
+
* // Read an array of audio samples
|
|
623
|
+
* const samples = memory.i16Array(0x12345678n, 1024);
|
|
624
|
+
* console.log('Audio samples:', samples);
|
|
625
|
+
*
|
|
626
|
+
* // Write audio samples
|
|
627
|
+
* const newSamples = new Int16Array([-100, 200, -300, 400]);
|
|
628
|
+
* memory.i16Array(0x12345678n, newSamples);
|
|
629
|
+
* ```
|
|
691
630
|
*/
|
|
631
|
+
public i16Array(address: bigint, length: number): Int16Array;
|
|
632
|
+
public i16Array(address: bigint, values: Int16Array): this;
|
|
633
|
+
public i16Array(address: bigint, lengthOrValues: Int16Array | number): Int16Array | this {
|
|
634
|
+
if (typeof lengthOrValues === 'number') {
|
|
635
|
+
const length = lengthOrValues;
|
|
636
|
+
const scratch = new Int16Array(length);
|
|
692
637
|
|
|
693
|
-
|
|
694
|
-
address = BigInt(address);
|
|
695
|
-
|
|
696
|
-
const size = this.readUInt32LE(address);
|
|
638
|
+
this.read(address, scratch);
|
|
697
639
|
|
|
698
|
-
|
|
699
|
-
return new Uint32Array(0);
|
|
640
|
+
return scratch;
|
|
700
641
|
}
|
|
701
642
|
|
|
702
|
-
const
|
|
703
|
-
|
|
704
|
-
if (elementsPtr === 0n) {
|
|
705
|
-
return new Uint32Array(0);
|
|
706
|
-
}
|
|
643
|
+
const values = lengthOrValues;
|
|
707
644
|
|
|
708
|
-
|
|
645
|
+
this.write(address, values);
|
|
709
646
|
|
|
710
|
-
return
|
|
647
|
+
return this;
|
|
711
648
|
}
|
|
712
649
|
|
|
713
650
|
/**
|
|
714
|
-
*
|
|
715
|
-
*
|
|
716
|
-
* @param
|
|
717
|
-
* @param
|
|
651
|
+
* Reads a signed 32-bit integer value from memory or writes a signed 32-bit integer value to memory.
|
|
652
|
+
*
|
|
653
|
+
* @param address - Memory address to read from or write to
|
|
654
|
+
* @param value - Optional value to write. If omitted, performs a read operation
|
|
655
|
+
* @returns The int32 value when reading, or this Memory instance when writing
|
|
656
|
+
*
|
|
657
|
+
* @example
|
|
658
|
+
* ```typescript
|
|
659
|
+
* const memory = new Memory('game.exe');
|
|
660
|
+
*
|
|
661
|
+
* // Read a player's score
|
|
662
|
+
* const score = memory.i32(0x12345678n);
|
|
663
|
+
* console.log('Player score:', score);
|
|
664
|
+
*
|
|
665
|
+
* // Set a new score
|
|
666
|
+
* memory.i32(0x12345678n, 999999);
|
|
667
|
+
* ```
|
|
718
668
|
*/
|
|
669
|
+
public i32(address: bigint): number;
|
|
670
|
+
public i32(address: bigint, value: number): this;
|
|
671
|
+
public i32(address: bigint, value?: number): number | this {
|
|
672
|
+
if (value === undefined) {
|
|
673
|
+
this.read(address, this.Scratch4);
|
|
719
674
|
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
const indexOf = buffer.indexOf(0);
|
|
724
|
-
|
|
725
|
-
const end = indexOf !== -1 ? indexOf : buffer.length;
|
|
726
|
-
const start = 0;
|
|
675
|
+
return i32(this.Scratch4.ptr);
|
|
676
|
+
}
|
|
727
677
|
|
|
728
|
-
|
|
729
|
-
}
|
|
678
|
+
this.Scratch4Buffer.writeInt32LE(value);
|
|
730
679
|
|
|
731
|
-
|
|
732
|
-
* Read a 16‑bit big-endian unsigned integer.
|
|
733
|
-
* @param address Source address.
|
|
734
|
-
*/
|
|
680
|
+
this.write(address, this.Scratch4);
|
|
735
681
|
|
|
736
|
-
|
|
737
|
-
return this.readBuffer(address, 0x02, Memory.Scratch2).readUInt16BE();
|
|
682
|
+
return this;
|
|
738
683
|
}
|
|
739
684
|
|
|
740
685
|
/**
|
|
741
|
-
*
|
|
742
|
-
*
|
|
686
|
+
* Reads an array of signed 32-bit integer values from memory or writes an array of signed 32-bit integer values to memory.
|
|
687
|
+
*
|
|
688
|
+
* @param address - Memory address to read from or write to
|
|
689
|
+
* @param lengthOrValues - Length of array to read, or array of values to write
|
|
690
|
+
* @returns Int32Array when reading, or this Memory instance when writing
|
|
691
|
+
*
|
|
692
|
+
* @example
|
|
693
|
+
* ```typescript
|
|
694
|
+
* const memory = new Memory('game.exe');
|
|
695
|
+
*
|
|
696
|
+
* // Read inventory item counts
|
|
697
|
+
* const inventory = memory.i32Array(0x12345678n, 20);
|
|
698
|
+
* console.log('Inventory:', inventory);
|
|
699
|
+
*
|
|
700
|
+
* // Set inventory values
|
|
701
|
+
* const newInventory = new Int32Array([99, 50, 25, 10, 5]);
|
|
702
|
+
* memory.i32Array(0x12345678n, newInventory);
|
|
703
|
+
* ```
|
|
743
704
|
*/
|
|
705
|
+
public i32Array(address: bigint, length: number): Int32Array;
|
|
706
|
+
public i32Array(address: bigint, values: Int32Array): this;
|
|
707
|
+
public i32Array(address: bigint, lengthOrValues: Int32Array | number): Int32Array | this {
|
|
708
|
+
if (typeof lengthOrValues === 'number') {
|
|
709
|
+
const length = lengthOrValues;
|
|
710
|
+
const scratch = new Int32Array(length);
|
|
744
711
|
|
|
745
|
-
|
|
746
|
-
return this.readBuffer(address, 0x02, Memory.Scratch2).readUInt16LE();
|
|
747
|
-
}
|
|
712
|
+
this.read(address, scratch);
|
|
748
713
|
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
* @param address Source address.
|
|
752
|
-
*/
|
|
714
|
+
return scratch;
|
|
715
|
+
}
|
|
753
716
|
|
|
754
|
-
|
|
755
|
-
return this.readBuffer(address, 0x04, Memory.Scratch4).readUInt32BE();
|
|
756
|
-
}
|
|
717
|
+
const values = lengthOrValues;
|
|
757
718
|
|
|
758
|
-
|
|
759
|
-
* Read a 32‑bit little-endian unsigned integer.
|
|
760
|
-
* @param address Source address.
|
|
761
|
-
*/
|
|
719
|
+
this.write(address, values);
|
|
762
720
|
|
|
763
|
-
|
|
764
|
-
return this.readBuffer(address, 0x04, Memory.Scratch4).readUInt32LE();
|
|
721
|
+
return this;
|
|
765
722
|
}
|
|
766
723
|
|
|
767
724
|
/**
|
|
768
|
-
*
|
|
769
|
-
*
|
|
725
|
+
* Reads a signed 64-bit integer value from memory or writes a signed 64-bit integer value to memory.
|
|
726
|
+
*
|
|
727
|
+
* @param address - Memory address to read from or write to
|
|
728
|
+
* @param value - Optional value to write. If omitted, performs a read operation
|
|
729
|
+
* @returns The int64 value when reading, or this Memory instance when writing
|
|
730
|
+
*
|
|
731
|
+
* @example
|
|
732
|
+
* ```typescript
|
|
733
|
+
* const memory = new Memory('database.exe');
|
|
734
|
+
*
|
|
735
|
+
* // Read a large number (timestamp, file size, etc.)
|
|
736
|
+
* const timestamp = memory.i64(0x12345678n);
|
|
737
|
+
* console.log('Timestamp:', timestamp);
|
|
738
|
+
*
|
|
739
|
+
* // Write a large number
|
|
740
|
+
* memory.i64(0x12345678n, 9223372036854775807n);
|
|
741
|
+
* ```
|
|
770
742
|
*/
|
|
743
|
+
public i64(address: bigint): bigint;
|
|
744
|
+
public i64(address: bigint, value: bigint): this;
|
|
745
|
+
public i64(address: bigint, value?: bigint): bigint | this {
|
|
746
|
+
if (value === undefined) {
|
|
747
|
+
this.read(address, this.Scratch8);
|
|
771
748
|
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
// ? I don't have the brain-power for this right now… 🫠…
|
|
749
|
+
return i64(this.Scratch8.ptr);
|
|
750
|
+
}
|
|
777
751
|
|
|
778
|
-
|
|
779
|
-
* Read a big-endian unsigned integer of arbitrary byte length.
|
|
780
|
-
* @param address Source address.
|
|
781
|
-
* @param byteLength Number of bytes (1–6).
|
|
782
|
-
* @param scratch Optional Buffer to reuse for improved performance.
|
|
783
|
-
*/
|
|
752
|
+
this.Scratch8Buffer.writeBigInt64LE(value);
|
|
784
753
|
|
|
785
|
-
|
|
786
|
-
return this.readBuffer(address, byteLength, scratch).readUIntBE(0, byteLength);
|
|
787
|
-
}
|
|
754
|
+
this.write(address, this.Scratch8);
|
|
788
755
|
|
|
789
|
-
|
|
790
|
-
* Read a little-endian unsigned integer of arbitrary byte length.
|
|
791
|
-
* @param address Source address.
|
|
792
|
-
* @param byteLength Number of bytes (1–6).
|
|
793
|
-
* @param scratch Optional Buffer to reuse for improved performance.
|
|
794
|
-
*/
|
|
795
|
-
|
|
796
|
-
public readUIntLE(address: bigint | number, byteLength: number, scratch?: Buffer): number {
|
|
797
|
-
return this.readBuffer(address, byteLength, scratch).readUIntLE(0, byteLength);
|
|
756
|
+
return this;
|
|
798
757
|
}
|
|
799
758
|
|
|
800
|
-
// ? …
|
|
801
|
-
|
|
802
759
|
/**
|
|
803
|
-
*
|
|
804
|
-
*
|
|
805
|
-
* @
|
|
760
|
+
* Reads an array of signed 64-bit integer values from memory or writes an array of signed 64-bit integer values to memory.
|
|
761
|
+
*
|
|
762
|
+
* @param address - Memory address to read from or write to
|
|
763
|
+
* @param lengthOrValues - Length of array to read, or array of values to write
|
|
764
|
+
* @returns BigInt64Array when reading, or this Memory instance when writing
|
|
765
|
+
*
|
|
766
|
+
* @example
|
|
767
|
+
* ```typescript
|
|
768
|
+
* const memory = new Memory('database.exe');
|
|
769
|
+
*
|
|
770
|
+
* // Read an array of large numbers
|
|
771
|
+
* const largeNumbers = memory.i64Array(0x12345678n, 10);
|
|
772
|
+
* console.log('Large numbers:', largeNumbers);
|
|
773
|
+
*
|
|
774
|
+
* // Write an array of large numbers
|
|
775
|
+
* const newNumbers = new BigInt64Array([1n, 2n, 3n, 4n, 5n]);
|
|
776
|
+
* memory.i64Array(0x12345678n, newNumbers);
|
|
777
|
+
* ```
|
|
806
778
|
*/
|
|
779
|
+
public i64Array(address: bigint, length: number): BigInt64Array;
|
|
780
|
+
public i64Array(address: bigint, values: BigInt64Array): this;
|
|
781
|
+
public i64Array(address: bigint, lengthOrValues: BigInt64Array | number): BigInt64Array | this {
|
|
782
|
+
if (typeof lengthOrValues === 'number') {
|
|
783
|
+
const length = lengthOrValues;
|
|
784
|
+
const scratch = new BigInt64Array(length);
|
|
807
785
|
|
|
808
|
-
|
|
809
|
-
const buffer = this.readBuffer(address, 0x0c, Memory.Scratch12);
|
|
786
|
+
this.read(address, scratch);
|
|
810
787
|
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
const z = buffer.readFloatLE(0x08);
|
|
814
|
-
|
|
815
|
-
return { x, y, z };
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
// ? …
|
|
819
|
-
|
|
820
|
-
/**
|
|
821
|
-
* Write a signed 64‑bit big-endian integer.
|
|
822
|
-
* @param address Destination address.
|
|
823
|
-
* @param value Value to write.
|
|
824
|
-
* @param force When true, temporarily enables `PAGE_EXECUTE_READWRITE` to bypass protection.
|
|
825
|
-
* @returns `this` for chaining.
|
|
826
|
-
* @throws {Win32Error} If the underlying write or protection change fails.
|
|
827
|
-
*/
|
|
788
|
+
return scratch;
|
|
789
|
+
}
|
|
828
790
|
|
|
829
|
-
|
|
830
|
-
Memory.Scratch8.writeBigInt64BE(value);
|
|
791
|
+
const values = lengthOrValues;
|
|
831
792
|
|
|
832
|
-
this.
|
|
793
|
+
this.write(address, values);
|
|
833
794
|
|
|
834
795
|
return this;
|
|
835
796
|
}
|
|
836
797
|
|
|
837
798
|
/**
|
|
838
|
-
*
|
|
839
|
-
*
|
|
840
|
-
* @param
|
|
841
|
-
* @param
|
|
842
|
-
* @returns
|
|
843
|
-
*
|
|
799
|
+
* Reads a signed 8-bit integer value from memory or writes a signed 8-bit integer value to memory.
|
|
800
|
+
*
|
|
801
|
+
* @param address - Memory address to read from or write to
|
|
802
|
+
* @param value - Optional value to write. If omitted, performs a read operation
|
|
803
|
+
* @returns The int8 value when reading, or this Memory instance when writing
|
|
804
|
+
*
|
|
805
|
+
* @example
|
|
806
|
+
* ```typescript
|
|
807
|
+
* const memory = new Memory('game.exe');
|
|
808
|
+
*
|
|
809
|
+
* // Read a small signed value (e.g., direction, state)
|
|
810
|
+
* const direction = memory.i8(0x12345678n);
|
|
811
|
+
* console.log('Direction:', direction);
|
|
812
|
+
*
|
|
813
|
+
* // Write a small signed value
|
|
814
|
+
* memory.i8(0x12345678n, -127);
|
|
815
|
+
* ```
|
|
844
816
|
*/
|
|
817
|
+
public i8(address: bigint): number;
|
|
818
|
+
public i8(address: bigint, value: number): this;
|
|
819
|
+
public i8(address: bigint, value?: number): number | this {
|
|
820
|
+
if (value === undefined) {
|
|
821
|
+
this.read(address, this.Scratch1);
|
|
845
822
|
|
|
846
|
-
|
|
847
|
-
|
|
823
|
+
return i8(this.Scratch1.ptr);
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
this.Scratch1Buffer.writeInt8(value);
|
|
848
827
|
|
|
849
|
-
this.
|
|
828
|
+
this.write(address, this.Scratch1);
|
|
850
829
|
|
|
851
830
|
return this;
|
|
852
831
|
}
|
|
853
832
|
|
|
854
833
|
/**
|
|
855
|
-
*
|
|
856
|
-
*
|
|
857
|
-
* @param
|
|
858
|
-
* @param
|
|
859
|
-
* @returns
|
|
860
|
-
*
|
|
834
|
+
* Reads an array of signed 8-bit integer values from memory or writes an array of signed 8-bit integer values to memory.
|
|
835
|
+
*
|
|
836
|
+
* @param address - Memory address to read from or write to
|
|
837
|
+
* @param lengthOrValues - Length of array to read, or array of values to write
|
|
838
|
+
* @returns Int8Array when reading, or this Memory instance when writing
|
|
839
|
+
*
|
|
840
|
+
* @example
|
|
841
|
+
* ```typescript
|
|
842
|
+
* const memory = new Memory('game.exe');
|
|
843
|
+
*
|
|
844
|
+
* // Read an array of small signed values
|
|
845
|
+
* const directions = memory.i8Array(0x12345678n, 8);
|
|
846
|
+
* console.log('Movement directions:', directions);
|
|
847
|
+
*
|
|
848
|
+
* // Write movement directions
|
|
849
|
+
* const newDirections = new Int8Array([-1, 0, 1, -1, 0, 1, -1, 0]);
|
|
850
|
+
* memory.i8Array(0x12345678n, newDirections);
|
|
851
|
+
* ```
|
|
861
852
|
*/
|
|
853
|
+
public i8Array(address: bigint, length: number): Int8Array;
|
|
854
|
+
public i8Array(address: bigint, values: Int8Array): this;
|
|
855
|
+
public i8Array(address: bigint, lengthOrValues: Int8Array | number): Int8Array | this {
|
|
856
|
+
if (typeof lengthOrValues === 'number') {
|
|
857
|
+
const length = lengthOrValues;
|
|
858
|
+
const scratch = new Int8Array(length);
|
|
862
859
|
|
|
863
|
-
|
|
864
|
-
Memory.Scratch8.writeBigUInt64BE(value);
|
|
865
|
-
|
|
866
|
-
this.writeBuffer(address, Memory.Scratch8, force);
|
|
860
|
+
this.read(address, scratch);
|
|
867
861
|
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
/**
|
|
872
|
-
* Write an unsigned 64‑bit little-endian integer.
|
|
873
|
-
* @param address Destination address.
|
|
874
|
-
* @param value Value to write.
|
|
875
|
-
* @param force When true, temporarily enables `PAGE_EXECUTE_READWRITE` to bypass protection.
|
|
876
|
-
* @returns `this` for chaining.
|
|
877
|
-
* @throws {Win32Error} If the underlying write or protection change fails.
|
|
878
|
-
*/
|
|
862
|
+
return scratch;
|
|
863
|
+
}
|
|
879
864
|
|
|
880
|
-
|
|
881
|
-
Memory.Scratch8.writeBigUInt64LE(value);
|
|
865
|
+
const values = lengthOrValues;
|
|
882
866
|
|
|
883
|
-
this.
|
|
867
|
+
this.write(address, values);
|
|
884
868
|
|
|
885
869
|
return this;
|
|
886
870
|
}
|
|
887
871
|
|
|
888
872
|
/**
|
|
889
|
-
*
|
|
890
|
-
*
|
|
891
|
-
*
|
|
892
|
-
* @param
|
|
893
|
-
* @
|
|
894
|
-
* @
|
|
895
|
-
|
|
873
|
+
* Reads a quaternion (4D rotation) from memory or writes a quaternion to memory.
|
|
874
|
+
* Quaternions are stored as four 32-bit floats: x, y, z, w.
|
|
875
|
+
*
|
|
876
|
+
* @param address - Memory address to read from or write to
|
|
877
|
+
* @param value - Optional quaternion to write. If omitted, performs a read operation
|
|
878
|
+
* @returns The Quaternion object when reading, or this Memory instance when writing
|
|
879
|
+
*
|
|
880
|
+
* @example
|
|
881
|
+
* ```typescript
|
|
882
|
+
* const memory = new Memory('game.exe');
|
|
883
|
+
*
|
|
884
|
+
* // Read player rotation
|
|
885
|
+
* const rotation = memory.quaternion(0x12345678n);
|
|
886
|
+
* console.log('Player rotation:', rotation);
|
|
887
|
+
*
|
|
888
|
+
* // Set player rotation to identity
|
|
889
|
+
* memory.quaternion(0x12345678n, { x: 0, y: 0, z: 0, w: 1 });
|
|
890
|
+
* ```
|
|
891
|
+
*/
|
|
892
|
+
public quaternion(address: bigint): Quaternion;
|
|
893
|
+
public quaternion(address: bigint, value: Quaternion): this;
|
|
894
|
+
public quaternion(address: bigint, value?: Quaternion): Quaternion | this {
|
|
895
|
+
if (value === undefined) {
|
|
896
|
+
this.read(address, this.Scratch16);
|
|
897
|
+
|
|
898
|
+
const w = f32(this.Scratch16.ptr, 0x0c);
|
|
899
|
+
const x = f32(this.Scratch16.ptr);
|
|
900
|
+
const y = f32(this.Scratch16.ptr, 0x04);
|
|
901
|
+
const z = f32(this.Scratch16.ptr, 0x08);
|
|
902
|
+
|
|
903
|
+
return { w, x, y, z };
|
|
904
|
+
}
|
|
896
905
|
|
|
897
|
-
|
|
898
|
-
|
|
906
|
+
this.Scratch16Buffer.writeFloatLE(value.w, 0x0c);
|
|
907
|
+
this.Scratch16Buffer.writeFloatLE(value.x);
|
|
908
|
+
this.Scratch16Buffer.writeFloatLE(value.y, 0x04);
|
|
909
|
+
this.Scratch16Buffer.writeFloatLE(value.z, 0x08);
|
|
899
910
|
|
|
900
|
-
this.
|
|
911
|
+
this.write(address, this.Scratch16);
|
|
901
912
|
|
|
902
913
|
return this;
|
|
903
914
|
}
|
|
904
915
|
|
|
905
916
|
/**
|
|
906
|
-
*
|
|
907
|
-
*
|
|
908
|
-
* @param address
|
|
909
|
-
* @param
|
|
910
|
-
* @
|
|
911
|
-
*
|
|
912
|
-
* @
|
|
913
|
-
*
|
|
917
|
+
* Reads an array of quaternions from memory or writes an array of quaternions to memory.
|
|
918
|
+
*
|
|
919
|
+
* @param address - Memory address to read from or write to
|
|
920
|
+
* @param lengthOrValues - Length of array to read, or array of quaternions to write
|
|
921
|
+
* @returns Array of Quaternion objects when reading, or this Memory instance when writing
|
|
922
|
+
*
|
|
923
|
+
* @example
|
|
924
|
+
* ```typescript
|
|
925
|
+
* const memory = new Memory('game.exe');
|
|
926
|
+
*
|
|
927
|
+
* // Read bone rotations for skeletal animation
|
|
928
|
+
* const boneRotations = memory.quaternionArray(0x12345678n, 50);
|
|
929
|
+
* console.log('Bone rotations:', boneRotations);
|
|
930
|
+
*
|
|
931
|
+
* // Set all bones to identity rotation
|
|
932
|
+
* const identityRotations = Array(50).fill({ x: 0, y: 0, z: 0, w: 1 });
|
|
933
|
+
* memory.quaternionArray(0x12345678n, identityRotations);
|
|
934
|
+
* ```
|
|
914
935
|
*/
|
|
936
|
+
public quaternionArray(address: bigint, length: number): Quaternion[];
|
|
937
|
+
public quaternionArray(address: bigint, values: Quaternion[]): this;
|
|
938
|
+
public quaternionArray(address: bigint, lengthOrValues: Quaternion[] | number): Quaternion[] | this {
|
|
939
|
+
if (typeof lengthOrValues === 'number') {
|
|
940
|
+
const length = lengthOrValues;
|
|
941
|
+
const scratch = new Float32Array(length * 0x04); // 4 * f32 per Quaternion
|
|
915
942
|
|
|
916
|
-
|
|
917
|
-
const { hProcess } = this;
|
|
918
|
-
|
|
919
|
-
address = BigInt(address);
|
|
943
|
+
this.read(address, scratch);
|
|
920
944
|
|
|
921
|
-
|
|
922
|
-
const lpBuffer = buffer;
|
|
923
|
-
// const lpNumberOfBytesWritten = 0n;
|
|
924
|
-
const nSize = BigInt(buffer.byteLength);
|
|
945
|
+
const result = new Array<Quaternion>(length);
|
|
925
946
|
|
|
926
|
-
|
|
927
|
-
|
|
947
|
+
for (let i = 0, j = 0; i < length; i++, j += 0x04) {
|
|
948
|
+
const w = scratch[j + 0x03];
|
|
949
|
+
const x = scratch[j];
|
|
950
|
+
const y = scratch[j + 0x01];
|
|
951
|
+
const z = scratch[j + 0x02];
|
|
928
952
|
|
|
929
|
-
|
|
930
|
-
throw new Win32Error('WriteProcessMemory', Kernel32.GetLastError());
|
|
953
|
+
result[i] = { w, x, y, z };
|
|
931
954
|
}
|
|
932
955
|
|
|
933
|
-
return
|
|
956
|
+
return result;
|
|
934
957
|
}
|
|
935
958
|
|
|
936
|
-
const
|
|
937
|
-
const
|
|
938
|
-
const lpAddress = address;
|
|
939
|
-
const lpflOldProtect = Memory.Scratch4;
|
|
959
|
+
const values = lengthOrValues;
|
|
960
|
+
const scratch = new Float32Array(values.length * 0x04);
|
|
940
961
|
|
|
941
|
-
|
|
962
|
+
for (let i = 0, j = 0; i < values.length; i++, j += 0x04) {
|
|
963
|
+
const quaternion = values[i];
|
|
942
964
|
|
|
943
|
-
|
|
944
|
-
|
|
965
|
+
scratch[j + 0x03] = quaternion.w;
|
|
966
|
+
scratch[j] = quaternion.x;
|
|
967
|
+
scratch[j + 0x01] = quaternion.y;
|
|
968
|
+
scratch[j + 0x02] = quaternion.z;
|
|
945
969
|
}
|
|
946
970
|
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
if (!bWriteProcessMemory) {
|
|
950
|
-
Kernel32.VirtualProtectEx(hProcess, lpAddress, nSize, lpflOldProtect.readUInt32LE(), Memory.Scratch4_2);
|
|
951
|
-
|
|
952
|
-
throw new Win32Error('WriteProcessMemory', Kernel32.GetLastError());
|
|
953
|
-
}
|
|
954
|
-
|
|
955
|
-
Kernel32.VirtualProtectEx(hProcess, lpAddress, nSize, lpflOldProtect.readUInt32LE(), Memory.Scratch4_2);
|
|
971
|
+
this.write(address, scratch);
|
|
956
972
|
|
|
957
973
|
return this;
|
|
958
974
|
}
|
|
959
975
|
|
|
960
976
|
/**
|
|
961
|
-
*
|
|
962
|
-
*
|
|
963
|
-
* @param
|
|
964
|
-
* @param
|
|
965
|
-
* @returns
|
|
966
|
-
*
|
|
977
|
+
* Reads an unsigned 16-bit integer value from memory or writes an unsigned 16-bit integer value to memory.
|
|
978
|
+
*
|
|
979
|
+
* @param address - Memory address to read from or write to
|
|
980
|
+
* @param value - Optional value to write. If omitted, performs a read operation
|
|
981
|
+
* @returns The uint16 value when reading, or this Memory instance when writing
|
|
982
|
+
*
|
|
983
|
+
* @example
|
|
984
|
+
* ```typescript
|
|
985
|
+
* const memory = new Memory('game.exe');
|
|
986
|
+
*
|
|
987
|
+
* // Read a port number or small positive value
|
|
988
|
+
* const port = memory.u16(0x12345678n);
|
|
989
|
+
* console.log('Network port:', port);
|
|
990
|
+
*
|
|
991
|
+
* // Write a port number
|
|
992
|
+
* memory.u16(0x12345678n, 8080);
|
|
993
|
+
* ```
|
|
967
994
|
*/
|
|
995
|
+
public u16(address: bigint): number;
|
|
996
|
+
public u16(address: bigint, value: number): this;
|
|
997
|
+
public u16(address: bigint, value?: number): number | this {
|
|
998
|
+
if (value === undefined) {
|
|
999
|
+
this.read(address, this.Scratch2);
|
|
968
1000
|
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
this.writeBuffer(address, Memory.Scratch8, force);
|
|
973
|
-
|
|
974
|
-
return this;
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
/**
|
|
978
|
-
* Write a 64‑bit little-endian IEEE-754 float.
|
|
979
|
-
* @param address Destination address.
|
|
980
|
-
* @param value Value to write.
|
|
981
|
-
* @param force When true, temporarily enables `PAGE_EXECUTE_READWRITE` to bypass protection.
|
|
982
|
-
* @returns `this` for chaining.
|
|
983
|
-
* @throws {Win32Error} If the underlying write or protection change fails.
|
|
984
|
-
*/
|
|
1001
|
+
return u16(this.Scratch2.ptr);
|
|
1002
|
+
}
|
|
985
1003
|
|
|
986
|
-
|
|
987
|
-
Memory.Scratch8.writeDoubleLE(value);
|
|
1004
|
+
this.Scratch2Buffer.writeUInt16LE(value);
|
|
988
1005
|
|
|
989
|
-
this.
|
|
1006
|
+
this.write(address, this.Scratch2);
|
|
990
1007
|
|
|
991
1008
|
return this;
|
|
992
1009
|
}
|
|
993
1010
|
|
|
994
1011
|
/**
|
|
995
|
-
*
|
|
996
|
-
*
|
|
997
|
-
* @param
|
|
998
|
-
* @param
|
|
999
|
-
* @returns
|
|
1000
|
-
*
|
|
1012
|
+
* Reads an array of unsigned 16-bit integer values from memory or writes an array of unsigned 16-bit integer values to memory.
|
|
1013
|
+
*
|
|
1014
|
+
* @param address - Memory address to read from or write to
|
|
1015
|
+
* @param lengthOrValues - Length of array to read, or array of values to write
|
|
1016
|
+
* @returns Uint16Array when reading, or this Memory instance when writing
|
|
1017
|
+
*
|
|
1018
|
+
* @example
|
|
1019
|
+
* ```typescript
|
|
1020
|
+
* const memory = new Memory('network_app.exe');
|
|
1021
|
+
*
|
|
1022
|
+
* // Read an array of port numbers
|
|
1023
|
+
* const ports = memory.u16Array(0x12345678n, 10);
|
|
1024
|
+
* console.log('Active ports:', ports);
|
|
1025
|
+
*
|
|
1026
|
+
* // Write port configuration
|
|
1027
|
+
* const newPorts = new Uint16Array([80, 443, 8080, 3000, 5432]);
|
|
1028
|
+
* memory.u16Array(0x12345678n, newPorts);
|
|
1029
|
+
* ```
|
|
1001
1030
|
*/
|
|
1031
|
+
public u16Array(address: bigint, length: number): Uint16Array;
|
|
1032
|
+
public u16Array(address: bigint, values: Uint16Array): this;
|
|
1033
|
+
public u16Array(address: bigint, lengthOrValues: Uint16Array | number): Uint16Array | this {
|
|
1034
|
+
if (typeof lengthOrValues === 'number') {
|
|
1035
|
+
const length = lengthOrValues;
|
|
1036
|
+
const scratch = new Uint16Array(length);
|
|
1002
1037
|
|
|
1003
|
-
|
|
1004
|
-
Memory.Scratch4.writeFloatBE(value);
|
|
1005
|
-
|
|
1006
|
-
this.writeBuffer(address, Memory.Scratch4, force);
|
|
1038
|
+
this.read(address, scratch);
|
|
1007
1039
|
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
/**
|
|
1012
|
-
* Write a 32‑bit little-endian IEEE-754 float.
|
|
1013
|
-
* @param address Destination address.
|
|
1014
|
-
* @param value Value to write.
|
|
1015
|
-
* @param force When true, temporarily enables `PAGE_EXECUTE_READWRITE` to bypass protection.
|
|
1016
|
-
* @returns `this` for chaining.
|
|
1017
|
-
* @throws {Win32Error} If the underlying write or protection change fails.
|
|
1018
|
-
*/
|
|
1040
|
+
return scratch;
|
|
1041
|
+
}
|
|
1019
1042
|
|
|
1020
|
-
|
|
1021
|
-
Memory.Scratch4.writeFloatLE(value);
|
|
1043
|
+
const values = lengthOrValues;
|
|
1022
1044
|
|
|
1023
|
-
this.
|
|
1045
|
+
this.write(address, values);
|
|
1024
1046
|
|
|
1025
1047
|
return this;
|
|
1026
1048
|
}
|
|
1027
1049
|
|
|
1028
1050
|
/**
|
|
1029
|
-
*
|
|
1030
|
-
*
|
|
1031
|
-
* @param
|
|
1032
|
-
* @param
|
|
1033
|
-
* @returns
|
|
1034
|
-
*
|
|
1051
|
+
* Reads an unsigned 32-bit integer value from memory or writes an unsigned 32-bit integer value to memory.
|
|
1052
|
+
*
|
|
1053
|
+
* @param address - Memory address to read from or write to
|
|
1054
|
+
* @param value - Optional value to write. If omitted, performs a read operation
|
|
1055
|
+
* @returns The uint32 value when reading, or this Memory instance when writing
|
|
1056
|
+
*
|
|
1057
|
+
* @example
|
|
1058
|
+
* ```typescript
|
|
1059
|
+
* const memory = new Memory('game.exe');
|
|
1060
|
+
*
|
|
1061
|
+
* // Read player's money (always positive)
|
|
1062
|
+
* const money = memory.u32(0x12345678n);
|
|
1063
|
+
* console.log('Player money:', money);
|
|
1064
|
+
*
|
|
1065
|
+
* // Give player maximum money
|
|
1066
|
+
* memory.u32(0x12345678n, 4294967295);
|
|
1067
|
+
* ```
|
|
1035
1068
|
*/
|
|
1069
|
+
public u32(address: bigint): number;
|
|
1070
|
+
public u32(address: bigint, value: number): this;
|
|
1071
|
+
public u32(address: bigint, value?: number): number | this {
|
|
1072
|
+
if (value === undefined) {
|
|
1073
|
+
this.read(address, this.Scratch4);
|
|
1036
1074
|
|
|
1037
|
-
|
|
1038
|
-
|
|
1075
|
+
return u32(this.Scratch4.ptr);
|
|
1076
|
+
}
|
|
1039
1077
|
|
|
1040
|
-
this.
|
|
1078
|
+
this.Scratch4Buffer.writeUInt32LE(value);
|
|
1079
|
+
|
|
1080
|
+
this.write(address, this.Scratch4);
|
|
1041
1081
|
|
|
1042
1082
|
return this;
|
|
1043
1083
|
}
|
|
1044
1084
|
|
|
1045
1085
|
/**
|
|
1046
|
-
*
|
|
1047
|
-
*
|
|
1048
|
-
* @param
|
|
1049
|
-
* @param
|
|
1050
|
-
* @returns
|
|
1051
|
-
*
|
|
1086
|
+
* Reads an array of unsigned 32-bit integer values from memory or writes an array of unsigned 32-bit integer values to memory.
|
|
1087
|
+
*
|
|
1088
|
+
* @param address - Memory address to read from or write to
|
|
1089
|
+
* @param lengthOrValues - Length of array to read, or array of values to write
|
|
1090
|
+
* @returns Uint32Array when reading, or this Memory instance when writing
|
|
1091
|
+
*
|
|
1092
|
+
* @example
|
|
1093
|
+
* ```typescript
|
|
1094
|
+
* const memory = new Memory('game.exe');
|
|
1095
|
+
*
|
|
1096
|
+
* // Read resource amounts
|
|
1097
|
+
* const resources = memory.u32Array(0x12345678n, 6);
|
|
1098
|
+
* console.log('Resources:', resources);
|
|
1099
|
+
*
|
|
1100
|
+
* // Set resource amounts
|
|
1101
|
+
* const newResources = new Uint32Array([1000, 2000, 3000, 4000, 5000, 6000]);
|
|
1102
|
+
* memory.u32Array(0x12345678n, newResources);
|
|
1103
|
+
* ```
|
|
1052
1104
|
*/
|
|
1105
|
+
public u32Array(address: bigint, length: number): Uint32Array;
|
|
1106
|
+
public u32Array(address: bigint, values: Uint32Array): this;
|
|
1107
|
+
public u32Array(address: bigint, lengthOrValues: Uint32Array | number): Uint32Array | this {
|
|
1108
|
+
if (typeof lengthOrValues === 'number') {
|
|
1109
|
+
const length = lengthOrValues;
|
|
1110
|
+
const scratch = new Uint32Array(length);
|
|
1053
1111
|
|
|
1054
|
-
|
|
1055
|
-
Memory.Scratch2.writeInt16LE(value);
|
|
1056
|
-
|
|
1057
|
-
this.writeBuffer(address, Memory.Scratch2, force);
|
|
1058
|
-
|
|
1059
|
-
return this;
|
|
1060
|
-
}
|
|
1112
|
+
this.read(address, scratch);
|
|
1061
1113
|
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
* @param address Destination address.
|
|
1065
|
-
* @param value Value to write.
|
|
1066
|
-
* @param force When true, temporarily enables `PAGE_EXECUTE_READWRITE` to bypass protection.
|
|
1067
|
-
* @returns `this` for chaining.
|
|
1068
|
-
* @throws {Win32Error} If the underlying write or protection change fails.
|
|
1069
|
-
*/
|
|
1114
|
+
return scratch;
|
|
1115
|
+
}
|
|
1070
1116
|
|
|
1071
|
-
|
|
1072
|
-
Memory.Scratch4.writeInt32BE(value);
|
|
1117
|
+
const values = lengthOrValues;
|
|
1073
1118
|
|
|
1074
|
-
this.
|
|
1119
|
+
this.write(address, values);
|
|
1075
1120
|
|
|
1076
1121
|
return this;
|
|
1077
1122
|
}
|
|
1078
1123
|
|
|
1079
1124
|
/**
|
|
1080
|
-
*
|
|
1081
|
-
*
|
|
1082
|
-
* @param
|
|
1083
|
-
* @param
|
|
1084
|
-
* @returns
|
|
1085
|
-
*
|
|
1125
|
+
* Reads an unsigned 64-bit integer value from memory or writes an unsigned 64-bit integer value to memory.
|
|
1126
|
+
*
|
|
1127
|
+
* @param address - Memory address to read from or write to
|
|
1128
|
+
* @param value - Optional value to write. If omitted, performs a read operation
|
|
1129
|
+
* @returns The uint64 value when reading, or this Memory instance when writing
|
|
1130
|
+
*
|
|
1131
|
+
* @example
|
|
1132
|
+
* ```typescript
|
|
1133
|
+
* const memory = new Memory('database.exe');
|
|
1134
|
+
*
|
|
1135
|
+
* // Read a very large positive number
|
|
1136
|
+
* const recordId = memory.u64(0x12345678n);
|
|
1137
|
+
* console.log('Record ID:', recordId);
|
|
1138
|
+
*
|
|
1139
|
+
* // Write a very large positive number
|
|
1140
|
+
* memory.u64(0x12345678n, 18446744073709551615n);
|
|
1141
|
+
* ```
|
|
1086
1142
|
*/
|
|
1143
|
+
public u64(address: bigint): bigint;
|
|
1144
|
+
public u64(address: bigint, value: bigint): this;
|
|
1145
|
+
public u64(address: bigint, value?: bigint): bigint | this {
|
|
1146
|
+
if (value === undefined) {
|
|
1147
|
+
this.read(address, this.Scratch8);
|
|
1148
|
+
|
|
1149
|
+
return u64(this.Scratch8.ptr);
|
|
1150
|
+
}
|
|
1087
1151
|
|
|
1088
|
-
|
|
1089
|
-
Memory.Scratch4.writeInt32LE(value);
|
|
1152
|
+
this.Scratch8Buffer.writeBigUInt64LE(value);
|
|
1090
1153
|
|
|
1091
|
-
this.
|
|
1154
|
+
this.write(address, this.Scratch8);
|
|
1092
1155
|
|
|
1093
1156
|
return this;
|
|
1094
1157
|
}
|
|
1095
1158
|
|
|
1096
1159
|
/**
|
|
1097
|
-
*
|
|
1098
|
-
*
|
|
1099
|
-
* @param
|
|
1100
|
-
* @param
|
|
1101
|
-
* @returns
|
|
1102
|
-
*
|
|
1160
|
+
* Reads an array of unsigned 64-bit integer values from memory or writes an array of unsigned 64-bit integer values to memory.
|
|
1161
|
+
*
|
|
1162
|
+
* @param address - Memory address to read from or write to
|
|
1163
|
+
* @param lengthOrValues - Length of array to read, or array of values to write
|
|
1164
|
+
* @returns BigUint64Array when reading, or this Memory instance when writing
|
|
1165
|
+
*
|
|
1166
|
+
* @example
|
|
1167
|
+
* ```typescript
|
|
1168
|
+
* const memory = new Memory('database.exe');
|
|
1169
|
+
*
|
|
1170
|
+
* // Read an array of record IDs
|
|
1171
|
+
* const recordIds = memory.u64Array(0x12345678n, 100);
|
|
1172
|
+
* console.log('Record IDs:', recordIds);
|
|
1173
|
+
*
|
|
1174
|
+
* // Write record IDs
|
|
1175
|
+
* const newIds = new BigUint64Array([1000n, 2000n, 3000n, 4000n]);
|
|
1176
|
+
* memory.u64Array(0x12345678n, newIds);
|
|
1177
|
+
* ```
|
|
1103
1178
|
*/
|
|
1179
|
+
public u64Array(address: bigint, length: number): BigUint64Array;
|
|
1180
|
+
public u64Array(address: bigint, values: BigUint64Array): this;
|
|
1181
|
+
public u64Array(address: bigint, lengthOrValues: BigUint64Array | number): BigUint64Array | this {
|
|
1182
|
+
if (typeof lengthOrValues === 'number') {
|
|
1183
|
+
const length = lengthOrValues;
|
|
1184
|
+
const scratch = new BigUint64Array(length);
|
|
1104
1185
|
|
|
1105
|
-
|
|
1106
|
-
Memory.Scratch1.writeInt8(value);
|
|
1107
|
-
|
|
1108
|
-
this.writeBuffer(address, Memory.Scratch1, force);
|
|
1109
|
-
|
|
1110
|
-
return this;
|
|
1111
|
-
}
|
|
1112
|
-
|
|
1113
|
-
// + TODO: Implement scratch…
|
|
1186
|
+
this.read(address, scratch);
|
|
1114
1187
|
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
* @param address Destination address.
|
|
1118
|
-
* @param value Value to write.
|
|
1119
|
-
* @param force When true, temporarily enables `PAGE_EXECUTE_READWRITE` to bypass protection.
|
|
1120
|
-
* @returns `this` for chaining.
|
|
1121
|
-
* @throws {Win32Error} If the underlying write or protection change fails.
|
|
1122
|
-
*/
|
|
1188
|
+
return scratch;
|
|
1189
|
+
}
|
|
1123
1190
|
|
|
1124
|
-
|
|
1125
|
-
const buffer = Buffer.allocUnsafe(byteLength);
|
|
1126
|
-
/* */ buffer.writeIntBE(value, 0, byteLength);
|
|
1191
|
+
const values = lengthOrValues;
|
|
1127
1192
|
|
|
1128
|
-
this.
|
|
1193
|
+
this.write(address, values);
|
|
1129
1194
|
|
|
1130
1195
|
return this;
|
|
1131
1196
|
}
|
|
1132
1197
|
|
|
1133
|
-
// + TODO: Implement scratch…
|
|
1134
|
-
|
|
1135
1198
|
/**
|
|
1136
|
-
*
|
|
1137
|
-
*
|
|
1138
|
-
* @param
|
|
1139
|
-
* @param
|
|
1140
|
-
* @returns
|
|
1141
|
-
*
|
|
1199
|
+
* Reads an unsigned 8-bit integer value from memory or writes an unsigned 8-bit integer value to memory.
|
|
1200
|
+
*
|
|
1201
|
+
* @param address - Memory address to read from or write to
|
|
1202
|
+
* @param value - Optional value to write. If omitted, performs a read operation
|
|
1203
|
+
* @returns The uint8 value when reading, or this Memory instance when writing
|
|
1204
|
+
*
|
|
1205
|
+
* @example
|
|
1206
|
+
* ```typescript
|
|
1207
|
+
* const memory = new Memory('game.exe');
|
|
1208
|
+
*
|
|
1209
|
+
* // Read a byte value (0-255)
|
|
1210
|
+
* const opacity = memory.u8(0x12345678n);
|
|
1211
|
+
* console.log('UI opacity:', opacity);
|
|
1212
|
+
*
|
|
1213
|
+
* // Set opacity to maximum
|
|
1214
|
+
* memory.u8(0x12345678n, 255);
|
|
1215
|
+
* ```
|
|
1142
1216
|
*/
|
|
1217
|
+
public u8(address: bigint): number;
|
|
1218
|
+
public u8(address: bigint, value: number): this;
|
|
1219
|
+
public u8(address: bigint, value?: number): number | this {
|
|
1220
|
+
if (value === undefined) {
|
|
1221
|
+
this.read(address, this.Scratch1);
|
|
1222
|
+
|
|
1223
|
+
return u8(this.Scratch1.ptr);
|
|
1224
|
+
}
|
|
1143
1225
|
|
|
1144
|
-
|
|
1145
|
-
const buffer = Buffer.allocUnsafe(byteLength);
|
|
1146
|
-
/* */ buffer.writeIntLE(value, 0, byteLength);
|
|
1226
|
+
this.Scratch1Buffer.writeUInt8(value);
|
|
1147
1227
|
|
|
1148
|
-
this.
|
|
1228
|
+
this.write(address, this.Scratch1);
|
|
1149
1229
|
|
|
1150
1230
|
return this;
|
|
1151
1231
|
}
|
|
1152
1232
|
|
|
1153
|
-
// + TODO: Implement scratch…
|
|
1154
|
-
|
|
1155
1233
|
/**
|
|
1156
|
-
*
|
|
1157
|
-
*
|
|
1158
|
-
* @param
|
|
1159
|
-
* @param
|
|
1160
|
-
* @returns
|
|
1161
|
-
*
|
|
1234
|
+
* Reads an array of unsigned 8-bit integer values from memory or writes an array of unsigned 8-bit integer values to memory.
|
|
1235
|
+
*
|
|
1236
|
+
* @param address - Memory address to read from or write to
|
|
1237
|
+
* @param lengthOrValues - Length of array to read, or array of values to write
|
|
1238
|
+
* @returns Uint8Array when reading, or this Memory instance when writing
|
|
1239
|
+
*
|
|
1240
|
+
* @example
|
|
1241
|
+
* ```typescript
|
|
1242
|
+
* const memory = new Memory('image_editor.exe');
|
|
1243
|
+
*
|
|
1244
|
+
* // Read pixel data
|
|
1245
|
+
* const pixels = memory.u8Array(0x12345678n, 1024);
|
|
1246
|
+
* console.log('Pixel data:', pixels);
|
|
1247
|
+
*
|
|
1248
|
+
* // Write pixel data
|
|
1249
|
+
* const newPixels = new Uint8Array([255, 128, 64, 32, 16, 8, 4, 2]);
|
|
1250
|
+
* memory.u8Array(0x12345678n, newPixels);
|
|
1251
|
+
* ```
|
|
1162
1252
|
*/
|
|
1253
|
+
public u8Array(address: bigint, length: number): Uint8Array;
|
|
1254
|
+
public u8Array(address: bigint, values: Uint8Array): this;
|
|
1255
|
+
public u8Array(address: bigint, lengthOrValues: Uint8Array | number): Uint8Array | this {
|
|
1256
|
+
if (typeof lengthOrValues === 'number') {
|
|
1257
|
+
const length = lengthOrValues;
|
|
1258
|
+
const scratch = new Uint8Array(length);
|
|
1163
1259
|
|
|
1164
|
-
|
|
1165
|
-
|
|
1260
|
+
this.read(address, scratch);
|
|
1261
|
+
|
|
1262
|
+
return scratch;
|
|
1263
|
+
}
|
|
1166
1264
|
|
|
1167
|
-
const
|
|
1168
|
-
/* */ buffer.write(value, 0, byteLength, 'utf8');
|
|
1265
|
+
const values = lengthOrValues;
|
|
1169
1266
|
|
|
1170
|
-
this.
|
|
1267
|
+
this.write(address, values);
|
|
1171
1268
|
|
|
1172
1269
|
return this;
|
|
1173
1270
|
}
|
|
1174
1271
|
|
|
1175
1272
|
/**
|
|
1176
|
-
*
|
|
1177
|
-
*
|
|
1178
|
-
*
|
|
1179
|
-
* @param
|
|
1180
|
-
* @
|
|
1181
|
-
* @
|
|
1273
|
+
* Reads a 2D vector from memory or writes a 2D vector to memory.
|
|
1274
|
+
* Vectors are stored as two 32-bit floats: x, y.
|
|
1275
|
+
*
|
|
1276
|
+
* @param address - Memory address to read from or write to
|
|
1277
|
+
* @param value - Optional vector to write. If omitted, performs a read operation
|
|
1278
|
+
* @returns The Vector2 object when reading, or this Memory instance when writing
|
|
1279
|
+
*
|
|
1280
|
+
* @example
|
|
1281
|
+
* ```typescript
|
|
1282
|
+
* const memory = new Memory('game.exe');
|
|
1283
|
+
*
|
|
1284
|
+
* // Read player position
|
|
1285
|
+
* const position = memory.vector2(0x12345678n);
|
|
1286
|
+
* console.log('Player position:', position);
|
|
1287
|
+
*
|
|
1288
|
+
* // Teleport player to origin
|
|
1289
|
+
* memory.vector2(0x12345678n, { x: 0, y: 0 });
|
|
1290
|
+
* ```
|
|
1182
1291
|
*/
|
|
1292
|
+
public vector2(address: bigint): Vector2;
|
|
1293
|
+
public vector2(address: bigint, value: Vector2): this;
|
|
1294
|
+
public vector2(address: bigint, value?: Vector2): Vector2 | this {
|
|
1295
|
+
if (value === undefined) {
|
|
1296
|
+
this.read(address, this.Scratch8);
|
|
1183
1297
|
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
this.writeBuffer(address, Memory.Scratch2, force);
|
|
1298
|
+
const x = f32(this.Scratch8.ptr);
|
|
1299
|
+
const y = f32(this.Scratch8.ptr, 0x04);
|
|
1188
1300
|
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
/**
|
|
1193
|
-
* Write a 16‑bit little-endian unsigned integer.
|
|
1194
|
-
* @param address Destination address.
|
|
1195
|
-
* @param value Value to write.
|
|
1196
|
-
* @param force When true, temporarily enables `PAGE_EXECUTE_READWRITE` to bypass protection.
|
|
1197
|
-
* @returns `this` for chaining.
|
|
1198
|
-
* @throws {Win32Error} If the underlying write or protection change fails.
|
|
1199
|
-
*/
|
|
1301
|
+
return { x, y };
|
|
1302
|
+
}
|
|
1200
1303
|
|
|
1201
|
-
|
|
1202
|
-
|
|
1304
|
+
this.Scratch8Buffer.writeFloatLE(value.x);
|
|
1305
|
+
this.Scratch8Buffer.writeFloatLE(value.y, 0x04);
|
|
1203
1306
|
|
|
1204
|
-
this.
|
|
1307
|
+
this.write(address, this.Scratch8);
|
|
1205
1308
|
|
|
1206
1309
|
return this;
|
|
1207
1310
|
}
|
|
1208
1311
|
|
|
1209
1312
|
/**
|
|
1210
|
-
*
|
|
1211
|
-
*
|
|
1212
|
-
* @param
|
|
1213
|
-
* @param
|
|
1214
|
-
* @returns
|
|
1215
|
-
*
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1313
|
+
* Reads an array of 2D vectors from memory or writes an array of 2D vectors to memory.
|
|
1314
|
+
*
|
|
1315
|
+
* @param address - Memory address to read from or write to
|
|
1316
|
+
* @param lengthOrValues - Length of array to read, or array of vectors to write
|
|
1317
|
+
* @returns Array of Vector2 objects when reading, or this Memory instance when writing
|
|
1318
|
+
*
|
|
1319
|
+
* @example
|
|
1320
|
+
* ```typescript
|
|
1321
|
+
* const memory = new Memory('game.exe');
|
|
1322
|
+
*
|
|
1323
|
+
* // Read waypoints for AI pathfinding
|
|
1324
|
+
* const waypoints = memory.vector2Array(0x12345678n, 20);
|
|
1325
|
+
* console.log('AI waypoints:', waypoints);
|
|
1326
|
+
*
|
|
1327
|
+
* // Set new waypoints
|
|
1328
|
+
* const newWaypoints = [
|
|
1329
|
+
* { x: 10, y: 20 },
|
|
1330
|
+
* { x: 30, y: 40 },
|
|
1331
|
+
* { x: 50, y: 60 }
|
|
1332
|
+
* ];
|
|
1333
|
+
* memory.vector2Array(0x12345678n, newWaypoints);
|
|
1334
|
+
* ```
|
|
1335
|
+
*/
|
|
1336
|
+
public vector2Array(address: bigint, length: number): Vector2[];
|
|
1337
|
+
public vector2Array(address: bigint, values: Vector2[]): this;
|
|
1338
|
+
public vector2Array(address: bigint, lengthOrValues: Vector2[] | number): Vector2[] | this {
|
|
1339
|
+
if (typeof lengthOrValues === 'number') {
|
|
1340
|
+
const length = lengthOrValues;
|
|
1341
|
+
const scratch = new Float32Array(length * 2);
|
|
1342
|
+
|
|
1343
|
+
this.read(address, scratch);
|
|
1344
|
+
|
|
1345
|
+
const result = new Array<Vector2>(length);
|
|
1346
|
+
|
|
1347
|
+
for (let i = 0, j = 0; i < length; i++, j += 0x02) {
|
|
1348
|
+
const x = scratch[j];
|
|
1349
|
+
const y = scratch[j + 0x01];
|
|
1350
|
+
|
|
1351
|
+
result[i] = { x, y };
|
|
1352
|
+
}
|
|
1220
1353
|
|
|
1221
|
-
|
|
1354
|
+
return result;
|
|
1355
|
+
}
|
|
1222
1356
|
|
|
1223
|
-
|
|
1224
|
-
|
|
1357
|
+
const values = lengthOrValues;
|
|
1358
|
+
const scratch = new Float32Array(values.length * 0x02);
|
|
1225
1359
|
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
* @param address Destination address.
|
|
1229
|
-
* @param value Value to write.
|
|
1230
|
-
* @param force When true, temporarily enables `PAGE_EXECUTE_READWRITE` to bypass protection.
|
|
1231
|
-
* @returns `this` for chaining.
|
|
1232
|
-
* @throws {Win32Error} If the underlying write or protection change fails.
|
|
1233
|
-
*/
|
|
1360
|
+
for (let i = 0, j = 0; i < values.length; i++, j += 0x02) {
|
|
1361
|
+
const vector2 = values[i];
|
|
1234
1362
|
|
|
1235
|
-
|
|
1236
|
-
|
|
1363
|
+
scratch[j] = vector2.x;
|
|
1364
|
+
scratch[j + 0x01] = vector2.y;
|
|
1365
|
+
}
|
|
1237
1366
|
|
|
1238
|
-
this.
|
|
1367
|
+
this.write(address, scratch);
|
|
1239
1368
|
|
|
1240
1369
|
return this;
|
|
1241
1370
|
}
|
|
1242
1371
|
|
|
1243
1372
|
/**
|
|
1244
|
-
*
|
|
1245
|
-
*
|
|
1246
|
-
*
|
|
1247
|
-
* @param
|
|
1248
|
-
* @
|
|
1249
|
-
* @
|
|
1373
|
+
* Reads a 3D vector from memory or writes a 3D vector to memory.
|
|
1374
|
+
* Vectors are stored as three 32-bit floats: x, y, z.
|
|
1375
|
+
*
|
|
1376
|
+
* @param address - Memory address to read from or write to
|
|
1377
|
+
* @param value - Optional vector to write. If omitted, performs a read operation
|
|
1378
|
+
* @returns The Vector3 object when reading, or this Memory instance when writing
|
|
1379
|
+
*
|
|
1380
|
+
* @example
|
|
1381
|
+
* ```typescript
|
|
1382
|
+
* const memory = new Memory('game.exe');
|
|
1383
|
+
*
|
|
1384
|
+
* // Read player 3D position
|
|
1385
|
+
* const position = memory.vector3(0x12345678n);
|
|
1386
|
+
* console.log('Player 3D position:', position);
|
|
1387
|
+
*
|
|
1388
|
+
* // Teleport player to specific location
|
|
1389
|
+
* memory.vector3(0x12345678n, { x: 100, y: 50, z: 200 });
|
|
1390
|
+
* ```
|
|
1250
1391
|
*/
|
|
1392
|
+
public vector3(address: bigint): Vector3;
|
|
1393
|
+
public vector3(address: bigint, value: Vector3): this;
|
|
1394
|
+
public vector3(address: bigint, value?: Vector3): Vector3 | this {
|
|
1395
|
+
if (value === undefined) {
|
|
1396
|
+
this.read(address, this.Scratch12);
|
|
1251
1397
|
|
|
1252
|
-
|
|
1253
|
-
|
|
1398
|
+
const x = f32(this.Scratch12.ptr);
|
|
1399
|
+
const y = f32(this.Scratch12.ptr, 0x04);
|
|
1400
|
+
const z = f32(this.Scratch12.ptr, 0x08);
|
|
1254
1401
|
|
|
1255
|
-
|
|
1402
|
+
return { x, y, z };
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
this.Scratch12Buffer.writeFloatLE(value.x);
|
|
1406
|
+
this.Scratch12Buffer.writeFloatLE(value.y, 0x04);
|
|
1407
|
+
this.Scratch12Buffer.writeFloatLE(value.z, 0x08);
|
|
1408
|
+
|
|
1409
|
+
this.write(address, this.Scratch12);
|
|
1256
1410
|
|
|
1257
1411
|
return this;
|
|
1258
1412
|
}
|
|
1259
1413
|
|
|
1260
|
-
// + TODO: Implement scratch…
|
|
1261
|
-
|
|
1262
1414
|
/**
|
|
1263
|
-
*
|
|
1264
|
-
*
|
|
1265
|
-
* @param
|
|
1266
|
-
* @param
|
|
1267
|
-
* @returns
|
|
1268
|
-
*
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1415
|
+
* Reads an array of 3D vectors from memory or writes an array of 3D vectors to memory.
|
|
1416
|
+
*
|
|
1417
|
+
* @param address - Memory address to read from or write to
|
|
1418
|
+
* @param lengthOrValues - Length of array to read, or array of vectors to write
|
|
1419
|
+
* @returns Array of Vector3 objects when reading, or this Memory instance when writing
|
|
1420
|
+
*
|
|
1421
|
+
* @example
|
|
1422
|
+
* ```typescript
|
|
1423
|
+
* const memory = new Memory('game.exe');
|
|
1424
|
+
*
|
|
1425
|
+
* // Read vertex positions for 3D model
|
|
1426
|
+
* const vertices = memory.vector3Array(0x12345678n, 500);
|
|
1427
|
+
* console.log('3D vertices:', vertices);
|
|
1428
|
+
*
|
|
1429
|
+
* // Update vertex positions
|
|
1430
|
+
* const newVertices = [
|
|
1431
|
+
* { x: 1.0, y: 0.0, z: 0.0 },
|
|
1432
|
+
* { x: 0.0, y: 1.0, z: 0.0 },
|
|
1433
|
+
* { x: 0.0, y: 0.0, z: 1.0 }
|
|
1434
|
+
* ];
|
|
1435
|
+
* memory.vector3Array(0x12345678n, newVertices);
|
|
1436
|
+
* ```
|
|
1437
|
+
*/
|
|
1438
|
+
public vector3Array(address: bigint, length: number): Vector3[];
|
|
1439
|
+
public vector3Array(address: bigint, values: Vector3[]): this;
|
|
1440
|
+
public vector3Array(address: bigint, lengthOrValues: Vector3[] | number): Vector3[] | this {
|
|
1441
|
+
if (typeof lengthOrValues === 'number') {
|
|
1442
|
+
const length = lengthOrValues;
|
|
1443
|
+
const scratch = new Float32Array(length * 0x03);
|
|
1444
|
+
|
|
1445
|
+
this.read(address, scratch);
|
|
1446
|
+
|
|
1447
|
+
const result = new Array<Vector3>(length);
|
|
1448
|
+
|
|
1449
|
+
for (let i = 0, j = 0; i < length; i++, j += 0x03) {
|
|
1450
|
+
const x = scratch[j];
|
|
1451
|
+
const y = scratch[j + 0x01];
|
|
1452
|
+
const z = scratch[j + 0x02];
|
|
1453
|
+
|
|
1454
|
+
result[i] = { x, y, z };
|
|
1455
|
+
}
|
|
1276
1456
|
|
|
1277
|
-
|
|
1278
|
-
|
|
1457
|
+
return result;
|
|
1458
|
+
}
|
|
1279
1459
|
|
|
1280
|
-
|
|
1460
|
+
const values = lengthOrValues;
|
|
1461
|
+
const scratch = new Float32Array(values.length * 0x03);
|
|
1281
1462
|
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
* @param address Destination address.
|
|
1285
|
-
* @param value Value to write.
|
|
1286
|
-
* @param force When true, temporarily enables `PAGE_EXECUTE_READWRITE` to bypass protection.
|
|
1287
|
-
* @returns `this` for chaining.
|
|
1288
|
-
* @throws {Win32Error} If the underlying write or protection change fails.
|
|
1289
|
-
*/
|
|
1463
|
+
for (let i = 0, j = 0; i < values.length; i++, j += 0x03) {
|
|
1464
|
+
const vector3 = values[i];
|
|
1290
1465
|
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1466
|
+
scratch[j] = vector3.x;
|
|
1467
|
+
scratch[j + 0x01] = vector3.y;
|
|
1468
|
+
scratch[j + 0x02] = vector3.z;
|
|
1469
|
+
}
|
|
1294
1470
|
|
|
1295
|
-
this.
|
|
1471
|
+
this.write(address, scratch);
|
|
1296
1472
|
|
|
1297
1473
|
return this;
|
|
1298
1474
|
}
|