bun-memory 1.1.18 → 1.1.21

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 CHANGED
@@ -42,19 +42,28 @@ const modules = memory.modules;
42
42
 
43
43
  const client = modules['client.dll'];
44
44
 
45
- console.log(`Base address: 0x${client.base.toString(16)}`);
46
- console.log(`Size: ${client.size} bytes`);
45
+ console.log(`Base: 0x%s — Size: %d bytes…`, client.base.toString(16), client.size);
46
+
47
+ const address = memory.follow(client.base + 0x12345678n, [0x10n, 0x20n, 0x30n]);
47
48
 
48
49
  // Read a 32-bit integer…
49
- const value = memory.i32(client.base + 0x12345678n);
50
+ const value = memory.i32(address);
50
51
 
51
52
  // Write a float…
52
- memory.f32(client.base + 0x12345678n, 3.14159);
53
+ memory.f32(address, 3.14159);
53
54
 
54
55
  // Clean up…
55
56
  memory.close();
56
57
  ```
57
58
 
59
+ ### API — Follow / Read / Write
60
+
61
+ Low-level helpers for pointer resolution and raw, allocation-free memory transfers. Use these when you need maximum control or want to reuse your own scratches.
62
+
63
+ - follow
64
+ - read
65
+ - write
66
+
58
67
  ### API — Typed Reads / Writes
59
68
 
60
69
  A `Memory` instance exposes typed helpers for reading and writing process memory. Pairs indicate
@@ -45,22 +45,17 @@ if (ClientPtr === undefined) {
45
45
  }
46
46
  // !
47
47
 
48
- const dec = new TextDecoder('utf-8');
49
-
50
- const buffer = Buffer.allocUnsafe(32);
51
- const pointers = [4749947025128n, 4749937696488n, 4749757372648n, 4745786211048n, 4745800086248n, 4748263960808n, 4748274490088n, 4748287459048n, 4747096900328n, 4747107757288n];
52
-
53
48
  console.time('test');
54
49
 
55
- while (true) {
56
- for (const pointer of pointers) {
57
- const cString = cs2.cString(pointer, 32).toString();
50
+ // while (true) {
51
+ // for (const pointer of pointers) {
52
+ // const cString = cs2.cString(pointer, 32).toString();
58
53
 
59
- if (cString.length > 32) {
60
- console.log(cString);
61
- }
62
- }
63
- }
54
+ // if (cString.length > 32) {
55
+ // console.log(cString);
56
+ // }
57
+ // }
58
+ // }
64
59
 
65
60
  console.timeEnd('test');
66
61
 
package/package.json CHANGED
@@ -22,7 +22,7 @@
22
22
  "url": "git://github.com/obscuritysrl/bun-memory.git"
23
23
  },
24
24
  "type": "module",
25
- "version": "1.1.18",
25
+ "version": "1.1.21",
26
26
  "main": "./index.ts",
27
27
  "keywords": [
28
28
  "bun",
package/structs/Memory.ts CHANGED
@@ -261,23 +261,91 @@ class Memory {
261
261
  // Core memory operations
262
262
 
263
263
  /**
264
- * Reads data from the target process memory into a scratch buffer.
265
- * This is a low-level method used internally by the typed read methods.
264
+ * Follows a multi-level pointer chain and returns the resolved absolute address.
265
+ *
266
+ * This helper walks a sequence of offsets starting at `address`, repeatedly
267
+ * dereferencing intermediate pointers as 64-bit unsigned integers, and finally
268
+ * adding the last offset without dereferencing.
269
+ *
270
+ * Semantics:
271
+ * - If `offsets` is empty, the original `address` is returned unchanged.
272
+ * - For each offset except the last, the method adds the offset to the current
273
+ * address and dereferences a `uint64` at that location to get the next base.
274
+ * - If any intermediate dereference yields `0n`:
275
+ * - when `throw_` is `true`, an error is thrown;
276
+ * - otherwise, `-1n` is returned to indicate a null chain.
277
+ * - After all intermediate dereferences succeed, the final offset is **added**
278
+ * (no dereference) and the resulting absolute address is returned.
279
+ *
280
+ * @param address - Starting absolute memory address (BigInt).
281
+ * @param offsets - Readonly list of `bigint` offsets that define the pointer path.
282
+ * @param throw_ - When `true`, throw on a null pointer encounter (default: `false`).
283
+ * @returns The resolved absolute address, or `-1n` if a null pointer was encountered and `throw_` is `false`.
284
+ * @throws {Error} When a null pointer is encountered and `throw_` is `true`.
266
285
  *
267
- * @param address - Memory address to read from
268
- * @param scratch - Buffer to store the read data
269
- * @returns This Memory instance for method chaining
270
- * @throws {Win32Error} When the read operation fails
286
+ * @example
287
+ * ```typescript
288
+ * // Typical multi-level pointer chain (moduleBase + 0x123456 → [0x10, 0x20] → +0x30 final)
289
+ * const client = memory.modules['client.dll'];
290
+ * const resolved = memory.follow(client.base, [0x10n, 0x20n, 0x30n]);
291
+ *
292
+ * if (resolved !== -1n) {
293
+ * const health = memory.f32(resolved);
294
+ * console.log('Health:', health);
295
+ * }
296
+ * ```
297
+ */
298
+ public follow(address: bigint, offsets: readonly bigint[], throw_ = false): bigint {
299
+ const last = offsets.length - 1;
300
+
301
+ if (last === -1) {
302
+ return address;
303
+ }
304
+
305
+ for (let i = 0; i < last; i++) {
306
+ address = this.u64((address += offsets[i]));
307
+
308
+ if (address === 0n) {
309
+ if (throw_) {
310
+ throw new Error('address must not be 0n.');
311
+ }
312
+
313
+ return -1n;
314
+ }
315
+ }
316
+
317
+ return address + offsets[last];
318
+ }
319
+
320
+ /**
321
+ * Reads data from the target process memory into the provided scratch buffer and returns that
322
+ * same scratch object (strongly typed). This is a low-level, zero-copy helper used internally by
323
+ * typed readers.
324
+ *
325
+ * @typeParam T - Concrete scratch type extending {@link Scratch} (for example, `Scratch16`,
326
+ * `Scratch32`, a CString scratch, etc.).
327
+ *
328
+ * @param address - Absolute memory address to read from (BigInt).
329
+ * @param scratch - Destination scratch instance to receive the bytes.
330
+ * @returns The same scratch instance you passed in, typed as `T`.
331
+ * @throws {Win32Error} When the underlying `ReadProcessMemory` call fails.
271
332
  *
272
333
  * @todo Research what it will take to add CString to the Scratch type.
273
334
  *
274
335
  * @example
275
- * ```typescript
276
- * const buffer = new Uint8Array(4);
277
- * memory.read(0x12345678n, buffer);
336
+ * ```ts
337
+ * // Strongly typed result based on the scratch you pass in:
338
+ * const s16 = memory.Scratch16;
339
+ * const out16 = memory.read(0x12345678n, s16);
340
+ * ```
341
+ *
342
+ * @example
343
+ * ```ts
344
+ * const myScratch = Buffer.allocUnsafe(64);
345
+ * const out = memory.read(0x1000_2000n, myScratch);
278
346
  * ```
279
347
  */
280
- public read(address: bigint, scratch: Scratch): this {
348
+ public read<T extends Scratch>(address: bigint, scratch: T): T {
281
349
  const lpBaseAddress = address;
282
350
  const lpBuffer = scratch.ptr;
283
351
  const nSize = scratch.byteLength;
@@ -289,7 +357,7 @@ class Memory {
289
357
  throw new Win32Error('ReadProcessMemory', Kernel32.GetLastError());
290
358
  }
291
359
 
292
- return this;
360
+ return scratch;
293
361
  }
294
362
 
295
363
  /**
@@ -306,7 +374,7 @@ class Memory {
306
374
  * memory.write(0x12345678n, buffer);
307
375
  * ```
308
376
  */
309
- private write(address: bigint, scratch: Scratch): void {
377
+ private write(address: bigint, scratch: Scratch): this {
310
378
  const lpBaseAddress = address;
311
379
  const lpBuffer = scratch.ptr;
312
380
  const nSize = scratch.byteLength;
@@ -318,7 +386,7 @@ class Memory {
318
386
  throw new Win32Error('WriteProcessMemory', Kernel32.GetLastError());
319
387
  }
320
388
 
321
- return;
389
+ return this;
322
390
  }
323
391
 
324
392
  // Public read / write methods…