@cheatron/native-mock 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.
@@ -0,0 +1,18 @@
1
+ import { SimulatedProcess } from './process';
2
+ import { HandleObject } from './handles';
3
+ import * as D from 'win32-def';
4
+ export declare class Kernel {
5
+ static instance: Kernel;
6
+ private processes;
7
+ private nextPid;
8
+ currentProcess: SimulatedProcess;
9
+ constructor();
10
+ private createSystemProcess;
11
+ createProcess(name: string): SimulatedProcess;
12
+ getProcess(pid: number): SimulatedProcess | undefined;
13
+ OpenProcess(dwDesiredAccess: number, bInheritHandle: boolean, dwProcessId: number): D.HANDLE;
14
+ CloseHandle(hObject: D.HANDLE): boolean;
15
+ getObjectFromHandle(handle: D.HANDLE): HandleObject | undefined;
16
+ }
17
+ export declare const kernel: Kernel;
18
+ //# sourceMappingURL=kernel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kernel.d.ts","sourceRoot":"","sources":["../../src/os/kernel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,CAAC,MAAM,WAAW,CAAC;AAE/B,qBAAa,MAAM;IACjB,OAAc,QAAQ,EAAE,MAAM,CAAC;IAE/B,OAAO,CAAC,SAAS,CAA4C;IAC7D,OAAO,CAAC,OAAO,CAAa;IAGrB,cAAc,EAAE,gBAAgB,CAAC;;IAaxC,OAAO,CAAC,mBAAmB;IAKpB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB;IAQ7C,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAMrD,WAAW,CAChB,eAAe,EAAE,MAAM,EACvB,cAAc,EAAE,OAAO,EACvB,WAAW,EAAE,MAAM,GAClB,CAAC,CAAC,MAAM;IAeJ,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC,MAAM,GAAG,OAAO;IAKvC,mBAAmB,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,GAAG,YAAY,GAAG,SAAS;CAGvE;AAGD,eAAO,MAAM,MAAM,QAAe,CAAC"}
@@ -0,0 +1,50 @@
1
+ import { SimulatedProcess } from './process';
2
+ export class Kernel {
3
+ static instance;
4
+ processes = new Map();
5
+ nextPid = 4; // System processes start low
6
+ // The process that is "executing" the API calls (e.g. the test runner or the cheat tool)
7
+ currentProcess;
8
+ constructor() {
9
+ Kernel.instance = this;
10
+ // Bootstrapping: Create a "System" process and "Current" process
11
+ this.createSystemProcess();
12
+ // The "Current Process" is the one running the tests/code.
13
+ // We simulate it as a process so it can have a Handle Table.
14
+ this.currentProcess = new SimulatedProcess(9999, 'CurrentTestRunner.exe');
15
+ this.processes.set(this.currentProcess.id, this.currentProcess);
16
+ }
17
+ createSystemProcess() {
18
+ const sys = new SimulatedProcess(4, 'System');
19
+ this.processes.set(4, sys);
20
+ }
21
+ createProcess(name) {
22
+ const pid = this.nextPid;
23
+ this.nextPid += 4;
24
+ const proc = new SimulatedProcess(pid, name);
25
+ this.processes.set(pid, proc);
26
+ return proc;
27
+ }
28
+ getProcess(pid) {
29
+ return this.processes.get(pid);
30
+ }
31
+ // --- Syscalls (Simulated) ---
32
+ OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId) {
33
+ const targetProc = this.processes.get(dwProcessId);
34
+ if (!targetProc) {
35
+ return 0n; // NULL
36
+ }
37
+ // Create a handle in the CURRENT process's handle table pointing to the TARGET process
38
+ const handle = this.currentProcess.handles.createHandle(targetProc, 'Process', dwDesiredAccess);
39
+ return handle;
40
+ }
41
+ CloseHandle(hObject) {
42
+ return this.currentProcess.handles.closeHandle(hObject);
43
+ }
44
+ // Helper to dereference a handle from the current process context
45
+ getObjectFromHandle(handle) {
46
+ return this.currentProcess.handles.getObject(handle);
47
+ }
48
+ }
49
+ // Global kernel instance
50
+ export const kernel = new Kernel();
@@ -0,0 +1,24 @@
1
+ import { type MemoryBasicInformation } from '../constants';
2
+ export declare class MemoryManager {
3
+ private pages;
4
+ constructor();
5
+ /**
6
+ * Aligns an address down to the nearest page boundary.
7
+ */
8
+ private alignDown;
9
+ /**
10
+ * Aligns an address up to the nearest page boundary.
11
+ */
12
+ private alignUp;
13
+ /**
14
+ * Allocates memory.
15
+ * Simplification: Always allocates MEM_PRIVATE, MEM_COMMIT | MEM_RESERVE.
16
+ */
17
+ allocate(address: number, // If 0, find free space
18
+ size: number, _allocationType?: number, protect?: number): number;
19
+ free(address: number, _size?: number, _freeType?: number): boolean;
20
+ read(address: number, size: number): Buffer;
21
+ write(address: number, data: Buffer): number;
22
+ query(address: number): MemoryBasicInformation;
23
+ }
24
+ //# sourceMappingURL=memory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/os/memory.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,sBAAsB,EAI5B,MAAM,cAAc,CAAC;AAYtB,qBAAa,aAAa;IACxB,OAAO,CAAC,KAAK,CAA2B;;IAIxC;;OAEG;IACH,OAAO,CAAC,SAAS;IAIjB;;OAEG;IACH,OAAO,CAAC,OAAO;IAIf;;;OAGG;IACH,QAAQ,CACN,OAAO,EAAE,MAAM,EAAE,wBAAwB;IACzC,IAAI,EAAE,MAAM,EACZ,eAAe,GAAE,MAAiD,EAClE,OAAO,GAAE,MAAmC,GAC3C,MAAM;IA+CT,IAAI,CACF,OAAO,EAAE,MAAM,EACf,KAAK,GAAE,MAAU,EACjB,SAAS,GAAE,MAAe,GACzB,OAAO;IASV,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM;IAiC3C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM;IAwC5C,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,sBAAsB;CA0B/C"}
@@ -0,0 +1,147 @@
1
+ import { MemoryProtection, MemoryState, MemoryType, } from '../constants';
2
+ const PAGE_SIZE = 4096;
3
+ export class MemoryManager {
4
+ pages = new Map();
5
+ constructor() { }
6
+ /**
7
+ * Aligns an address down to the nearest page boundary.
8
+ */
9
+ alignDown(address) {
10
+ return Math.floor(address / PAGE_SIZE) * PAGE_SIZE;
11
+ }
12
+ /**
13
+ * Aligns an address up to the nearest page boundary.
14
+ */
15
+ alignUp(address) {
16
+ return Math.ceil(address / PAGE_SIZE) * PAGE_SIZE;
17
+ }
18
+ /**
19
+ * Allocates memory.
20
+ * Simplification: Always allocates MEM_PRIVATE, MEM_COMMIT | MEM_RESERVE.
21
+ */
22
+ allocate(address, // If 0, find free space
23
+ size, _allocationType = MemoryState.COMMIT | MemoryState.RESERVE, protect = MemoryProtection.READWRITE) {
24
+ const numPages = Math.ceil(size / PAGE_SIZE);
25
+ // Simple allocator: if address is 0, find a gap
26
+ // Start searching from 0x10000 (64KB) to avoid null pointer issues
27
+ let startAddress = address > 0 ? this.alignDown(address) : 0x10000;
28
+ if (address === 0) {
29
+ // Find a specialized gap
30
+ // This is a naive implementation; performance might key for huge allocations
31
+ while (true) {
32
+ let collision = false;
33
+ for (let i = 0; i < numPages; i++) {
34
+ if (this.pages.has(startAddress + i * PAGE_SIZE)) {
35
+ collision = true;
36
+ break;
37
+ }
38
+ }
39
+ if (!collision)
40
+ break;
41
+ startAddress += PAGE_SIZE;
42
+ }
43
+ }
44
+ else {
45
+ // Verify requested range is free
46
+ for (let i = 0; i < numPages; i++) {
47
+ const _addr = startAddress + i * PAGE_SIZE;
48
+ // Should check for collisions or if re-allocation is allowed
49
+ }
50
+ }
51
+ for (let i = 0; i < numPages; i++) {
52
+ const pageAddr = startAddress + i * PAGE_SIZE;
53
+ const page = this.pages.get(pageAddr) || {
54
+ address: pageAddr,
55
+ data: Buffer.alloc(PAGE_SIZE),
56
+ state: MemoryState.FREE,
57
+ protect: MemoryProtection.NOACCESS,
58
+ type: MemoryType.PRIVATE,
59
+ };
60
+ page.state = MemoryState.COMMIT;
61
+ page.protect = protect;
62
+ this.pages.set(pageAddr, page);
63
+ }
64
+ return startAddress;
65
+ }
66
+ free(address, _size = 0, _freeType = 0x8000 /* MEM_RELEASE */) {
67
+ const startAddress = this.alignDown(address);
68
+ if (this.pages.has(startAddress)) {
69
+ this.pages.delete(startAddress);
70
+ return true;
71
+ }
72
+ return false;
73
+ }
74
+ read(address, size) {
75
+ const result = Buffer.alloc(size);
76
+ let bytesRead = 0;
77
+ let currentAddr = address;
78
+ while (bytesRead < size) {
79
+ const pageAddr = this.alignDown(currentAddr);
80
+ const offsetInPage = currentAddr - pageAddr;
81
+ const bytesToRead = Math.min(PAGE_SIZE - offsetInPage, size - bytesRead);
82
+ const page = this.pages.get(pageAddr);
83
+ if (page &&
84
+ page.state & MemoryState.COMMIT &&
85
+ !(page.protect & MemoryProtection.NOACCESS)) {
86
+ page.data.copy(result, bytesRead, offsetInPage, offsetInPage + bytesToRead);
87
+ }
88
+ else {
89
+ // Unmapped or inaccessible
90
+ }
91
+ bytesRead += bytesToRead;
92
+ currentAddr += bytesToRead;
93
+ }
94
+ return result;
95
+ }
96
+ write(address, data) {
97
+ let bytesWritten = 0;
98
+ let currentAddr = address;
99
+ const size = data.length;
100
+ while (bytesWritten < size) {
101
+ const pageAddr = this.alignDown(currentAddr);
102
+ const offsetInPage = currentAddr - pageAddr;
103
+ const bytesToWrite = Math.min(PAGE_SIZE - offsetInPage, size - bytesWritten);
104
+ const page = this.pages.get(pageAddr);
105
+ if (page && page.state & MemoryState.COMMIT) {
106
+ // Check write permissions
107
+ if (page.protect &
108
+ (MemoryProtection.READWRITE | MemoryProtection.EXECUTE_READWRITE)) {
109
+ data.copy(page.data, offsetInPage, bytesWritten, bytesWritten + bytesToWrite);
110
+ }
111
+ else {
112
+ break;
113
+ }
114
+ }
115
+ else {
116
+ break;
117
+ }
118
+ bytesWritten += bytesToWrite;
119
+ currentAddr += bytesToWrite;
120
+ }
121
+ return bytesWritten;
122
+ }
123
+ query(address) {
124
+ const pageAddr = this.alignDown(address);
125
+ const page = this.pages.get(pageAddr);
126
+ if (!page) {
127
+ return {
128
+ BaseAddress: BigInt(pageAddr),
129
+ AllocationBase: BigInt(pageAddr),
130
+ AllocationProtect: MemoryProtection.NOACCESS,
131
+ RegionSize: BigInt(PAGE_SIZE),
132
+ State: MemoryState.FREE,
133
+ Protect: MemoryProtection.NOACCESS,
134
+ Type: MemoryType.PRIVATE,
135
+ };
136
+ }
137
+ return {
138
+ BaseAddress: BigInt(page.address),
139
+ AllocationBase: BigInt(page.address),
140
+ AllocationProtect: page.protect,
141
+ RegionSize: BigInt(PAGE_SIZE),
142
+ State: page.state,
143
+ Protect: page.protect,
144
+ Type: page.type,
145
+ };
146
+ }
147
+ }
@@ -0,0 +1,17 @@
1
+ import { MemoryManager } from './memory';
2
+ import { HandleTable } from './handles';
3
+ import { SimulatedThread } from './thread';
4
+ export declare class SimulatedProcess {
5
+ id: number;
6
+ name: string;
7
+ memory: MemoryManager;
8
+ handles: HandleTable;
9
+ threads: Map<number, SimulatedThread>;
10
+ exitCode: number | null;
11
+ private nextThreadId;
12
+ constructor(pid: number, name: string);
13
+ createThread(): SimulatedThread;
14
+ getThread(tid: number): SimulatedThread | undefined;
15
+ terminate(exitCode?: number): void;
16
+ }
17
+ //# sourceMappingURL=process.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process.d.ts","sourceRoot":"","sources":["../../src/os/process.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C,qBAAa,gBAAgB;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,WAAW,CAAC;IACrB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IACtC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEtC,OAAO,CAAC,YAAY,CAAgB;gBAExB,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IAQrC,YAAY,IAAI,eAAe;IAO/B,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS;IAInD,SAAS,CAAC,QAAQ,GAAE,MAAU;CAO/B"}
@@ -0,0 +1,35 @@
1
+ import { MemoryManager } from './memory';
2
+ import { HandleTable } from './handles';
3
+ import { SimulatedThread } from './thread';
4
+ export class SimulatedProcess {
5
+ id;
6
+ name;
7
+ memory;
8
+ handles;
9
+ threads;
10
+ exitCode = null;
11
+ nextThreadId = 1000;
12
+ constructor(pid, name) {
13
+ this.id = pid;
14
+ this.name = name;
15
+ this.memory = new MemoryManager();
16
+ this.handles = new HandleTable();
17
+ this.threads = new Map();
18
+ }
19
+ createThread() {
20
+ const tid = this.nextThreadId++;
21
+ const thread = new SimulatedThread(tid, this.id);
22
+ this.threads.set(tid, thread);
23
+ return thread;
24
+ }
25
+ getThread(tid) {
26
+ return this.threads.get(tid);
27
+ }
28
+ terminate(exitCode = 0) {
29
+ this.exitCode = exitCode;
30
+ // Cleanup threads
31
+ this.threads.clear();
32
+ // Cleanup handles?
33
+ // In a real OS, handles are closed.
34
+ }
35
+ }
@@ -0,0 +1,40 @@
1
+ export declare enum ThreadState {
2
+ INITIALIZED = 0,
3
+ READY = 1,
4
+ RUNNING = 2,
5
+ WAITING = 3,
6
+ TERMINATED = 4
7
+ }
8
+ export interface ThreadContext {
9
+ Rip: bigint;
10
+ Rax: bigint;
11
+ Rbx: bigint;
12
+ Rcx: bigint;
13
+ Rdx: bigint;
14
+ Rsi: bigint;
15
+ Rdi: bigint;
16
+ Rbp: bigint;
17
+ Rsp: bigint;
18
+ R8: bigint;
19
+ R9: bigint;
20
+ R10: bigint;
21
+ R11: bigint;
22
+ R12: bigint;
23
+ R13: bigint;
24
+ R14: bigint;
25
+ R15: bigint;
26
+ EFlags: number;
27
+ }
28
+ export declare class SimulatedThread {
29
+ id: number;
30
+ state: ThreadState;
31
+ suspendCount: number;
32
+ context: ThreadContext;
33
+ ownerProcessId: number;
34
+ constructor(tid: number, ownerPid: number);
35
+ suspend(): number;
36
+ resume(): number;
37
+ getContext(_flags: number): ThreadContext;
38
+ setContext(ctx: Partial<ThreadContext>): void;
39
+ }
40
+ //# sourceMappingURL=thread.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thread.d.ts","sourceRoot":"","sources":["../../src/os/thread.ts"],"names":[],"mappings":"AAAA,oBAAY,WAAW;IACrB,WAAW,IAAA;IACX,KAAK,IAAA;IACL,OAAO,IAAA;IACP,OAAO,IAAA;IACP,UAAU,IAAA;CACX;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,eAAe;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,WAAW,CAAC;IACnB,YAAY,EAAE,MAAM,CAAK;IACzB,OAAO,EAAE,aAAa,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;gBAElB,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;IA4BzC,OAAO,IAAI,MAAM;IAQjB,MAAM,IAAI,MAAM;IAUhB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa;IAKzC,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,IAAI;CAG9C"}
@@ -0,0 +1,64 @@
1
+ export var ThreadState;
2
+ (function (ThreadState) {
3
+ ThreadState[ThreadState["INITIALIZED"] = 0] = "INITIALIZED";
4
+ ThreadState[ThreadState["READY"] = 1] = "READY";
5
+ ThreadState[ThreadState["RUNNING"] = 2] = "RUNNING";
6
+ ThreadState[ThreadState["WAITING"] = 3] = "WAITING";
7
+ ThreadState[ThreadState["TERMINATED"] = 4] = "TERMINATED";
8
+ })(ThreadState || (ThreadState = {}));
9
+ export class SimulatedThread {
10
+ id;
11
+ state;
12
+ suspendCount = 0;
13
+ context;
14
+ ownerProcessId;
15
+ constructor(tid, ownerPid) {
16
+ this.id = tid;
17
+ this.ownerProcessId = ownerPid;
18
+ this.state = ThreadState.INITIALIZED;
19
+ // Initialize context with some reasonable defaults
20
+ this.context = {
21
+ Rip: 0x7ff700001000n, // Dummy entry point
22
+ Rax: 0n,
23
+ Rbx: 0n,
24
+ Rcx: 0n,
25
+ Rdx: 0n,
26
+ Rsi: 0n,
27
+ Rdi: 0n,
28
+ Rbp: 0n,
29
+ Rsp: 0x000000e0000n, // Dummy stack
30
+ R8: 0n,
31
+ R9: 0n,
32
+ R10: 0n,
33
+ R11: 0n,
34
+ R12: 0n,
35
+ R13: 0n,
36
+ R14: 0n,
37
+ R15: 0n,
38
+ EFlags: 0x202, // IF bit set
39
+ };
40
+ }
41
+ suspend() {
42
+ this.suspendCount++;
43
+ if (this.state === ThreadState.RUNNING) {
44
+ this.state = ThreadState.WAITING; // Simplification
45
+ }
46
+ return this.suspendCount - 1; // Return previous count
47
+ }
48
+ resume() {
49
+ if (this.suspendCount > 0) {
50
+ this.suspendCount--;
51
+ if (this.suspendCount === 0) {
52
+ this.state = ThreadState.READY; // Ready to run
53
+ }
54
+ }
55
+ return this.suspendCount;
56
+ }
57
+ getContext(_flags) {
58
+ // Should respect flags (CONTEXT_FULL, etc.), but for mock we return full.
59
+ return { ...this.context };
60
+ }
61
+ setContext(ctx) {
62
+ Object.assign(this.context, ctx);
63
+ }
64
+ }
@@ -0,0 +1,30 @@
1
+ import * as D from 'win32-def';
2
+ import { type MemoryBasicInformation } from './constants';
3
+ /**
4
+ * Represents a remote process
5
+ */
6
+ export declare class Process {
7
+ protected _handle: D.HANDLE | null;
8
+ protected _pid: number;
9
+ constructor(handle: D.HANDLE | null, autoClose?: boolean, pid?: number);
10
+ static open(pid: number, access?: number): Process;
11
+ static current(): CurrentProcess;
12
+ get handle(): D.HANDLE | null;
13
+ get pid(): number;
14
+ isValid(): boolean;
15
+ close(): void;
16
+ read(address: number | bigint, size: number): Buffer;
17
+ write(address: number | bigint, buffer: Buffer): void;
18
+ query(address: number | bigint): MemoryBasicInformation;
19
+ }
20
+ /**
21
+ * Represents the current process (singleton)
22
+ */
23
+ export declare class CurrentProcess extends Process {
24
+ constructor();
25
+ close(): void;
26
+ query(address: number | bigint): MemoryBasicInformation;
27
+ }
28
+ export declare const currentProcess: CurrentProcess;
29
+ export declare const currentProcessId: number;
30
+ //# sourceMappingURL=process.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process.d.ts","sourceRoot":"","sources":["../src/process.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,CAAC,MAAM,WAAW,CAAC;AAC/B,OAAO,EAIL,KAAK,sBAAsB,EAC5B,MAAM,aAAa,CAAC;AAerB;;GAEG;AACH,qBAAa,OAAO;IAClB,SAAS,CAAC,OAAO,EAAE,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC;IACnC,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC;gBAGrB,MAAM,EAAE,CAAC,CAAC,MAAM,GAAG,IAAI,EACvB,SAAS,GAAE,OAAc,EACzB,GAAG,GAAE,MAAU;IASjB,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,MAAiC,GAAG,OAAO;IAU5E,MAAM,CAAC,OAAO,IAAI,cAAc;IAIhC,IAAI,MAAM,oBAET;IACD,IAAI,GAAG,WAEN;IAED,OAAO,IAAI,OAAO;IAIlB,KAAK;IAUL,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM;IAyBpD,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAsBrD,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,sBAAsB;CAkBxD;AAED;;GAEG;AACH,qBAAa,cAAe,SAAQ,OAAO;;IAMhC,KAAK;IAIL,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,sBAAsB;CAQjE;AAGD,eAAO,MAAM,cAAc,gBAAuB,CAAC;AACnD,eAAO,MAAM,gBAAgB,QAAqB,CAAC"}
@@ -0,0 +1,124 @@
1
+ import { Kernel32Impl as Kernel32 } from './kernel32';
2
+ import { ProcessAccess, MEMORY_BASIC_INFORMATION, MBI_SIZE, } from './constants';
3
+ import koffi from 'koffi';
4
+ import { log } from './logger';
5
+ /**
6
+ * Handle management registry for automatic cleanup
7
+ */
8
+ const registry = new FinalizationRegistry((handle) => {
9
+ if (handle) {
10
+ log.trace('Process', 'Closing orphaned handle via GC');
11
+ Kernel32.CloseHandle(handle);
12
+ }
13
+ });
14
+ /**
15
+ * Represents a remote process
16
+ */
17
+ export class Process {
18
+ _handle;
19
+ _pid;
20
+ constructor(handle, autoClose = true, pid = 0) {
21
+ this._handle = handle;
22
+ this._pid = pid;
23
+ if (autoClose && handle) {
24
+ registry.register(this, handle, this);
25
+ }
26
+ }
27
+ static open(pid, access = ProcessAccess.ALL_ACCESS) {
28
+ log.debug('Process', `Opening process ${pid}`, { access });
29
+ const handle = Kernel32.OpenProcess(access, 0, pid);
30
+ if (!handle) {
31
+ log.error('Process', `Failed to open process ${pid}`);
32
+ throw new Error(`Failed to open process ${pid}`);
33
+ }
34
+ return new Process(handle, true, pid);
35
+ }
36
+ static current() {
37
+ return currentProcess;
38
+ }
39
+ get handle() {
40
+ return this._handle;
41
+ }
42
+ get pid() {
43
+ return this._pid;
44
+ }
45
+ isValid() {
46
+ return this._handle !== null && this._handle !== undefined;
47
+ }
48
+ close() {
49
+ if (this.isValid()) {
50
+ log.debug('Process', `Closing process handle ${this._pid}`);
51
+ const h = this._handle;
52
+ this._handle = null;
53
+ registry.unregister(this);
54
+ Kernel32.CloseHandle(h);
55
+ }
56
+ }
57
+ read(address, size) {
58
+ if (!this.isValid())
59
+ throw new Error('Process handle is closed');
60
+ const buffer = Buffer.alloc(size);
61
+ const success = Kernel32.ReadProcessMemory(this._handle, address, buffer, size, null);
62
+ if (!success) {
63
+ const errCode = Kernel32.GetLastError
64
+ ? Kernel32.GetLastError()
65
+ : 'unknown';
66
+ log.error('Process', `ReadProcessMemory failed at ${address}`, {
67
+ size,
68
+ errCode,
69
+ });
70
+ throw new Error('ReadProcessMemory failed');
71
+ }
72
+ return buffer;
73
+ }
74
+ write(address, buffer) {
75
+ if (!this.isValid())
76
+ throw new Error('Process handle is closed');
77
+ const success = Kernel32.WriteProcessMemory(this._handle, address, buffer, buffer.length, null);
78
+ if (!success) {
79
+ const errCode = Kernel32.GetLastError
80
+ ? Kernel32.GetLastError()
81
+ : 'unknown';
82
+ log.error('Process', `WriteProcessMemory failed at ${address}`, {
83
+ size: buffer.length,
84
+ errCode,
85
+ });
86
+ throw new Error('WriteProcessMemory failed');
87
+ }
88
+ }
89
+ query(address) {
90
+ if (!this.isValid())
91
+ throw new Error('Process handle is closed');
92
+ const buffer = Buffer.alloc(MBI_SIZE);
93
+ const result = Kernel32.VirtualQueryEx(this._handle, address, buffer, MBI_SIZE);
94
+ if (!result) {
95
+ log.error('Process', `VirtualQueryEx failed at ${address}`);
96
+ throw new Error('VirtualQueryEx failed');
97
+ }
98
+ const info = koffi.decode(buffer, MEMORY_BASIC_INFORMATION);
99
+ return info;
100
+ }
101
+ }
102
+ /**
103
+ * Represents the current process (singleton)
104
+ */
105
+ export class CurrentProcess extends Process {
106
+ constructor() {
107
+ // Current process uses a pseudo-handle that doesn't need closing
108
+ super(Kernel32.GetCurrentProcess(), false, Kernel32.GetCurrentProcessId());
109
+ }
110
+ close() {
111
+ this._handle = null;
112
+ }
113
+ query(address) {
114
+ const buffer = Buffer.alloc(MBI_SIZE);
115
+ const result = Kernel32.VirtualQuery(address, buffer, MBI_SIZE);
116
+ if (!result)
117
+ throw new Error('VirtualQuery failed');
118
+ const info = koffi.decode(buffer, MEMORY_BASIC_INFORMATION);
119
+ return info;
120
+ }
121
+ }
122
+ // Export a pre-initialized instance of the current process
123
+ export const currentProcess = new CurrentProcess();
124
+ export const currentProcessId = currentProcess.pid;
@@ -0,0 +1,28 @@
1
+ import * as D from 'win32-def';
2
+ import { type ThreadContext } from './constants';
3
+ /**
4
+ * Represents a thread handle
5
+ */
6
+ export declare class Thread {
7
+ protected _handle: D.HANDLE | null;
8
+ constructor(handle: D.HANDLE | null, autoClose?: boolean);
9
+ static open(threadId: number, access?: number): Thread;
10
+ static current(): CurrentThread;
11
+ static currentId(): number;
12
+ isValid(): boolean;
13
+ close(): void;
14
+ suspend(): number;
15
+ resume(): number;
16
+ getContext(flags?: number): ThreadContext;
17
+ setContext(ctx: ThreadContext): void;
18
+ get handle(): D.HANDLE | null;
19
+ }
20
+ /**
21
+ * Represents the current thread (singleton)
22
+ */
23
+ export declare class CurrentThread extends Thread {
24
+ constructor();
25
+ close(): void;
26
+ }
27
+ export declare const currentThread: CurrentThread;
28
+ //# sourceMappingURL=thread.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thread.d.ts","sourceRoot":"","sources":["../src/thread.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,CAAC,MAAM,WAAW,CAAC;AAC/B,OAAO,EAKL,KAAK,aAAa,EACnB,MAAM,aAAa,CAAC;AAerB;;GAEG;AACH,qBAAa,MAAM;IACjB,SAAS,CAAC,OAAO,EAAE,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC;gBAEvB,MAAM,EAAE,CAAC,CAAC,MAAM,GAAG,IAAI,EAAE,SAAS,GAAE,OAAc;IAO9D,MAAM,CAAC,IAAI,CACT,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,MAAgC,GACvC,MAAM;IAUT,MAAM,CAAC,OAAO,IAAI,aAAa;IAI/B,MAAM,CAAC,SAAS,IAAI,MAAM;IAI1B,OAAO,IAAI,OAAO;IAIlB,KAAK;IAUL,OAAO,IAAI,MAAM;IAUjB,MAAM,IAAI,MAAM;IAUhB,UAAU,CAAC,KAAK,GAAE,MAA0B,GAAG,aAAa;IAgB5D,UAAU,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI;IAapC,IAAI,MAAM,oBAET;CACF;AAED;;GAEG;AACH,qBAAa,aAAc,SAAQ,MAAM;;IAM9B,KAAK;CAGf;AAGD,eAAO,MAAM,aAAa,eAAsB,CAAC"}