cheatengine 5.8.13 → 5.8.15
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/bin/cheatengine +30 -40
- package/ce_mcp_server.js +367 -0
- package/package.json +9 -7
- package/src/base.js +235 -0
- package/src/pipe-client.js +316 -0
- package/src/tool-registry.js +887 -0
- package/ce_mcp_server.py +0 -2509
|
@@ -0,0 +1,887 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool Registry for MCP Tools
|
|
3
|
+
* Defines all 60+ tools available in the Cheat Engine MCP Bridge
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const ToolCategory = {
|
|
7
|
+
SYSTEM: 'system',
|
|
8
|
+
MEMORY: 'memory',
|
|
9
|
+
SCANNING: 'scanning',
|
|
10
|
+
SYMBOLS: 'symbols',
|
|
11
|
+
DEBUG: 'debug',
|
|
12
|
+
ANALYSIS: 'analysis',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
class ToolParam {
|
|
16
|
+
constructor(name, type, description = '', required = false, defaultValue = null, enumValues = null) {
|
|
17
|
+
this.name = name;
|
|
18
|
+
this.type = type;
|
|
19
|
+
this.description = description;
|
|
20
|
+
this.required = required;
|
|
21
|
+
this.default = defaultValue;
|
|
22
|
+
this.enum = enumValues || [];
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
class Tool {
|
|
27
|
+
constructor(name, description, luaCommand, category, params = []) {
|
|
28
|
+
this.name = name;
|
|
29
|
+
this.description = description;
|
|
30
|
+
this.luaCommand = luaCommand;
|
|
31
|
+
this.category = category;
|
|
32
|
+
this.params = params;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
toMCPSchema() {
|
|
36
|
+
const properties = {};
|
|
37
|
+
const required = [];
|
|
38
|
+
|
|
39
|
+
for (const p of this.params) {
|
|
40
|
+
const prop = { type: p.type };
|
|
41
|
+
if (p.description) prop.description = p.description;
|
|
42
|
+
if (p.default !== null) prop.default = p.default;
|
|
43
|
+
if (p.enum && p.enum.length > 0) prop.enum = p.enum;
|
|
44
|
+
properties[p.name] = prop;
|
|
45
|
+
|
|
46
|
+
if (p.required) {
|
|
47
|
+
required.push(p.name);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
name: this.name,
|
|
53
|
+
description: this.description,
|
|
54
|
+
inputSchema: {
|
|
55
|
+
type: 'object',
|
|
56
|
+
properties,
|
|
57
|
+
required,
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
class ToolRegistry {
|
|
64
|
+
constructor() {
|
|
65
|
+
this.tools = new Map();
|
|
66
|
+
this.ADDR_PARAM = new ToolParam(
|
|
67
|
+
'address',
|
|
68
|
+
'string',
|
|
69
|
+
"Address expression (e.g. 'game.exe+123' or '0x123456')",
|
|
70
|
+
true
|
|
71
|
+
);
|
|
72
|
+
this._registerAllTools();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
_registerAllTools() {
|
|
76
|
+
this._registerSystemTools();
|
|
77
|
+
this._registerMemoryTools();
|
|
78
|
+
this._registerScanningTools();
|
|
79
|
+
this._registerSymbolTools();
|
|
80
|
+
this._registerDebugTools();
|
|
81
|
+
this._registerAnalysisTools();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
_register(tool) {
|
|
85
|
+
this.tools.set(tool.name, tool);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
_registerSystemTools() {
|
|
89
|
+
this._register(new Tool(
|
|
90
|
+
'ce_ping',
|
|
91
|
+
'Test connection to Cheat Engine and get bridge status. ' +
|
|
92
|
+
'If connection fails, automatically returns diagnostic info with troubleshooting suggestions. ' +
|
|
93
|
+
'Returns connection health metrics (last_success_time, connection_attempts, consecutive_errors, total_errors, is_connected) ' +
|
|
94
|
+
'and server metrics (uptime, total_calls, error_rate, per-tool statistics).',
|
|
95
|
+
'ping',
|
|
96
|
+
ToolCategory.SYSTEM
|
|
97
|
+
));
|
|
98
|
+
|
|
99
|
+
this._register(new Tool(
|
|
100
|
+
'ce_get_process_info',
|
|
101
|
+
'[INIT] Get current attached process info and refresh symbols. CALL THIS FIRST in any session. ' +
|
|
102
|
+
'Returns: {name, pid, is64bit, ce_version, debugger: {active, interfaceName, canBreak}, ' +
|
|
103
|
+
'last_symbol_refresh (timestamp), symbol_refresh_count, module_count}. ' +
|
|
104
|
+
'Also triggers symbol handler refresh to ensure symbols are up-to-date after module loading.',
|
|
105
|
+
'get_process_info',
|
|
106
|
+
ToolCategory.SYSTEM
|
|
107
|
+
));
|
|
108
|
+
|
|
109
|
+
this._register(new Tool(
|
|
110
|
+
'ce_execute_lua',
|
|
111
|
+
'Execute raw Lua code in CE',
|
|
112
|
+
'execute_lua',
|
|
113
|
+
ToolCategory.SYSTEM,
|
|
114
|
+
[new ToolParam('code', 'string', 'Lua code to execute', true)]
|
|
115
|
+
));
|
|
116
|
+
|
|
117
|
+
this._register(new Tool(
|
|
118
|
+
'ce_get_selected_address',
|
|
119
|
+
'Get the currently selected address in CE Memory View/Disassembler',
|
|
120
|
+
'get_selected_address',
|
|
121
|
+
ToolCategory.SYSTEM
|
|
122
|
+
));
|
|
123
|
+
|
|
124
|
+
this._register(new Tool(
|
|
125
|
+
'ce_get_logs',
|
|
126
|
+
'Get log entries from the Cheat Engine MCP Bridge Lua side. ' +
|
|
127
|
+
'Returns: {entries: [{timestamp, level, category, message, data}], count}. ' +
|
|
128
|
+
'Useful for debugging and monitoring bridge operations.',
|
|
129
|
+
'get_logs',
|
|
130
|
+
ToolCategory.SYSTEM,
|
|
131
|
+
[
|
|
132
|
+
new ToolParam('count', 'integer', 'Maximum number of log entries to return (default: 100)', false, 100),
|
|
133
|
+
new ToolParam('min_level', 'integer', 'Minimum log level to include: 1=DEBUG, 2=INFO, 3=WARN, 4=ERROR (default: 1)', false, 1),
|
|
134
|
+
]
|
|
135
|
+
));
|
|
136
|
+
|
|
137
|
+
this._register(new Tool(
|
|
138
|
+
'ce_list_processes',
|
|
139
|
+
'List running processes. Use before ce_attach_process to find available targets. ' +
|
|
140
|
+
'Returns: {count, processes: [{pid, name}]}.',
|
|
141
|
+
'list_processes',
|
|
142
|
+
ToolCategory.SYSTEM,
|
|
143
|
+
[
|
|
144
|
+
new ToolParam('filter', 'string', 'Filter processes by name (case-insensitive substring match)'),
|
|
145
|
+
new ToolParam('max_results', 'integer', 'Maximum results to return (default: 100)', false, 100),
|
|
146
|
+
]
|
|
147
|
+
));
|
|
148
|
+
|
|
149
|
+
this._register(new Tool(
|
|
150
|
+
'ce_attach_process',
|
|
151
|
+
'Attach to a process by PID or name. Clears caches and scan sessions after attaching. ' +
|
|
152
|
+
'Returns: {success, pid, name, is64bit}.',
|
|
153
|
+
'attach_process',
|
|
154
|
+
ToolCategory.SYSTEM,
|
|
155
|
+
[new ToolParam('target', 'string', "Process ID (number) or process name (e.g. 'game.exe')", true)]
|
|
156
|
+
));
|
|
157
|
+
|
|
158
|
+
this._register(new Tool(
|
|
159
|
+
'ce_auto_assemble',
|
|
160
|
+
'Execute an Auto Assembler script. Supports enable/disable scripts, code injection, etc. ' +
|
|
161
|
+
'Returns: {success, target_self}.',
|
|
162
|
+
'auto_assemble',
|
|
163
|
+
ToolCategory.SYSTEM,
|
|
164
|
+
[
|
|
165
|
+
new ToolParam('script', 'string', 'Auto Assembler script content', true),
|
|
166
|
+
new ToolParam('target_self', 'boolean', 'Target CE process itself (for internal scripts)', false, false),
|
|
167
|
+
]
|
|
168
|
+
));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
_registerMemoryTools() {
|
|
172
|
+
const memTypes = ['byte', 'word', 'dword', 'qword', 'float', 'double', 'string', 'bytes'];
|
|
173
|
+
|
|
174
|
+
this._register(new Tool(
|
|
175
|
+
'ce_read_memory',
|
|
176
|
+
'Read a single memory value. Returns: {address, type, value}. ' +
|
|
177
|
+
'For reading multiple addresses, use ce_read_memory_batch instead for better performance.',
|
|
178
|
+
'read_memory',
|
|
179
|
+
ToolCategory.MEMORY,
|
|
180
|
+
[
|
|
181
|
+
this.ADDR_PARAM,
|
|
182
|
+
new ToolParam('type', 'string', 'Value type', true, null, memTypes),
|
|
183
|
+
new ToolParam('size', 'integer', 'Size for string/bytes', false, 100),
|
|
184
|
+
]
|
|
185
|
+
));
|
|
186
|
+
|
|
187
|
+
this._register(new Tool(
|
|
188
|
+
'ce_read_memory_batch',
|
|
189
|
+
'[PERFORMANCE] Read multiple memory addresses in ONE request. ' +
|
|
190
|
+
'ALWAYS prefer this over multiple ce_read_memory calls for better performance. ' +
|
|
191
|
+
'Example: [{"address": "game.exe+100", "type": "dword", "id": "hp"}, {"address": "game.exe+104", "type": "float", "id": "mp"}]. ' +
|
|
192
|
+
'Returns: {results: [{id, address, type, value, error?}]}.',
|
|
193
|
+
'read_memory_batch',
|
|
194
|
+
ToolCategory.MEMORY,
|
|
195
|
+
[new ToolParam('requests', 'array', 'Array of {address, type, id?, size?} objects', true)]
|
|
196
|
+
));
|
|
197
|
+
|
|
198
|
+
this._register(new Tool(
|
|
199
|
+
'ce_write_memory',
|
|
200
|
+
'Write memory value. Returns: {success: true, address, bytes_written}.',
|
|
201
|
+
'write_memory',
|
|
202
|
+
ToolCategory.MEMORY,
|
|
203
|
+
[
|
|
204
|
+
this.ADDR_PARAM,
|
|
205
|
+
new ToolParam('type', 'string', 'Value type', true, null, memTypes),
|
|
206
|
+
new ToolParam('value', 'string', 'Value to write (number, string, or byte array)', true),
|
|
207
|
+
]
|
|
208
|
+
));
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
_registerScanningTools() {
|
|
212
|
+
this._register(new Tool(
|
|
213
|
+
'ce_aob_scan',
|
|
214
|
+
"Scan for byte pattern (AOB/signature) in memory. Supports ?? wildcards for variable bytes. " +
|
|
215
|
+
"USE WHEN: Finding code by signature, locating functions, making version-independent scripts. " +
|
|
216
|
+
"NOT FOR: Finding data values (use ce_scan_new or ce_value_scan). " +
|
|
217
|
+
"Returns: {count, results: [addresses]}.",
|
|
218
|
+
'aob_scan',
|
|
219
|
+
ToolCategory.SCANNING,
|
|
220
|
+
[
|
|
221
|
+
new ToolParam('aob_string', 'string', "e.g. '48 89 5C 24 ?? 48 83 EC 20'", true),
|
|
222
|
+
new ToolParam('module', 'string', "Limit scan to specific module for better performance (e.g. 'game.exe', 'UnityPlayer.dll')"),
|
|
223
|
+
new ToolParam('protection', 'string', 'Flags like -C+X', false, '-C+X'),
|
|
224
|
+
new ToolParam('start', 'string', 'Start address (optional, use module instead for better performance)'),
|
|
225
|
+
new ToolParam('stop', 'string', 'Stop address (optional)'),
|
|
226
|
+
new ToolParam('max_results', 'integer', 'Maximum results', false, 100),
|
|
227
|
+
]
|
|
228
|
+
));
|
|
229
|
+
|
|
230
|
+
this._register(new Tool(
|
|
231
|
+
'ce_value_scan',
|
|
232
|
+
'[MANUAL POINTER TRACING - STEP 2] Scan memory for a specific value. ' +
|
|
233
|
+
'USE WHEN: After ce_find_what_accesses returns a register value, search for that value to find pointer storage. ' +
|
|
234
|
+
"Example: RBX=0x12345678 accessed your address -> scan for 0x12345678 (qword) -> find where pointer is stored. " +
|
|
235
|
+
"NOT FOR: Finding game values like health/gold (use ce_scan_new for value hunting). " +
|
|
236
|
+
'Returns: {count, results: [{address, symbol, isStatic}], value_searched, type, module}. ' +
|
|
237
|
+
'TIP: isStatic=true means the address is in a module (potential static base found!).',
|
|
238
|
+
'value_scan',
|
|
239
|
+
ToolCategory.SCANNING,
|
|
240
|
+
[
|
|
241
|
+
new ToolParam('value', 'string', "Value to search for (e.g. '0x255D5E758' or '12345')", true),
|
|
242
|
+
new ToolParam('type', 'string', 'Value type', true, null, ['byte', 'word', 'dword', 'qword', 'float', 'double', 'string']),
|
|
243
|
+
new ToolParam('module', 'string', "Limit scan to specific module (e.g. 'game.exe')"),
|
|
244
|
+
new ToolParam('protection', 'string', 'Memory protection flags (default: \'+W-C\' for writable memory)', false, '+W-C'),
|
|
245
|
+
new ToolParam('start', 'string', 'Start address (optional)'),
|
|
246
|
+
new ToolParam('stop', 'string', 'Stop address (optional)'),
|
|
247
|
+
new ToolParam('max_results', 'integer', 'Maximum results', false, 100),
|
|
248
|
+
new ToolParam('is_hex', 'boolean', 'Treat value as hexadecimal (auto-detected if omitted)'),
|
|
249
|
+
]
|
|
250
|
+
));
|
|
251
|
+
|
|
252
|
+
this._register(new Tool(
|
|
253
|
+
'ce_scan_new',
|
|
254
|
+
'[VALUE HUNTING] Start scan session to find unknown addresses by observing value changes. ' +
|
|
255
|
+
"USE WHEN: You don't know the address but can observe changes (health 100->95). " +
|
|
256
|
+
"WORKFLOW: ce_scan_new -> change value in game -> ce_scan_next('decreased') -> repeat until few results. " +
|
|
257
|
+
'NOT FOR: Pointer tracing (use ce_value_scan). NOT FOR: Code patterns (use ce_aob_scan). ' +
|
|
258
|
+
'Returns: {session_id, count}. Use ce_scan_next to filter, ce_scan_results to get addresses.',
|
|
259
|
+
'scan_new',
|
|
260
|
+
ToolCategory.SCANNING,
|
|
261
|
+
[
|
|
262
|
+
new ToolParam('value', 'string', "Value to search for (e.g. '100' or '0x64')", true),
|
|
263
|
+
new ToolParam('type', 'string', 'Value type', true, null, ['byte', 'word', 'dword', 'qword', 'float', 'double', 'string']),
|
|
264
|
+
new ToolParam('module', 'string', "Limit scan to specific module (e.g. 'game.exe')"),
|
|
265
|
+
new ToolParam('protection', 'string', 'Memory protection flags', false, '+W-C'),
|
|
266
|
+
new ToolParam('start', 'string', 'Start address (optional)'),
|
|
267
|
+
new ToolParam('stop', 'string', 'Stop address (optional)'),
|
|
268
|
+
new ToolParam('is_hex', 'boolean', 'Treat value as hexadecimal (auto-detected if omitted)'),
|
|
269
|
+
]
|
|
270
|
+
));
|
|
271
|
+
|
|
272
|
+
this._register(new Tool(
|
|
273
|
+
'ce_scan_next',
|
|
274
|
+
'Continue scanning (filter) an existing session. Supports various scan types. ' +
|
|
275
|
+
'Returns: {session_id, count, scan_number, scan_type}.',
|
|
276
|
+
'scan_next',
|
|
277
|
+
ToolCategory.SCANNING,
|
|
278
|
+
[
|
|
279
|
+
new ToolParam('session_id', 'string', 'Session ID from ce_scan_new', true),
|
|
280
|
+
new ToolParam('value', 'string', 'Value to search for', true),
|
|
281
|
+
new ToolParam('scan_type', 'string', 'Scan type for filtering', false, 'exact',
|
|
282
|
+
['exact', 'increased', 'decreased', 'changed', 'unchanged', 'increased_by', 'decreased_by', 'bigger_than', 'smaller_than', 'between']),
|
|
283
|
+
new ToolParam('value2', 'string', "Second value for 'between' scan type"),
|
|
284
|
+
new ToolParam('is_hex', 'boolean', 'Treat value as hexadecimal'),
|
|
285
|
+
]
|
|
286
|
+
));
|
|
287
|
+
|
|
288
|
+
this._register(new Tool(
|
|
289
|
+
'ce_scan_results',
|
|
290
|
+
'Get paginated results from a scan session. ' +
|
|
291
|
+
'Returns: {session_id, total_count, start_index, returned_count, has_more, results: [{index, address, symbol, value, isStatic}]}.',
|
|
292
|
+
'scan_results',
|
|
293
|
+
ToolCategory.SCANNING,
|
|
294
|
+
[
|
|
295
|
+
new ToolParam('session_id', 'string', 'Session ID from ce_scan_new', true),
|
|
296
|
+
new ToolParam('start_index', 'integer', 'Starting index for pagination', false, 0),
|
|
297
|
+
new ToolParam('limit', 'integer', 'Maximum results to return (max 1000)', false, 100),
|
|
298
|
+
]
|
|
299
|
+
));
|
|
300
|
+
|
|
301
|
+
this._register(new Tool(
|
|
302
|
+
'ce_scan_close',
|
|
303
|
+
'Close a scan session and release resources. ' +
|
|
304
|
+
'Returns: {session_id, closed, active_sessions}.',
|
|
305
|
+
'scan_close',
|
|
306
|
+
ToolCategory.SCANNING,
|
|
307
|
+
[new ToolParam('session_id', 'string', 'Session ID to close', true)]
|
|
308
|
+
));
|
|
309
|
+
|
|
310
|
+
this._register(new Tool(
|
|
311
|
+
'ce_scan_list',
|
|
312
|
+
'List all active scan sessions. ' +
|
|
313
|
+
'Returns: {active_sessions, max_sessions, sessions: [{session_id, count, scan_count, value_type, ...}]}.',
|
|
314
|
+
'scan_list',
|
|
315
|
+
ToolCategory.SCANNING
|
|
316
|
+
));
|
|
317
|
+
|
|
318
|
+
this._register(new Tool(
|
|
319
|
+
'ce_enum_modules',
|
|
320
|
+
'List all loaded modules (DLLs). Returns: {count, modules: [{name, base, size}]}.',
|
|
321
|
+
'enum_modules',
|
|
322
|
+
ToolCategory.SCANNING
|
|
323
|
+
));
|
|
324
|
+
|
|
325
|
+
this._register(new Tool(
|
|
326
|
+
'ce_get_address_list',
|
|
327
|
+
'Get all records from Cheat Table (address list)',
|
|
328
|
+
'get_address_list',
|
|
329
|
+
ToolCategory.SCANNING,
|
|
330
|
+
[new ToolParam('include_script', 'boolean', 'Include script content for AA script entries', false, false)]
|
|
331
|
+
));
|
|
332
|
+
|
|
333
|
+
this._register(new Tool(
|
|
334
|
+
'ce_add_address_record',
|
|
335
|
+
'Add a new record to Cheat Table',
|
|
336
|
+
'add_address_record',
|
|
337
|
+
ToolCategory.SCANNING,
|
|
338
|
+
[
|
|
339
|
+
new ToolParam('description', 'string', 'Record description/name', true),
|
|
340
|
+
new ToolParam('address', 'string', 'Address expression', true),
|
|
341
|
+
new ToolParam('value_type', 'string', 'Value type', false, 'dword',
|
|
342
|
+
['byte', 'word', 'dword', 'qword', 'float', 'double', 'string', 'bytes', 'script']),
|
|
343
|
+
new ToolParam('script', 'string', 'AA script content (only for script type)'),
|
|
344
|
+
]
|
|
345
|
+
));
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
_registerSymbolTools() {
|
|
349
|
+
this._register(new Tool(
|
|
350
|
+
'ce_get_address',
|
|
351
|
+
"Resolve expression to address. Returns: {expression, address}. " +
|
|
352
|
+
"Supports: 'game.exe+0x1234', '[[base]+10]+20', symbol names.",
|
|
353
|
+
'get_address',
|
|
354
|
+
ToolCategory.SYMBOLS,
|
|
355
|
+
[new ToolParam('expression', 'string', 'Address expression to resolve', true)]
|
|
356
|
+
));
|
|
357
|
+
|
|
358
|
+
this._register(new Tool(
|
|
359
|
+
'ce_get_symbol',
|
|
360
|
+
'[SYMBOL LOOKUP] Get symbol name, RTTI class info, and module details for an address. ' +
|
|
361
|
+
'USE WHEN: Identifying what code/data an address belongs to, checking if address is in a module. ' +
|
|
362
|
+
'Returns: {address, symbol, hasSymbol, rttiClassName, hasRTTI, inModule, inSystemModule, ' +
|
|
363
|
+
'moduleInfo: {name, base, size, offset, is64bit, path}}. ' +
|
|
364
|
+
"TIP: inModule=true with symbol containing '.exe+' or '.dll+' indicates a static address.",
|
|
365
|
+
'get_symbol',
|
|
366
|
+
ToolCategory.SYMBOLS,
|
|
367
|
+
[
|
|
368
|
+
this.ADDR_PARAM,
|
|
369
|
+
new ToolParam('include_module', 'boolean', 'Include module name in symbol', false, true),
|
|
370
|
+
]
|
|
371
|
+
));
|
|
372
|
+
|
|
373
|
+
this._register(new Tool(
|
|
374
|
+
'ce_get_region_info',
|
|
375
|
+
'Get memory region info (base, size, protection)',
|
|
376
|
+
'get_region_info',
|
|
377
|
+
ToolCategory.SYMBOLS,
|
|
378
|
+
[this.ADDR_PARAM]
|
|
379
|
+
));
|
|
380
|
+
|
|
381
|
+
this._register(new Tool(
|
|
382
|
+
'ce_auto_guess',
|
|
383
|
+
'Guess the value type at an address (byte/word/dword/qword/float/double/string/pointer). ' +
|
|
384
|
+
'USE WHEN: Analyzing unknown memory, determining how to read a value. ' +
|
|
385
|
+
'Returns: {type_id, type_name, value}. Useful for structure field analysis.',
|
|
386
|
+
'auto_guess',
|
|
387
|
+
ToolCategory.SYMBOLS,
|
|
388
|
+
[this.ADDR_PARAM]
|
|
389
|
+
));
|
|
390
|
+
|
|
391
|
+
this._register(new Tool(
|
|
392
|
+
'ce_resolve_pointer',
|
|
393
|
+
'[VERIFICATION] Resolve a KNOWN pointer chain to get final address and optionally read value. ' +
|
|
394
|
+
'USE WHEN: You already have base+offsets and want to verify the chain works or read the value. ' +
|
|
395
|
+
'Returns CE notation like \'[[game.exe+123]+10]+20\' for easy copy to CE. ' +
|
|
396
|
+
'NOT FOR: Discovering pointer paths (use ce_find_pointer_path for discovery). ' +
|
|
397
|
+
'Returns: {base, offsets, final_address, ceNotation, chain: [{level, address, value, symbol}]}.',
|
|
398
|
+
'resolve_pointer',
|
|
399
|
+
ToolCategory.SYMBOLS,
|
|
400
|
+
[
|
|
401
|
+
new ToolParam('base', 'string', 'Base address or symbol', true),
|
|
402
|
+
new ToolParam('offsets', 'array', 'Array of offsets, e.g. [0x100, 0x20, 0x8]', true),
|
|
403
|
+
new ToolParam('read_value', 'boolean', 'Read value at final address', false, false),
|
|
404
|
+
new ToolParam('value_type', 'string', 'Value type to read if read_value=true', false, 'dword',
|
|
405
|
+
['byte', 'word', 'dword', 'qword', 'float', 'double', 'pointer']),
|
|
406
|
+
]
|
|
407
|
+
));
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
_registerDebugTools() {
|
|
411
|
+
this._register(new Tool(
|
|
412
|
+
'ce_disassemble',
|
|
413
|
+
'Basic disassembly - get raw instructions at an address. ' +
|
|
414
|
+
'USE WHEN: You just need to see assembly code (opcodes, bytes). ' +
|
|
415
|
+
'FOR DEEPER ANALYSIS: Use ce_analyze_code (extracts calls/jumps) or ce_build_cfg (function-level CFG). ' +
|
|
416
|
+
'Returns: {instructions: [{address, opcode, bytes, size}], nextAddress}.',
|
|
417
|
+
'disassemble',
|
|
418
|
+
ToolCategory.DEBUG,
|
|
419
|
+
[
|
|
420
|
+
this.ADDR_PARAM,
|
|
421
|
+
new ToolParam('count', 'integer', 'Number of instructions', false, 10),
|
|
422
|
+
new ToolParam('direction', 'string', 'Disassembly direction: forward (default) or backward using getPreviousOpcode', false, 'forward', ['forward', 'backward']),
|
|
423
|
+
]
|
|
424
|
+
));
|
|
425
|
+
|
|
426
|
+
this._register(new Tool(
|
|
427
|
+
'ce_get_instruction_info',
|
|
428
|
+
'Get detailed instruction info. Returns: {address, opcode, parameters, bytes, size, isCall, isJump, isRet, isConditionalJump, readsMemory, writesMemory}.',
|
|
429
|
+
'get_instruction_info',
|
|
430
|
+
ToolCategory.DEBUG,
|
|
431
|
+
[this.ADDR_PARAM]
|
|
432
|
+
));
|
|
433
|
+
|
|
434
|
+
this._register(new Tool(
|
|
435
|
+
'ce_set_breakpoint',
|
|
436
|
+
'Set a hardware breakpoint. Returns: {success, address, type}. Use ce_remove_breakpoint to remove.',
|
|
437
|
+
'set_breakpoint',
|
|
438
|
+
ToolCategory.DEBUG,
|
|
439
|
+
[
|
|
440
|
+
this.ADDR_PARAM,
|
|
441
|
+
new ToolParam('type', 'string', 'Breakpoint type', false, 'execute', ['execute', 'write', 'access']),
|
|
442
|
+
new ToolParam('size', 'integer', 'Size in bytes for write/access breakpoints', false, 1),
|
|
443
|
+
]
|
|
444
|
+
));
|
|
445
|
+
|
|
446
|
+
this._register(new Tool(
|
|
447
|
+
'ce_remove_breakpoint',
|
|
448
|
+
'Remove a debug breakpoint',
|
|
449
|
+
'remove_breakpoint',
|
|
450
|
+
ToolCategory.DEBUG,
|
|
451
|
+
[this.ADDR_PARAM]
|
|
452
|
+
));
|
|
453
|
+
|
|
454
|
+
this._register(new Tool(
|
|
455
|
+
'ce_get_breakpoints',
|
|
456
|
+
'List all active breakpoints',
|
|
457
|
+
'get_breakpoints',
|
|
458
|
+
ToolCategory.DEBUG
|
|
459
|
+
));
|
|
460
|
+
|
|
461
|
+
this._register(new Tool(
|
|
462
|
+
'ce_break_and_get_regs',
|
|
463
|
+
'[SINGLE CAPTURE] Set breakpoint and capture registers ONCE when hit. ' +
|
|
464
|
+
'USE WHEN: You need register values at ONE specific point (function args, pointer values). ' +
|
|
465
|
+
'Returns all registers + call stack at breakpoint hit. ' +
|
|
466
|
+
'FOR MULTI-STEP: Use ce_break_and_trace to trace execution flow. ' +
|
|
467
|
+
'Returns: {triggered, registers: {rax-r15, rip, rflags}, callStack, returnAddress}.',
|
|
468
|
+
'break_and_get_regs',
|
|
469
|
+
ToolCategory.DEBUG,
|
|
470
|
+
[
|
|
471
|
+
this.ADDR_PARAM,
|
|
472
|
+
new ToolParam('timeout', 'integer', 'Timeout in ms', false, 5000),
|
|
473
|
+
new ToolParam('stack_depth', 'integer', 'Number of stack entries to read', false, 16),
|
|
474
|
+
new ToolParam('include_xmm', 'boolean', 'Include XMM registers (SSE/AVX) for floating point analysis', false, false),
|
|
475
|
+
]
|
|
476
|
+
));
|
|
477
|
+
|
|
478
|
+
this._register(new Tool(
|
|
479
|
+
'ce_break_and_trace',
|
|
480
|
+
'[EXECUTION TRACE] Trace code execution step-by-step from breakpoint. Most powerful debugging tool. ' +
|
|
481
|
+
'USE WHEN: Understanding algorithm logic, following data transformations, debugging execution flow. ' +
|
|
482
|
+
'Captures FULL register state at EACH instruction. ' +
|
|
483
|
+
'FOR SINGLE SNAPSHOT: Use ce_break_and_get_regs instead. ' +
|
|
484
|
+
"Returns: {steps, stop_reason, trace: [{step, address, instruction, registers}]}. " +
|
|
485
|
+
"Stop reasons: 'ret', 'end_address', 'max_steps', 'timeout'.",
|
|
486
|
+
'break_and_trace',
|
|
487
|
+
ToolCategory.DEBUG,
|
|
488
|
+
[
|
|
489
|
+
this.ADDR_PARAM,
|
|
490
|
+
new ToolParam('max_steps', 'integer', 'Maximum instructions to trace', false, 100),
|
|
491
|
+
new ToolParam('timeout', 'integer', 'Timeout in ms', false, 10000),
|
|
492
|
+
new ToolParam('stop_on_ret', 'boolean', 'Stop tracing when ret is encountered', false, true),
|
|
493
|
+
new ToolParam('trace_into_call', 'boolean', 'Trace into call instructions (step into vs step over)', false, false),
|
|
494
|
+
new ToolParam('end_address', 'string', 'Stop tracing when this address is reached'),
|
|
495
|
+
new ToolParam('initial_regs', 'object',
|
|
496
|
+
'Set register values at first hit. ' +
|
|
497
|
+
'Example: {"rcx": "0x12345", "rdx": 100}'),
|
|
498
|
+
]
|
|
499
|
+
));
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
_registerAnalysisTools() {
|
|
503
|
+
// Analysis tools - first batch
|
|
504
|
+
this._register(new Tool(
|
|
505
|
+
'ce_find_what_accesses',
|
|
506
|
+
'[MANUAL POINTER TRACING - STEP 1] Find code that reads/writes an address (like CE\'s F5 key). ' +
|
|
507
|
+
'This tool monitors for 10 seconds - user must trigger memory access during this time. ' +
|
|
508
|
+
'USE WHEN: ce_find_pointer_path failed and you need manual pointer tracing. ' +
|
|
509
|
+
'Returns register values (e.g., RBX=0x12345678) showing what pointer was used to access the address. ' +
|
|
510
|
+
'WORKFLOW: Take register value -> ce_value_scan to find where that pointer is stored -> repeat until static base.',
|
|
511
|
+
'find_what_accesses',
|
|
512
|
+
ToolCategory.ANALYSIS,
|
|
513
|
+
[
|
|
514
|
+
this.ADDR_PARAM,
|
|
515
|
+
new ToolParam('user_prompted', 'boolean',
|
|
516
|
+
"REQUIRED: Set true ONLY after you told user 'I will monitor for 10 seconds, please change the value in game NOW'. " +
|
|
517
|
+
'You must prompt user BEFORE calling.', true),
|
|
518
|
+
new ToolParam('size', 'integer', 'Size to monitor (1/2/4/8 bytes)', false, 4),
|
|
519
|
+
new ToolParam('duration_ms', 'integer', 'Monitoring duration in milliseconds (default 10000 = 10 seconds)', false, 10000),
|
|
520
|
+
new ToolParam('max_records', 'integer', 'Maximum hit records before stopping', false, 1000),
|
|
521
|
+
]
|
|
522
|
+
));
|
|
523
|
+
|
|
524
|
+
this._register(new Tool(
|
|
525
|
+
'ce_find_what_writes',
|
|
526
|
+
'Find code that WRITES to an address (like CE\'s F6 key). Monitors writes only, ignores reads. ' +
|
|
527
|
+
'This tool monitors for 10 seconds - user must trigger memory write during this time. ' +
|
|
528
|
+
'USE WHEN: Finding what MODIFIES a value (e.g., what decreases player health). ' +
|
|
529
|
+
'USE ce_find_what_accesses instead if you need both reads and writes for pointer tracing.',
|
|
530
|
+
'find_what_writes',
|
|
531
|
+
ToolCategory.ANALYSIS,
|
|
532
|
+
[
|
|
533
|
+
this.ADDR_PARAM,
|
|
534
|
+
new ToolParam('user_prompted', 'boolean',
|
|
535
|
+
"REQUIRED: Set true ONLY after you told user 'I will monitor for 10 seconds, please trigger a write in game NOW'. " +
|
|
536
|
+
'You must prompt user BEFORE calling.', true),
|
|
537
|
+
new ToolParam('size', 'integer', 'Size to monitor (1/2/4/8 bytes)', false, 4),
|
|
538
|
+
new ToolParam('duration_ms', 'integer', 'Monitoring duration in milliseconds (default 10000 = 10 seconds)', false, 10000),
|
|
539
|
+
new ToolParam('max_records', 'integer', 'Maximum hit records before stopping', false, 1000),
|
|
540
|
+
]
|
|
541
|
+
));
|
|
542
|
+
|
|
543
|
+
this._register(new Tool(
|
|
544
|
+
'ce_analyze_code',
|
|
545
|
+
'Static analysis of code block - disassembly PLUS extracted calls, jumps, memory references. ' +
|
|
546
|
+
'USE WHEN: Understanding what a code section does without execution. ' +
|
|
547
|
+
'Returns call targets, jump destinations, memory access patterns. ' +
|
|
548
|
+
'FOR FUNCTION-LEVEL: Use ce_build_cfg. FOR DYNAMIC ANALYSIS: Use ce_break_and_trace. ' +
|
|
549
|
+
'Returns: {instructions, calls, jumps, memory_refs}.',
|
|
550
|
+
'analyze_code',
|
|
551
|
+
ToolCategory.ANALYSIS,
|
|
552
|
+
[
|
|
553
|
+
this.ADDR_PARAM,
|
|
554
|
+
new ToolParam('count', 'integer', 'Number of instructions to analyze', false, 20),
|
|
555
|
+
]
|
|
556
|
+
));
|
|
557
|
+
|
|
558
|
+
this._register(new Tool(
|
|
559
|
+
'ce_build_cfg',
|
|
560
|
+
'Build Control Flow Graph for an entire function. ' +
|
|
561
|
+
'USE WHEN: Analyzing function structure, finding loops, understanding complex branching. ' +
|
|
562
|
+
'Returns basic blocks, edges, loop detection, cyclomatic complexity. ' +
|
|
563
|
+
'TIP: Use ce_find_function_boundaries first if you don\'t know function start address. ' +
|
|
564
|
+
'Returns: {entry, blocks: [{id, start, end, successors, predecessors, isLoopHeader}], edges, loops, complexity}.',
|
|
565
|
+
'build_cfg',
|
|
566
|
+
ToolCategory.ANALYSIS,
|
|
567
|
+
[
|
|
568
|
+
this.ADDR_PARAM,
|
|
569
|
+
new ToolParam('max_instructions', 'integer', 'Maximum instructions to analyze (default 500)', false, 500),
|
|
570
|
+
new ToolParam('max_blocks', 'integer', 'Maximum basic blocks (default 100)', false, 100),
|
|
571
|
+
new ToolParam('detect_loops', 'boolean', 'Detect and annotate loop structures', false, true),
|
|
572
|
+
new ToolParam('include_disasm', 'boolean', 'Include disassembly in each block', false, true),
|
|
573
|
+
]
|
|
574
|
+
));
|
|
575
|
+
|
|
576
|
+
this._register(new Tool(
|
|
577
|
+
'ce_detect_patterns',
|
|
578
|
+
'[PATTERN RECOGNITION] Detect common code patterns in a function. ' +
|
|
579
|
+
'USE WHEN: Quick function classification, finding crypto code, detecting anti-debug, locating string usage. ' +
|
|
580
|
+
'Detects: switch_tables (jump tables), virtual_calls (vtable calls), string_refs (string literals), ' +
|
|
581
|
+
'crypto_constants (MD5/SHA/TEA/Blowfish magic numbers), anti_debug (IsDebuggerPresent/rdtsc), ' +
|
|
582
|
+
'comparisons (cmp/test), memory_patterns (struct field access). ' +
|
|
583
|
+
'Returns: {patterns: {switch_tables, virtual_calls, string_refs, crypto_constants, anti_debug, comparisons, memory_patterns}, ' +
|
|
584
|
+
'summary: {has_switch, has_virtual_calls, has_strings, has_crypto, has_anti_debug, unique_offsets}}.',
|
|
585
|
+
'detect_patterns',
|
|
586
|
+
ToolCategory.ANALYSIS,
|
|
587
|
+
[
|
|
588
|
+
this.ADDR_PARAM,
|
|
589
|
+
new ToolParam('max_instructions', 'integer', 'Maximum instructions to scan', false, 200),
|
|
590
|
+
new ToolParam('patterns', 'array', 'Specific patterns to detect (omit for all)'),
|
|
591
|
+
]
|
|
592
|
+
));
|
|
593
|
+
|
|
594
|
+
this._register(new Tool(
|
|
595
|
+
'ce_compare_functions',
|
|
596
|
+
'Compare two functions for similarity. Returns matching blocks, differing instructions, ' +
|
|
597
|
+
'and similarity score. Useful for patch analysis and finding similar code.',
|
|
598
|
+
'compare_functions',
|
|
599
|
+
ToolCategory.ANALYSIS,
|
|
600
|
+
[
|
|
601
|
+
new ToolParam('address1', 'string', 'First function address', true),
|
|
602
|
+
new ToolParam('address2', 'string', 'Second function address', true),
|
|
603
|
+
new ToolParam('max_instructions', 'integer', 'Max instructions per function', false, 200),
|
|
604
|
+
]
|
|
605
|
+
));
|
|
606
|
+
|
|
607
|
+
this._register(new Tool(
|
|
608
|
+
'ce_trace_dataflow',
|
|
609
|
+
"Trace how a SINGLE register's value flows through code. " +
|
|
610
|
+
"USE WHEN: 'Where does RAX get its value?' or 'Where is RCX used after this?'. " +
|
|
611
|
+
'Tracks ONE register only. FOR CROSS-REGISTER: Use ce_program_slice which follows data across mov/xchg. ' +
|
|
612
|
+
'Returns: {definitions: [where value comes from], uses: [where value goes]}.',
|
|
613
|
+
'trace_dataflow',
|
|
614
|
+
ToolCategory.ANALYSIS,
|
|
615
|
+
[
|
|
616
|
+
this.ADDR_PARAM,
|
|
617
|
+
new ToolParam('register', 'string', "Register to trace (e.g. 'rax', 'rcx')", true),
|
|
618
|
+
new ToolParam('max_instructions', 'integer', 'Max instructions to analyze', false, 100),
|
|
619
|
+
new ToolParam('direction', 'string', "Trace direction: 'forward' (uses) or 'backward' (definitions)", false, 'both', ['forward', 'backward', 'both']),
|
|
620
|
+
]
|
|
621
|
+
));
|
|
622
|
+
|
|
623
|
+
this._register(new Tool(
|
|
624
|
+
'ce_program_slice',
|
|
625
|
+
'[ADVANCED] Compute program slice - find ALL instructions affecting a value (backward) or affected by it (forward). ' +
|
|
626
|
+
"USE WHEN: Understanding 'how is this value computed' across multiple registers and memory operations. " +
|
|
627
|
+
'Unlike ce_trace_dataflow, follows data through register transfers (mov rax,rbx). ' +
|
|
628
|
+
'Essential for complex algorithm reverse engineering. ' +
|
|
629
|
+
'Returns: {slice_instructions, dependencies}.',
|
|
630
|
+
'program_slice',
|
|
631
|
+
ToolCategory.ANALYSIS,
|
|
632
|
+
[
|
|
633
|
+
this.ADDR_PARAM,
|
|
634
|
+
new ToolParam('criterion', 'string', "Slicing criterion: register name (e.g. 'rax') or memory pattern (e.g. '[rbx+10]')", true),
|
|
635
|
+
new ToolParam('direction', 'string', "Slice direction: 'backward' (what affects this) or 'forward' (what this affects)", false, 'backward', ['backward', 'forward']),
|
|
636
|
+
new ToolParam('max_instructions', 'integer', 'Max instructions to analyze', false, 200),
|
|
637
|
+
new ToolParam('follow_calls', 'boolean', 'Follow into called functions (increases depth but slower)', false, false),
|
|
638
|
+
]
|
|
639
|
+
));
|
|
640
|
+
|
|
641
|
+
this._register(new Tool(
|
|
642
|
+
'ce_analyze_struct_access',
|
|
643
|
+
'Infer structure fields by scanning memory values at an address. ' +
|
|
644
|
+
'USE WHEN: Analyzing object/struct layout, finding field offsets. ' +
|
|
645
|
+
'Scans memory range and guesses field types. ' +
|
|
646
|
+
'FOR DYNAMIC ANALYSIS: Use ce_trace_struct_access to see what code accesses the struct.',
|
|
647
|
+
'analyze_struct_access',
|
|
648
|
+
ToolCategory.ANALYSIS,
|
|
649
|
+
[
|
|
650
|
+
new ToolParam('base_address', 'string', this.ADDR_PARAM.description, true),
|
|
651
|
+
new ToolParam('scan_range', 'integer', 'Range to scan in bytes', false, 512),
|
|
652
|
+
]
|
|
653
|
+
));
|
|
654
|
+
|
|
655
|
+
this._register(new Tool(
|
|
656
|
+
'ce_trace_struct_access',
|
|
657
|
+
'Dynamic trace: Monitor what code accesses a memory region. ' +
|
|
658
|
+
'USE WHEN: Finding which code reads/writes struct fields, understanding object usage patterns. ' +
|
|
659
|
+
'FOR STATIC ANALYSIS: Use ce_analyze_struct_access to infer field types from values.',
|
|
660
|
+
'trace_struct_access',
|
|
661
|
+
ToolCategory.ANALYSIS,
|
|
662
|
+
[
|
|
663
|
+
this.ADDR_PARAM,
|
|
664
|
+
new ToolParam('size', 'integer', 'Size to monitor', false, 4),
|
|
665
|
+
new ToolParam('mode', 'string', 'Trace mode', false, 'read_write', ['read_write', 'write']),
|
|
666
|
+
new ToolParam('duration_ms', 'integer', 'Duration in milliseconds', false, 1000),
|
|
667
|
+
]
|
|
668
|
+
));
|
|
669
|
+
|
|
670
|
+
this._register(new Tool(
|
|
671
|
+
'ce_cleanup',
|
|
672
|
+
'Force remove ALL breakpoints and traces set by MCP tools. ' +
|
|
673
|
+
'USE WHEN: Game frozen due to stuck breakpoint, cleaning up after analysis, resetting debug state. ' +
|
|
674
|
+
'Safe to call anytime - cleans up zombie resources.',
|
|
675
|
+
'cleanup_breakpoints',
|
|
676
|
+
ToolCategory.ANALYSIS
|
|
677
|
+
));
|
|
678
|
+
|
|
679
|
+
this._register(new Tool(
|
|
680
|
+
'ce_find_pointer_path',
|
|
681
|
+
'[RECOMMENDED FIRST] Automatically find static pointer path to a dynamic address. ' +
|
|
682
|
+
'This tool monitors memory access internally for ~10 seconds per level - user must trigger access during this time. ' +
|
|
683
|
+
'USE WHEN: You have a dynamic address and need a stable pointer chain (base+offsets). ' +
|
|
684
|
+
'Returns: {success, base_address, offsets, ce_pointer_notation, steps, suggestions}. ' +
|
|
685
|
+
"Strategies: 'hybrid' (recommended), 'f5', 'value_scan'. " +
|
|
686
|
+
'NOT FOR: Addresses already static (module+offset format).',
|
|
687
|
+
'find_pointer_path',
|
|
688
|
+
ToolCategory.ANALYSIS,
|
|
689
|
+
[
|
|
690
|
+
this.ADDR_PARAM,
|
|
691
|
+
new ToolParam('user_prompted', 'boolean',
|
|
692
|
+
"REQUIRED: Set true ONLY after you told user 'I will trace pointers, please interact with the value in game NOW (change it, take damage, use feature)'. " +
|
|
693
|
+
'You must prompt user BEFORE calling.', true),
|
|
694
|
+
new ToolParam('max_depth', 'integer', 'Maximum pointer chain depth (1-10)', false, 7),
|
|
695
|
+
new ToolParam('duration_ms', 'integer', 'Monitoring duration per level in ms (default 10000)', false, 10000),
|
|
696
|
+
new ToolParam('max_results', 'integer', 'Maximum candidate pointers to evaluate per level', false, 10),
|
|
697
|
+
new ToolParam('strategy', 'string',
|
|
698
|
+
"Search strategy: 'hybrid' (recommended, F5 + value_scan + region-based scoring), 'f5' (pure F5 method), 'value_scan' (pure pointer search)",
|
|
699
|
+
false, 'hybrid', ['hybrid', 'f5', 'value_scan']),
|
|
700
|
+
]
|
|
701
|
+
));
|
|
702
|
+
|
|
703
|
+
this._register(new Tool(
|
|
704
|
+
'ce_find_references',
|
|
705
|
+
'Find all code locations referencing an address. Returns: {address, count, references: [{address, instruction}]}.',
|
|
706
|
+
'find_references',
|
|
707
|
+
ToolCategory.ANALYSIS,
|
|
708
|
+
[
|
|
709
|
+
this.ADDR_PARAM,
|
|
710
|
+
new ToolParam('limit', 'integer', 'Maximum results to return', false, 50),
|
|
711
|
+
]
|
|
712
|
+
));
|
|
713
|
+
|
|
714
|
+
this._register(new Tool(
|
|
715
|
+
'ce_find_call_references',
|
|
716
|
+
'Find all CALL instructions targeting a function (who calls this function). ' +
|
|
717
|
+
'USE WHEN: Understanding function usage, finding entry points, tracing call hierarchy. ' +
|
|
718
|
+
'Auto-detects module from target address for better performance. ' +
|
|
719
|
+
'Returns: {count, callers: [{address, instruction, symbol}]}.',
|
|
720
|
+
'find_call_references',
|
|
721
|
+
ToolCategory.ANALYSIS,
|
|
722
|
+
[
|
|
723
|
+
this.ADDR_PARAM,
|
|
724
|
+
new ToolParam('module', 'string', "Limit scan to specific module (e.g. 'game.exe'). If omitted, auto-detects from target address."),
|
|
725
|
+
new ToolParam('limit', 'integer', 'Maximum results to return', false, 100),
|
|
726
|
+
]
|
|
727
|
+
));
|
|
728
|
+
|
|
729
|
+
this._register(new Tool(
|
|
730
|
+
'ce_find_function_boundaries',
|
|
731
|
+
'Find function start/end by prologue/epilogue patterns. ' +
|
|
732
|
+
"USE WHEN: You have an address inside a function and need to find where the function starts/ends. " +
|
|
733
|
+
'WORKFLOW: Use this first, then ce_build_cfg for full function analysis. ' +
|
|
734
|
+
'Returns: {function_start, function_end, size}.',
|
|
735
|
+
'find_function_boundaries',
|
|
736
|
+
ToolCategory.ANALYSIS,
|
|
737
|
+
[
|
|
738
|
+
this.ADDR_PARAM,
|
|
739
|
+
new ToolParam('max_search', 'integer', 'Maximum bytes to search for boundaries', false, 4096),
|
|
740
|
+
]
|
|
741
|
+
));
|
|
742
|
+
|
|
743
|
+
this._register(new Tool(
|
|
744
|
+
'ce_checksum_memory',
|
|
745
|
+
'Calculate MD5 hash of a memory region. Useful for detecting code modifications.',
|
|
746
|
+
'checksum_memory',
|
|
747
|
+
ToolCategory.ANALYSIS,
|
|
748
|
+
[
|
|
749
|
+
this.ADDR_PARAM,
|
|
750
|
+
new ToolParam('size', 'integer', 'Number of bytes to hash', false, 256),
|
|
751
|
+
]
|
|
752
|
+
));
|
|
753
|
+
|
|
754
|
+
this._register(new Tool(
|
|
755
|
+
'ce_generate_signature',
|
|
756
|
+
'Generate unique AOB signature for code at an address. ' +
|
|
757
|
+
'USE WHEN: Creating version-independent scripts, documenting code locations for future game updates. ' +
|
|
758
|
+
'WORKFLOW: Generate signature here -> after game update, use ce_aob_scan to relocate. ' +
|
|
759
|
+
'Returns: {address, signature, mask, unique}.',
|
|
760
|
+
'generate_signature',
|
|
761
|
+
ToolCategory.ANALYSIS,
|
|
762
|
+
[this.ADDR_PARAM]
|
|
763
|
+
));
|
|
764
|
+
|
|
765
|
+
this._registerHookTools();
|
|
766
|
+
this._registerEmulationTools();
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
_registerHookTools() {
|
|
770
|
+
this._register(new Tool(
|
|
771
|
+
'ce_hook_function',
|
|
772
|
+
'[NON-BLOCKING] Hook a function to intercept calls and capture arguments automatically. ' +
|
|
773
|
+
'USE WHEN: Monitoring function calls without stopping execution (e.g., logging all damage events). ' +
|
|
774
|
+
'Captures first 4 args: x64 uses RCX/RDX/R8/R9, x32 uses stack. ' +
|
|
775
|
+
'WORKFLOW: ce_hook_function -> let game run -> ce_get_hook_log to retrieve captured data. ' +
|
|
776
|
+
'FOR SINGLE CAPTURE: Use ce_break_and_get_regs instead.',
|
|
777
|
+
'hook_function',
|
|
778
|
+
ToolCategory.ANALYSIS,
|
|
779
|
+
[
|
|
780
|
+
this.ADDR_PARAM,
|
|
781
|
+
new ToolParam('name', 'string', 'Hook identifier name', true),
|
|
782
|
+
new ToolParam('capture_args', 'integer', 'Number of arguments to capture (0-4, default 4)', false, 4),
|
|
783
|
+
new ToolParam('capture_return', 'boolean', 'Reserved for future use (not implemented)', false, true),
|
|
784
|
+
new ToolParam('max_records', 'integer', 'Fixed at 64 (circular buffer)', false, 64),
|
|
785
|
+
new ToolParam('calling_convention', 'string', 'Calling convention hint', false, 'auto', ['auto', 'fastcall', 'stdcall', 'cdecl']),
|
|
786
|
+
]
|
|
787
|
+
));
|
|
788
|
+
|
|
789
|
+
this._register(new Tool(
|
|
790
|
+
'ce_unhook_function',
|
|
791
|
+
'Remove a function hook by name and restore original code. ' +
|
|
792
|
+
'Call this when finished analyzing to clean up resources and free memory.',
|
|
793
|
+
'unhook_function',
|
|
794
|
+
ToolCategory.ANALYSIS,
|
|
795
|
+
[new ToolParam('name', 'string', 'Hook identifier name', true)]
|
|
796
|
+
));
|
|
797
|
+
|
|
798
|
+
this._register(new Tool(
|
|
799
|
+
'ce_list_hooks',
|
|
800
|
+
'List all active function hooks with their status and call counts. ' +
|
|
801
|
+
'Use to check which hooks are currently installed before adding new ones.',
|
|
802
|
+
'list_hooks',
|
|
803
|
+
ToolCategory.ANALYSIS
|
|
804
|
+
));
|
|
805
|
+
|
|
806
|
+
this._register(new Tool(
|
|
807
|
+
'ce_get_hook_log',
|
|
808
|
+
'Get captured function call arguments. Returns entries with args[1-4] containing: ' +
|
|
809
|
+
'x64: RCX(this/arg1), RDX(arg2), R8(arg3), R9(arg4); ' +
|
|
810
|
+
'x32: stack params [esp+10/14/18/1C]. ' +
|
|
811
|
+
'Values are hex addresses/integers. total_calls shows how many times function was called.',
|
|
812
|
+
'get_hook_log',
|
|
813
|
+
ToolCategory.ANALYSIS,
|
|
814
|
+
[
|
|
815
|
+
new ToolParam('name', 'string', 'Hook identifier name', true),
|
|
816
|
+
new ToolParam('clear', 'boolean', 'Clear log after reading', false, false),
|
|
817
|
+
new ToolParam('limit', 'integer', 'Max records to return (1-64, default 50)', false, 50),
|
|
818
|
+
]
|
|
819
|
+
));
|
|
820
|
+
|
|
821
|
+
this._register(new Tool(
|
|
822
|
+
'ce_clear_hook_log',
|
|
823
|
+
'Clear the call log and reset total_calls counter for a hook. ' +
|
|
824
|
+
'Use when: 1) Starting fresh measurement, 2) Log buffer full (64 entries max), ' +
|
|
825
|
+
'3) Want to count calls from a specific point. Omit name to clear all hooks.',
|
|
826
|
+
'clear_hook_log',
|
|
827
|
+
ToolCategory.ANALYSIS,
|
|
828
|
+
[new ToolParam('name', 'string', 'Hook name (omit to clear all)')]
|
|
829
|
+
));
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
_registerEmulationTools() {
|
|
833
|
+
this._register(new Tool(
|
|
834
|
+
'ce_call_function',
|
|
835
|
+
'[DANGEROUS] Call a function in target process - EXECUTES REAL CODE! ' +
|
|
836
|
+
'USE WHEN: Testing function behavior, calling game functions programmatically. ' +
|
|
837
|
+
'Uses x64 fastcall (RCX, RDX, R8, R9). May crash game if used incorrectly. ' +
|
|
838
|
+
'Returns: {success, return_value, return_type}.',
|
|
839
|
+
'call_function',
|
|
840
|
+
ToolCategory.ANALYSIS,
|
|
841
|
+
[
|
|
842
|
+
this.ADDR_PARAM,
|
|
843
|
+
new ToolParam('args', 'array', 'Function arguments (up to 4 for fastcall). Can be integers or address expressions.', false, []),
|
|
844
|
+
new ToolParam('timeout', 'integer', 'Timeout in milliseconds', false, 5000),
|
|
845
|
+
new ToolParam('return_type', 'string', 'How to interpret return value', false, 'qword',
|
|
846
|
+
['byte', 'word', 'dword', 'qword', 'float', 'double', 'pointer']),
|
|
847
|
+
]
|
|
848
|
+
));
|
|
849
|
+
|
|
850
|
+
this._register(new Tool(
|
|
851
|
+
'ce_symbolic_trace',
|
|
852
|
+
'[NO EXECUTION] Lightweight symbolic execution - interprets instructions WITHOUT running them. ' +
|
|
853
|
+
'USE WHEN: Understanding code logic safely, analyzing potentially dangerous code, static algorithm analysis. ' +
|
|
854
|
+
"Produces readable expressions like 'rax = ((arg0 + 5) << 2)'. " +
|
|
855
|
+
'Supports: mov, movzx, movsxd, lea, add, sub, xor, and, or, shl, shr, imul, cmp, test, cmovxx. ' +
|
|
856
|
+
'FOR ACTUAL EXECUTION: Use ce_break_and_trace instead. ' +
|
|
857
|
+
'Returns: {trace: [{address, instruction, effects}], final_state: {reg: expression}, stop_reason}.',
|
|
858
|
+
'symbolic_trace',
|
|
859
|
+
ToolCategory.ANALYSIS,
|
|
860
|
+
[
|
|
861
|
+
this.ADDR_PARAM,
|
|
862
|
+
new ToolParam('count', 'integer', 'Number of instructions to trace', false, 30),
|
|
863
|
+
new ToolParam('initial_state', 'object',
|
|
864
|
+
'Initial register values as symbols or concrete values. ' +
|
|
865
|
+
'Example: {"rcx": "this_ptr", "rdx": "arg1", "r8": 0}'),
|
|
866
|
+
new ToolParam('stop_on_call', 'boolean', 'Stop tracing when encountering a call instruction', false, true),
|
|
867
|
+
new ToolParam('stop_on_ret', 'boolean', 'Stop tracing when encountering a ret instruction', false, true),
|
|
868
|
+
new ToolParam('simplify', 'boolean', 'Simplify expressions (e.g., x^x=0, x+0=x)', false, true),
|
|
869
|
+
]
|
|
870
|
+
));
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
getTool(name) {
|
|
874
|
+
return this.tools.get(name) || null;
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
getAllSchemas() {
|
|
878
|
+
return Array.from(this.tools.values()).map(tool => tool.toMCPSchema());
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
getLuaCommand(toolName) {
|
|
882
|
+
const tool = this.tools.get(toolName);
|
|
883
|
+
return tool ? tool.luaCommand : null;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
module.exports = { ToolRegistry, ToolCategory, Tool, ToolParam };
|