@bingine/cemem 0.0.0-stage → 0.1.1
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/PORT-SCOPE.md +169 -0
- package/README.md +451 -2
- package/dist/cjs/addr/expression.js +650 -0
- package/dist/cjs/addr/expression.js.map +1 -0
- package/dist/cjs/addresslist/memory-record.js +612 -0
- package/dist/cjs/addresslist/memory-record.js.map +1 -0
- package/dist/cjs/asm/assembler.js +354 -0
- package/dist/cjs/asm/assembler.js.map +1 -0
- package/dist/cjs/asm/autoassembler-directives.js +362 -0
- package/dist/cjs/asm/autoassembler-directives.js.map +1 -0
- package/dist/cjs/asm/autoassembler.js +2163 -0
- package/dist/cjs/asm/autoassembler.js.map +1 -0
- package/dist/cjs/asm/backend.js +394 -0
- package/dist/cjs/asm/backend.js.map +1 -0
- package/dist/cjs/asm/disassembler.js +715 -0
- package/dist/cjs/asm/disassembler.js.map +1 -0
- package/dist/cjs/backend/dbk-backend.js +683 -0
- package/dist/cjs/backend/dbk-backend.js.map +1 -0
- package/dist/cjs/backend/native-ce-backend.js +667 -0
- package/dist/cjs/backend/native-ce-backend.js.map +1 -0
- package/dist/cjs/backend/self-backend.js +449 -0
- package/dist/cjs/backend/self-backend.js.map +1 -0
- package/dist/cjs/backend/types.js +48 -0
- package/dist/cjs/backend/types.js.map +1 -0
- package/dist/cjs/cheattable/ct-format.js +533 -0
- package/dist/cjs/cheattable/ct-format.js.map +1 -0
- package/dist/cjs/index.js +219 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/lua/aa.js +230 -0
- package/dist/cjs/lua/aa.js.map +1 -0
- package/dist/cjs/lua/addressing.js +118 -0
- package/dist/cjs/lua/addressing.js.map +1 -0
- package/dist/cjs/lua/api.js +736 -0
- package/dist/cjs/lua/api.js.map +1 -0
- package/dist/cjs/lua/asm.js +220 -0
- package/dist/cjs/lua/asm.js.map +1 -0
- package/dist/cjs/lua/bridge.js +526 -0
- package/dist/cjs/lua/bridge.js.map +1 -0
- package/dist/cjs/lua/marshal.js +562 -0
- package/dist/cjs/lua/marshal.js.map +1 -0
- package/dist/cjs/lua/memory.js +213 -0
- package/dist/cjs/lua/memory.js.map +1 -0
- package/dist/cjs/lua/objects.js +1070 -0
- package/dist/cjs/lua/objects.js.map +1 -0
- package/dist/cjs/lua/scan.js +543 -0
- package/dist/cjs/lua/scan.js.map +1 -0
- package/dist/cjs/lua/types.js +103 -0
- package/dist/cjs/lua/types.js.map +1 -0
- package/dist/cjs/scanner/matcher.js +781 -0
- package/dist/cjs/scanner/matcher.js.map +1 -0
- package/dist/cjs/scanner/pointer-scan.js +546 -0
- package/dist/cjs/scanner/pointer-scan.js.map +1 -0
- package/dist/cjs/scanner/scanner.js +513 -0
- package/dist/cjs/scanner/scanner.js.map +1 -0
- package/dist/cjs/scanner/shared-layout.js +207 -0
- package/dist/cjs/scanner/shared-layout.js.map +1 -0
- package/dist/cjs/types.js +190 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/cjs/value-codec.js +515 -0
- package/dist/cjs/value-codec.js.map +1 -0
- package/dist/cjs/win/process.js +475 -0
- package/dist/cjs/win/process.js.map +1 -0
- package/dist/cjs/win/scan-worker.js +84 -0
- package/dist/cjs/win/scan-worker.js.map +1 -0
- package/dist/cjs/win/winapi.js +403 -0
- package/dist/cjs/win/winapi.js.map +1 -0
- package/dist/types/addr/expression.d.ts +98 -0
- package/dist/types/addr/expression.d.ts.map +1 -0
- package/dist/types/addresslist/memory-record.d.ts +218 -0
- package/dist/types/addresslist/memory-record.d.ts.map +1 -0
- package/dist/types/asm/assembler.d.ts +106 -0
- package/dist/types/asm/assembler.d.ts.map +1 -0
- package/dist/types/asm/autoassembler-directives.d.ts +118 -0
- package/dist/types/asm/autoassembler-directives.d.ts.map +1 -0
- package/dist/types/asm/autoassembler.d.ts +434 -0
- package/dist/types/asm/autoassembler.d.ts.map +1 -0
- package/dist/types/asm/backend.d.ts +167 -0
- package/dist/types/asm/backend.d.ts.map +1 -0
- package/dist/types/asm/disassembler.d.ts +194 -0
- package/dist/types/asm/disassembler.d.ts.map +1 -0
- package/dist/types/backend/dbk-backend.d.ts +353 -0
- package/dist/types/backend/dbk-backend.d.ts.map +1 -0
- package/dist/types/backend/native-ce-backend.d.ts +213 -0
- package/dist/types/backend/native-ce-backend.d.ts.map +1 -0
- package/dist/types/backend/self-backend.d.ts +64 -0
- package/dist/types/backend/self-backend.d.ts.map +1 -0
- package/dist/types/backend/types.d.ts +176 -0
- package/dist/types/backend/types.d.ts.map +1 -0
- package/dist/types/cheattable/ct-format.d.ts +100 -0
- package/dist/types/cheattable/ct-format.d.ts.map +1 -0
- package/dist/types/index.d.ts +54 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/lua/aa.d.ts +107 -0
- package/dist/types/lua/aa.d.ts.map +1 -0
- package/dist/types/lua/addressing.d.ts +41 -0
- package/dist/types/lua/addressing.d.ts.map +1 -0
- package/dist/types/lua/api.d.ts +46 -0
- package/dist/types/lua/api.d.ts.map +1 -0
- package/dist/types/lua/asm.d.ts +42 -0
- package/dist/types/lua/asm.d.ts.map +1 -0
- package/dist/types/lua/bridge.d.ts +129 -0
- package/dist/types/lua/bridge.d.ts.map +1 -0
- package/dist/types/lua/marshal.d.ts +244 -0
- package/dist/types/lua/marshal.d.ts.map +1 -0
- package/dist/types/lua/memory.d.ts +61 -0
- package/dist/types/lua/memory.d.ts.map +1 -0
- package/dist/types/lua/objects.d.ts +210 -0
- package/dist/types/lua/objects.d.ts.map +1 -0
- package/dist/types/lua/scan.d.ts +106 -0
- package/dist/types/lua/scan.d.ts.map +1 -0
- package/dist/types/lua/types.d.ts +146 -0
- package/dist/types/lua/types.d.ts.map +1 -0
- package/dist/types/scanner/matcher.d.ts +122 -0
- package/dist/types/scanner/matcher.d.ts.map +1 -0
- package/dist/types/scanner/pointer-scan.d.ts +187 -0
- package/dist/types/scanner/pointer-scan.d.ts.map +1 -0
- package/dist/types/scanner/scanner.d.ts +150 -0
- package/dist/types/scanner/scanner.d.ts.map +1 -0
- package/dist/types/scanner/shared-layout.d.ts +96 -0
- package/dist/types/scanner/shared-layout.d.ts.map +1 -0
- package/dist/types/types.d.ts +239 -0
- package/dist/types/types.d.ts.map +1 -0
- package/dist/types/value-codec.d.ts +134 -0
- package/dist/types/value-codec.d.ts.map +1 -0
- package/dist/types/win/process.d.ts +190 -0
- package/dist/types/win/process.d.ts.map +1 -0
- package/dist/types/win/scan-worker.d.ts +51 -0
- package/dist/types/win/scan-worker.d.ts.map +1 -0
- package/dist/types/win/winapi.d.ts +144 -0
- package/dist/types/win/winapi.d.ts.map +1 -0
- package/docs/assembler.md +371 -0
- package/docs/autoassembler.md +423 -0
- package/docs/backend-dbk.md +305 -0
- package/docs/backends.md +273 -0
- package/docs/evidence/atomics-wait-io.js +20 -0
- package/docs/evidence/mini-repro.js +98 -0
- package/docs/lua-api.md +718 -0
- package/package.json +60 -4
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 反汇编器(x86 / x64 / ARM / Thumb / ARM64)—— 输出对齐 Cheat Engine 的
|
|
3
|
+
* `TLastDisassembleData`。
|
|
4
|
+
*
|
|
5
|
+
* 后端是 Capstone(见 `backend.ts` 里关于"CE 自己也用 binutils 而非手写解码器"的说明)。
|
|
6
|
+
* 本文件负责把 Capstone 的原始结果加工成 CE 需要的那套字段与文本格式。
|
|
7
|
+
*
|
|
8
|
+
* CE 的字段定义见 `lastdisassembledata.pas` 的 `TLastDisassembleData`:
|
|
9
|
+
* address / prefix / opcode / parameters / description / Bytes /
|
|
10
|
+
* SeperatorCount+Seperators / modrmValue / parameterValue /
|
|
11
|
+
* datasize / isfloat / isfloat64 / hasSib / sibIndex / sibScaler /
|
|
12
|
+
* isjump / iscall / isret / isrep / isconditionaljump / riprelative
|
|
13
|
+
* / Disassembler(dcX86 / dcArm / dcArm64 / dcThumb)
|
|
14
|
+
*
|
|
15
|
+
* ## 文本格式
|
|
16
|
+
* CE 在 binutils 分支下产出 `"地址 - 字节 - 操作码 参数"`。
|
|
17
|
+
* 本模块的 `formatDisassembled` 产同样形状的字符串,同时把 opcode / parameters
|
|
18
|
+
* 分开保留(CE 内部也是分开存的,显示时才拼)。
|
|
19
|
+
*
|
|
20
|
+
* ## 多架构差异(这里是最容易出错的地方)
|
|
21
|
+
*
|
|
22
|
+
* | 项目 | x86/x64 | ARM/Thumb | ARM64 |
|
|
23
|
+
* | --- | --- | --- | --- |
|
|
24
|
+
* | 指令长度 | 1–15 字节,可变 | 4(Thumb 2/4) | 4 |
|
|
25
|
+
* | 前缀 | `rep`/`lock`/段前缀 | 无 | 无 |
|
|
26
|
+
* | 条件执行 | `jcc` 助记符(7x/0F 8x) | **指令后缀**(`moveq`/`addne`) | `b.eq` 助记符 |
|
|
27
|
+
* | 返回 | `ret`/`retf`/`iret` | `bx lr`/`pop {...,pc}`/`mov pc,lr` | `ret` |
|
|
28
|
+
* | 调用 | `call` | `bl`/`blx` | `bl`/`blr` |
|
|
29
|
+
* | 寄存器 | `rax`/`eax`/`al`… | `r0`–`r15`、`sp`/`lr`/`pc` | `x0`–`x30`、`w0`–`w30`、`sp`/`xzr`/`wzr` |
|
|
30
|
+
* | RIP 相对 | 有(`[rip+disp]`) | 无 | 有(`adrp`/`ldr xN, [pc,...]`、`b`/`bl` 的 PC 相对) |
|
|
31
|
+
*
|
|
32
|
+
* ## 已知限制
|
|
33
|
+
* `capstone-wasm` 不暴露 detail(`readInsn` 是私有的),因此拿不到
|
|
34
|
+
* "操作数类型/寄存器使用/内存操作数宽度"等结构信息。
|
|
35
|
+
* 本模块改用**助记符表**判定流程控制与数据宽度,对主流指令足够;
|
|
36
|
+
* 冷门指令会被标成 `isjump=false`(宁可少标,不要错标)。
|
|
37
|
+
*/
|
|
38
|
+
import { type AsmArchitecture, type AsmEndianness } from './backend';
|
|
39
|
+
/** 值类型提示,对齐 CE 的 TDisAssemblerValueType */
|
|
40
|
+
export declare enum DisasmValueType {
|
|
41
|
+
None = 0,
|
|
42
|
+
Address = 1,
|
|
43
|
+
Value = 2
|
|
44
|
+
}
|
|
45
|
+
/** 反汇编器类型,对齐 CE 的 TDisassemblerClass */
|
|
46
|
+
export declare enum DisassemblerClass {
|
|
47
|
+
X86 = "dcX86",
|
|
48
|
+
Arm = "dcArm",
|
|
49
|
+
Arm64 = "dcArm64",
|
|
50
|
+
Thumb = "dcThumb"
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* 一条反汇编结果,字段对齐 CE 的 `TLastDisassembleData`。
|
|
54
|
+
*/
|
|
55
|
+
export interface DisassembledInstruction {
|
|
56
|
+
/** 指令地址 */
|
|
57
|
+
address: bigint;
|
|
58
|
+
/** 前缀文本(x86 的 `rep`/`lock`),ARM 上恒为空串 */
|
|
59
|
+
prefix: string;
|
|
60
|
+
/** 前缀字节数 */
|
|
61
|
+
prefixSize: number;
|
|
62
|
+
/**
|
|
63
|
+
* 操作码。
|
|
64
|
+
*
|
|
65
|
+
* 多架构注意:
|
|
66
|
+
* - x86:`mov`、`je`
|
|
67
|
+
* - ARM/Thumb:Capstone 把条件后缀并进助记符(`moveq`),本字段保持原样;
|
|
68
|
+
* 条件码另见 `conditionCode`。
|
|
69
|
+
* - ARM64:`b`、`b.eq` 会拆成 `opcode='b'` + `suffix='.eq'`
|
|
70
|
+
* (为与 x86 的 `jcc` 形态对齐,便于上层统一判断条件跳转)。
|
|
71
|
+
*/
|
|
72
|
+
opcode: string;
|
|
73
|
+
/** 助记符后缀(ARM64 的 `.eq` 等);无后缀时为空串 */
|
|
74
|
+
suffix: string;
|
|
75
|
+
/** 参数部分 */
|
|
76
|
+
parameters: string;
|
|
77
|
+
/** 描述(本移植留空) */
|
|
78
|
+
description: string;
|
|
79
|
+
/** 机器码字节,**每个字节一个 `Buffer`**(对齐 CE 的 `Bytes: array of byte`) */
|
|
80
|
+
bytes: Buffer[];
|
|
81
|
+
/** 分隔点:字节数组里 前缀/操作码/modrm/sib/额外 的分界下标 */
|
|
82
|
+
separators: number[];
|
|
83
|
+
separatorCount: number;
|
|
84
|
+
/** 内存操作数(modrm)指向的值 */
|
|
85
|
+
modrmValueType: DisasmValueType;
|
|
86
|
+
modrmValue: bigint;
|
|
87
|
+
/** 其它参数里的值(立即数/位移) */
|
|
88
|
+
parameterValueType: DisasmValueType;
|
|
89
|
+
parameterValue: bigint;
|
|
90
|
+
/** 该指令读写的数据宽度(字节);不确定时为 0 */
|
|
91
|
+
dataSize: number;
|
|
92
|
+
/** 是否读写单精度浮点(仅在有把握时为 true) */
|
|
93
|
+
isFloat: boolean;
|
|
94
|
+
/** 是否读写双精度浮点 */
|
|
95
|
+
isFloat64: boolean;
|
|
96
|
+
/** 是否含 SIB 字节(仅 x86 有意义) */
|
|
97
|
+
hasSib: boolean;
|
|
98
|
+
sibIndex: number;
|
|
99
|
+
sibScaler: number;
|
|
100
|
+
/** 是否可能改变 pc/rip */
|
|
101
|
+
isJump: boolean;
|
|
102
|
+
/** 是否 call(含 `bl`) */
|
|
103
|
+
isCall: boolean;
|
|
104
|
+
/** 是否 ret(含 ARM 的 `bx lr`、`pop {...,pc}`) */
|
|
105
|
+
isRet: boolean;
|
|
106
|
+
/** 是否带 rep/repe/repne 前缀(仅 x86) */
|
|
107
|
+
isRep: boolean;
|
|
108
|
+
/** 是否条件跳转/条件执行 */
|
|
109
|
+
isConditionalJump: boolean;
|
|
110
|
+
/** 条件码文本(如 `eq`/`ne`);无则空串 */
|
|
111
|
+
conditionCode: string;
|
|
112
|
+
/** RIP 相对(x86)或 PC 相对(ARM64)偏移下标;-1 表示不是 */
|
|
113
|
+
ripRelative: number;
|
|
114
|
+
/** 是否 64 位目标 */
|
|
115
|
+
is64Bit: boolean;
|
|
116
|
+
/** 使用的架构 */
|
|
117
|
+
arch: AsmArchitecture;
|
|
118
|
+
/** 反汇编器类型(对齐 CE 的 Disassembler 字段) */
|
|
119
|
+
disassemblerClass: DisassemblerClass;
|
|
120
|
+
/** 原始助记符(含后缀,小写) */
|
|
121
|
+
mnemonic: string;
|
|
122
|
+
/** 指令长度 */
|
|
123
|
+
size: number;
|
|
124
|
+
}
|
|
125
|
+
export interface DisassembleOptions {
|
|
126
|
+
/** 架构,默认 `x64` */
|
|
127
|
+
arch?: AsmArchitecture;
|
|
128
|
+
/** 兼容旧写法:位数。arch 优先 */
|
|
129
|
+
bitness?: 32 | 64;
|
|
130
|
+
/** 字节序,默认 little */
|
|
131
|
+
endianness?: AsmEndianness;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* 反汇编内存中的一段代码。
|
|
135
|
+
*
|
|
136
|
+
* @param code 机器码缓冲区
|
|
137
|
+
* @param address 该缓冲区在目标进程中的地址
|
|
138
|
+
* @param maxInstructions 最多解多少条,默认 1
|
|
139
|
+
* @param options 架构/字节序
|
|
140
|
+
*/
|
|
141
|
+
export declare function disassemble(code: Buffer, address: bigint, maxInstructions?: number, options?: DisassembleOptions): DisassembledInstruction[];
|
|
142
|
+
/** 反汇编一条指令 */
|
|
143
|
+
export declare function disassembleOne(code: Buffer, address: bigint, options?: DisassembleOptions): DisassembledInstruction;
|
|
144
|
+
/**
|
|
145
|
+
* 从目标进程读内存并反汇编一条。
|
|
146
|
+
*
|
|
147
|
+
* 读取长度按架构取:x86 最长指令 15 字节(读 16 留余量),
|
|
148
|
+
* ARM/ARM64 定长 4 字节(读 16 同样够用,且便于 Thumb 的 4 字节指令)。
|
|
149
|
+
*/
|
|
150
|
+
export declare function disassembleAt(readMemory: (address: bigint, size: number) => Buffer, address: bigint, options?: DisassembleOptions): DisassembledInstruction;
|
|
151
|
+
/** 按架构取"对齐步长"(用于在解不出指令时安全推进) */
|
|
152
|
+
export declare function alignmentOf(arch: AsmArchitecture): number;
|
|
153
|
+
/** 把字节数组格式化成 `48 8B 05 10 20 30 40`(大写、空格分隔) */
|
|
154
|
+
export declare function formatBytes(bytes: Buffer[] | Buffer): string;
|
|
155
|
+
/**
|
|
156
|
+
* 格式化为 CE 风格的完整行:`地址 - 字节 - 操作码 参数`。
|
|
157
|
+
*
|
|
158
|
+
* 对齐 `disassembler.pas` 里 binutils 分支的写法。注意 CE 在字节与 ` - `
|
|
159
|
+
* 之间**多一个空格**(逐字节拼接时末尾留了空格),本函数保留这一细节。
|
|
160
|
+
*/
|
|
161
|
+
export declare function formatDisassembled(insn: DisassembledInstruction, options?: {
|
|
162
|
+
addressWidth?: number;
|
|
163
|
+
showAddress?: boolean;
|
|
164
|
+
showBytes?: boolean;
|
|
165
|
+
}): string;
|
|
166
|
+
/**
|
|
167
|
+
* 取出一条指令的**完整文本**(含前缀与后缀),可用于重新汇编。
|
|
168
|
+
*
|
|
169
|
+
* 为什么需要单独的函数:
|
|
170
|
+
* - x86:CE 把 `rep`/`lock` 前缀单独存在 `prefix` 字段,
|
|
171
|
+
* 只用 `opcode + parameters` 会丢掉 `F3`/`F2`。
|
|
172
|
+
* - ARM:条件码在助记符里,本模块已把它并回 `opcode`,
|
|
173
|
+
* 但 ARM64 的 `.eq` 后缀需要补上。
|
|
174
|
+
*/
|
|
175
|
+
export declare function instructionText(insn: DisassembledInstruction): string;
|
|
176
|
+
/**
|
|
177
|
+
* 把 CE 风格的整行拆回各段(对齐 `disassembler.pas` 的 `splitDisassembledString`)。
|
|
178
|
+
*/
|
|
179
|
+
export declare function splitDisassembledString(disassembled: string): {
|
|
180
|
+
address: string;
|
|
181
|
+
bytes: string;
|
|
182
|
+
opcode: string;
|
|
183
|
+
parameters: string;
|
|
184
|
+
};
|
|
185
|
+
/**
|
|
186
|
+
* 计算跳转/调用的目标地址(非流程指令返回 null)。
|
|
187
|
+
*
|
|
188
|
+
* 覆盖形态:
|
|
189
|
+
* - x86:`0x...`
|
|
190
|
+
* - ARM:`#0x...`(Capstone 对 ARM 分支通常直接给绝对地址)
|
|
191
|
+
* - ARM64:`0x...`
|
|
192
|
+
*/
|
|
193
|
+
export declare function branchTarget(insn: DisassembledInstruction): bigint | null;
|
|
194
|
+
//# sourceMappingURL=disassembler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"disassembler.d.ts","sourceRoot":"","sources":["../../../src/asm/disassembler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DBK 内核驱动后端 —— 通过 CE 自己的 DBK 驱动 ioctl 读写受保护进程内存。
|
|
3
|
+
*
|
|
4
|
+
* ## 为什么需要它
|
|
5
|
+
*
|
|
6
|
+
* `SelfBackend` 走用户态 `OpenProcess`/`ReadProcessMemory`,因此**受保护进程
|
|
7
|
+
* (PPL、反作弊、以更高权限运行的进程)读不到**。CE 解决这个问题的办法是加载
|
|
8
|
+
* 自己的内核驱动(DBK64),再用一组 ioctl 让内核代劳。
|
|
9
|
+
* 本后端就是那组 ioctl 的 Node 实现。
|
|
10
|
+
*
|
|
11
|
+
* ## 协议来源(全部逐行核对 CE 源码,不是猜的)
|
|
12
|
+
*
|
|
13
|
+
* 1. **ioctl 码**(`Cheat Engine/dbk32/DBK32functions.pas` 第 26–93 行):
|
|
14
|
+
*
|
|
15
|
+
* ```pascal
|
|
16
|
+
* const METHOD_BUFFERED = 0;
|
|
17
|
+
* const FILE_DEVICE_UNKNOWN = $00000022;
|
|
18
|
+
* const FILE_RW_ACCESS = 3;
|
|
19
|
+
* IOCTL_CE_READMEMORY = (IOCTL_UNKNOWN_BASE shl 16) or ($0800 shl 2) or METHOD_BUFFERED or (FILE_RW_ACCESS shl 14);
|
|
20
|
+
* IOCTL_CE_WRITEMEMORY = (IOCTL_UNKNOWN_BASE shl 16) or ($0801 shl 2) or ...
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* 2. **读内存的调用形态**(`ReadProcessMemory64_Internal`,第 1523–1584 行):
|
|
24
|
+
*
|
|
25
|
+
* ```pascal
|
|
26
|
+
* type TInputstruct = packed record
|
|
27
|
+
* processid: uint64;
|
|
28
|
+
* startaddress: uint64;
|
|
29
|
+
* bytestoread: word;
|
|
30
|
+
* end;
|
|
31
|
+
* ...
|
|
32
|
+
* input.processid := processid;
|
|
33
|
+
* input.bytestoread := toread;
|
|
34
|
+
* input.startaddress := mempointer;
|
|
35
|
+
* deviceiocontrol(hdevice, cc, @input, sizeof(input), pointer(bufpointer), toread, br, nil)
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* 注意 `packed record` —— **没有对齐填充**,所以输入是 8+8+2 = **18 字节**。
|
|
39
|
+
* 输出直接写到调用方缓冲区。
|
|
40
|
+
*
|
|
41
|
+
* 3. **写内存的调用形态**(第 1620–1686 行):
|
|
42
|
+
*
|
|
43
|
+
* ```pascal
|
|
44
|
+
* input: TInputstruct absolute ao[0]; // 512 字节的固定缓冲
|
|
45
|
+
* zeromemory(@ao[0], 512);
|
|
46
|
+
* input.bytestowrite := towrite;
|
|
47
|
+
* input.startaddress := mempointer;
|
|
48
|
+
* copymemory(@ao[sizeof(tinputstruct)], bufpointer2, towrite); // 数据紧跟头部
|
|
49
|
+
* deviceiocontrol(hdevice, cc, @ao[0], 512, @ao[0], 512, br, nil); // 同一块缓冲 in/out
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* 单次最多写 `512 - sizeof(TInputstruct)` 字节。
|
|
53
|
+
*
|
|
54
|
+
* 4. **设备名**:`CreateFileW('\\.\' + servicename, ...)`(第 3436 行),
|
|
55
|
+
* 而 `servicename` 默认是 `'CEDRIVER73'`(第 3194 行),
|
|
56
|
+
* 也可被 CE 目录下的 `driver.dat` / `driver64.dat` 覆盖
|
|
57
|
+
* (第 3217–3225 行按行读入)。因此设备名**不能写死**,本模块按
|
|
58
|
+
* `driver.dat` → 默认值 → 常见候选的顺序探测。
|
|
59
|
+
*
|
|
60
|
+
* ## 必须说明的风险(诚实部分)
|
|
61
|
+
*
|
|
62
|
+
* **本后端的 ioctl 结构布局无法在没有管理员权限的机器上验证。**
|
|
63
|
+
* 原因是:只有内核驱动真正加载后 `\\.\CEDRIVER73` 才存在,而加载驱动需要管理员。
|
|
64
|
+
* 因此:
|
|
65
|
+
* - `IOCTL` 码与输入结构是**按源码逐行实现**的,不是猜测;
|
|
66
|
+
* - 但"驱动侧实际期待的结构布局"只能靠**加载后实测**确认;
|
|
67
|
+
* - 所以本模块提供 `probe()`:它用 `IOCTL_CE_GETVERSION`(一个参数最少的 ioctl)
|
|
68
|
+
* 验证设备是否真的响应,并如实报告结果,而不是假装能用。
|
|
69
|
+
*
|
|
70
|
+
* @module node-cheat-engine/backend/dbk-backend
|
|
71
|
+
*/
|
|
72
|
+
import { type BackendCapabilities, type BackendModuleInfo, type BackendProcessHandle, type BackendProcessInfo, type BackendRegion, type BackendRegionFilter, type MemoryBackend } from './types';
|
|
73
|
+
/**
|
|
74
|
+
* 本后端用到的 ioctl(功能号来自 `DBK32functions.pas` 的常量定义)。
|
|
75
|
+
*
|
|
76
|
+
* 值可以直接算出来校验:`0x22 << 16 = 0x220000`,`access << 14 = 0xC000`,
|
|
77
|
+
* 所以 READMEMORY(func 0)= `0x220000 | 0 | 0 | 0xC000` = **`0x22C000`**。
|
|
78
|
+
*/
|
|
79
|
+
export declare const DBK_IOCTL: Readonly<{
|
|
80
|
+
READ_MEMORY: number;
|
|
81
|
+
WRITE_MEMORY: number;
|
|
82
|
+
OPEN_PROCESS: number;
|
|
83
|
+
QUERY_VIRTUAL_MEMORY: number;
|
|
84
|
+
TEST: number;
|
|
85
|
+
/** `IOCTL_CE_GETVERSION`:功能号 0x16,参数最少,适合当"驱动是否活着"的探针 */
|
|
86
|
+
GET_VERSION: number;
|
|
87
|
+
/** `IOCTL_CE_MAKEWRITABLE`:功能号 0x19,把只读页改成可写 */
|
|
88
|
+
MAKE_WRITABLE: number;
|
|
89
|
+
/** `IOCTL_CE_ALLOCATEMEM`:功能号 0x1F */
|
|
90
|
+
ALLOCATE_MEM: number;
|
|
91
|
+
/** `IOCTL_CE_SUSPENDPROCESS` / `RESUMEPROCESS`:功能号 0x24 / 0x25 */
|
|
92
|
+
SUSPEND_PROCESS: number;
|
|
93
|
+
RESUME_PROCESS: number;
|
|
94
|
+
}>;
|
|
95
|
+
/** 读内存输入结构的大小:`processid(8) + startaddress(8) + bytestoread(2)`。 */
|
|
96
|
+
export declare const DBK_READ_INPUT_SIZE = 18;
|
|
97
|
+
/** 写内存输入结构的大小:`processid(8) + startaddress(8) + bytestowrite(2)`。 */
|
|
98
|
+
export declare const DBK_WRITE_INPUT_SIZE = 18;
|
|
99
|
+
/** 写内存用的固定缓冲区总大小(CE 源码里写死 512)。 */
|
|
100
|
+
export declare const DBK_WRITE_BUFFER_SIZE = 512;
|
|
101
|
+
/** 单次写入能携带的最大数据量。 */
|
|
102
|
+
export declare const DBK_MAX_WRITE_PAYLOAD: number;
|
|
103
|
+
/** 读内存单次请求上限(CE 按 4096 分块)。 */
|
|
104
|
+
export declare const DBK_MAX_READ_CHUNK = 4096;
|
|
105
|
+
/**
|
|
106
|
+
* 分配内存输入结构的大小。
|
|
107
|
+
*
|
|
108
|
+
* CE 源码(`DBK32functions.pas:2217-2223`):
|
|
109
|
+
* ```pascal
|
|
110
|
+
* x: record
|
|
111
|
+
* processid: uint64;
|
|
112
|
+
* baseaddress: uint64;
|
|
113
|
+
* size: uint64;
|
|
114
|
+
* AllocationType: uint64;
|
|
115
|
+
* Protect: uint64;
|
|
116
|
+
* end;
|
|
117
|
+
* ```
|
|
118
|
+
* 5 × uint64 = **40 字节**。
|
|
119
|
+
*
|
|
120
|
+
* ⚠️ 与读/写不同,这个记录**没有** `packed` 关键字 —— 但 5 个 uint64 本来
|
|
121
|
+
* 就是 8 字节对齐的,所以 packed 与否大小相同(40),无需纠结。
|
|
122
|
+
*/
|
|
123
|
+
export declare const DBK_ALLOC_INPUT_SIZE = 40;
|
|
124
|
+
/**
|
|
125
|
+
* 打包「读内存」的输入结构。
|
|
126
|
+
*
|
|
127
|
+
* `packed record` 语义:**没有对齐填充**,所以是紧凑的 18 字节。
|
|
128
|
+
* 这一点很关键 —— 若按自然对齐会变成 24 字节,驱动读到的 `bytestoread` 位置就错了。
|
|
129
|
+
*
|
|
130
|
+
* @param pid 目标进程 id(CE 用 uint64 存)
|
|
131
|
+
* @param address 起始地址
|
|
132
|
+
* @param bytesToRead 要读的字节数(word,最大 65535)
|
|
133
|
+
* @returns 18 字节的输入缓冲
|
|
134
|
+
*/
|
|
135
|
+
export declare function packReadInput(pid: number, address: bigint, bytesToRead: number): Buffer;
|
|
136
|
+
/**
|
|
137
|
+
* 打包「写内存」的输入缓冲(512 字节固定大小,数据紧跟头部)。
|
|
138
|
+
*
|
|
139
|
+
* 与读不同,写是**同一块 512 字节缓冲同时当 in/out**:
|
|
140
|
+
* - 前 18 字节是 `{ processid, startaddress, bytestowrite }`;
|
|
141
|
+
* - 从第 18 字节起是要写的数据;
|
|
142
|
+
* - 调用时 in/out 都传这块缓冲。
|
|
143
|
+
*
|
|
144
|
+
* @param pid 目标进程 id
|
|
145
|
+
* @param address 起始地址
|
|
146
|
+
* @param data 要写入的数据(长度不得超过 `DBK_MAX_WRITE_PAYLOAD`)
|
|
147
|
+
* @returns 512 字节的缓冲区
|
|
148
|
+
*/
|
|
149
|
+
export declare function packWriteBuffer(pid: number, address: bigint, data: Buffer): Buffer;
|
|
150
|
+
/**
|
|
151
|
+
* 打包「分配内存」的输入缓冲(40 字节,5 个 uint64)。
|
|
152
|
+
*
|
|
153
|
+
* 驱动侧(`IOPLDispatcher.c` 的 `IOCTL_CE_ALLOCATEMEM` 分支)会
|
|
154
|
+
* `KeAttachProcess` 到目标进程再调 `ZwAllocateVirtualMemory`,
|
|
155
|
+
* 并**把结果地址写回同一块 SystemBuffer 的前 8 字节**:
|
|
156
|
+
*
|
|
157
|
+
* ```c
|
|
158
|
+
* *(PUINT64)Irp->AssociatedIrp.SystemBuffer = 0;
|
|
159
|
+
* *(PUINT_PTR)Irp->AssociatedIrp.SystemBuffer = (UINT_PTR)BaseAddress;
|
|
160
|
+
* ```
|
|
161
|
+
*
|
|
162
|
+
* 所以调用方必须**用同一块缓冲同时作为 in 与 out**,再从偏移 0 读返回值。
|
|
163
|
+
*/
|
|
164
|
+
export declare function packAllocInput(pid: number, baseAddress: bigint, size: bigint, allocationType: bigint, protect: bigint): Buffer;
|
|
165
|
+
/** 从驱动写回的缓冲里读出分配到的地址(偏移 0 的 uint64)。 */
|
|
166
|
+
export declare function readAllocResult(buffer: Buffer): bigint;
|
|
167
|
+
/** 默认设备名(`DBK32functions.pas` 第 3194 行的出厂值)。 */
|
|
168
|
+
export declare const DEFAULT_DBK_SERVICE_NAME = "CEDRIVER73";
|
|
169
|
+
/**
|
|
170
|
+
* 从 CE 目录的 `driver.dat` / `driver64.dat` 读取真实服务名。
|
|
171
|
+
*
|
|
172
|
+
* 文件格式(`DBK32functions.pas` 第 3217–3225 行,按行读):
|
|
173
|
+
* ```
|
|
174
|
+
* 第 1 行:servicename
|
|
175
|
+
* 第 2 行:processeventname
|
|
176
|
+
* 第 3 行:threadeventname
|
|
177
|
+
* 第 4 行:sysfile
|
|
178
|
+
* ...
|
|
179
|
+
* ```
|
|
180
|
+
* 只有第 1 行与本后端相关。
|
|
181
|
+
*
|
|
182
|
+
* @param ceDir Cheat Engine 安装目录(可选;给了才去读)
|
|
183
|
+
* @returns 服务名,读不到返回 null
|
|
184
|
+
*/
|
|
185
|
+
export declare function readServiceNameFromDriverDat(ceDir: string | undefined): string | null;
|
|
186
|
+
/**
|
|
187
|
+
* 解析要用的设备路径。
|
|
188
|
+
*
|
|
189
|
+
* 顺序:显式配置 → `driver.dat` → 默认 `CEDRIVER73`。
|
|
190
|
+
*
|
|
191
|
+
* @param options.serviceName 显式服务名
|
|
192
|
+
* @param options.cheatEngineDir CE 安装目录(用于读 driver.dat)
|
|
193
|
+
* @returns `\\.\<服务名>`
|
|
194
|
+
*/
|
|
195
|
+
export declare function resolveDevicePath(options?: {
|
|
196
|
+
serviceName?: string;
|
|
197
|
+
cheatEngineDir?: string;
|
|
198
|
+
}): string;
|
|
199
|
+
export interface DbkBackendOptions {
|
|
200
|
+
/** 显式服务名(不加 `\\.\` 前缀也可)。默认按 driver.dat → CEDRIVER73 解析。 */
|
|
201
|
+
serviceName?: string;
|
|
202
|
+
/** Cheat Engine 安装目录(用于读 driver.dat 拿真实服务名)。 */
|
|
203
|
+
cheatEngineDir?: string;
|
|
204
|
+
/**
|
|
205
|
+
* 允许在驱动不可用时**回退**到用户态实现(`SelfBackend`)。
|
|
206
|
+
* 默认 `false`:本后端的语义就是"走驱动",静默回退会让人误以为已经绕过了保护。
|
|
207
|
+
*/
|
|
208
|
+
fallbackToSelf?: boolean;
|
|
209
|
+
/** 设备已打开的句柄(测试注入用;给了一般不该再让本类自己开) */
|
|
210
|
+
deviceHandle?: unknown;
|
|
211
|
+
}
|
|
212
|
+
/** 设备不可用(未加载驱动 / 权限不足)时抛出的错误。 */
|
|
213
|
+
export declare class DbkDeviceError extends Error {
|
|
214
|
+
readonly devicePath: string;
|
|
215
|
+
readonly win32Code?: number | undefined;
|
|
216
|
+
constructor(message: string, devicePath: string, win32Code?: number | undefined);
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* 通过 CE 的 DBK 内核驱动读写目标进程内存。
|
|
220
|
+
*
|
|
221
|
+
* ## 与其他后端的关系
|
|
222
|
+
*
|
|
223
|
+
* - 进程/模块/线程枚举、分配、改保护这些**不需要内核**的操作,直接委托给
|
|
224
|
+
* 内部的 `SelfBackend`(避免重复实现,也避免为这些操作引入未验证的 ioctl);
|
|
225
|
+
* - **只有内存读写走驱动** —— 这正是受保护进程唯一需要内核的地方,
|
|
226
|
+
* 也是风险最高、最需要精确复刻协议的部分。
|
|
227
|
+
*
|
|
228
|
+
* ## 为什么同步
|
|
229
|
+
*
|
|
230
|
+
* 驱动 ioctl 是同步的系统调用(`DeviceIoControl` 直接返回),
|
|
231
|
+
* 因此本后端声明 `capabilities.sync === true`,扫描/指针扫描/AutoAssembler
|
|
232
|
+
* 都能正常使用(它们内部要求同步后端)。
|
|
233
|
+
*/
|
|
234
|
+
export declare class DbkBackend implements MemoryBackend {
|
|
235
|
+
private readonly options;
|
|
236
|
+
readonly name = "dbk";
|
|
237
|
+
readonly capabilities: BackendCapabilities;
|
|
238
|
+
private readonly self;
|
|
239
|
+
private device;
|
|
240
|
+
private devicePath;
|
|
241
|
+
private versionCache;
|
|
242
|
+
constructor(options?: DbkBackendOptions);
|
|
243
|
+
/** 当前使用的设备路径(诊断用)。 */
|
|
244
|
+
get resolvedDevicePath(): string;
|
|
245
|
+
/** 驱动是否已打开。 */
|
|
246
|
+
get isDeviceOpen(): boolean;
|
|
247
|
+
/**
|
|
248
|
+
* 打开驱动设备。**需要驱动已加载**(加载驱动需要管理员权限,见 kernel-driver.js)。
|
|
249
|
+
*
|
|
250
|
+
* @throws DbkDeviceError 打开失败(未加载驱动 / 权限不足 / 名字不对)
|
|
251
|
+
*/
|
|
252
|
+
openDevice(): void;
|
|
253
|
+
/** 关闭设备句柄。 */
|
|
254
|
+
closeDevice(): void;
|
|
255
|
+
/**
|
|
256
|
+
* 拿驱动版本,用来确认「设备真的在工作」。
|
|
257
|
+
*
|
|
258
|
+
* 这是**最有价值的自检**:它用参数最少的 ioctl,
|
|
259
|
+
* 能区分「设备打不开」与「设备打开了但协议不对」这两种完全不同的故障。
|
|
260
|
+
*
|
|
261
|
+
* @returns 版本字符串;调用失败返回 null
|
|
262
|
+
*/
|
|
263
|
+
queryVersion(): string | null;
|
|
264
|
+
/**
|
|
265
|
+
* 一次性自检:设备能否打开、版本能否读到。
|
|
266
|
+
*
|
|
267
|
+
* 供工具输出使用 —— 让"驱动到底通没通"变成一个明确的结论,而不是靠猜。
|
|
268
|
+
*/
|
|
269
|
+
probe(): {
|
|
270
|
+
devicePath: string;
|
|
271
|
+
opened: boolean;
|
|
272
|
+
version: string | null;
|
|
273
|
+
error: string | null;
|
|
274
|
+
};
|
|
275
|
+
/** 发一个 ioctl。in/out 可以是同一块 Buffer(METHOD_BUFFERED 允许)。 */
|
|
276
|
+
private ioctl;
|
|
277
|
+
listProcesses(): BackendProcessInfo[];
|
|
278
|
+
openProcess(pid: number): BackendProcessHandle;
|
|
279
|
+
closeProcess(handle: BackendProcessHandle): void;
|
|
280
|
+
queryModules(handle: BackendProcessHandle): BackendModuleInfo[];
|
|
281
|
+
queryThreads(handle: BackendProcessHandle): number[];
|
|
282
|
+
isWow64(handle: BackendProcessHandle): boolean;
|
|
283
|
+
queryRegions(handle: BackendProcessHandle, filter?: BackendRegionFilter): BackendRegion[];
|
|
284
|
+
/**
|
|
285
|
+
* 读内存 —— 通过 `IOCTL_CE_READMEMORY`。
|
|
286
|
+
*
|
|
287
|
+
* 按 CE 的做法**分块**(每块最多 4096 字节,且不跨页边界,
|
|
288
|
+
* 因为驱动侧按页处理)。读失败时返回已读到的部分,不抛错(与接口约定一致)。
|
|
289
|
+
*/
|
|
290
|
+
readProcessMemory(handle: BackendProcessHandle, address: bigint, size: number): Buffer;
|
|
291
|
+
/**
|
|
292
|
+
* 写内存 —— 通过 `IOCTL_CE_WRITEMEMORY`。
|
|
293
|
+
*
|
|
294
|
+
* 按 CE 的做法分块:每块最多 `512 - 18 = 494` 字节,
|
|
295
|
+
* 且 512 字节缓冲**同时作为 in 与 out**。
|
|
296
|
+
*
|
|
297
|
+
* 失败**抛错**(写失败静默化会写坏目标,接口约定要求抛错)。
|
|
298
|
+
*/
|
|
299
|
+
writeProcessMemory(_handle: BackendProcessHandle, address: bigint, data: Buffer, _fixProtection?: boolean): number;
|
|
300
|
+
/**
|
|
301
|
+
* 在目标进程内分配内存 —— 通过 `IOCTL_CE_ALLOCATEMEM`。
|
|
302
|
+
*
|
|
303
|
+
* 驱动会 `KeAttachProcess` 到目标进程后用 `ZwAllocateVirtualMemory` 分配,
|
|
304
|
+
* 因此**受保护进程也能分配**(这是用户态 `VirtualAllocEx` 做不到的)。
|
|
305
|
+
*
|
|
306
|
+
* @param size 请求大小(会被驱动按页向上取整)
|
|
307
|
+
* @param protect 保护属性,默认 `PAGE_EXECUTE_READWRITE`
|
|
308
|
+
* @param preferred 期望地址(`0n` 表示由系统选择),对应 `lpAddress`
|
|
309
|
+
* @returns 实际分配到的基址
|
|
310
|
+
*/
|
|
311
|
+
allocate(handle: BackendProcessHandle, size: number, protect?: number, preferred?: bigint): bigint;
|
|
312
|
+
/**
|
|
313
|
+
* 改变内存保护属性。
|
|
314
|
+
*
|
|
315
|
+
* ## 为什么这里**不走驱动**
|
|
316
|
+
*
|
|
317
|
+
* CE 的驱动里确实有一个 `IOCTL_CE_MAKEWRITABLE`(功能号 `$0819`),但它
|
|
318
|
+
* **在 x64 驱动上是空的**(`DBKKernel/IOPLDispatcher.c:474-491`):
|
|
319
|
+
*
|
|
320
|
+
* ```c
|
|
321
|
+
* case IOCTL_CE_MAKEWRITABLE:
|
|
322
|
+
* #ifdef AMD64
|
|
323
|
+
* //untill I know how win64 handles paging, not implemented
|
|
324
|
+
* #else
|
|
325
|
+
* ... MakeWritable(...)
|
|
326
|
+
* #endif
|
|
327
|
+
* ```
|
|
328
|
+
*
|
|
329
|
+
* 所以 64 位 Windows 上发这个 ioctl 只会得到「无效请求」。
|
|
330
|
+
* 而且它的输入结构里**没有 processid** —— 它是按调用者(驱动自己所在的
|
|
331
|
+
* 进程上下文)的页表改 PTE 的,本来也不适合"改另一个进程的保护"。
|
|
332
|
+
*
|
|
333
|
+
* 因此本方法**委托 `SelfBackend`(用户态 `VirtualProtectEx`)**,并明确告知这一点。
|
|
334
|
+
* 好消息是:**内存写入不需要先改保护** —— 驱动的写路径本身就绕过了页保护,
|
|
335
|
+
* 只读页照样能写。需要 `changeProtection` 的场景主要是为了「把页改成可执行」
|
|
336
|
+
* 或「让目标自己也能写」,那属于目标进程自身的权限,用户态调用是否成功取决于
|
|
337
|
+
* 当前进程对目标的权限(受保护进程仍会失败)。
|
|
338
|
+
*
|
|
339
|
+
* @returns 旧保护值(用户态 `VirtualProtectEx` 的返回值)
|
|
340
|
+
*/
|
|
341
|
+
changeProtection(handle: BackendProcessHandle, address: bigint, size: number, newProtect: number): number;
|
|
342
|
+
/**
|
|
343
|
+
* 释放内存。
|
|
344
|
+
*
|
|
345
|
+
* 驱动**没有**提供释放 ioctl(`KernelFree` 释放的是驱动自己内核态分配的内存,
|
|
346
|
+
* 不是目标进程的)。因此这里委托用户态 `VirtualFreeEx`;
|
|
347
|
+
* 对受保护进程会失败 —— 那种情况下这块内存会一直留在目标进程里,
|
|
348
|
+
* 这是需要如实告诉调用方的限制。
|
|
349
|
+
*/
|
|
350
|
+
free(handle: BackendProcessHandle, address: bigint): void;
|
|
351
|
+
dispose(): void;
|
|
352
|
+
}
|
|
353
|
+
//# sourceMappingURL=dbk-backend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dbk-backend.d.ts","sourceRoot":"","sources":["../../../src/backend/dbk-backend.ts"],"names":[],"mappings":"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"}
|