@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,736 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Cheat Engine Lua API 的 Node 版实现(子集)。
|
|
4
|
+
*
|
|
5
|
+
* 约定(与 CE 官方 Lua API 的对齐与差异都写在 docs/lua-api.md):
|
|
6
|
+
* - 参数顺序尽量与 CE 一致,方便直接搬运脚本;
|
|
7
|
+
* - 读内存失败返回 nil(CE 风格);参数错误 / 地址无法解析直接抛 Lua 错误
|
|
8
|
+
* (CE 的 getAddress 也是抛错,脚本可以用 pcall 捕获);
|
|
9
|
+
* - 地址参数可以是整数,也可以是字符串表达式('base+40'、'kernel32.dll+1000'、'mySymbol')。
|
|
10
|
+
*
|
|
11
|
+
* 类型映射:
|
|
12
|
+
* - readInteger 返回 32 位整数,**默认按有符号解释**(signed=true);
|
|
13
|
+
* CE 官方默认 signed=false(返回 dword)。这是本移植与 CE 的**有意差异**,
|
|
14
|
+
* 需要无符号语义时显式传 readInteger(addr, false)。
|
|
15
|
+
* - readByte / readSmallInteger 保持 CE 默认(无符号)。
|
|
16
|
+
* - readQword 返回 64 位整数(Lua 5.4 的 integer 是 int64,超出 2^63-1 按位回绕)。
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.ErrorSink = void 0;
|
|
20
|
+
exports.createApi = createApi;
|
|
21
|
+
const node_os_1 = require("node:os");
|
|
22
|
+
const process_1 = require("../win/process");
|
|
23
|
+
const addressing_1 = require("./addressing");
|
|
24
|
+
const types_1 = require("./types");
|
|
25
|
+
const marshal_1 = require("./marshal");
|
|
26
|
+
const objects_1 = require("./objects");
|
|
27
|
+
const scan_1 = require("./scan");
|
|
28
|
+
const aa_1 = require("./aa");
|
|
29
|
+
const memory_1 = require("./memory");
|
|
30
|
+
const asm_1 = require("./asm");
|
|
31
|
+
/** 引擎当前的最后一条错误信息(对齐 CE 的 getLastError 思路)。 */
|
|
32
|
+
class ErrorSink {
|
|
33
|
+
message = '';
|
|
34
|
+
set(text) {
|
|
35
|
+
this.message = text;
|
|
36
|
+
}
|
|
37
|
+
clear() {
|
|
38
|
+
this.message = '';
|
|
39
|
+
}
|
|
40
|
+
get() {
|
|
41
|
+
return this.message;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
exports.ErrorSink = ErrorSink;
|
|
45
|
+
/**
|
|
46
|
+
* 创建全部 API 函数。
|
|
47
|
+
*
|
|
48
|
+
* @param ctx 引擎能力(进程、符号表、扫描会话、输出)
|
|
49
|
+
*/
|
|
50
|
+
function createApi(ctx) {
|
|
51
|
+
const errorSink = new ErrorSink();
|
|
52
|
+
const resolver = new addressing_1.AddressResolver({
|
|
53
|
+
symbols: ctx.symbols,
|
|
54
|
+
findModule: (name) => ctx.findModule(name),
|
|
55
|
+
});
|
|
56
|
+
const recordHost = {
|
|
57
|
+
resolve: (expr) => resolver.resolve(expr),
|
|
58
|
+
read: (addr, size) => ctx.requireProcess().readMemory(addr, size),
|
|
59
|
+
write: (addr, data) => ctx.requireProcess().writeMemory(addr, data),
|
|
60
|
+
};
|
|
61
|
+
const addressList = new objects_1.LuaAddressList(recordHost);
|
|
62
|
+
/** 解析地址参数:整数或字符串表达式;失败抛 Lua 错误。 */
|
|
63
|
+
const addrOf = (m, args, n, funcName) => {
|
|
64
|
+
const v = m.requireArg(args, n, funcName);
|
|
65
|
+
try {
|
|
66
|
+
return resolver.resolve(v);
|
|
67
|
+
}
|
|
68
|
+
catch (e) {
|
|
69
|
+
m.raise(`${funcName}: ${(0, marshal_1.describeError)(e)}`);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
/** 读一段内存;长度不足视为失败返回 null(CE 风格)。 */
|
|
73
|
+
const readOrNull = (addr, size) => {
|
|
74
|
+
const proc = ctx.currentProcess();
|
|
75
|
+
if (!proc || size < 0)
|
|
76
|
+
return null;
|
|
77
|
+
const buf = proc.readMemory(addr, size);
|
|
78
|
+
return buf.length === size ? buf : null;
|
|
79
|
+
};
|
|
80
|
+
/** 写字节;失败返回 null。 */
|
|
81
|
+
const writeBytesAt = (addr, data) => {
|
|
82
|
+
try {
|
|
83
|
+
return ctx.requireProcess().writeMemory(addr, data);
|
|
84
|
+
}
|
|
85
|
+
catch (e) {
|
|
86
|
+
errorSink.set((0, marshal_1.describeError)(e));
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
/** 读取整数(按宽度与符号)。 */
|
|
91
|
+
const readInt = (addr, size, signed) => {
|
|
92
|
+
const buf = readOrNull(addr, size);
|
|
93
|
+
if (!buf)
|
|
94
|
+
return null;
|
|
95
|
+
switch (size) {
|
|
96
|
+
case 1:
|
|
97
|
+
return signed ? BigInt(buf.readInt8(0)) : BigInt(buf.readUInt8(0));
|
|
98
|
+
case 2:
|
|
99
|
+
return signed ? BigInt(buf.readInt16LE(0)) : BigInt(buf.readUInt16LE(0));
|
|
100
|
+
case 4:
|
|
101
|
+
return signed ? BigInt(buf.readInt32LE(0)) : BigInt(buf.readUInt32LE(0));
|
|
102
|
+
default:
|
|
103
|
+
return signed ? buf.readBigInt64LE(0) : buf.readBigUInt64LE(0);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
/** 写整数(按宽度回绕,与 CE 的隐式截断一致)。 */
|
|
107
|
+
const writeInt = (addr, value, size) => {
|
|
108
|
+
const buf = Buffer.alloc(size);
|
|
109
|
+
const v = BigInt.asUintN(size * 8, value);
|
|
110
|
+
if (size === 1)
|
|
111
|
+
buf.writeUInt8(Number(v), 0);
|
|
112
|
+
else if (size === 2)
|
|
113
|
+
buf.writeUInt16LE(Number(v), 0);
|
|
114
|
+
else if (size === 4)
|
|
115
|
+
buf.writeUInt32LE(Number(v), 0);
|
|
116
|
+
else
|
|
117
|
+
buf.writeBigUInt64LE(v, 0);
|
|
118
|
+
return writeBytesAt(addr, buf) !== null;
|
|
119
|
+
};
|
|
120
|
+
/** 组装 AOB 扫描参数。 */
|
|
121
|
+
const aobOptions = (m, patternIndex, protectionIndex, alignmentIndex, alignParamIndex, funcName, args, defaults = {}) => {
|
|
122
|
+
const findModule = (name) => {
|
|
123
|
+
const mod = ctx.findModule(name);
|
|
124
|
+
if (!mod)
|
|
125
|
+
m.raise(`${funcName}: 找不到模块 "${name}"`);
|
|
126
|
+
return { baseAddress: mod.baseAddress, size: mod.size };
|
|
127
|
+
};
|
|
128
|
+
return {
|
|
129
|
+
pattern: m.argToString(args, patternIndex, funcName),
|
|
130
|
+
protection: args[protectionIndex - 1] === undefined
|
|
131
|
+
? defaults.protection ?? ''
|
|
132
|
+
: m.argToString(args, protectionIndex, funcName),
|
|
133
|
+
alignmentType: args[alignmentIndex - 1] === undefined ? 0 : m.argToInt(args, alignmentIndex, funcName, 0),
|
|
134
|
+
alignmentParam: args[alignParamIndex - 1] === undefined ? '1' : m.argToString(args, alignParamIndex, funcName, '1'),
|
|
135
|
+
moduleName: defaults.moduleName,
|
|
136
|
+
unique: defaults.unique,
|
|
137
|
+
findModule,
|
|
138
|
+
};
|
|
139
|
+
};
|
|
140
|
+
const functions = {
|
|
141
|
+
/* ---------------- 进程 ---------------- */
|
|
142
|
+
openProcess(args, m) {
|
|
143
|
+
const target = m.requireArg(args, 1, 'openProcess');
|
|
144
|
+
errorSink.clear();
|
|
145
|
+
let pid;
|
|
146
|
+
if (typeof target === 'string') {
|
|
147
|
+
const found = findProcessId(target);
|
|
148
|
+
if (found === null) {
|
|
149
|
+
errorSink.set(`找不到进程:${target}`);
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
pid = found;
|
|
153
|
+
}
|
|
154
|
+
else if (typeof target === 'number' || typeof target === 'bigint') {
|
|
155
|
+
pid = Number(target);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
m.raise(`openProcess: 参数必须是进程名或进程号,收到 ${target === null ? 'nil' : typeof target}`);
|
|
159
|
+
// m.raise 会抛错,这里给 TS 一个明确赋值以满足严格模式
|
|
160
|
+
pid = 0;
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
const proc = process_1.Process.open(pid);
|
|
164
|
+
ctx.attachProcess(proc);
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
catch (e) {
|
|
168
|
+
errorSink.set((0, marshal_1.describeError)(e));
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
closeProcess() {
|
|
173
|
+
const proc = ctx.currentProcess();
|
|
174
|
+
if (proc) {
|
|
175
|
+
ctx.attachProcess(null);
|
|
176
|
+
proc.close();
|
|
177
|
+
}
|
|
178
|
+
return true;
|
|
179
|
+
},
|
|
180
|
+
getOpenedProcessID() {
|
|
181
|
+
return BigInt(ctx.processId());
|
|
182
|
+
},
|
|
183
|
+
getOpenedProcessHandle() {
|
|
184
|
+
const proc = ctx.currentProcess();
|
|
185
|
+
if (!proc)
|
|
186
|
+
return 0n;
|
|
187
|
+
// 暴露"后端句柄 id"作为整数;原生 CE 后端下对应 ceserver 侧的句柄值
|
|
188
|
+
return BigInt(proc.backendHandle.id);
|
|
189
|
+
},
|
|
190
|
+
getProcessName() {
|
|
191
|
+
const proc = ctx.currentProcess();
|
|
192
|
+
return proc ? proc.name : null;
|
|
193
|
+
},
|
|
194
|
+
getProcessIDFromProcessName(args, m) {
|
|
195
|
+
const pid = findProcessId(m.argToString(args, 1, 'getProcessIDFromProcessName'));
|
|
196
|
+
return pid === null ? null : BigInt(pid);
|
|
197
|
+
},
|
|
198
|
+
getProcessList() {
|
|
199
|
+
const out = new Map();
|
|
200
|
+
for (const p of (0, process_1.listProcesses)())
|
|
201
|
+
out.set(BigInt(p.pid), p.name);
|
|
202
|
+
return out;
|
|
203
|
+
},
|
|
204
|
+
targetIs64Bit() {
|
|
205
|
+
const proc = ctx.currentProcess();
|
|
206
|
+
return proc ? !proc.isWow64() : false;
|
|
207
|
+
},
|
|
208
|
+
cheatEngineIs64Bit() {
|
|
209
|
+
return process.arch === 'x64' || process.arch === 'arm64';
|
|
210
|
+
},
|
|
211
|
+
/* ---------------- 读内存 ---------------- */
|
|
212
|
+
readBytes(args, m) {
|
|
213
|
+
const addr = addrOf(m, args, 1, 'readBytes');
|
|
214
|
+
const count = m.argToInt(args, 2, 'readBytes', 1);
|
|
215
|
+
const asTable = args[2] === undefined ? true : m.argToBoolean(args, 3, true);
|
|
216
|
+
if (count < 0)
|
|
217
|
+
m.raise(`readBytes: 字节数不能为负:${count}`);
|
|
218
|
+
const buf = readOrNull(addr, count);
|
|
219
|
+
if (!buf)
|
|
220
|
+
return null;
|
|
221
|
+
const bytes = [...buf].map((b) => BigInt(b));
|
|
222
|
+
return asTable ? bytes : new types_1.MultiReturn(...bytes);
|
|
223
|
+
},
|
|
224
|
+
readByte(args, m) {
|
|
225
|
+
const addr = addrOf(m, args, 1, 'readByte');
|
|
226
|
+
const signed = args[1] === undefined ? false : m.argToBoolean(args, 2, false);
|
|
227
|
+
return readInt(addr, 1, signed);
|
|
228
|
+
},
|
|
229
|
+
readShortInteger(args, m) {
|
|
230
|
+
const addr = addrOf(m, args, 1, 'readShortInteger');
|
|
231
|
+
const signed = args[1] === undefined ? false : m.argToBoolean(args, 2, false);
|
|
232
|
+
return readInt(addr, 1, signed);
|
|
233
|
+
},
|
|
234
|
+
readSmallInteger(args, m) {
|
|
235
|
+
const addr = addrOf(m, args, 1, 'readSmallInteger');
|
|
236
|
+
const signed = args[1] === undefined ? false : m.argToBoolean(args, 2, false);
|
|
237
|
+
return readInt(addr, 2, signed);
|
|
238
|
+
},
|
|
239
|
+
/** 注意:默认 signed=true,与 CE 官方(false)相反,见文件头说明。 */
|
|
240
|
+
readInteger(args, m) {
|
|
241
|
+
const addr = addrOf(m, args, 1, 'readInteger');
|
|
242
|
+
const signed = args[1] === undefined ? true : m.argToBoolean(args, 2, true);
|
|
243
|
+
return readInt(addr, 4, signed);
|
|
244
|
+
},
|
|
245
|
+
readQword(args, m) {
|
|
246
|
+
const addr = addrOf(m, args, 1, 'readQword');
|
|
247
|
+
const v = readInt(addr, 8, false);
|
|
248
|
+
return v === null ? null : BigInt.asIntN(64, v);
|
|
249
|
+
},
|
|
250
|
+
readPointer(args, m) {
|
|
251
|
+
const addr = addrOf(m, args, 1, 'readPointer');
|
|
252
|
+
const proc = ctx.currentProcess();
|
|
253
|
+
if (!proc)
|
|
254
|
+
return null;
|
|
255
|
+
return proc.isWow64() ? readInt(addr, 4, false) : readInt(addr, 8, false);
|
|
256
|
+
},
|
|
257
|
+
readFloat(args, m) {
|
|
258
|
+
const addr = addrOf(m, args, 1, 'readFloat');
|
|
259
|
+
const buf = readOrNull(addr, 4);
|
|
260
|
+
return buf ? buf.readFloatLE(0) : null;
|
|
261
|
+
},
|
|
262
|
+
readDouble(args, m) {
|
|
263
|
+
const addr = addrOf(m, args, 1, 'readDouble');
|
|
264
|
+
const buf = readOrNull(addr, 8);
|
|
265
|
+
return buf ? buf.readDoubleLE(0) : null;
|
|
266
|
+
},
|
|
267
|
+
/** readString(address [, maxlength=50 [, widechar=false]]) */
|
|
268
|
+
readString(args, m) {
|
|
269
|
+
const addr = addrOf(m, args, 1, 'readString');
|
|
270
|
+
const maxLength = m.argToInt(args, 2, 'readString', 50);
|
|
271
|
+
const wide = args[2] === undefined ? false : m.argToBoolean(args, 3, false);
|
|
272
|
+
if (maxLength < 0)
|
|
273
|
+
m.raise(`readString: 长度不能为负:${maxLength}`);
|
|
274
|
+
// CE:widechar 时 maxsize := maxsize + 1(按字符计)
|
|
275
|
+
const chars = wide ? maxLength + 1 : maxLength;
|
|
276
|
+
const buf = readOrNull(addr, wide ? chars * 2 : chars);
|
|
277
|
+
if (!buf)
|
|
278
|
+
return null;
|
|
279
|
+
if (wide) {
|
|
280
|
+
let end = buf.length;
|
|
281
|
+
for (let i = 0; i + 1 < buf.length; i += 2) {
|
|
282
|
+
if (buf.readUInt16LE(i) === 0) {
|
|
283
|
+
end = i;
|
|
284
|
+
break;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return buf.toString('utf16le', 0, end);
|
|
288
|
+
}
|
|
289
|
+
const nul = buf.indexOf(0);
|
|
290
|
+
return buf.toString('latin1', 0, nul === -1 ? buf.length : nul);
|
|
291
|
+
},
|
|
292
|
+
/* ---------------- 写内存 ---------------- */
|
|
293
|
+
writeBytes(args, m) {
|
|
294
|
+
const addr = addrOf(m, args, 1, 'writeBytes');
|
|
295
|
+
const bytes = collectBytes(m, args, 2, 'writeBytes');
|
|
296
|
+
if (bytes.length === 0)
|
|
297
|
+
return 0n;
|
|
298
|
+
const written = writeBytesAt(addr, Buffer.from(bytes));
|
|
299
|
+
return BigInt(written ?? 0);
|
|
300
|
+
},
|
|
301
|
+
writeByte(args, m) {
|
|
302
|
+
return writeInt(addrOf(m, args, 1, 'writeByte'), m.argToBigInt(args, 2, 'writeByte'), 1);
|
|
303
|
+
},
|
|
304
|
+
writeShortInteger(args, m) {
|
|
305
|
+
return writeInt(addrOf(m, args, 1, 'writeShortInteger'), m.argToBigInt(args, 2, 'writeShortInteger'), 1);
|
|
306
|
+
},
|
|
307
|
+
writeSmallInteger(args, m) {
|
|
308
|
+
return writeInt(addrOf(m, args, 1, 'writeSmallInteger'), m.argToBigInt(args, 2, 'writeSmallInteger'), 2);
|
|
309
|
+
},
|
|
310
|
+
writeInteger(args, m) {
|
|
311
|
+
return writeInt(addrOf(m, args, 1, 'writeInteger'), m.argToBigInt(args, 2, 'writeInteger'), 4);
|
|
312
|
+
},
|
|
313
|
+
writeQword(args, m) {
|
|
314
|
+
return writeInt(addrOf(m, args, 1, 'writeQword'), m.argToBigInt(args, 2, 'writeQword'), 8);
|
|
315
|
+
},
|
|
316
|
+
writePointer(args, m) {
|
|
317
|
+
const addr = addrOf(m, args, 1, 'writePointer');
|
|
318
|
+
const proc = ctx.currentProcess();
|
|
319
|
+
const size = proc && proc.isWow64() ? 4 : 8;
|
|
320
|
+
return writeInt(addr, m.argToBigInt(args, 2, 'writePointer'), size);
|
|
321
|
+
},
|
|
322
|
+
writeFloat(args, m) {
|
|
323
|
+
const addr = addrOf(m, args, 1, 'writeFloat');
|
|
324
|
+
const buf = Buffer.alloc(4);
|
|
325
|
+
buf.writeFloatLE(m.argToNumber(args, 2, 'writeFloat'), 0);
|
|
326
|
+
return writeBytesAt(addr, buf) !== null;
|
|
327
|
+
},
|
|
328
|
+
writeDouble(args, m) {
|
|
329
|
+
const addr = addrOf(m, args, 1, 'writeDouble');
|
|
330
|
+
const buf = Buffer.alloc(8);
|
|
331
|
+
buf.writeDoubleLE(m.argToNumber(args, 2, 'writeDouble'), 0);
|
|
332
|
+
return writeBytesAt(addr, buf) !== null;
|
|
333
|
+
},
|
|
334
|
+
/** writeString(address, text [, widechar=false]) —— 不写结尾的 \0(与 CE 一致) */
|
|
335
|
+
writeString(args, m) {
|
|
336
|
+
const addr = addrOf(m, args, 1, 'writeString');
|
|
337
|
+
const text = m.argToString(args, 2, 'writeString');
|
|
338
|
+
const wide = args[2] === undefined ? false : m.argToBoolean(args, 3, false);
|
|
339
|
+
const buf = wide ? Buffer.from(text, 'utf16le') : Buffer.from(text, 'latin1');
|
|
340
|
+
return writeBytesAt(addr, buf) !== null;
|
|
341
|
+
},
|
|
342
|
+
/* ---------------- 地址 ---------------- */
|
|
343
|
+
getAddress(args, m) {
|
|
344
|
+
return addrOf(m, args, 1, 'getAddress');
|
|
345
|
+
},
|
|
346
|
+
getAddressSafe(args) {
|
|
347
|
+
return args[0] === undefined ? null : resolver.tryResolve(args[0]);
|
|
348
|
+
},
|
|
349
|
+
getModuleBase(args) {
|
|
350
|
+
const name = typeof args[0] === 'string' ? args[0] : String(args[0] ?? '');
|
|
351
|
+
const mod = ctx.findModule(name);
|
|
352
|
+
return mod ? mod.baseAddress : null;
|
|
353
|
+
},
|
|
354
|
+
getModuleSize(args) {
|
|
355
|
+
const name = typeof args[0] === 'string' ? args[0] : String(args[0] ?? '');
|
|
356
|
+
const mod = ctx.findModule(name);
|
|
357
|
+
return mod ? BigInt(mod.size) : null;
|
|
358
|
+
},
|
|
359
|
+
enumModules() {
|
|
360
|
+
return ctx.listModules().map((mod) => ({
|
|
361
|
+
Name: mod.name,
|
|
362
|
+
Address: mod.baseAddress,
|
|
363
|
+
Size: BigInt(mod.size),
|
|
364
|
+
Path: mod.path,
|
|
365
|
+
}));
|
|
366
|
+
},
|
|
367
|
+
/** 地址落在哪个模块里(`module.dll+1A2B`),不在任何模块里返回 nil。 */
|
|
368
|
+
getNameFromAddress(args, m) {
|
|
369
|
+
const addr = addrOf(m, args, 1, 'getNameFromAddress');
|
|
370
|
+
return moduleRelativeName(ctx, addr);
|
|
371
|
+
},
|
|
372
|
+
inModule(args, m) {
|
|
373
|
+
return moduleRelativeName(ctx, addrOf(m, args, 1, 'inModule')) !== null;
|
|
374
|
+
},
|
|
375
|
+
/* ---------------- 内存属性(保护 / 区域 / 分配) ---------------- */
|
|
376
|
+
/**
|
|
377
|
+
* `getMemoryProtection(address)` → 可读的保护属性名(`'RW'`/`'RWX'`/`'n'`…)。
|
|
378
|
+
* 找不到区域返回 nil。数值版本见 `getMemoryProtectionInfo`。
|
|
379
|
+
*/
|
|
380
|
+
getMemoryProtection(args, m) {
|
|
381
|
+
const addr = addrOf(m, args, 1, 'getMemoryProtection');
|
|
382
|
+
const region = (0, memory_1.regionAt)(ctx.requireProcess(), addr);
|
|
383
|
+
return region ? (0, memory_1.describeProtect)(region.protect).name : null;
|
|
384
|
+
},
|
|
385
|
+
/**
|
|
386
|
+
* 本移植扩展:`getMemoryProtectionInfo(address)` → 带数值与布尔位的完整描述。
|
|
387
|
+
* 脚本要精确比较 Win32 数值时用它,比字符串名字可靠。
|
|
388
|
+
*/
|
|
389
|
+
getMemoryProtectionInfo(args, m) {
|
|
390
|
+
const addr = addrOf(m, args, 1, 'getMemoryProtectionInfo');
|
|
391
|
+
const region = (0, memory_1.regionAt)(ctx.requireProcess(), addr);
|
|
392
|
+
if (!region)
|
|
393
|
+
return null;
|
|
394
|
+
return { ...(0, memory_1.describeProtect)(region.protect), value: region.protect };
|
|
395
|
+
},
|
|
396
|
+
/**
|
|
397
|
+
* `setMemoryProtection(address, protection)` → 旧的保护属性名。
|
|
398
|
+
* 第二个参数接受 CE 的名字(`'RWX'`)或 Win32 数值(`0x40`)。
|
|
399
|
+
* 注意:只对**一整页**生效(Windows 页粒度),与 CE 行为一致。
|
|
400
|
+
*/
|
|
401
|
+
setMemoryProtection(args, m) {
|
|
402
|
+
const addr = addrOf(m, args, 1, 'setMemoryProtection');
|
|
403
|
+
const raw = m.requireArg(args, 2, 'setMemoryProtection');
|
|
404
|
+
const parsed = (0, memory_1.parseProtectName)(typeof raw === 'bigint' || typeof raw === 'number' ? Number(raw) : String(raw));
|
|
405
|
+
if (parsed === null) {
|
|
406
|
+
m.raise(`setMemoryProtection: 无法识别的保护属性:${(0, marshal_1.describeValue)(raw)}`);
|
|
407
|
+
}
|
|
408
|
+
const region = (0, memory_1.regionAt)(ctx.requireProcess(), addr);
|
|
409
|
+
const size = region ? Number(region.memorySize) : 0x1000;
|
|
410
|
+
try {
|
|
411
|
+
// parsed 在上方的 null 分支已 raise;此处用 ?? 兜底仅为满足 TS 收窄
|
|
412
|
+
const old = ctx.requireProcess().protect(addr, size, parsed ?? 0x04);
|
|
413
|
+
return (0, memory_1.describeProtect)(old).name;
|
|
414
|
+
}
|
|
415
|
+
catch (e) {
|
|
416
|
+
errorSink.set((0, marshal_1.describeError)(e));
|
|
417
|
+
m.raise(`setMemoryProtection: ${(0, marshal_1.describeError)(e)}`);
|
|
418
|
+
}
|
|
419
|
+
},
|
|
420
|
+
/**
|
|
421
|
+
* `fullAccess(address)` → 把该地址所在页改成 `RWX`,返回旧保护属性名。
|
|
422
|
+
* 常用于给自修改代码 / hook 打补丁。
|
|
423
|
+
*/
|
|
424
|
+
fullAccess(args, m) {
|
|
425
|
+
const addr = addrOf(m, args, 1, 'fullAccess');
|
|
426
|
+
const region = (0, memory_1.regionAt)(ctx.requireProcess(), addr);
|
|
427
|
+
const size = region ? Number(region.memorySize) : 0x1000;
|
|
428
|
+
try {
|
|
429
|
+
const old = ctx.requireProcess().protect(addr, size, 0x40 /* PAGE_EXECUTE_READWRITE */);
|
|
430
|
+
return (0, memory_1.describeProtect)(old).name;
|
|
431
|
+
}
|
|
432
|
+
catch (e) {
|
|
433
|
+
errorSink.set((0, marshal_1.describeError)(e));
|
|
434
|
+
m.raise(`fullAccess: ${(0, marshal_1.describeError)(e)}`);
|
|
435
|
+
}
|
|
436
|
+
},
|
|
437
|
+
/** `getRegionSize(address)` → 该地址所在区域的字节数。 */
|
|
438
|
+
getRegionSize(args, m) {
|
|
439
|
+
const addr = addrOf(m, args, 1, 'getRegionSize');
|
|
440
|
+
const size = (0, memory_1.regionSizeAt)(ctx.requireProcess(), addr);
|
|
441
|
+
return size === null ? null : BigInt(size);
|
|
442
|
+
},
|
|
443
|
+
/**
|
|
444
|
+
* 本移植扩展:`enumRegions([filterTable])`。
|
|
445
|
+
* filterTable 支持 `{ protection='RWX', type='MEM_PRIVATE' }`,均可省略。
|
|
446
|
+
*/
|
|
447
|
+
enumRegions(args) {
|
|
448
|
+
const table = args[0];
|
|
449
|
+
const filter = {};
|
|
450
|
+
if (table && typeof table === 'object' && !Array.isArray(table)) {
|
|
451
|
+
const t = table;
|
|
452
|
+
if (typeof t.protection === 'string')
|
|
453
|
+
filter.protection = t.protection;
|
|
454
|
+
if (typeof t.type === 'string')
|
|
455
|
+
filter.type = t.type;
|
|
456
|
+
}
|
|
457
|
+
return (0, memory_1.enumRegions)(ctx.requireProcess(), filter);
|
|
458
|
+
},
|
|
459
|
+
/**
|
|
460
|
+
* `allocateMemory(size [, protection])` → 在目标进程里分配一块内存的地址。
|
|
461
|
+
* `protection` 默认 `'RWX'`(与 CE 一致:hook 场景最常用)。
|
|
462
|
+
*
|
|
463
|
+
* ⚠️ 分配结果**不会自动释放**:脚本结束/引擎销毁都不会 free,
|
|
464
|
+
* 必须自己调用 `deAlloc(addr)`,否则目标进程会一直泄漏这段内存。
|
|
465
|
+
*/
|
|
466
|
+
allocateMemory(args, m) {
|
|
467
|
+
const size = m.argToInt(args, 1, 'allocateMemory');
|
|
468
|
+
if (size <= 0) {
|
|
469
|
+
m.raise(`allocateMemory: 大小必须为正数,收到 ${size}`);
|
|
470
|
+
}
|
|
471
|
+
const raw = args[1];
|
|
472
|
+
const parsed = (0, memory_1.parseProtectName)(raw === undefined || raw === null ? 'RWX' : typeof raw === 'bigint' || typeof raw === 'number' ? Number(raw) : String(raw));
|
|
473
|
+
if (parsed === null) {
|
|
474
|
+
m.raise(`allocateMemory: 无法识别的保护属性:${(0, marshal_1.describeValue)(raw)}`);
|
|
475
|
+
}
|
|
476
|
+
try {
|
|
477
|
+
// 同上:null 分支已 raise,兜底仅为满足 TS 收窄
|
|
478
|
+
return ctx.requireProcess().allocate(size, parsed ?? 0x40);
|
|
479
|
+
}
|
|
480
|
+
catch (e) {
|
|
481
|
+
errorSink.set((0, marshal_1.describeError)(e));
|
|
482
|
+
m.raise(`allocateMemory: ${(0, marshal_1.describeError)(e)}`);
|
|
483
|
+
}
|
|
484
|
+
},
|
|
485
|
+
/** `deAlloc(address)` → 释放 allocateMemory 分配的内存,成功 true。 */
|
|
486
|
+
deAlloc(args, m) {
|
|
487
|
+
const addr = addrOf(m, args, 1, 'deAlloc');
|
|
488
|
+
try {
|
|
489
|
+
ctx.requireProcess().free(addr);
|
|
490
|
+
return true;
|
|
491
|
+
}
|
|
492
|
+
catch (e) {
|
|
493
|
+
errorSink.set((0, marshal_1.describeError)(e));
|
|
494
|
+
return false;
|
|
495
|
+
}
|
|
496
|
+
},
|
|
497
|
+
/* ---------------- 符号 ---------------- */
|
|
498
|
+
registerSymbol(args, m) {
|
|
499
|
+
const name = m.argToString(args, 1, 'registerSymbol');
|
|
500
|
+
const target = m.requireArg(args, 2, 'registerSymbol');
|
|
501
|
+
try {
|
|
502
|
+
ctx.symbols.register(name, resolver.resolve(target));
|
|
503
|
+
return true;
|
|
504
|
+
}
|
|
505
|
+
catch (e) {
|
|
506
|
+
errorSink.set((0, marshal_1.describeError)(e));
|
|
507
|
+
return new types_1.MultiReturn(false, (0, marshal_1.describeError)(e));
|
|
508
|
+
}
|
|
509
|
+
},
|
|
510
|
+
unregisterSymbol(args, m) {
|
|
511
|
+
ctx.symbols.unregister(m.argToString(args, 1, 'unregisterSymbol'));
|
|
512
|
+
},
|
|
513
|
+
getSymbols() {
|
|
514
|
+
return ctx.symbols.entries().map(([name, address]) => ({ Name: name, Address: address }));
|
|
515
|
+
},
|
|
516
|
+
enumRegisteredSymbols() {
|
|
517
|
+
return ctx.symbols.entries().map(([name, address]) => ({ name, address }));
|
|
518
|
+
},
|
|
519
|
+
deleteAllRegisteredSymbols() {
|
|
520
|
+
ctx.symbols.clear();
|
|
521
|
+
},
|
|
522
|
+
/* ---------------- 扫描 ---------------- */
|
|
523
|
+
AOBScan(args, m) {
|
|
524
|
+
const opts = aobOptions(m, 1, 2, 3, 4, 'AOBScan', args);
|
|
525
|
+
const hits = (0, scan_1.scanAob)(ctx.requireProcess(), ctx.scanner, m, opts);
|
|
526
|
+
return new objects_1.LuaStringList(hits.map(scan_1.addressToCeHex));
|
|
527
|
+
},
|
|
528
|
+
AOBScanUnique(args, m) {
|
|
529
|
+
const opts = aobOptions(m, 1, 2, 3, 4, 'AOBScanUnique', args, { unique: true });
|
|
530
|
+
return (0, scan_1.scanAobFirst)(ctx.requireProcess(), ctx.scanner, m, opts);
|
|
531
|
+
},
|
|
532
|
+
/** 扩展:CE 只有 AOBScanModuleUnique,这里补一个返回 StringList 的版本。 */
|
|
533
|
+
AOBScanModule(args, m) {
|
|
534
|
+
const opts = aobOptions(m, 2, 3, 4, 5, 'AOBScanModule', args, {
|
|
535
|
+
moduleName: m.argToString(args, 1, 'AOBScanModule'),
|
|
536
|
+
protection: '*X*W*C',
|
|
537
|
+
});
|
|
538
|
+
const hits = (0, scan_1.scanAob)(ctx.requireProcess(), ctx.scanner, m, opts);
|
|
539
|
+
return new objects_1.LuaStringList(hits.map(scan_1.addressToCeHex));
|
|
540
|
+
},
|
|
541
|
+
AOBScanModuleUnique(args, m) {
|
|
542
|
+
const opts = aobOptions(m, 2, 3, 4, 5, 'AOBScanModuleUnique', args, {
|
|
543
|
+
moduleName: m.argToString(args, 1, 'AOBScanModuleUnique'),
|
|
544
|
+
protection: '*X*W*C',
|
|
545
|
+
unique: true,
|
|
546
|
+
});
|
|
547
|
+
return (0, scan_1.scanAobFirst)(ctx.requireProcess(), ctx.scanner, m, opts);
|
|
548
|
+
},
|
|
549
|
+
/* ---------------- 扫描会话 ---------------- */
|
|
550
|
+
/** 创建显式扫描会话:newScanSession({ parallel=true, maxSnapshotBytes=..., blockSize=... }) */
|
|
551
|
+
newScanSession(args) {
|
|
552
|
+
const proc = ctx.requireProcess();
|
|
553
|
+
const opts = {};
|
|
554
|
+
const table = args[0];
|
|
555
|
+
if (table && typeof table === 'object' && !Array.isArray(table)) {
|
|
556
|
+
const t = table;
|
|
557
|
+
if (t.parallel !== undefined)
|
|
558
|
+
opts.parallel = t.parallel !== false;
|
|
559
|
+
if (t.maxSnapshotBytes !== undefined)
|
|
560
|
+
opts.maxSnapshotBytes = Number(t.maxSnapshotBytes);
|
|
561
|
+
if (t.blockSize !== undefined)
|
|
562
|
+
opts.blockSize = Number(t.blockSize);
|
|
563
|
+
if (t.parallelMinBytes !== undefined)
|
|
564
|
+
opts.parallelMinBytes = Number(t.parallelMinBytes);
|
|
565
|
+
}
|
|
566
|
+
return new objects_1.LuaScanSession(proc, ctx.scanner, opts);
|
|
567
|
+
},
|
|
568
|
+
/** 隐式会话(等价于 CE 里的「当前扫描结果」) */
|
|
569
|
+
firstScan(args, m) {
|
|
570
|
+
const session = ctx.defaultScanSession();
|
|
571
|
+
return BigInt(session.firstScan((0, scan_1.parseScanConfig)(m, m.requireArg(args, 1, 'firstScan'), 'firstScan')));
|
|
572
|
+
},
|
|
573
|
+
nextScan(args, m) {
|
|
574
|
+
const session = ctx.defaultScanSession();
|
|
575
|
+
return BigInt(session.nextScan((0, scan_1.parseScanConfig)(m, m.requireArg(args, 1, 'nextScan'), 'nextScan')));
|
|
576
|
+
},
|
|
577
|
+
getScanSession() {
|
|
578
|
+
return ctx.defaultScanSession();
|
|
579
|
+
},
|
|
580
|
+
getScanResultCount() {
|
|
581
|
+
return BigInt(ctx.defaultScanSession().count);
|
|
582
|
+
},
|
|
583
|
+
getScanResult(args, m) {
|
|
584
|
+
return ctx.defaultScanSession().result(m.argToInt(args, 1, 'getScanResult', 0));
|
|
585
|
+
},
|
|
586
|
+
getScanResults() {
|
|
587
|
+
return ctx.defaultScanSession().all();
|
|
588
|
+
},
|
|
589
|
+
resetScanResults() {
|
|
590
|
+
ctx.defaultScanSession().reset();
|
|
591
|
+
},
|
|
592
|
+
getScanSessionStats() {
|
|
593
|
+
return ctx.defaultScanSession().stats;
|
|
594
|
+
},
|
|
595
|
+
/* ---------------- 表 ---------------- */
|
|
596
|
+
getAddressList() {
|
|
597
|
+
return addressList;
|
|
598
|
+
},
|
|
599
|
+
/* ---------------- 工具 ---------------- */
|
|
600
|
+
sleep(args, m) {
|
|
601
|
+
const ms = m.argToNumber(args, 1, 'sleep', 0);
|
|
602
|
+
if (!Number.isFinite(ms) || ms < 0)
|
|
603
|
+
m.raise(`sleep: 毫秒数非法:${ms}`);
|
|
604
|
+
blockingSleep(Math.min(ms, ctx.maxSleepMs));
|
|
605
|
+
},
|
|
606
|
+
/** 系统运行时间(毫秒),对齐 Win32 的 GetTickCount64 */
|
|
607
|
+
getTickCount() {
|
|
608
|
+
return BigInt(Math.round((0, node_os_1.uptime)() * 1000));
|
|
609
|
+
},
|
|
610
|
+
showMessage(args, m) {
|
|
611
|
+
const text = args[0] === undefined ? '' : formatLuaValue(m.requireArg(args, 1, 'showMessage'));
|
|
612
|
+
ctx.output(text, 'message');
|
|
613
|
+
return true;
|
|
614
|
+
},
|
|
615
|
+
print(args) {
|
|
616
|
+
ctx.output(args.map((v) => formatLuaValue(v)).join('\t'), 'print');
|
|
617
|
+
},
|
|
618
|
+
getLastError() {
|
|
619
|
+
return errorSink.get();
|
|
620
|
+
},
|
|
621
|
+
clearLastError() {
|
|
622
|
+
errorSink.clear();
|
|
623
|
+
},
|
|
624
|
+
};
|
|
625
|
+
// Auto Assembler 的 Lua 绑定(autoAssemble / autoAssembleCheck / autoAssembleError)
|
|
626
|
+
// 单独成模块(src/lua/aa.ts)以保持与引擎层的边界清晰。
|
|
627
|
+
const aa = (0, aa_1.createAutoAssemblerApi)({
|
|
628
|
+
currentProcess: () => ctx.currentProcess(),
|
|
629
|
+
symbols: ctx.symbols,
|
|
630
|
+
}, errorSink);
|
|
631
|
+
Object.assign(functions, aa.functions);
|
|
632
|
+
// 汇编 / 反汇编的 Lua 绑定(assemble / assembleBytes / disassemble / disassembleBytes)
|
|
633
|
+
//
|
|
634
|
+
// 这里的错误一律抛 JS Error:统一入口 `LuaEngine.invokeApi` 会用 `m.raise`
|
|
635
|
+
// 把它翻译成真正的 Lua 错误(脚本侧可以用 pcall 捕获),
|
|
636
|
+
// 效果与在各个 API 里直接 raise 一致,但不必把 marshal 传进子模块。
|
|
637
|
+
Object.assign(functions, (0, asm_1.createAsmApi)({
|
|
638
|
+
currentProcess: () => ctx.currentProcess(),
|
|
639
|
+
symbols: () => ctx.symbols.snapshot(),
|
|
640
|
+
}, (message) => {
|
|
641
|
+
throw new Error(message);
|
|
642
|
+
}, (value) => {
|
|
643
|
+
try {
|
|
644
|
+
return resolver.resolve(value);
|
|
645
|
+
}
|
|
646
|
+
catch (e) {
|
|
647
|
+
throw new Error(`地址解析失败:${(0, marshal_1.describeError)(e)}`);
|
|
648
|
+
}
|
|
649
|
+
}));
|
|
650
|
+
return { functions, errorSink, resolver, addressList, autoAssembler: aa.instance };
|
|
651
|
+
}
|
|
652
|
+
/* ------------------------------------------------------------------ *
|
|
653
|
+
* 辅助
|
|
654
|
+
* ------------------------------------------------------------------ */
|
|
655
|
+
/** 按名字查找 pid(大小写不敏感,先精确匹配再包含匹配)。 */
|
|
656
|
+
function findProcessId(name) {
|
|
657
|
+
const needle = name.trim().toLowerCase();
|
|
658
|
+
if (needle === '')
|
|
659
|
+
return null;
|
|
660
|
+
const all = (0, process_1.listProcesses)();
|
|
661
|
+
const exact = all.find((p) => p.name.toLowerCase() === needle);
|
|
662
|
+
if (exact)
|
|
663
|
+
return exact.pid;
|
|
664
|
+
const partial = all.find((p) => p.name.toLowerCase().includes(needle));
|
|
665
|
+
return partial ? partial.pid : null;
|
|
666
|
+
}
|
|
667
|
+
/** 地址是否落在某个模块里;是则返回 `module+OFFSET`。 */
|
|
668
|
+
function moduleRelativeName(ctx, addr) {
|
|
669
|
+
for (const mod of ctx.listModules()) {
|
|
670
|
+
const base = mod.baseAddress;
|
|
671
|
+
if (addr >= base && addr < base + BigInt(mod.size)) {
|
|
672
|
+
return `${mod.name}+${(addr - base).toString(16).toUpperCase()}`;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
return null;
|
|
676
|
+
}
|
|
677
|
+
/** 把 Lua 值格式化成 print/showMessage 用的字符串。 */
|
|
678
|
+
function formatLuaValue(v) {
|
|
679
|
+
if (v === null || v === undefined)
|
|
680
|
+
return 'nil';
|
|
681
|
+
if (typeof v === 'string')
|
|
682
|
+
return v;
|
|
683
|
+
if (typeof v === 'bigint')
|
|
684
|
+
return v.toString();
|
|
685
|
+
if (typeof v === 'number')
|
|
686
|
+
return String(v);
|
|
687
|
+
if (typeof v === 'boolean')
|
|
688
|
+
return v ? 'true' : 'false';
|
|
689
|
+
if (Array.isArray(v))
|
|
690
|
+
return 'table';
|
|
691
|
+
return 'userdata';
|
|
692
|
+
}
|
|
693
|
+
/** 把 writeBytes 的参数(表或变参)收集成字节数组。 */
|
|
694
|
+
function collectBytes(m, args, startIndex, funcName) {
|
|
695
|
+
const out = [];
|
|
696
|
+
const first = args[startIndex - 1];
|
|
697
|
+
if (first === undefined || first === null)
|
|
698
|
+
return out;
|
|
699
|
+
const pushByte = (v, what) => {
|
|
700
|
+
if (typeof v === 'bigint' || typeof v === 'number') {
|
|
701
|
+
out.push(Number(BigInt.asUintN(8, BigInt(v))));
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
m.raise(`${funcName}: ${what} 必须是整数 0..255,收到 ${v === null ? 'nil' : typeof v}`);
|
|
705
|
+
};
|
|
706
|
+
if (Array.isArray(first)) {
|
|
707
|
+
first.forEach((v, i) => pushByte(v, `第 ${i + 1} 个字节`));
|
|
708
|
+
return out;
|
|
709
|
+
}
|
|
710
|
+
if (typeof first === 'object') {
|
|
711
|
+
const table = first;
|
|
712
|
+
// CE 的字节表是 1 基;如果只给了 [0] 则按 0 基处理
|
|
713
|
+
const base = table['0'] !== undefined && table['1'] === undefined ? 0 : 1;
|
|
714
|
+
for (let i = base; table[String(i)] !== undefined; i++)
|
|
715
|
+
pushByte(table[String(i)], `第 ${i} 个字节`);
|
|
716
|
+
return out;
|
|
717
|
+
}
|
|
718
|
+
for (let i = startIndex; i <= args.length; i++) {
|
|
719
|
+
pushByte(args[i - 1], `第 ${i - startIndex + 1} 个字节`);
|
|
720
|
+
}
|
|
721
|
+
return out;
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* 阻塞式 sleep。
|
|
725
|
+
*
|
|
726
|
+
* 本桥是同步桥,`sleep` 会真的阻塞 Node 主线程(事件循环也停),
|
|
727
|
+
* 用 `Atomics.wait` 实现(实测请求 60ms 得到 72ms)。
|
|
728
|
+
* CE 的 sleep 同样是阻塞的,脚本语义一致;上限由 LuaEngineOptions.maxSleepMs 控制。
|
|
729
|
+
*/
|
|
730
|
+
function blockingSleep(ms) {
|
|
731
|
+
if (ms <= 0)
|
|
732
|
+
return;
|
|
733
|
+
const shared = new Int32Array(new SharedArrayBuffer(4));
|
|
734
|
+
Atomics.wait(shared, 0, 0, ms);
|
|
735
|
+
}
|
|
736
|
+
//# sourceMappingURL=api.js.map
|