iflow-mcp_renqabs-x32dbg-mcp 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,18 @@
1
+ Metadata-Version: 2.4
2
+ Name: iflow-mcp_renqabs-x32dbg-mcp
3
+ Version: 0.1.0
4
+ Summary: Your project description
5
+ License: MIT
6
+ Requires-Python: >=3.11
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: x32dbg>=1.1.0
9
+ Requires-Dist: mcp>=1.6.0
10
+
11
+ 基于python x32dbg以及x64dbg [Lyscript](http://lyscript.lyshark.com)插件LyScript.dp32实现的简易MCP server
12
+
13
+ 参数说明:
14
+ 可根据实际情况修改脚本中的网络连接参数,跟x64dbg LyScript插件配置LyScript.ini保持一致
15
+
16
+ ```DEBUGGER_IP``` : 默认值```127.0.0.1```
17
+
18
+ ```DEBUGGER_PORT``` : 默认值```6589```
@@ -0,0 +1,8 @@
1
+ 基于python x32dbg以及x64dbg [Lyscript](http://lyscript.lyshark.com)插件LyScript.dp32实现的简易MCP server
2
+
3
+ 参数说明:
4
+ 可根据实际情况修改脚本中的网络连接参数,跟x64dbg LyScript插件配置LyScript.ini保持一致
5
+
6
+ ```DEBUGGER_IP``` : 默认值```127.0.0.1```
7
+
8
+ ```DEBUGGER_PORT``` : 默认值```6589```
@@ -0,0 +1,18 @@
1
+ Metadata-Version: 2.4
2
+ Name: iflow-mcp_renqabs-x32dbg-mcp
3
+ Version: 0.1.0
4
+ Summary: Your project description
5
+ License: MIT
6
+ Requires-Python: >=3.11
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: x32dbg>=1.1.0
9
+ Requires-Dist: mcp>=1.6.0
10
+
11
+ 基于python x32dbg以及x64dbg [Lyscript](http://lyscript.lyshark.com)插件LyScript.dp32实现的简易MCP server
12
+
13
+ 参数说明:
14
+ 可根据实际情况修改脚本中的网络连接参数,跟x64dbg LyScript插件配置LyScript.ini保持一致
15
+
16
+ ```DEBUGGER_IP``` : 默认值```127.0.0.1```
17
+
18
+ ```DEBUGGER_PORT``` : 默认值```6589```
@@ -0,0 +1,9 @@
1
+ README.md
2
+ pyproject.toml
3
+ x32dbg_mcp_server.py
4
+ iflow_mcp_renqabs_x32dbg_mcp.egg-info/PKG-INFO
5
+ iflow_mcp_renqabs_x32dbg_mcp.egg-info/SOURCES.txt
6
+ iflow_mcp_renqabs_x32dbg_mcp.egg-info/dependency_links.txt
7
+ iflow_mcp_renqabs_x32dbg_mcp.egg-info/entry_points.txt
8
+ iflow_mcp_renqabs_x32dbg_mcp.egg-info/requires.txt
9
+ iflow_mcp_renqabs_x32dbg_mcp.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ x32dbg-mcp = x32dbg_mcp_server:main
@@ -0,0 +1,14 @@
1
+ [project]
2
+ name = "iflow-mcp_renqabs-x32dbg-mcp"
3
+ version = "0.1.0"
4
+ description = "Your project description"
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ license = {text = "MIT"}
8
+ dependencies = [
9
+ "x32dbg>=1.1.0",
10
+ "mcp>=1.6.0",
11
+ ]
12
+
13
+ [project.scripts]
14
+ x32dbg-mcp = "x32dbg_mcp_server:main"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,545 @@
1
+ from typing import TypedDict, Annotated, Optional
2
+ from pydantic import Field
3
+ from mcp.server.fastmcp import FastMCP
4
+ from x32dbg import Debugger
5
+
6
+ # Module related classes
7
+ class ModuleInfo:
8
+ def __init__(self, debugger):
9
+ self.debugger = debugger
10
+
11
+ # Accept integer type parameters passed in
12
+ def get_module_info(self, decimal_address, info_type):
13
+ try:
14
+ ref = self.debugger.script_runcmd_ex("mod.{}({})".
15
+ format(info_type, hex(decimal_address)))
16
+ return ref if ref is not None else False
17
+ except Exception:
18
+ return False
19
+
20
+ def base(self, decimal_address):
21
+ return self.get_module_info(decimal_address, "base")
22
+
23
+ def party(self, decimal_address):
24
+ return self.get_module_info(decimal_address, "party")
25
+
26
+ def size(self, decimal_address):
27
+ return self.get_module_info(decimal_address, "size")
28
+
29
+ def hash(self, decimal_address):
30
+ return self.get_module_info(decimal_address, "hash")
31
+
32
+ def entry(self, decimal_address):
33
+ return self.get_module_info(decimal_address, "entry")
34
+
35
+ def system(self, decimal_address):
36
+ return self.get_module_info(decimal_address, "system")
37
+
38
+ def user(self, decimal_address):
39
+ return self.get_module_info(decimal_address, "user")
40
+
41
+ def main(self):
42
+ return self.get_module_info(0, "main")
43
+
44
+ def rva(self, decimal_address):
45
+ return self.get_module_info(decimal_address, "rva")
46
+
47
+ def offset(self, decimal_address):
48
+ return self.get_module_info(decimal_address, "offset")
49
+
50
+ def isexport(self, decimal_address):
51
+ return self.get_module_info(decimal_address, "isexport")
52
+
53
+
54
+ # Memory related classes
55
+ class MemoryInfo:
56
+ def __init__(self, debugger):
57
+ self.debugger = debugger
58
+
59
+ def get_memory_info(self, decimal_address, info_type):
60
+ try:
61
+ ref = self.debugger.script_runcmd_ex("mem.{}({})".
62
+ format(info_type, hex(decimal_address)))
63
+ return ref if ref is not None else False
64
+ except Exception:
65
+ return False
66
+
67
+ def valid(self, decimal_address):
68
+ return self.get_memory_info(decimal_address, "valid")
69
+
70
+ def base(self, decimal_address):
71
+ return self.get_memory_info(decimal_address, "base")
72
+
73
+ def size(self, decimal_address):
74
+ return self.get_memory_info(decimal_address, "size")
75
+
76
+ def iscode(self, decimal_address):
77
+ return self.get_memory_info(decimal_address, "iscode")
78
+
79
+ def decodepointer(self, decimal_address):
80
+ return self.get_memory_info(decimal_address, "decodepointer")
81
+
82
+ def bswap(self, decimal_address):
83
+ return self.debugger.script_runcmd_ex("bswap({})".format(decimal_address))
84
+
85
+ def readbyte(self, decimal_address):
86
+ return self.debugger.script_runcmd_ex("ReadByte({})".format(decimal_address))
87
+
88
+ def readword(self, decimal_address):
89
+ return self.debugger.script_runcmd_ex("ReadWord({})".format(decimal_address))
90
+
91
+ def readdword(self, decimal_address):
92
+ return self.debugger.script_runcmd_ex("ReadDword({})".format(decimal_address))
93
+
94
+ def readqword(self, decimal_address):
95
+ return self.debugger.script_runcmd_ex("ReadQword({})".format(decimal_address))
96
+
97
+ def readptr(self, decimal_address):
98
+ return self.debugger.script_runcmd_ex("ReadPtr({})".format(decimal_address))
99
+
100
+ def firstchance(self):
101
+ return self.debugger.script_runcmd_ex("ex.firstchance()")
102
+
103
+ def addr(self):
104
+ return self.debugger.script_runcmd_ex("ex.addr()")
105
+
106
+ def code(self):
107
+ return self.debugger.script_runcmd_ex("ex.code()")
108
+
109
+ def flags(self):
110
+ return self.debugger.script_runcmd_ex("ex.flags()")
111
+
112
+ def infocount(self):
113
+ return self.debugger.script_runcmd_ex("ex.infocount()")
114
+
115
+ def info(self, decimal_address):
116
+ return self.debugger.script_runcmd_ex("ex.info({})".format(decimal_address))
117
+
118
+
119
+ # Global debugger and module info instances
120
+ dbg: Optional[Debugger] = None
121
+ module: Optional[ModuleInfo] = None
122
+ mem: Optional[MemoryInfo] = None
123
+ mcp = FastMCP("x32dbg-mcp", log_level="ERROR")
124
+ DEBUGGER_IP = "127.0.0.1"
125
+ DEBUGGER_PORT = 6589
126
+
127
+ @mcp.tool()
128
+ def connect_debugger():
129
+ """Connect to the x32dbg debugger"""
130
+ global dbg, module,mem
131
+ dbg = Debugger(address=DEBUGGER_IP, port=DEBUGGER_PORT)
132
+ if dbg.connect():
133
+ module = ModuleInfo(dbg)
134
+ mem = MemoryInfo(dbg)
135
+ return {"status": "connected", "success": True}
136
+ return {"status": "failed to connect", "success": False}
137
+
138
+
139
+ @mcp.tool()
140
+ def disconnect_debugger()->bool:
141
+ """Disconnect from the x32dbg debugger"""
142
+ global dbg
143
+ if dbg:
144
+ dbg.close_connect()
145
+ return True
146
+ return False
147
+
148
+ @mcp.tool()
149
+ def debug_run()->bool:
150
+ """Continue debugging"""
151
+ global dbg
152
+ if dbg:
153
+ dbg.debug_run()
154
+ return True
155
+ return False
156
+
157
+ @mcp.tool()
158
+ def debug_pause()->bool:
159
+ """Pause debugging"""
160
+ global dbg
161
+ if dbg:
162
+ dbg.debug_pause()
163
+ return True
164
+ return False
165
+
166
+ @mcp.tool()
167
+ def debug_stop()->bool:
168
+ """Stop debugging"""
169
+ global dbg
170
+ if dbg:
171
+ dbg.debug_stop()
172
+ return True
173
+ return False
174
+
175
+ @mcp.tool()
176
+ def debug_step_into()->bool:
177
+ """Step into"""
178
+ global dbg
179
+ if dbg:
180
+ return dbg.debug_stepin()
181
+ return False
182
+
183
+ @mcp.tool()
184
+ def debug_step_over()->bool:
185
+ """Step over"""
186
+ global dbg
187
+ if dbg:
188
+ return dbg.debug_stepover()
189
+ return False
190
+
191
+ @mcp.tool()
192
+ def debug_step_out()->bool:
193
+ """Step out"""
194
+ global dbg
195
+ if dbg:
196
+ return dbg.debug_stepout()
197
+ return False
198
+
199
+ @mcp.tool()
200
+ def debug_setcount(action:Annotated[str,Field(description="action, choice of [stepout, stepin, stepover, run, pause, stop, wait]")],
201
+ count: Annotated[int,Field(description="count of step")])->bool:
202
+ """Set the debug action and count"""
203
+ global dbg
204
+ if dbg:
205
+ return dbg.debug_setcount(action,count)
206
+ return False
207
+
208
+ class ModuleMeta(TypedDict):
209
+ Base: int
210
+ Entry: str
211
+ Name: str
212
+ Path: str
213
+ size: int
214
+
215
+ @mcp.tool()
216
+ def get_module():
217
+ """Get the Module Info"""
218
+ global dbg
219
+ if dbg:
220
+ return dbg.get_module()
221
+ return []
222
+
223
+
224
+ @mcp.tool()
225
+ def get_register(register_name: Annotated[str,Field(description="The name of the register (eip, eax, ebx, ecx, edx, esi, edi, esp, ebp)")]):
226
+ """
227
+ Get the value of a CPU register.
228
+ Returns:Hexadecimal string representation of the register value
229
+ """
230
+ global dbg
231
+ if not dbg:
232
+ return "debugger not connected"
233
+ if register_name.lower() not in ['eip', 'eax', 'ebx', 'ecx', 'edx', 'esi', 'edi', 'esp', 'ebp']:
234
+ return "invalid register name"
235
+ return str(dbg.get_register(register_name))
236
+
237
+
238
+ @mcp.tool()
239
+ def set_register(register_name: Annotated[str,Field(description="The name of the register (eip, eax, ebx, ecx, edx, esi, edi, esp, ebp)")],
240
+ value: Annotated[int,Field(description="value")]) -> bool:
241
+ """
242
+ Get the value of a CPU register.
243
+ Returns:True if the register was set successfully, False otherwise
244
+ """
245
+ global dbg
246
+ if not dbg:
247
+ return False
248
+ if register_name.lower() not in ['eip', 'eax', 'ebx', 'ecx', 'edx', 'esi', 'edi', 'esp', 'ebp']:
249
+ return False
250
+ return dbg.set_register(register_name,value)
251
+
252
+ @mcp.tool()
253
+ def get_flag(flag_name: Annotated[str,Field(description="The name of the flag (zf, cf, sf, pf, af, df, of, if, tf)")]) -> bool:
254
+ """
255
+ Get the value of a CPU flag.
256
+ Returns:Boolean value for the flag
257
+ """
258
+ global dbg
259
+ if not dbg:
260
+ return False
261
+
262
+ if flag_name.lower() not in ['zf', 'cf', 'sf', 'pf', 'af', 'df', 'of', 'if', 'tf']:
263
+ return False
264
+ return dbg.get_flag_register(flag_name)
265
+
266
+ @mcp.tool()
267
+ def set_flag(flag_name: Annotated[str,Field(description="The name of the flag (zf, cf, sf, pf, af, df, of, if, tf)")],
268
+ value: Annotated[bool,Field(description="value")]) -> bool:
269
+ """
270
+ Get the value of a CPU flag.
271
+ Returns:True if the flag was set successfully, False otherwise
272
+ """
273
+ global dbg
274
+ if not dbg:
275
+ return False
276
+
277
+ if flag_name.lower() not in ['zf', 'cf', 'sf', 'pf', 'af', 'df', 'of', 'if', 'tf']:
278
+ return False
279
+ return dbg.set_flag_register(flag_name, value)
280
+
281
+
282
+ class ModuleData(TypedDict):
283
+ party: bool
284
+ base: str
285
+ size: str
286
+ hash: str
287
+ entry: str
288
+ system: bool
289
+ user: bool
290
+ rva: str
291
+ foa: str
292
+
293
+
294
+ def hex_str_to_int(hex_str: str):
295
+ """Convert a hexadecimal string to an integer."""
296
+ try:
297
+ return int(hex_str, 16)
298
+ except ValueError as e:
299
+ return None
300
+
301
+ @mcp.tool()
302
+ def get_module_info(address:Annotated[str,Field(description="hexadecimal address")]):
303
+ """Obtain the module information where the destination address is located"""
304
+ int_address = hex_str_to_int(address)
305
+ if int_address is None:
306
+ return {"error": "invalid hex address"}
307
+ global module, dbg
308
+ if not module or not dbg:
309
+ return {"error": "debugger not connected"}
310
+
311
+ result = {
312
+ "party": module.party(int_address),
313
+ "base": hex(module.base(int_address)),
314
+ "size": hex(module.size(int_address)),
315
+ "hash": hex(module.hash(int_address)),
316
+ "entry": hex(module.entry(int_address)),
317
+ "system": module.system(int_address),
318
+ "user": module.user(int_address),
319
+ "rva": hex(module.rva(int_address)),
320
+ "foa": hex(module.offset(int_address)),
321
+ }
322
+ return result
323
+
324
+
325
+ def bypass_check_of_isdebug_present()->bool:
326
+ peb = dbg.get_peb_address(dbg.get_process_id())
327
+ return dbg.set_memory_byte(peb + 2, 0)
328
+
329
+ def long_to_ulong(inter,is_64 = False):
330
+ if is_64 == False:
331
+ return inter & ((1 << 32) - 1)
332
+ else:
333
+ return inter & ((1 << 64) - 1)
334
+
335
+ def ulong_to_long(inter,is_64 = False):
336
+ if is_64 == False:
337
+ return (inter & ((1 << 31) - 1)) - (inter & (1 << 31))
338
+ else:
339
+ return (inter & ((1 << 63) - 1)) - (inter & (1 << 63))
340
+
341
+
342
+ class CallStackInfo(TypedDict):
343
+ stack_id: str
344
+ ret_address: str
345
+ module_base: str
346
+ module_name: str
347
+ module_path: str
348
+
349
+ @mcp.tool()
350
+ def get_call_stack(depth:Annotated[int,Field(description="depth")]=10):
351
+ """Get the Call Stack"""
352
+ global dbg
353
+ global mem
354
+ call_stack = []
355
+ module_list = []
356
+ if dbg:
357
+ module_list = dbg.get_module()
358
+ for index in range(0,depth):
359
+ stack_address = dbg.peek_stack(index)
360
+ if stack_address <= 0:
361
+ mod_base = 0
362
+ else:
363
+ mod_base = dbg.get_base_from_address(long_to_ulong(stack_address))
364
+ if mod_base > 0 and len(module_list) > 0:
365
+ for x in module_list:
366
+ if mod_base == x.get("Base") and mem.iscode(stack_address):
367
+ call_stack.append({
368
+ "stack_id": str(index),
369
+ "ret_address": hex(stack_address),
370
+ "module_base": hex(mod_base),
371
+ "module_name": x.get('Name'),
372
+ "module_path": x.get('Path')
373
+ })
374
+ return call_stack
375
+
376
+ @mcp.tool()
377
+ def memory_readbyte(address: Annotated[str,Field(description="hexadecimal address")]):
378
+ """Read a byte from memory"""
379
+ global mem
380
+ if not mem:
381
+ return "memory module not initialized"
382
+ int_address = hex_str_to_int(address)
383
+ if int_address is None:
384
+ return "invalid hex address"
385
+ return mem.readbyte(address)
386
+
387
+ @mcp.tool()
388
+ def memory_readword(address: Annotated[str,Field(description="hexadecimal address")]):
389
+ """Read a word from memory"""
390
+ global mem
391
+ if not mem:
392
+ return "memory module not initialized"
393
+ int_address = hex_str_to_int(address)
394
+ if int_address is None:
395
+ return "invalid hex address"
396
+ return mem.readword(address)
397
+
398
+ @mcp.tool()
399
+ def memory_readdword(address: Annotated[str,Field(description="hexadecimal address")]):
400
+ """Read a dword from memory"""
401
+ global mem
402
+ if not mem:
403
+ return "memory module not initialized"
404
+ int_address = hex_str_to_int(address)
405
+ if int_address is None:
406
+ return "invalid hex address"
407
+ return mem.readdword(address)
408
+
409
+
410
+ class BreakpointInfo(TypedDict):
411
+ bpx_type: int
412
+ address: str
413
+ enabled: int
414
+ single_shoot: int
415
+ active: int
416
+ name: str
417
+ mod: str
418
+ slot: int
419
+ hit_count: int
420
+ fast_resume: int
421
+ silent: int
422
+ break_condition: str
423
+ log_text: str
424
+ log_condition: str
425
+ command_text: str
426
+ command_condition: str
427
+
428
+ @mcp.tool()
429
+ def get_breakpoints():
430
+ """Get All Breakpoints"""
431
+ global dbg
432
+ if not dbg:
433
+ return []
434
+ bp_list = dbg.get_breakpoint()
435
+ for bp in bp_list:
436
+ bp['Address'] = hex(bp['Address'])
437
+ return bp_list
438
+
439
+ @mcp.tool()
440
+ def set_breakpoint(address: Annotated[str,Field(description="hexadecimal address")]):
441
+ """Set a breakpoint"""
442
+ global dbg
443
+ if not dbg:
444
+ return "debugger not connected"
445
+ int_address = hex_str_to_int(address)
446
+ if int_address is None:
447
+ return "invalid hex address"
448
+ return dbg.set_breakpoint(int_address)
449
+
450
+
451
+ @mcp.tool()
452
+ def set_breakpoint_by_name(name: Annotated[str,Field(description="function name")]) -> bool:
453
+ """Set a breakpoint by the function name"""
454
+ global dbg
455
+ if not dbg:
456
+ return False
457
+ if "." not in name:
458
+ return False
459
+ parts = name.split(".")
460
+ if len(parts) != 2:
461
+ return False
462
+ dll_name, func_name = parts
463
+ if dll_name and func_name:
464
+ if len(dll_name) > 0 and len(func_name) > 0:
465
+ bp_addr = dbg.get_module_proc_addr(dll_name, func_name)
466
+ return dbg.set_breakpoint(bp_addr)
467
+ return False
468
+
469
+ @mcp.tool()
470
+ def delete_breakpoint(address: Annotated[str,Field(description="hexadecimal address")]) -> bool:
471
+ """Delete a breakpoint by address"""
472
+ global dbg
473
+ if not dbg:
474
+ return False
475
+ int_address = hex_str_to_int(address)
476
+ if int_address is None:
477
+ return False
478
+ return dbg.delete_breakpoint(int_address)
479
+
480
+ @mcp.tool()
481
+ def set_hardware_breakpoint(address:Annotated[str,Field(description="hexadecimal address")],
482
+ type:Annotated[int,Field(description="hardware breakpoint type, HardwareAccess=0, HardwareWrite=1, HardwareExecute=2")] = 0) -> bool:
483
+ """
484
+ Set a hardware breakpoint
485
+ Returns:True if the hardware breakpoint was set successfully, False otherwise.
486
+ """
487
+ global dbg
488
+ if not dbg:
489
+ return False
490
+ int_address = hex_str_to_int(address)
491
+ if int_address is None:
492
+ return False
493
+ return dbg.set_hardware_breakpoint(int_address,type)
494
+
495
+ @mcp.tool()
496
+ def set_hardware_breakpoint_by_name(
497
+ name:Annotated[str,Field(description="function name")],
498
+ type:Annotated[int,Field(description="hardware breakpoint type, HardwareAccess=0, HardwareWrite=1, HardwareExecute=2")] = 0):
499
+ """
500
+ Set a hardware breakpoint by function name
501
+ Returns:True if the hardware breakpoint was set successfully, False otherwise.
502
+ """
503
+ global dbg
504
+ if not dbg:
505
+ return False
506
+ if "." not in name:
507
+ return False
508
+ parts = name.split(".")
509
+ if len(parts) != 2:
510
+ return False
511
+ dll_name, func_name = parts
512
+ if dll_name and func_name:
513
+ if len(dll_name) > 0 and len(func_name) > 0:
514
+ bp_addr = dbg.get_module_proc_addr(dll_name, func_name)
515
+ return dbg.set_hardware_breakpoint(bp_addr, type)
516
+ return False
517
+
518
+ @mcp.tool()
519
+ def delete_hardware_breakpoint(address:Annotated[str,Field(description="hexadecimal address")]) -> bool:
520
+ """Delete a hardware breakpoint by address"""
521
+ global dbg
522
+ if not dbg:
523
+ return False
524
+ int_address = hex_str_to_int(address)
525
+ if int_address is None:
526
+ return False
527
+ return dbg.delete_hardware_breakpoint(int_address)
528
+
529
+ def main():
530
+ print("Starting the x32dbg MCP server!")
531
+ mcp.run(transport="stdio")
532
+
533
+ def test():
534
+ if connect_debugger():
535
+ eip = get_register('eip')
536
+ mod_info = get_module_info(eip)
537
+ print(mod_info)
538
+ call_stacks = get_call_stack(30)
539
+ for call_stack in call_stacks:
540
+ print(call_stack)
541
+
542
+
543
+
544
+ if __name__ == "__main__":
545
+ main()