bun-memory 1.1.34 → 1.1.36
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 +14 -3
- package/example/benchmark.ts +71 -51
- package/example/trigger-bot.ts +4 -19
- package/package.json +1 -1
- package/structs/Memory.ts +249 -166
package/README.md
CHANGED
|
@@ -52,7 +52,8 @@ cs2.close();
|
|
|
52
52
|
## API Highlights
|
|
53
53
|
|
|
54
54
|
- `follow(address, offsets)` — Follow a pointer chain
|
|
55
|
-
- `
|
|
55
|
+
- `indexOf(needle, address, length, [all])` — Search for a buffer or array in memory (returns all matches if all=true)
|
|
56
|
+
- `pattern(needle, address, length, [all])` — Find a byte pattern in memory (supports wildcards, returns all matches if all=true)
|
|
56
57
|
- `read(address, scratch)` — Read memory into a scratch (no allocations)
|
|
57
58
|
- `write(address, scratch)` — Write a scratch to memory
|
|
58
59
|
- Module map: `memory.modules['client.dll']`
|
|
@@ -83,10 +84,16 @@ void cs2.read(0x12345678n, array); // Fills array in-place
|
|
|
83
84
|
const needle = 'deadbeef';
|
|
84
85
|
// const needle = 'de**beef';
|
|
85
86
|
// const needle = 'de????ef';
|
|
87
|
+
// Find first match
|
|
86
88
|
const address = cs2.pattern(needle, 0x10000000n, 0x1000);
|
|
87
89
|
if (address !== -1n) {
|
|
88
90
|
console.log(`Found at 0x${address.toString(16)}`);
|
|
89
91
|
}
|
|
92
|
+
// Find all matches
|
|
93
|
+
const allAddresses = cs2.pattern(needle, 0x10000000n, 0x1000, true);
|
|
94
|
+
for (const addr of allAddresses) {
|
|
95
|
+
console.log(`Found at 0x${addr.toString(16)}`);
|
|
96
|
+
}
|
|
90
97
|
```
|
|
91
98
|
|
|
92
99
|
## Example: Pointer Chains
|
|
@@ -103,11 +110,16 @@ const address = cs2.follow(0x10000000n, [0x10n, 0x20n]);
|
|
|
103
110
|
const needle = Buffer.from([0x01, 0x02, 0x03]);
|
|
104
111
|
// const needle = new Uint8Array([0x01, 0x02, 0x03]);
|
|
105
112
|
// const needle = new Uint32Array([0x012345, 0x123456, 0x234567]);
|
|
106
|
-
//
|
|
113
|
+
// Find first match
|
|
107
114
|
const address = cs2.indexOf(needle, 0x10000000n, 0x1000);
|
|
108
115
|
if (address !== -1n) {
|
|
109
116
|
console.log(`Found at 0x${address.toString(16)}`);
|
|
110
117
|
}
|
|
118
|
+
// Find all matches
|
|
119
|
+
const allAddresses = cs2.indexOf(needle, 0x10000000n, 0x1000, true);
|
|
120
|
+
for (const addr of allAddresses) {
|
|
121
|
+
console.log(`Found at 0x${addr.toString(16)}`);
|
|
122
|
+
}
|
|
111
123
|
```
|
|
112
124
|
|
|
113
125
|
## Example: Typed Arrays
|
|
@@ -117,7 +129,6 @@ if (address !== -1n) {
|
|
|
117
129
|
const array = cs2.f32Array(0x12345678n, 4); // Float32Array of length 4
|
|
118
130
|
// const array = cs2.u64Array(0x12345678n, 4);
|
|
119
131
|
// const array = cs2.vector3Array(0x12345678n, 4);
|
|
120
|
-
// …etc…
|
|
121
132
|
cs2.i32Array(0x12345678n, new Int32Array([1, 2, 3, 4]));
|
|
122
133
|
cs2.u64Array(0x12345678n, new BigUint64Array([1, 2, 3, 4]));
|
|
123
134
|
cs2.vector3Array(0x12345678n, [{ x: 1, y: 2, z: 3 }]);
|
package/example/benchmark.ts
CHANGED
|
@@ -10,6 +10,8 @@ import Memory from 'bun-memory';
|
|
|
10
10
|
import ClientDLLJSON from './offsets/client_dll.json';
|
|
11
11
|
import OffsetsJSON from './offsets/offsets.json';
|
|
12
12
|
|
|
13
|
+
const Iterations = 1e6;
|
|
14
|
+
|
|
13
15
|
// Load the needed offsets as bigints… It's ugly but… IntelliSense! 🫠…
|
|
14
16
|
const Client = {
|
|
15
17
|
...Object.fromEntries(Object.entries(ClientDLLJSON['client.dll'].classes).map(([class_, { fields }]) => [class_, Object.fromEntries(Object.entries(fields).map(([field, value]) => [field, BigInt(value)]))])),
|
|
@@ -32,11 +34,12 @@ if (ClientPtr === undefined) {
|
|
|
32
34
|
// Warmup…
|
|
33
35
|
console.log('Warming up…');
|
|
34
36
|
|
|
35
|
-
for (let i = 0; i <
|
|
37
|
+
for (let i = 0; i < Iterations; i++) {
|
|
36
38
|
const GlobalVarsPtr = cs2.u64(ClientPtr + Client.Other.dwGlobalVars);
|
|
37
39
|
/* */ const CurTime = cs2.f32(GlobalVarsPtr + 0x30n);
|
|
38
40
|
|
|
39
41
|
const lPlayerControllerPtr = cs2.u64(ClientPtr + Client.Other.dwLocalPlayerController);
|
|
42
|
+
/* */ const lPlayerName = cs2.string(lPlayerControllerPtr + Client.CBasePlayerController.m_iszPlayerName, 32);
|
|
40
43
|
|
|
41
44
|
const lPlayerPawnPtr = cs2.u64(ClientPtr + Client.Other.dwLocalPlayerPawn);
|
|
42
45
|
/* */ const lHealth = cs2.u32(lPlayerPawnPtr + Client.C_BaseEntity.m_iHealth);
|
|
@@ -52,75 +55,92 @@ const EntityListScratch = new BigUint64Array(0x200 / 0x08);
|
|
|
52
55
|
const EntityClassInfoNames = new Map<bigint, string>();
|
|
53
56
|
|
|
54
57
|
// Start the test…
|
|
55
|
-
|
|
58
|
+
for (let i = 0; i < 10; i++) {
|
|
59
|
+
console.log('Starting the test…');
|
|
56
60
|
|
|
57
|
-
const
|
|
61
|
+
const start = performance.now();
|
|
58
62
|
|
|
59
|
-
const EntityListPtr = cs2.u64(ClientPtr + Client.Other.dwEntityList);
|
|
63
|
+
const EntityListPtr = cs2.u64(ClientPtr + Client.Other.dwEntityList);
|
|
60
64
|
|
|
61
|
-
for (let
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
+
for (let j = 0; j < Iterations; j++) {
|
|
66
|
+
try {
|
|
67
|
+
// Traverse the entity list and store it in `BaseEntityPtrs`…
|
|
68
|
+
void cs2.read(EntityListPtr + 0x10n, EntityListScratch);
|
|
65
69
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
70
|
+
// Traverse each of the potential 64 entity chunks…
|
|
71
|
+
for (let k = 0; k < 0x40; k++) {
|
|
72
|
+
const EntityChunkPtr = EntityListScratch[k];
|
|
69
73
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
74
|
+
if (EntityChunkPtr === 0n) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
73
77
|
|
|
74
|
-
|
|
78
|
+
void cs2.read(EntityChunkPtr, EntityChunkScratch);
|
|
75
79
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const BaseEntityPtr = EntityChunkScratch[l];
|
|
80
|
+
// Traverse each of the potential 512 entities within this chunk…
|
|
81
|
+
for (let l = 0; l < 0x1e00; l += 0x0f) {
|
|
82
|
+
const BaseEntityPtr = EntityChunkScratch[l];
|
|
80
83
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
+
if (BaseEntityPtr === 0n) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
84
87
|
|
|
85
|
-
|
|
88
|
+
const EntityClassInfoPtr = EntityChunkScratch[l + 0x01];
|
|
86
89
|
|
|
87
|
-
|
|
90
|
+
let Name = EntityClassInfoNames.get(EntityClassInfoPtr);
|
|
88
91
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
92
|
+
if (Name === undefined) {
|
|
93
|
+
const SchemaClassInfoDataPtr = cs2.u64(EntityClassInfoPtr + 0x30n);
|
|
94
|
+
/* */ const NamePtr = cs2.u64(SchemaClassInfoDataPtr + 0x08n);
|
|
95
|
+
// /* */ Name = cs2.buffer(NamePtr, 0x20).toString();
|
|
96
|
+
/* */ Name = cs2.string(NamePtr, 0x40);
|
|
93
97
|
|
|
94
|
-
|
|
95
|
-
|
|
98
|
+
EntityClassInfoNames.set(EntityClassInfoPtr, Name);
|
|
99
|
+
}
|
|
96
100
|
|
|
97
|
-
|
|
101
|
+
let BaseEntityPtrs_ = BaseEntityPtrs.get(Name);
|
|
98
102
|
|
|
99
|
-
|
|
100
|
-
|
|
103
|
+
if (BaseEntityPtrs_ === undefined) {
|
|
104
|
+
BaseEntityPtrs_ = [];
|
|
101
105
|
|
|
102
|
-
|
|
103
|
-
|
|
106
|
+
BaseEntityPtrs.set(Name, BaseEntityPtrs_);
|
|
107
|
+
}
|
|
104
108
|
|
|
105
|
-
|
|
109
|
+
BaseEntityPtrs_.push(BaseEntityPtr);
|
|
110
|
+
}
|
|
106
111
|
}
|
|
107
|
-
}
|
|
108
112
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
113
|
+
// ! —————————————————————————————————————————————————————————————————————————————————————————————
|
|
114
|
+
// ! YOUR CODE GOES HERE…
|
|
115
|
+
// ! —————————————————————————————————————————————————————————————————————————————————————————————
|
|
116
|
+
|
|
117
|
+
// // Log our entities…
|
|
118
|
+
// console.log(
|
|
119
|
+
// 'Entities found this tick: %O',
|
|
120
|
+
// Object.fromEntries([...BaseEntityPtrs.entries()].map(([Name, { length }]) => [Name, length])) //
|
|
121
|
+
// );
|
|
122
|
+
|
|
123
|
+
// ! —————————————————————————————————————————————————————————————————————————————————————————————
|
|
124
|
+
// ! YOUR CODE ENDS HERE…
|
|
125
|
+
// ! —————————————————————————————————————————————————————————————————————————————————————————————
|
|
126
|
+
} finally {
|
|
127
|
+
// Clear the entity list…
|
|
128
|
+
for (const BaseEntityPtrs_ of BaseEntityPtrs.values()) {
|
|
129
|
+
BaseEntityPtrs_.length = 0;
|
|
130
|
+
}
|
|
120
131
|
}
|
|
121
132
|
}
|
|
122
|
-
}
|
|
123
133
|
|
|
124
|
-
const
|
|
134
|
+
const end = performance.now();
|
|
135
|
+
|
|
136
|
+
const total = end - start;
|
|
137
|
+
const average = total / Iterations;
|
|
125
138
|
|
|
126
|
-
console.log(
|
|
139
|
+
console.log(
|
|
140
|
+
'Completed %d iterations in %ss, averaging %sms (%sµs) each…', //
|
|
141
|
+
Iterations,
|
|
142
|
+
(total / 1_000).toFixed(2),
|
|
143
|
+
average.toFixed(2),
|
|
144
|
+
(average * 1_000).toFixed(2)
|
|
145
|
+
);
|
|
146
|
+
}
|
package/example/trigger-bot.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FFIType, dlopen } from 'bun:ffi';
|
|
1
|
+
import { FFIType, dlopen, ptr } from 'bun:ffi';
|
|
2
2
|
import { sleep } from 'bun';
|
|
3
3
|
|
|
4
4
|
import Memory from 'bun-memory';
|
|
@@ -32,28 +32,13 @@ const Client = {
|
|
|
32
32
|
const cs2 = new Memory('cs2.exe');
|
|
33
33
|
|
|
34
34
|
// Get the base for client.dll…
|
|
35
|
-
const ClientPtr = cs2.modules['client.dll']?.base;
|
|
36
|
-
|
|
37
|
-
if (ClientPtr === undefined) {
|
|
38
|
-
throw new TypeError('ClientPtr must not be undefined.');
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Get client.dll…
|
|
42
35
|
const client = cs2.modules['client.dll'];
|
|
43
36
|
|
|
44
37
|
if (client === undefined) {
|
|
45
|
-
throw new
|
|
38
|
+
throw new TypeError('client must not be undefined.');
|
|
46
39
|
}
|
|
47
40
|
|
|
48
|
-
|
|
49
|
-
const needle = '40555356488dac24????????4881ec????????488bda488bf14885c9';
|
|
50
|
-
|
|
51
|
-
// Find a byte pattern in memory (supports wildcards: ** and ??)…
|
|
52
|
-
const addNametag = cs2.pattern(needle, client.base, client.size);
|
|
53
|
-
|
|
54
|
-
if (addNametag !== -1n) {
|
|
55
|
-
console.log(`Found at 0x${addNametag.toString(16)}`); // Found at 0x7ffc8964e490
|
|
56
|
-
}
|
|
41
|
+
const ClientPtr = client.base;
|
|
57
42
|
|
|
58
43
|
// Create a cache for class name strings… 🫠…
|
|
59
44
|
const Cache_Names = new Map<bigint, string>();
|
|
@@ -114,7 +99,7 @@ async function tick(ClientPtr: bigint) {
|
|
|
114
99
|
if (Name === undefined) {
|
|
115
100
|
const SchemaClassInfoDataPtr = cs2.u64(EntityClassInfoPtr + 0x30n);
|
|
116
101
|
/* */ const NamePtr = cs2.u64(SchemaClassInfoDataPtr + 0x08n);
|
|
117
|
-
/* */ Name = cs2.
|
|
102
|
+
/* */ Name = cs2.string(NamePtr, 0x80);
|
|
118
103
|
|
|
119
104
|
Cache_Names.set(EntityClassInfoPtr, Name);
|
|
120
105
|
}
|
package/package.json
CHANGED
package/structs/Memory.ts
CHANGED
|
@@ -7,7 +7,10 @@ const {
|
|
|
7
7
|
symbols: { CloseHandle, CreateToolhelp32Snapshot, GetLastError, Module32FirstW, Module32NextW, OpenProcess, Process32FirstW, Process32NextW, ReadProcessMemory, VirtualQueryEx, WriteProcessMemory },
|
|
8
8
|
} = dlopen('kernel32.dll', {
|
|
9
9
|
CloseHandle: { args: [FFIType.u64], returns: FFIType.bool },
|
|
10
|
+
CreateRemoteThread: { args: [FFIType.u64, FFIType.u64, FFIType.u64, FFIType.u64, FFIType.u64, FFIType.u32, FFIType.ptr], returns: FFIType.u64 },
|
|
10
11
|
CreateToolhelp32Snapshot: { args: [FFIType.u32, FFIType.u32], returns: FFIType.u64 },
|
|
12
|
+
GetCurrentProcess: { args: [], returns: FFIType.ptr },
|
|
13
|
+
GetExitCodeThread: { args: [FFIType.u64, FFIType.ptr], returns: FFIType.bool },
|
|
11
14
|
GetLastError: { returns: FFIType.u32 },
|
|
12
15
|
IsWow64Process2: { args: [FFIType.u64, FFIType.ptr, FFIType.ptr], returns: FFIType.bool },
|
|
13
16
|
Module32FirstW: { args: [FFIType.u64, FFIType.ptr], returns: FFIType.bool },
|
|
@@ -16,8 +19,11 @@ const {
|
|
|
16
19
|
Process32FirstW: { args: [FFIType.u64, FFIType.ptr], returns: FFIType.bool },
|
|
17
20
|
Process32NextW: { args: [FFIType.u64, FFIType.ptr], returns: FFIType.bool },
|
|
18
21
|
ReadProcessMemory: { args: [FFIType.u64, FFIType.u64, FFIType.ptr, FFIType.u64_fast, FFIType.ptr], returns: FFIType.bool },
|
|
22
|
+
VirtualAllocEx: { args: [FFIType.u64, FFIType.u64, FFIType.u64, FFIType.u32, FFIType.u32], returns: FFIType.u64 },
|
|
23
|
+
VirtualFreeEx: { args: [FFIType.u64, FFIType.u64, FFIType.u64, FFIType.u32], returns: FFIType.bool },
|
|
19
24
|
VirtualProtectEx: { args: [FFIType.u64, FFIType.u64, FFIType.u64, FFIType.u32, FFIType.ptr], returns: FFIType.bool },
|
|
20
25
|
VirtualQueryEx: { args: [FFIType.u64, FFIType.u64, FFIType.ptr, FFIType.u64], returns: FFIType.u64 },
|
|
26
|
+
WaitForSingleObject: { args: [FFIType.u64, FFIType.u32], returns: FFIType.u32 },
|
|
21
27
|
WriteProcessMemory: { args: [FFIType.u64, FFIType.u64, FFIType.ptr, FFIType.u64_fast, FFIType.ptr], returns: FFIType.bool },
|
|
22
28
|
});
|
|
23
29
|
|
|
@@ -28,6 +34,7 @@ const {
|
|
|
28
34
|
*
|
|
29
35
|
* Many scalar reads utilize `TypedArray` scratches to avoid a second FFI hop, such as calling `bun:ffi.read.*`.
|
|
30
36
|
*
|
|
37
|
+
* @todo Add call method for calling functions in remote process.
|
|
31
38
|
* @todo Add support for 32 or 64-bit processes using IsWow64Process2 (Windows 10+).
|
|
32
39
|
* @todo When adding 32-bit support, several u64 will need changed to u64_fast.
|
|
33
40
|
*
|
|
@@ -109,6 +116,8 @@ class Memory {
|
|
|
109
116
|
throw new Error(`Process not found: ${identifier}…`);
|
|
110
117
|
}
|
|
111
118
|
|
|
119
|
+
// public call(address: bigint, args: { type: FFIType, value: any }[] = [], timeout: number = 5_000): bigint {}
|
|
120
|
+
|
|
112
121
|
/**
|
|
113
122
|
* Memory protection flags for safe and unsafe regions.
|
|
114
123
|
* Used to filter readable/writable memory areas.
|
|
@@ -124,7 +133,8 @@ class Memory {
|
|
|
124
133
|
*/
|
|
125
134
|
private static readonly Patterns = {
|
|
126
135
|
MatchAll: /(?:[0-9A-Fa-f]{2})+/g,
|
|
127
|
-
Test: /^(?:\*{2}|[0-9A-Fa-f]{2}
|
|
136
|
+
Test: /^(?=.*[0-9A-Fa-f]{2})(?:\*{2}|\?{2}|[0-9A-Fa-f]{2})+$/,
|
|
137
|
+
// Test: /^(?:\*{2}|[0-9A-Fa-f]{2}|\?{2})+$/,
|
|
128
138
|
};
|
|
129
139
|
|
|
130
140
|
/**
|
|
@@ -170,9 +180,10 @@ class Memory {
|
|
|
170
180
|
private readonly ScratchMemoryBasicInformation = Buffer.allocUnsafe(0x30 /* sizeof(MEMORY_BASIC_INFORMATION) */);
|
|
171
181
|
private readonly ScratchModuleEntry32W = Buffer.allocUnsafe(0x438 /* sizeof(MODULEENTRY32W) */);
|
|
172
182
|
|
|
173
|
-
private static TextDecoderUTF16 = new TextDecoder('utf-16');
|
|
174
183
|
private static TextDecoderUTF8 = new TextDecoder('utf-8');
|
|
175
184
|
|
|
185
|
+
private static TextEncoderUTF8 = new TextEncoder('utf-8');
|
|
186
|
+
|
|
176
187
|
private readonly hProcess: bigint;
|
|
177
188
|
private readonly th32ProcessID: number;
|
|
178
189
|
|
|
@@ -189,43 +200,6 @@ class Memory {
|
|
|
189
200
|
return this._modules;
|
|
190
201
|
}
|
|
191
202
|
|
|
192
|
-
/**
|
|
193
|
-
* Returns memory regions in a given address range.
|
|
194
|
-
* @param address Start address.
|
|
195
|
-
* @param length Number of bytes to scan.
|
|
196
|
-
* @returns Array of memory regions.
|
|
197
|
-
*/
|
|
198
|
-
private regions(address: bigint, length: bigint | number): Region[] {
|
|
199
|
-
const dwLength = 0x30; /* sizeof(MEMORY_BASIC_INFORMATION) */
|
|
200
|
-
let lpAddress = BigInt(address); // prettier-ignore
|
|
201
|
-
const lpBuffer = this.ScratchMemoryBasicInformation;
|
|
202
|
-
|
|
203
|
-
const bVirtualQueryEx = !!VirtualQueryEx(this.hProcess, lpAddress, lpBuffer, dwLength);
|
|
204
|
-
|
|
205
|
-
if (!bVirtualQueryEx) {
|
|
206
|
-
throw new Win32Error('VirtualQueryEx', GetLastError());
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
const end = lpAddress + BigInt(length);
|
|
210
|
-
const result: Region[] = [];
|
|
211
|
-
|
|
212
|
-
do {
|
|
213
|
-
const baseAddress = lpBuffer.readBigUInt64LE();
|
|
214
|
-
const protect = lpBuffer.readUInt32LE(36);
|
|
215
|
-
const regionSize = lpBuffer.readBigUInt64LE(24);
|
|
216
|
-
const state = lpBuffer.readUInt32LE(32);
|
|
217
|
-
const type = lpBuffer.readUInt32LE(40);
|
|
218
|
-
|
|
219
|
-
if ((protect & Memory.MemoryProtections.Safe) !== 0 && (protect & Memory.MemoryProtections.Unsafe) === 0 && state === 0x1000 /* MEM_COMMIT */) {
|
|
220
|
-
result.push({ base: baseAddress, protect, size: regionSize, state, type });
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
lpAddress = baseAddress + regionSize;
|
|
224
|
-
} while (lpAddress < end && !!VirtualQueryEx(this.hProcess, lpAddress, lpBuffer, dwLength));
|
|
225
|
-
|
|
226
|
-
return result;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
203
|
/**
|
|
230
204
|
* Closes the process handle.
|
|
231
205
|
* @example
|
|
@@ -316,100 +290,6 @@ class Memory {
|
|
|
316
290
|
return;
|
|
317
291
|
}
|
|
318
292
|
|
|
319
|
-
/**
|
|
320
|
-
* Follows a pointer chain with offsets.
|
|
321
|
-
* @param address Base address.
|
|
322
|
-
* @param offsets Array of pointer offsets.
|
|
323
|
-
* @returns Final address after following the chain, or -1n if any pointer is null.
|
|
324
|
-
* @example
|
|
325
|
-
* ```ts
|
|
326
|
-
* const cs2 = new Memory('cs2.exe');
|
|
327
|
-
* const myAddress = cs2.follow(0x10000000n, [0x10n, 0x20n]);
|
|
328
|
-
* ```
|
|
329
|
-
*/
|
|
330
|
-
public follow(address: bigint, offsets: readonly bigint[]): bigint {
|
|
331
|
-
const last = offsets.length - 1;
|
|
332
|
-
|
|
333
|
-
for (let i = 0; i < last; i++) {
|
|
334
|
-
address = this.u64(address + offsets[i]);
|
|
335
|
-
|
|
336
|
-
if (address === 0n) {
|
|
337
|
-
return -1n;
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
return address + (offsets[last] ?? 0n);
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
/**
|
|
345
|
-
* Finds the address of a byte pattern in memory. `**` and `??` match any byte.
|
|
346
|
-
* @param needle Hex string pattern to search for (e.g., 'deadbeed', 'dead**ef', 'dead??ef').
|
|
347
|
-
* @param address Start address to search.
|
|
348
|
-
* @param length Number of bytes to search.
|
|
349
|
-
* @returns Address of the pattern if found, or -1n.
|
|
350
|
-
* @example
|
|
351
|
-
* ```ts
|
|
352
|
-
* const cs2 = new Memory('cs2.exe');
|
|
353
|
-
* const addr = cs2.pattern('dead**ef', 0x10000000n, 0x1000);
|
|
354
|
-
* ```
|
|
355
|
-
*/
|
|
356
|
-
public pattern(needle: string, address: bigint, length: number): bigint {
|
|
357
|
-
const test = Memory.Patterns.Test.test(needle);
|
|
358
|
-
|
|
359
|
-
if (!test) {
|
|
360
|
-
return -1n;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
const tokens = [...needle.matchAll(Memory.Patterns.MatchAll)]
|
|
364
|
-
.map((match) => ({ buffer: Buffer.from(match[0], 'hex'), index: match.index >>> 1, length: match[0].length >>> 1 })) //
|
|
365
|
-
.sort(({ length: a }, { length: b }) => b - a);
|
|
366
|
-
|
|
367
|
-
if (tokens.length === 0) {
|
|
368
|
-
return needle.length >>> 1 <= length ? address : -1n;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
const anchor = tokens.shift()!;
|
|
372
|
-
|
|
373
|
-
const haystack = this.buffer(address, length);
|
|
374
|
-
|
|
375
|
-
const end = length - (needle.length >>> 1);
|
|
376
|
-
let start = haystack.indexOf(anchor.buffer); // prettier-ignore
|
|
377
|
-
|
|
378
|
-
if (start === -1) {
|
|
379
|
-
return -1n;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
outer: do {
|
|
383
|
-
const base = start - anchor.index;
|
|
384
|
-
|
|
385
|
-
if (base < 0) {
|
|
386
|
-
continue;
|
|
387
|
-
} else if (base > end) {
|
|
388
|
-
return -1n;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
for (const { buffer, index, length } of tokens) {
|
|
392
|
-
const sourceEnd = base + index + length,
|
|
393
|
-
sourceStart = base + index,
|
|
394
|
-
target = buffer,
|
|
395
|
-
targetEnd = length,
|
|
396
|
-
targetStart = 0;
|
|
397
|
-
|
|
398
|
-
const compare = haystack.compare(target, targetStart, targetEnd, sourceStart, sourceEnd);
|
|
399
|
-
|
|
400
|
-
if (compare !== 0) {
|
|
401
|
-
continue outer;
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
return address + BigInt(base);
|
|
406
|
-
|
|
407
|
-
// Finish…
|
|
408
|
-
} while ((start = haystack.indexOf(anchor.buffer, start + 0x01)) !== -1);
|
|
409
|
-
|
|
410
|
-
return -1n;
|
|
411
|
-
}
|
|
412
|
-
|
|
413
293
|
/**
|
|
414
294
|
* Reads memory into a buffer.
|
|
415
295
|
* @param address Address to read from.
|
|
@@ -540,7 +420,13 @@ class Memory {
|
|
|
540
420
|
if (typeof lengthOrValue === 'number') {
|
|
541
421
|
const scratch = new Uint8Array(lengthOrValue);
|
|
542
422
|
|
|
543
|
-
this.read(address, scratch);
|
|
423
|
+
void this.read(address, scratch);
|
|
424
|
+
|
|
425
|
+
const indexOf = scratch.indexOf(0x00);
|
|
426
|
+
|
|
427
|
+
if (indexOf === -1) {
|
|
428
|
+
scratch[lengthOrValue - 1] = 0x00;
|
|
429
|
+
}
|
|
544
430
|
|
|
545
431
|
return new CString(scratch.ptr);
|
|
546
432
|
}
|
|
@@ -599,7 +485,7 @@ class Memory {
|
|
|
599
485
|
const length = lengthOrValues;
|
|
600
486
|
const scratch = new Float32Array(length);
|
|
601
487
|
|
|
602
|
-
this.read(address, scratch);
|
|
488
|
+
void this.read(address, scratch);
|
|
603
489
|
|
|
604
490
|
return scratch;
|
|
605
491
|
}
|
|
@@ -658,7 +544,7 @@ class Memory {
|
|
|
658
544
|
const length = lengthOrValues;
|
|
659
545
|
const scratch = new Float64Array(length);
|
|
660
546
|
|
|
661
|
-
this.read(address, scratch);
|
|
547
|
+
void this.read(address, scratch);
|
|
662
548
|
|
|
663
549
|
return scratch;
|
|
664
550
|
}
|
|
@@ -717,7 +603,7 @@ class Memory {
|
|
|
717
603
|
const length = lengthOrValues;
|
|
718
604
|
const scratch = new Int16Array(length);
|
|
719
605
|
|
|
720
|
-
this.read(address, scratch);
|
|
606
|
+
void this.read(address, scratch);
|
|
721
607
|
|
|
722
608
|
return scratch;
|
|
723
609
|
}
|
|
@@ -776,7 +662,7 @@ class Memory {
|
|
|
776
662
|
const length = lengthOrValues;
|
|
777
663
|
const scratch = new Int32Array(length);
|
|
778
664
|
|
|
779
|
-
this.read(address, scratch);
|
|
665
|
+
void this.read(address, scratch);
|
|
780
666
|
|
|
781
667
|
return scratch;
|
|
782
668
|
}
|
|
@@ -835,7 +721,7 @@ class Memory {
|
|
|
835
721
|
const length = lengthOrValues;
|
|
836
722
|
const scratch = new BigInt64Array(length);
|
|
837
723
|
|
|
838
|
-
this.read(address, scratch);
|
|
724
|
+
void this.read(address, scratch);
|
|
839
725
|
|
|
840
726
|
return scratch;
|
|
841
727
|
}
|
|
@@ -894,7 +780,7 @@ class Memory {
|
|
|
894
780
|
const length = lengthOrValues;
|
|
895
781
|
const scratch = new Int8Array(length);
|
|
896
782
|
|
|
897
|
-
this.read(address, scratch);
|
|
783
|
+
void this.read(address, scratch);
|
|
898
784
|
|
|
899
785
|
return scratch;
|
|
900
786
|
}
|
|
@@ -924,7 +810,7 @@ class Memory {
|
|
|
924
810
|
if (values === undefined) {
|
|
925
811
|
const scratch = new Float32Array(0x09);
|
|
926
812
|
|
|
927
|
-
this.read(address, scratch);
|
|
813
|
+
void this.read(address, scratch);
|
|
928
814
|
|
|
929
815
|
return scratch;
|
|
930
816
|
}
|
|
@@ -956,7 +842,7 @@ class Memory {
|
|
|
956
842
|
if (values === undefined) {
|
|
957
843
|
const scratch = new Float32Array(0x0c);
|
|
958
844
|
|
|
959
|
-
this.read(address, scratch);
|
|
845
|
+
void this.read(address, scratch);
|
|
960
846
|
|
|
961
847
|
return scratch;
|
|
962
848
|
}
|
|
@@ -988,7 +874,7 @@ class Memory {
|
|
|
988
874
|
if (values === undefined) {
|
|
989
875
|
const scratch = new Float32Array(0x10);
|
|
990
876
|
|
|
991
|
-
this.read(address, scratch);
|
|
877
|
+
void this.read(address, scratch);
|
|
992
878
|
|
|
993
879
|
return scratch;
|
|
994
880
|
}
|
|
@@ -1024,7 +910,7 @@ class Memory {
|
|
|
1024
910
|
|
|
1025
911
|
const scratch = new Uint32Array(size);
|
|
1026
912
|
|
|
1027
|
-
this.read(elementsPtr, scratch);
|
|
913
|
+
void this.read(elementsPtr, scratch);
|
|
1028
914
|
|
|
1029
915
|
return scratch;
|
|
1030
916
|
}
|
|
@@ -1054,7 +940,7 @@ class Memory {
|
|
|
1054
940
|
const Scratch8Float32Array = this.Scratch8Float32Array;
|
|
1055
941
|
|
|
1056
942
|
if (value === undefined) {
|
|
1057
|
-
this.read(address, Scratch8Float32Array);
|
|
943
|
+
void this.read(address, Scratch8Float32Array);
|
|
1058
944
|
|
|
1059
945
|
const x = Scratch8Float32Array[0x00],
|
|
1060
946
|
y = Scratch8Float32Array[0x01]; // prettier-ignore
|
|
@@ -1089,7 +975,7 @@ class Memory {
|
|
|
1089
975
|
const length = lengthOrValues;
|
|
1090
976
|
const scratch = new Float32Array(length * 2);
|
|
1091
977
|
|
|
1092
|
-
this.read(address, scratch);
|
|
978
|
+
void this.read(address, scratch);
|
|
1093
979
|
|
|
1094
980
|
const result = new Array<Vector2>(length);
|
|
1095
981
|
|
|
@@ -1152,7 +1038,7 @@ class Memory {
|
|
|
1152
1038
|
const Scratch12Float32Array = this.Scratch12Float32Array;
|
|
1153
1039
|
|
|
1154
1040
|
if (value === undefined) {
|
|
1155
|
-
this.read(address, Scratch12Float32Array);
|
|
1041
|
+
void this.read(address, Scratch12Float32Array);
|
|
1156
1042
|
|
|
1157
1043
|
const pitch = Scratch12Float32Array[0x00],
|
|
1158
1044
|
roll = Scratch12Float32Array[0x02],
|
|
@@ -1189,7 +1075,7 @@ class Memory {
|
|
|
1189
1075
|
const length = lengthOrValues;
|
|
1190
1076
|
const scratch = new Float32Array(length * 0x03);
|
|
1191
1077
|
|
|
1192
|
-
this.read(address, scratch);
|
|
1078
|
+
void this.read(address, scratch);
|
|
1193
1079
|
|
|
1194
1080
|
const result = new Array<QAngle>(length);
|
|
1195
1081
|
|
|
@@ -1266,7 +1152,7 @@ class Memory {
|
|
|
1266
1152
|
const Scratch16Float32Array = this.Scratch16Float32Array;
|
|
1267
1153
|
|
|
1268
1154
|
if (value === undefined) {
|
|
1269
|
-
this.read(address, Scratch16Float32Array);
|
|
1155
|
+
void this.read(address, Scratch16Float32Array);
|
|
1270
1156
|
|
|
1271
1157
|
const w = Scratch16Float32Array[0x03],
|
|
1272
1158
|
x = Scratch16Float32Array[0x00],
|
|
@@ -1305,7 +1191,7 @@ class Memory {
|
|
|
1305
1191
|
const length = lengthOrValues;
|
|
1306
1192
|
const scratch = new Float32Array(length * 0x04); // 4 * f32 per Quaternion
|
|
1307
1193
|
|
|
1308
|
-
this.read(address, scratch);
|
|
1194
|
+
void this.read(address, scratch);
|
|
1309
1195
|
|
|
1310
1196
|
const result = new Array<Quaternion>(length);
|
|
1311
1197
|
|
|
@@ -1384,7 +1270,7 @@ class Memory {
|
|
|
1384
1270
|
const Scratch3 = this.Scratch3;
|
|
1385
1271
|
|
|
1386
1272
|
if (value === undefined) {
|
|
1387
|
-
this.read(address, Scratch3);
|
|
1273
|
+
void this.read(address, Scratch3);
|
|
1388
1274
|
|
|
1389
1275
|
const r = Scratch3[0x00],
|
|
1390
1276
|
g = Scratch3[0x01],
|
|
@@ -1446,7 +1332,7 @@ class Memory {
|
|
|
1446
1332
|
const Scratch4 = this.Scratch4;
|
|
1447
1333
|
|
|
1448
1334
|
if (value === undefined) {
|
|
1449
|
-
this.read(address, Scratch4);
|
|
1335
|
+
void this.read(address, Scratch4);
|
|
1450
1336
|
|
|
1451
1337
|
const r = Scratch4[0x00],
|
|
1452
1338
|
g = Scratch4[0x01],
|
|
@@ -1492,6 +1378,43 @@ class Memory {
|
|
|
1492
1378
|
return this;
|
|
1493
1379
|
}
|
|
1494
1380
|
|
|
1381
|
+
/**
|
|
1382
|
+
* Reads or writes a UTF-8 string.
|
|
1383
|
+
* @param address Address to access.
|
|
1384
|
+
* @param lengthOrValue Length to read or string to write.
|
|
1385
|
+
* @returns The string at address, or this instance if writing.
|
|
1386
|
+
* @notice When writing, remember to null-terminate your string (e.g., 'hello\0').
|
|
1387
|
+
* @example
|
|
1388
|
+
* ```ts
|
|
1389
|
+
* const cs2 = new Memory('cs2.exe');
|
|
1390
|
+
* const myString = cs2.string(0x12345678n, 16);
|
|
1391
|
+
* cs2.string(0x12345678n, 'hello\0');
|
|
1392
|
+
* ```
|
|
1393
|
+
*/
|
|
1394
|
+
public string(address: bigint, length: number): string;
|
|
1395
|
+
public string(address: bigint, value: string): this;
|
|
1396
|
+
public string(address: bigint, lengthOrValue: number | string): string | this {
|
|
1397
|
+
if (typeof lengthOrValue === 'number') {
|
|
1398
|
+
const scratch = new Uint8Array(lengthOrValue);
|
|
1399
|
+
|
|
1400
|
+
void this.read(address, scratch);
|
|
1401
|
+
|
|
1402
|
+
const indexOf = scratch.indexOf(0x00);
|
|
1403
|
+
|
|
1404
|
+
return Memory.TextDecoderUTF8.decode(
|
|
1405
|
+
scratch.subarray(0, indexOf !== -1 ? indexOf : lengthOrValue) //
|
|
1406
|
+
);
|
|
1407
|
+
|
|
1408
|
+
// return new CString(scratch.ptr).valueOf();
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
const scratch = Memory.TextEncoderUTF8.encode(lengthOrValue);
|
|
1412
|
+
|
|
1413
|
+
this.write(address, scratch);
|
|
1414
|
+
|
|
1415
|
+
return this;
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1495
1418
|
/**
|
|
1496
1419
|
* Reads or writes a 16-bit unsigned integer.
|
|
1497
1420
|
* @param address Address to access.
|
|
@@ -1539,7 +1462,7 @@ class Memory {
|
|
|
1539
1462
|
const length = lengthOrValues;
|
|
1540
1463
|
const scratch = new Uint16Array(length);
|
|
1541
1464
|
|
|
1542
|
-
this.read(address, scratch);
|
|
1465
|
+
void this.read(address, scratch);
|
|
1543
1466
|
|
|
1544
1467
|
return scratch;
|
|
1545
1468
|
}
|
|
@@ -1598,7 +1521,7 @@ class Memory {
|
|
|
1598
1521
|
const length = lengthOrValues;
|
|
1599
1522
|
const scratch = new Uint32Array(length);
|
|
1600
1523
|
|
|
1601
|
-
this.read(address, scratch);
|
|
1524
|
+
void this.read(address, scratch);
|
|
1602
1525
|
|
|
1603
1526
|
return scratch;
|
|
1604
1527
|
}
|
|
@@ -1657,7 +1580,7 @@ class Memory {
|
|
|
1657
1580
|
const length = lengthOrValues;
|
|
1658
1581
|
const scratch = new BigUint64Array(length);
|
|
1659
1582
|
|
|
1660
|
-
this.read(address, scratch);
|
|
1583
|
+
void this.read(address, scratch);
|
|
1661
1584
|
|
|
1662
1585
|
return scratch;
|
|
1663
1586
|
}
|
|
@@ -1716,7 +1639,7 @@ class Memory {
|
|
|
1716
1639
|
const length = lengthOrValues;
|
|
1717
1640
|
const scratch = new Uint8Array(length);
|
|
1718
1641
|
|
|
1719
|
-
this.read(address, scratch);
|
|
1642
|
+
void this.read(address, scratch);
|
|
1720
1643
|
|
|
1721
1644
|
return scratch;
|
|
1722
1645
|
}
|
|
@@ -1854,7 +1777,7 @@ class Memory {
|
|
|
1854
1777
|
const Scratch12Float32Array = this.Scratch12Float32Array;
|
|
1855
1778
|
|
|
1856
1779
|
if (value === undefined) {
|
|
1857
|
-
this.read(address, Scratch12Float32Array);
|
|
1780
|
+
void this.read(address, Scratch12Float32Array);
|
|
1858
1781
|
|
|
1859
1782
|
const x = Scratch12Float32Array[0x00],
|
|
1860
1783
|
y = Scratch12Float32Array[0x01],
|
|
@@ -1891,7 +1814,7 @@ class Memory {
|
|
|
1891
1814
|
const length = lengthOrValues;
|
|
1892
1815
|
const scratch = new Float32Array(length * 0x03);
|
|
1893
1816
|
|
|
1894
|
-
this.read(address, scratch);
|
|
1817
|
+
void this.read(address, scratch);
|
|
1895
1818
|
|
|
1896
1819
|
const result = new Array<Vector3>(length);
|
|
1897
1820
|
|
|
@@ -2002,30 +1925,190 @@ class Memory {
|
|
|
2002
1925
|
|
|
2003
1926
|
// Public utility methods…
|
|
2004
1927
|
|
|
1928
|
+
/**
|
|
1929
|
+
* Follows a pointer chain with offsets.
|
|
1930
|
+
* @param address Base address.
|
|
1931
|
+
* @param offsets Array of pointer offsets.
|
|
1932
|
+
* @returns Final address after following the chain, or -1n if any pointer is null.
|
|
1933
|
+
* @example
|
|
1934
|
+
* ```ts
|
|
1935
|
+
* const cs2 = new Memory('cs2.exe');
|
|
1936
|
+
* const myAddress = cs2.follow(0x10000000n, [0x10n, 0x20n]);
|
|
1937
|
+
* ```
|
|
1938
|
+
*/
|
|
1939
|
+
public follow(address: bigint, offsets: readonly bigint[]): bigint {
|
|
1940
|
+
const last = offsets.length - 1;
|
|
1941
|
+
|
|
1942
|
+
for (let i = 0; i < last; i++) {
|
|
1943
|
+
address = this.u64(address + offsets[i]);
|
|
1944
|
+
|
|
1945
|
+
if (address === 0n) {
|
|
1946
|
+
return -1n;
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1950
|
+
return address + (offsets[last] ?? 0n);
|
|
1951
|
+
}
|
|
1952
|
+
|
|
2005
1953
|
/**
|
|
2006
1954
|
* Finds the address of a buffer within a memory region.
|
|
2007
|
-
* @param needle Buffer to search for.
|
|
1955
|
+
* @param needle Buffer or typed array to search for.
|
|
2008
1956
|
* @param address Start address.
|
|
2009
1957
|
* @param length Number of bytes to search.
|
|
2010
|
-
* @returns
|
|
1958
|
+
* @param all If true, returns all matches as an array. If false or omitted, returns the first match or -1n.
|
|
1959
|
+
* @returns Address of the buffer if found, or -1n. If all is true, returns an array of addresses.
|
|
2011
1960
|
* @example
|
|
2012
1961
|
* ```ts
|
|
2013
1962
|
* const cs2 = new Memory('cs2.exe');
|
|
2014
|
-
* const
|
|
1963
|
+
* const needle = Buffer.from('Hello world!');
|
|
1964
|
+
* // const needle = Buffer.from([0x01, 0x02, 0x03]);
|
|
1965
|
+
* // const needle = new Uint8Array([0x01, 0x02, 0x03]);
|
|
1966
|
+
* // const needle = new Float32Array([0x01, 0x02, 0x03]);
|
|
1967
|
+
* // Find first match
|
|
1968
|
+
* const address = cs2.indexOf(needle, 0x10000000n, 100);
|
|
1969
|
+
* // Find all matches
|
|
1970
|
+
* const allAddressess = cs2.indexOf(needle, 0x10000000n, 100, true);
|
|
2015
1971
|
* ```
|
|
2016
1972
|
*/
|
|
2017
|
-
public indexOf(needle: Scratch, address: bigint, length: number): bigint
|
|
1973
|
+
public indexOf(needle: Scratch, address: bigint, length: number): bigint;
|
|
1974
|
+
public indexOf(needle: Scratch, address: bigint, length: number, all: false): bigint;
|
|
1975
|
+
public indexOf(needle: Scratch, address: bigint, length: number, all: true): bigint[];
|
|
1976
|
+
public indexOf(needle: Scratch, address: bigint, length: number, all: boolean = false): bigint | bigint[] {
|
|
2018
1977
|
const haystack = Buffer.allocUnsafe(length);
|
|
2019
1978
|
|
|
2020
|
-
this.read(address, haystack);
|
|
2021
|
-
|
|
2022
1979
|
const needleBuffer = ArrayBuffer.isView(needle) //
|
|
2023
1980
|
? Buffer.from(needle.buffer, needle.byteOffset, needle.byteLength)
|
|
2024
1981
|
: Buffer.from(needle);
|
|
2025
1982
|
|
|
2026
|
-
|
|
1983
|
+
void this.read(address, haystack);
|
|
1984
|
+
|
|
1985
|
+
if (!all) {
|
|
1986
|
+
const indexOf = haystack.indexOf(needleBuffer);
|
|
1987
|
+
|
|
1988
|
+
return indexOf !== -1 ? BigInt(indexOf) + address : -1n;
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1991
|
+
const results: bigint[] = [];
|
|
1992
|
+
|
|
1993
|
+
let start = haystack.indexOf(needleBuffer);
|
|
1994
|
+
|
|
1995
|
+
if (start === -1) {
|
|
1996
|
+
return results;
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1999
|
+
do {
|
|
2000
|
+
results.push(address + BigInt(start));
|
|
2001
|
+
} while ((start = haystack.indexOf(needleBuffer, start + 0x01)) !== -1);
|
|
2002
|
+
|
|
2003
|
+
return results;
|
|
2004
|
+
}
|
|
2005
|
+
|
|
2006
|
+
/**
|
|
2007
|
+
* Finds the address of a byte pattern in memory. `**` and `??` match any byte.
|
|
2008
|
+
* @param needle Hex string pattern to search for (e.g., 'deadbeed', 'dead**ef', 'dead??ef').
|
|
2009
|
+
* @param address Start address to search.
|
|
2010
|
+
* @param length Number of bytes to search.
|
|
2011
|
+
* @param all If true, returns all matches as an array. If false or omitted, returns the first match or -1n.
|
|
2012
|
+
* @returns Address of the pattern if found, or -1n. If all is true, returns an array of addresses.
|
|
2013
|
+
* @example
|
|
2014
|
+
* ```ts
|
|
2015
|
+
* const cs2 = new Memory('cs2.exe');
|
|
2016
|
+
* // Find first match
|
|
2017
|
+
* const address = cs2.pattern('dead**ef', 0x10000000n, 0x1000);
|
|
2018
|
+
* // Find all matches
|
|
2019
|
+
* const allAddresses = cs2.pattern('dead**ef', 0x10000000n, 0x1000, true);
|
|
2020
|
+
* ```
|
|
2021
|
+
*/
|
|
2022
|
+
public pattern(needle: string, address: bigint, length: number): bigint;
|
|
2023
|
+
public pattern(needle: string, address: bigint, length: number, all: false): bigint;
|
|
2024
|
+
public pattern(needle: string, address: bigint, length: number, all: true): bigint[];
|
|
2025
|
+
public pattern(needle: string, address: bigint, length: number, all: boolean = false): bigint | bigint[] {
|
|
2026
|
+
const test = Memory.Patterns.Test.test(needle);
|
|
2027
|
+
|
|
2028
|
+
if (!test) {
|
|
2029
|
+
return !all ? -1n : [];
|
|
2030
|
+
}
|
|
2031
|
+
|
|
2032
|
+
// The RegExp test ensures that we have at least one token…
|
|
2033
|
+
|
|
2034
|
+
const tokens = [...needle.matchAll(Memory.Patterns.MatchAll)]
|
|
2035
|
+
.map((match) => ({ buffer: Buffer.from(match[0], 'hex'), index: match.index >>> 1, length: match[0].length >>> 1 })) //
|
|
2036
|
+
.sort(({ length: a }, { length: b }) => b - a);
|
|
2037
|
+
|
|
2038
|
+
const anchor = tokens.shift()!;
|
|
2039
|
+
|
|
2040
|
+
const haystack = this.buffer(address, length);
|
|
2041
|
+
|
|
2042
|
+
const end = length - (needle.length >>> 1);
|
|
2043
|
+
let start = haystack.indexOf(anchor.buffer); // prettier-ignore
|
|
2044
|
+
|
|
2045
|
+
if (start === -1) {
|
|
2046
|
+
return !all ? -1n : [];
|
|
2047
|
+
}
|
|
2048
|
+
|
|
2049
|
+
if (!all) {
|
|
2050
|
+
outer: do {
|
|
2051
|
+
const base = start - anchor.index;
|
|
2052
|
+
|
|
2053
|
+
if (base < 0) {
|
|
2054
|
+
continue;
|
|
2055
|
+
}
|
|
2056
|
+
|
|
2057
|
+
if (base > end) {
|
|
2058
|
+
return -1n;
|
|
2059
|
+
}
|
|
2060
|
+
|
|
2061
|
+
for (const { buffer, index, length } of tokens) {
|
|
2062
|
+
const sourceEnd = base + index + length,
|
|
2063
|
+
sourceStart = base + index,
|
|
2064
|
+
target = buffer,
|
|
2065
|
+
targetEnd = length,
|
|
2066
|
+
targetStart = 0; // prettier-ignore
|
|
2067
|
+
|
|
2068
|
+
const compare = haystack.compare(target, targetStart, targetEnd, sourceStart, sourceEnd);
|
|
2069
|
+
|
|
2070
|
+
if (compare !== 0) {
|
|
2071
|
+
continue outer;
|
|
2072
|
+
}
|
|
2073
|
+
}
|
|
2074
|
+
|
|
2075
|
+
return address + BigInt(base);
|
|
2076
|
+
} while ((start = haystack.indexOf(anchor.buffer, start + 0x01)) !== -1);
|
|
2077
|
+
|
|
2078
|
+
return -1n;
|
|
2079
|
+
}
|
|
2080
|
+
|
|
2081
|
+
const results: bigint[] = [];
|
|
2082
|
+
|
|
2083
|
+
outer: do {
|
|
2084
|
+
const base = start - anchor.index;
|
|
2085
|
+
|
|
2086
|
+
if (base < 0) {
|
|
2087
|
+
continue;
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2090
|
+
if (base > end) {
|
|
2091
|
+
return results;
|
|
2092
|
+
}
|
|
2093
|
+
|
|
2094
|
+
for (const { buffer, index, length } of tokens) {
|
|
2095
|
+
const sourceEnd = base + index + length,
|
|
2096
|
+
sourceStart = base + index,
|
|
2097
|
+
target = buffer,
|
|
2098
|
+
targetEnd = length,
|
|
2099
|
+
targetStart = 0; // prettier-ignore
|
|
2100
|
+
|
|
2101
|
+
const compare = haystack.compare(target, targetStart, targetEnd, sourceStart, sourceEnd);
|
|
2102
|
+
|
|
2103
|
+
if (compare !== 0) {
|
|
2104
|
+
continue outer;
|
|
2105
|
+
}
|
|
2106
|
+
}
|
|
2107
|
+
|
|
2108
|
+
results.push(address + BigInt(base));
|
|
2109
|
+
} while ((start = haystack.indexOf(anchor.buffer, start + 0x01)) !== -1);
|
|
2027
2110
|
|
|
2028
|
-
return
|
|
2111
|
+
return results;
|
|
2029
2112
|
}
|
|
2030
2113
|
}
|
|
2031
2114
|
|