@cheatron/nthread 1.0.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/LICENSE +21 -0
- package/README.md +175 -0
- package/dist/crt.d.ts +24 -0
- package/dist/crt.d.ts.map +1 -0
- package/dist/crt.js +28 -0
- package/dist/crt.js.map +1 -0
- package/dist/errors.d.ts +50 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +84 -0
- package/dist/errors.js.map +1 -0
- package/dist/globals.d.ts +48 -0
- package/dist/globals.d.ts.map +1 -0
- package/dist/globals.js +132 -0
- package/dist/globals.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +2 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +3 -0
- package/dist/logger.js.map +1 -0
- package/dist/memory/alloc-options.d.ts +23 -0
- package/dist/memory/alloc-options.d.ts.map +1 -0
- package/dist/memory/alloc-options.js +1 -0
- package/dist/memory/alloc-options.js.map +1 -0
- package/dist/memory/heap.d.ts +147 -0
- package/dist/memory/heap.d.ts.map +1 -0
- package/dist/memory/heap.js +276 -0
- package/dist/memory/heap.js.map +1 -0
- package/dist/memory/romem.d.ts +69 -0
- package/dist/memory/romem.d.ts.map +1 -0
- package/dist/memory/romem.js +108 -0
- package/dist/memory/romem.js.map +1 -0
- package/dist/nthread-heap.d.ts +65 -0
- package/dist/nthread-heap.d.ts.map +1 -0
- package/dist/nthread-heap.js +193 -0
- package/dist/nthread-heap.js.map +1 -0
- package/dist/nthread.d.ts +146 -0
- package/dist/nthread.d.ts.map +1 -0
- package/dist/nthread.js +421 -0
- package/dist/nthread.js.map +1 -0
- package/dist/thread/captured-thread.d.ts +68 -0
- package/dist/thread/captured-thread.d.ts.map +1 -0
- package/dist/thread/captured-thread.js +167 -0
- package/dist/thread/captured-thread.js.map +1 -0
- package/dist/thread/proxy-thread.d.ts +92 -0
- package/dist/thread/proxy-thread.d.ts.map +1 -0
- package/dist/thread/proxy-thread.js +154 -0
- package/dist/thread/proxy-thread.js.map +1 -0
- package/package.json +57 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import * as Native from '@cheatron/native';
|
|
2
|
+
import { NThread } from './nthread.js';
|
|
3
|
+
import type { CapturedThread } from './thread/captured-thread.js';
|
|
4
|
+
import type { ProxyThread } from './thread/proxy-thread.js';
|
|
5
|
+
import type { GeneralPurposeRegs } from './globals.js';
|
|
6
|
+
import type { AllocOptions } from './memory/alloc-options.js';
|
|
7
|
+
/** Default initial heap block size (bytes). */
|
|
8
|
+
export declare const DEFAULT_NTHREAD_HEAP_SIZE = 65536;
|
|
9
|
+
/** Default maximum heap size — heap doubles up to this limit before falling back to super. */
|
|
10
|
+
export declare const DEFAULT_NTHREAD_HEAP_MAX_SIZE: number;
|
|
11
|
+
/**
|
|
12
|
+
* NThreadHeap extends {@link NThread} with a single growing heap per injection.
|
|
13
|
+
*
|
|
14
|
+
* On first allocation a {@link Heap} of `heapSize` bytes is created in the
|
|
15
|
+
* target process via `calloc`. When it fills up the heap is **doubled** (up to
|
|
16
|
+
* `maxSize`) — the old block is kept resident so its existing allocations can
|
|
17
|
+
* still be freed, but new allocations come from the freshly grown block.
|
|
18
|
+
*
|
|
19
|
+
* When even a `maxSize`-sized block cannot satisfy a request (or `maxSize` is
|
|
20
|
+
* already reached), `super.threadAlloc()` is called — the base {@link NThread}
|
|
21
|
+
* allocator, which uses `msvcrt!malloc` / `calloc` / `realloc`.
|
|
22
|
+
*
|
|
23
|
+
* ### Lifecycle
|
|
24
|
+
* - `proxy.close()` destroys the active heap **and** all previous blocks, then
|
|
25
|
+
* restores the thread context via `super.threadClose()`.
|
|
26
|
+
*
|
|
27
|
+
* ### When to prefer NThreadHeap over NThread
|
|
28
|
+
* - Many small allocations: one `calloc` round-trip per block instead of per alloc
|
|
29
|
+
* - Readonly data: zone-typed allocs benefit from romem snapshot-skip writes
|
|
30
|
+
* - Predictable lifetime: all heap memory freed atomically on `proxy.close()`
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* const nt = new NThreadHeap(65536, 524288); // initial 64 KiB, max 512 KiB
|
|
35
|
+
* const [proxy] = await nt.inject(tid);
|
|
36
|
+
*
|
|
37
|
+
* const ptr = await proxy.alloc(64, { readonly: true, fill: 0 });
|
|
38
|
+
* await proxy.write(ptr, myBuffer);
|
|
39
|
+
*
|
|
40
|
+
* await proxy.close(); // destroys all heap blocks, restores thread
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export declare class NThreadHeap extends NThread {
|
|
44
|
+
/** Initial heap block size (bytes). */
|
|
45
|
+
readonly heapSize: number;
|
|
46
|
+
/** Maximum heap block size (bytes). Exceeded → super.threadAlloc(). */
|
|
47
|
+
readonly maxSize: number;
|
|
48
|
+
private state;
|
|
49
|
+
constructor(heapSize?: number, maxSize?: number, processId?: number, sleepAddress?: Native.NativePointer, pushretAddress?: Native.NativePointer, regKey?: GeneralPurposeRegs);
|
|
50
|
+
protected threadClose(proxy: ProxyThread, captured: CapturedThread, suicide?: number): Promise<void>;
|
|
51
|
+
protected threadAlloc(proxy: ProxyThread, size: number, opts?: AllocOptions): Promise<Native.NativePointer>;
|
|
52
|
+
protected threadFree(proxy: ProxyThread, ptr: Native.NativePointer): Promise<void>;
|
|
53
|
+
private getState;
|
|
54
|
+
/**
|
|
55
|
+
* Tries to allocate from the active heap. Grows the heap if full (up to
|
|
56
|
+
* `maxSize`). Returns `null` when the request must be served by super.
|
|
57
|
+
*/
|
|
58
|
+
private allocFromHeap;
|
|
59
|
+
/** Silent wrapper around `Heap.alloc` / `Heap.allocReadonly`. Returns `null` on failure. */
|
|
60
|
+
private tryAlloc;
|
|
61
|
+
/** Computes readonly zone size for a new heap block. */
|
|
62
|
+
private calcRoSize;
|
|
63
|
+
private reallocInternal;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=nthread-heap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nthread-heap.d.ts","sourceRoot":"","sources":["../src/nthread-heap.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAE9D,+CAA+C;AAC/C,eAAO,MAAM,yBAAyB,QAAQ,CAAC;AAE/C,8FAA8F;AAC9F,eAAO,MAAM,6BAA6B,QAAgC,CAAC;AAc3E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,qBAAa,WAAY,SAAQ,OAAO;IACtC,uCAAuC;IACvC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,uEAAuE;IACvE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB,OAAO,CAAC,KAAK,CAAsC;gBAGjD,QAAQ,CAAC,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,MAAM,EAClB,YAAY,CAAC,EAAE,MAAM,CAAC,aAAa,EACnC,cAAc,CAAC,EAAE,MAAM,CAAC,aAAa,EACrC,MAAM,CAAC,EAAE,kBAAkB;cAWJ,WAAW,CAClC,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,cAAc,EACxB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;cAYS,WAAW,CAClC,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,YAAY,GAClB,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC;cAoBP,UAAU,CACjC,KAAK,EAAE,WAAW,EAClB,GAAG,EAAE,MAAM,CAAC,aAAa,GACxB,OAAO,CAAC,IAAI,CAAC;IAiBhB,OAAO,CAAC,QAAQ;IAShB;;;OAGG;YACW,aAAa;IA4C3B,4FAA4F;IAC5F,OAAO,CAAC,QAAQ;IAQhB,wDAAwD;IACxD,OAAO,CAAC,UAAU;YAIJ,eAAe;CA0D9B"}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import * as Native from '@cheatron/native';
|
|
2
|
+
import { NThread } from './nthread.js';
|
|
3
|
+
import { Heap } from './memory/heap.js';
|
|
4
|
+
/** Default initial heap block size (bytes). */
|
|
5
|
+
export const DEFAULT_NTHREAD_HEAP_SIZE = 65536;
|
|
6
|
+
/** Default maximum heap size — heap doubles up to this limit before falling back to super. */
|
|
7
|
+
export const DEFAULT_NTHREAD_HEAP_MAX_SIZE = DEFAULT_NTHREAD_HEAP_SIZE * 8; // 512 KiB
|
|
8
|
+
/**
|
|
9
|
+
* NThreadHeap extends {@link NThread} with a single growing heap per injection.
|
|
10
|
+
*
|
|
11
|
+
* On first allocation a {@link Heap} of `heapSize` bytes is created in the
|
|
12
|
+
* target process via `calloc`. When it fills up the heap is **doubled** (up to
|
|
13
|
+
* `maxSize`) — the old block is kept resident so its existing allocations can
|
|
14
|
+
* still be freed, but new allocations come from the freshly grown block.
|
|
15
|
+
*
|
|
16
|
+
* When even a `maxSize`-sized block cannot satisfy a request (or `maxSize` is
|
|
17
|
+
* already reached), `super.threadAlloc()` is called — the base {@link NThread}
|
|
18
|
+
* allocator, which uses `msvcrt!malloc` / `calloc` / `realloc`.
|
|
19
|
+
*
|
|
20
|
+
* ### Lifecycle
|
|
21
|
+
* - `proxy.close()` destroys the active heap **and** all previous blocks, then
|
|
22
|
+
* restores the thread context via `super.threadClose()`.
|
|
23
|
+
*
|
|
24
|
+
* ### When to prefer NThreadHeap over NThread
|
|
25
|
+
* - Many small allocations: one `calloc` round-trip per block instead of per alloc
|
|
26
|
+
* - Readonly data: zone-typed allocs benefit from romem snapshot-skip writes
|
|
27
|
+
* - Predictable lifetime: all heap memory freed atomically on `proxy.close()`
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* const nt = new NThreadHeap(65536, 524288); // initial 64 KiB, max 512 KiB
|
|
32
|
+
* const [proxy] = await nt.inject(tid);
|
|
33
|
+
*
|
|
34
|
+
* const ptr = await proxy.alloc(64, { readonly: true, fill: 0 });
|
|
35
|
+
* await proxy.write(ptr, myBuffer);
|
|
36
|
+
*
|
|
37
|
+
* await proxy.close(); // destroys all heap blocks, restores thread
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export class NThreadHeap extends NThread {
|
|
41
|
+
/** Initial heap block size (bytes). */
|
|
42
|
+
heapSize;
|
|
43
|
+
/** Maximum heap block size (bytes). Exceeded → super.threadAlloc(). */
|
|
44
|
+
maxSize;
|
|
45
|
+
state = new Map();
|
|
46
|
+
constructor(heapSize, maxSize, processId, sleepAddress, pushretAddress, regKey) {
|
|
47
|
+
super(processId, sleepAddress, pushretAddress, regKey);
|
|
48
|
+
this.heapSize = heapSize ?? DEFAULT_NTHREAD_HEAP_SIZE;
|
|
49
|
+
this.maxSize = maxSize ?? DEFAULT_NTHREAD_HEAP_MAX_SIZE;
|
|
50
|
+
}
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
// Overrides
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
async threadClose(proxy, captured, suicide) {
|
|
55
|
+
const s = this.state.get(proxy);
|
|
56
|
+
if (s) {
|
|
57
|
+
const allHeaps = s.heap ? [...s.prevHeaps, s.heap] : s.prevHeaps;
|
|
58
|
+
for (const heap of allHeaps) {
|
|
59
|
+
await heap.destroy(proxy);
|
|
60
|
+
}
|
|
61
|
+
this.state.delete(proxy);
|
|
62
|
+
}
|
|
63
|
+
await super.threadClose(proxy, captured, suicide);
|
|
64
|
+
}
|
|
65
|
+
async threadAlloc(proxy, size, opts) {
|
|
66
|
+
if (opts?.address) {
|
|
67
|
+
return this.reallocInternal(proxy, opts.address, size, opts);
|
|
68
|
+
}
|
|
69
|
+
const ro = opts?.readonly ?? false;
|
|
70
|
+
const ptr = await this.allocFromHeap(proxy, size, ro);
|
|
71
|
+
// ptr === null → heap can't serve it; fall back to super (NThread/malloc)
|
|
72
|
+
if (ptr === null) {
|
|
73
|
+
return super.threadAlloc(proxy, size, opts);
|
|
74
|
+
}
|
|
75
|
+
if (opts?.fill !== undefined) {
|
|
76
|
+
await proxy.write(ptr, Buffer.alloc(size, opts.fill & 0xff));
|
|
77
|
+
}
|
|
78
|
+
return ptr;
|
|
79
|
+
}
|
|
80
|
+
async threadFree(proxy, ptr) {
|
|
81
|
+
const s = this.state.get(proxy);
|
|
82
|
+
const entry = s?.allocations.get(ptr.address);
|
|
83
|
+
s?.allocations.delete(ptr.address);
|
|
84
|
+
// Unknown or super-backed → delegate to NThread base (crt.free)
|
|
85
|
+
if (!entry || entry === 'super') {
|
|
86
|
+
return super.threadFree(proxy, ptr);
|
|
87
|
+
}
|
|
88
|
+
entry.heap.free(entry.alloc);
|
|
89
|
+
}
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
// Internal helpers
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
getState(proxy) {
|
|
94
|
+
let s = this.state.get(proxy);
|
|
95
|
+
if (!s) {
|
|
96
|
+
s = { heap: null, prevHeaps: [], allocations: new Map() };
|
|
97
|
+
this.state.set(proxy, s);
|
|
98
|
+
}
|
|
99
|
+
return s;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Tries to allocate from the active heap. Grows the heap if full (up to
|
|
103
|
+
* `maxSize`). Returns `null` when the request must be served by super.
|
|
104
|
+
*/
|
|
105
|
+
async allocFromHeap(proxy, size, ro) {
|
|
106
|
+
const s = this.getState(proxy);
|
|
107
|
+
if (s.heap) {
|
|
108
|
+
// Try current active heap
|
|
109
|
+
const result = this.tryAlloc(s.heap, size, ro);
|
|
110
|
+
if (result) {
|
|
111
|
+
s.allocations.set(result.remote.address, {
|
|
112
|
+
alloc: result,
|
|
113
|
+
heap: s.heap,
|
|
114
|
+
});
|
|
115
|
+
return result.remote;
|
|
116
|
+
}
|
|
117
|
+
// Full — can we grow?
|
|
118
|
+
if (s.heap.totalSize >= this.maxSize)
|
|
119
|
+
return null; // at ceiling
|
|
120
|
+
const newSize = Math.min(s.heap.totalSize * 2, this.maxSize);
|
|
121
|
+
if (size > newSize)
|
|
122
|
+
return null; // request too big for any heap block
|
|
123
|
+
s.prevHeaps.push(s.heap);
|
|
124
|
+
s.heap = await Heap.create(proxy, newSize, this.calcRoSize(newSize, ro));
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
// First alloc — create the initial heap
|
|
128
|
+
if (size > this.maxSize)
|
|
129
|
+
return null;
|
|
130
|
+
const initSize = Math.max(Math.min(this.heapSize, this.maxSize), size);
|
|
131
|
+
s.heap = await Heap.create(proxy, initSize, this.calcRoSize(initSize, ro));
|
|
132
|
+
}
|
|
133
|
+
const result = this.tryAlloc(s.heap, size, ro);
|
|
134
|
+
if (!result)
|
|
135
|
+
return null; // shouldn't happen
|
|
136
|
+
s.allocations.set(result.remote.address, { alloc: result, heap: s.heap });
|
|
137
|
+
return result.remote;
|
|
138
|
+
}
|
|
139
|
+
/** Silent wrapper around `Heap.alloc` / `Heap.allocReadonly`. Returns `null` on failure. */
|
|
140
|
+
tryAlloc(heap, size, ro) {
|
|
141
|
+
try {
|
|
142
|
+
return ro ? heap.allocReadonly(size) : heap.alloc(size);
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/** Computes readonly zone size for a new heap block. */
|
|
149
|
+
calcRoSize(totalSize, ro) {
|
|
150
|
+
return ro ? Math.floor((totalSize * 3) / 4) : Math.floor(totalSize / 4);
|
|
151
|
+
}
|
|
152
|
+
async reallocInternal(proxy, address, newSize, opts) {
|
|
153
|
+
const s = this.getState(proxy);
|
|
154
|
+
const entry = s.allocations.get(address.address);
|
|
155
|
+
if (!entry || entry === 'super') {
|
|
156
|
+
// Delegate entirely to NThread base (CRT realloc path)
|
|
157
|
+
if (entry === 'super')
|
|
158
|
+
s.allocations.delete(address.address);
|
|
159
|
+
const newPtr = await super.threadAlloc(proxy, newSize, opts);
|
|
160
|
+
s.allocations.set(newPtr.address, 'super');
|
|
161
|
+
return newPtr;
|
|
162
|
+
}
|
|
163
|
+
// Detect old zone: address < base + roSize → was readonly
|
|
164
|
+
const oldRo = entry.alloc.remote.address <
|
|
165
|
+
entry.heap.base.address + BigInt(entry.heap.roSize);
|
|
166
|
+
// Preserve old zone unless caller explicitly requests a change
|
|
167
|
+
const ro = opts?.readonly ?? oldRo;
|
|
168
|
+
// Allocate new block (heap if possible, otherwise super/CRT — but NOT realloc on old address)
|
|
169
|
+
const newRaw = await this.allocFromHeap(proxy, newSize, ro);
|
|
170
|
+
const newPtr = newRaw ??
|
|
171
|
+
(await super.threadAlloc(proxy, newSize, {
|
|
172
|
+
...opts,
|
|
173
|
+
address: undefined,
|
|
174
|
+
}));
|
|
175
|
+
if (!newRaw)
|
|
176
|
+
s.allocations.set(newPtr.address, 'super');
|
|
177
|
+
// Copy old content
|
|
178
|
+
const copyLen = Math.min(entry.alloc.size, newSize);
|
|
179
|
+
if (copyLen > 0) {
|
|
180
|
+
const oldData = await proxy.read(address, copyLen);
|
|
181
|
+
await proxy.write(newPtr, oldData);
|
|
182
|
+
}
|
|
183
|
+
// Fill newly added bytes when growing
|
|
184
|
+
if (newSize > copyLen && opts?.fill !== undefined) {
|
|
185
|
+
const fillPtr = new Native.NativePointer(newPtr.address + BigInt(copyLen));
|
|
186
|
+
await proxy.write(fillPtr, Buffer.alloc(newSize - copyLen, opts.fill & 0xff));
|
|
187
|
+
}
|
|
188
|
+
entry.heap.free(entry.alloc);
|
|
189
|
+
s.allocations.delete(address.address);
|
|
190
|
+
return newPtr;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
//# sourceMappingURL=nthread-heap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nthread-heap.js","sourceRoot":"","sources":["../src/nthread-heap.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import * as Native from '@cheatron/native';
|
|
2
|
+
import { type GeneralPurposeRegs } from './globals.js';
|
|
3
|
+
import { ProxyThread } from './thread/proxy-thread.js';
|
|
4
|
+
import { CapturedThread } from './thread/captured-thread.js';
|
|
5
|
+
import type { AllocOptions } from './memory/alloc-options.js';
|
|
6
|
+
export { STACK_ADD } from './thread/captured-thread.js';
|
|
7
|
+
/**
|
|
8
|
+
* Register-compatible argument type.
|
|
9
|
+
* All values are ultimately converted to bigint for register assignment (RCX, RDX, R8, R9).
|
|
10
|
+
* Accepts NativePointer (→ .address) and number (→ BigInt()) for convenience.
|
|
11
|
+
*/
|
|
12
|
+
export type Arg = bigint | number | Native.NativePointer;
|
|
13
|
+
/**
|
|
14
|
+
* NThread: Orchestrates non-invasive thread hijacking.
|
|
15
|
+
*
|
|
16
|
+
* It works by suspending a target thread, redirecting its Rip to a 'pushreg/ret' gadget,
|
|
17
|
+
* setting a 'sleep' address as the target, and waiting for the thread to 'land'
|
|
18
|
+
* at said sleep address. Once landed, the thread is effectively seized without
|
|
19
|
+
* stopping the underlying process or requiring complex debugging APIs.
|
|
20
|
+
*
|
|
21
|
+
* The actual thread state (context cache, suspend tracking, proxy) is managed by
|
|
22
|
+
* the contained CapturedThread instance, created during inject().
|
|
23
|
+
*/
|
|
24
|
+
export declare class NThread {
|
|
25
|
+
/** Optional process ID for diagnostics and logging */
|
|
26
|
+
processId?: number;
|
|
27
|
+
/** Address of an infinite loop gadget ('jmp .') used to hold the thread */
|
|
28
|
+
sleepAddress: Native.NativePointer;
|
|
29
|
+
/** Address of a pivot gadget ('push reg; ret') used to redirect execution */
|
|
30
|
+
pushretAddress: Native.NativePointer;
|
|
31
|
+
/** The register key (e.g., 'Rbx') used for the pushret pivot */
|
|
32
|
+
regKey: GeneralPurposeRegs;
|
|
33
|
+
/**
|
|
34
|
+
* Creates an NThread instance and prepares redirect gadgets.
|
|
35
|
+
* @param processId Optional process ID for diagnostics and logging.
|
|
36
|
+
* @param sleepAddress Optional explicit sleep gadget.
|
|
37
|
+
* @param pushretAddress Optional explicit pushret gadget.
|
|
38
|
+
* @param regKey Optional register preference for the pushret gadget.
|
|
39
|
+
*/
|
|
40
|
+
constructor(processId?: number, sleepAddress?: Native.NativePointer, pushretAddress?: Native.NativePointer, regKey?: GeneralPurposeRegs);
|
|
41
|
+
/**
|
|
42
|
+
* Executes the hijacking flow:
|
|
43
|
+
* 1. Create a CapturedThread from the given thread parameter.
|
|
44
|
+
* 2. Suspend the thread.
|
|
45
|
+
* 3. Capture and save current register state.
|
|
46
|
+
* 4. Redirect Rip to PushRet gadget.
|
|
47
|
+
* 5. Point the chosen register (RegKey) to the Sleep gadget.
|
|
48
|
+
* 6. Adjust stack (Rsp) to a safe scratch area.
|
|
49
|
+
* 7. Resume and wait for the thread to 'trap' itself in the loop.
|
|
50
|
+
*
|
|
51
|
+
* @param thread Thread object or Thread ID to hijack.
|
|
52
|
+
*/
|
|
53
|
+
inject(thread: Native.Thread | number): Promise<[ProxyThread, CapturedThread]>;
|
|
54
|
+
/** Writes data to the target process; dispatches NativePointer vs Buffer. */
|
|
55
|
+
private threadWrite;
|
|
56
|
+
/**
|
|
57
|
+
* Hook: called to release the proxy and captured thread.
|
|
58
|
+
* Subclasses can override to perform cleanup (e.g. destroy a heap pool)
|
|
59
|
+
* before closing. Default: terminate (if suicide) then close the handle.
|
|
60
|
+
*/
|
|
61
|
+
protected threadClose(_proxy: ProxyThread, captured: CapturedThread, suicide?: number): Promise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* Hook: allocates memory in the target process.
|
|
64
|
+
* Default: `malloc` / `calloc` / `malloc+memset` depending on `opts.fill`;
|
|
65
|
+
* delegates to `msvcrt!realloc` when `opts.address` is provided.
|
|
66
|
+
* Subclasses can override to use a pre-allocated heap instead.
|
|
67
|
+
*/
|
|
68
|
+
protected threadAlloc(proxy: ProxyThread, size: number, opts?: AllocOptions): Promise<Native.NativePointer>;
|
|
69
|
+
/**
|
|
70
|
+
* Hook: frees a pointer in the target process.
|
|
71
|
+
* Default: `msvcrt!free`.
|
|
72
|
+
* Subclasses can override to return the block to a managed heap instead.
|
|
73
|
+
*/
|
|
74
|
+
protected threadFree(proxy: ProxyThread, ptr: Native.NativePointer): Promise<void>;
|
|
75
|
+
/**
|
|
76
|
+
* Allocates memory for a string and writes it into the remote process via the captured thread.
|
|
77
|
+
* Null-terminates automatically.
|
|
78
|
+
*
|
|
79
|
+
* @param proxy The proxy for the captured thread.
|
|
80
|
+
* @param str String to encode and write.
|
|
81
|
+
* @param encoding Buffer encoding — defaults to `'utf16le'` (Windows wide string).
|
|
82
|
+
* @param opts Optional alloc options forwarded to `proxy.alloc()`.
|
|
83
|
+
*/
|
|
84
|
+
allocString(proxy: ProxyThread, str: string, encoding?: BufferEncoding, opts?: AllocOptions): Promise<Native.NativePointer>;
|
|
85
|
+
/**
|
|
86
|
+
* Executes a function call on a captured thread using the Windows x64 calling convention.
|
|
87
|
+
* The thread must be parked at the sleep address (after inject()).
|
|
88
|
+
*
|
|
89
|
+
* Supports up to 4 parameters mapped to RCX, RDX, R8, R9.
|
|
90
|
+
* Returns the value from RAX after the function completes.
|
|
91
|
+
*
|
|
92
|
+
* @param thread The captured thread to execute on.
|
|
93
|
+
* @param target Address of the function to call.
|
|
94
|
+
* @param args Up to 4 arguments (RCX, RDX, R8, R9).
|
|
95
|
+
* @param timeoutMs Timeout in ms for waiting on the function return (default: 5000).
|
|
96
|
+
* @returns RAX value as NativePointer.
|
|
97
|
+
*/
|
|
98
|
+
threadCall(thread: CapturedThread, target: Native.NativePointer | bigint, args?: Arg[], timeoutMs?: number): Promise<Native.NativePointer>;
|
|
99
|
+
/**
|
|
100
|
+
* Writes arbitrary data to the target process memory using hijacked memset calls.
|
|
101
|
+
* Decomposes the source buffer into runs of equal bytes and issues one
|
|
102
|
+
* `msvcrt!memset(dest + offset, value, runLength)` call per run.
|
|
103
|
+
*
|
|
104
|
+
* If the write range overlaps a registered read-only memory region, the
|
|
105
|
+
* overlapping portion is routed through writeMemorySafeBuffer (skips unchanged
|
|
106
|
+
* bytes) and the non-overlapping parts are written normally via recursive calls.
|
|
107
|
+
*
|
|
108
|
+
* @param dest Target address to write to.
|
|
109
|
+
* @param source The data to write.
|
|
110
|
+
*/
|
|
111
|
+
writeMemory(proxy: ProxyThread, dest: Native.NativePointer | bigint, source: Buffer | Uint8Array): Promise<number>;
|
|
112
|
+
/**
|
|
113
|
+
* Writes data from a NativePointer source to the target process memory
|
|
114
|
+
* using hijacked memset calls. Reads the source pointer byte-by-byte into
|
|
115
|
+
* a local buffer first, then delegates to the standard decomposed memset.
|
|
116
|
+
*
|
|
117
|
+
* Does NOT check read-only memory regions — this is intended for writing
|
|
118
|
+
* data we don't already know the contents of.
|
|
119
|
+
*
|
|
120
|
+
* @param thread The captured thread to execute on.
|
|
121
|
+
* @param dest Target address to write to.
|
|
122
|
+
* @param source Source pointer to read from (in our process).
|
|
123
|
+
* @param size Number of bytes to write.
|
|
124
|
+
*/
|
|
125
|
+
writeMemoryWithPointer(proxy: ProxyThread, dest: Native.NativePointer | bigint, source: Native.NativePointer, size: number): Promise<number>;
|
|
126
|
+
/**
|
|
127
|
+
* Safe write dispatcher: routes to the optimized variant based on `lastDest` type.
|
|
128
|
+
*
|
|
129
|
+
* @param dest Target address to write to.
|
|
130
|
+
* @param source The data to write.
|
|
131
|
+
* @param lastDest Either a snapshot Buffer of the previous state, or a single byte value
|
|
132
|
+
* representing a uniform fill (e.g. 0 means the region is all zeroes).
|
|
133
|
+
*/
|
|
134
|
+
writeMemorySafe(proxy: ProxyThread, dest: Native.NativePointer, source: Buffer, lastDest: Buffer | number): Promise<number>;
|
|
135
|
+
/**
|
|
136
|
+
* Safe write against a uniform fill value.
|
|
137
|
+
* Skips bytes that already equal `fillByte`.
|
|
138
|
+
*/
|
|
139
|
+
private writeMemorySafeUniform;
|
|
140
|
+
/**
|
|
141
|
+
* Safe write against a snapshot Buffer.
|
|
142
|
+
* Skips bytes that match the corresponding byte in `last`.
|
|
143
|
+
*/
|
|
144
|
+
private writeMemorySafeBuffer;
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=nthread.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nthread.d.ts","sourceRoot":"","sources":["../src/nthread.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAiB7D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAE9D,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAExD;;;;GAIG;AACH,MAAM,MAAM,GAAG,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC,aAAa,CAAC;AAIzD;;;;;;;;;;GAUG;AACH,qBAAa,OAAO;IAClB,sDAAsD;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAC;IAE1B,2EAA2E;IACpE,YAAY,EAAE,MAAM,CAAC,aAAa,CAAC;IAE1C,6EAA6E;IACtE,cAAc,EAAE,MAAM,CAAC,aAAa,CAAC;IAE5C,gEAAgE;IACzD,MAAM,EAAE,kBAAkB,CAAC;IAElC;;;;;;OAMG;gBAED,SAAS,CAAC,EAAE,MAAM,EAClB,YAAY,CAAC,EAAE,MAAM,CAAC,aAAa,EACnC,cAAc,CAAC,EAAE,MAAM,CAAC,aAAa,EACrC,MAAM,CAAC,EAAE,kBAAkB;IA6B7B;;;;;;;;;;;OAWG;IACG,MAAM,CACV,MAAM,EAAE,MAAM,CAAC,MAAM,GAAG,MAAM,GAC7B,OAAO,CAAC,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;IA6EzC,6EAA6E;YAC/D,WAAW;IAmBzB;;;;OAIG;cACa,WAAW,CACzB,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,cAAc,EACxB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IAKhB;;;;;OAKG;cACa,WAAW,CACzB,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,YAAY,GAClB,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC;IAqBhC;;;;OAIG;cACa,UAAU,CACxB,KAAK,EAAE,WAAW,EAClB,GAAG,EAAE,MAAM,CAAC,aAAa,GACxB,OAAO,CAAC,IAAI,CAAC;IAIhB;;;;;;;;OAQG;IACG,WAAW,CACf,KAAK,EAAE,WAAW,EAClB,GAAG,EAAE,MAAM,EACX,QAAQ,GAAE,cAA0B,EACpC,IAAI,CAAC,EAAE,YAAY,GAClB,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC;IAUhC;;;;;;;;;;;;OAYG;IACG,UAAU,CACd,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,MAAM,CAAC,aAAa,GAAG,MAAM,EACrC,IAAI,GAAE,GAAG,EAAO,EAChB,SAAS,GAAE,MAAa,GACvB,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC;IA+DhC;;;;;;;;;;;OAWG;IACG,WAAW,CACf,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,MAAM,CAAC,aAAa,GAAG,MAAM,EACnC,MAAM,EAAE,MAAM,GAAG,UAAU,GAC1B,OAAO,CAAC,MAAM,CAAC;IA+ElB;;;;;;;;;;;;OAYG;IACG,sBAAsB,CAC1B,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,MAAM,CAAC,aAAa,GAAG,MAAM,EACnC,MAAM,EAAE,MAAM,CAAC,aAAa,EAC5B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,CAAC;IA6BlB;;;;;;;OAOG;IACG,eAAe,CACnB,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,MAAM,CAAC,aAAa,EAC1B,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GAAG,MAAM,GACxB,OAAO,CAAC,MAAM,CAAC;IAOlB;;;OAGG;YACW,sBAAsB;IAkCpC;;;OAGG;YACW,qBAAqB;CAiCpC"}
|