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.
- iflow_mcp_renqabs_x32dbg_mcp-0.1.0/PKG-INFO +18 -0
- iflow_mcp_renqabs_x32dbg_mcp-0.1.0/README.md +8 -0
- iflow_mcp_renqabs_x32dbg_mcp-0.1.0/iflow_mcp_renqabs_x32dbg_mcp.egg-info/PKG-INFO +18 -0
- iflow_mcp_renqabs_x32dbg_mcp-0.1.0/iflow_mcp_renqabs_x32dbg_mcp.egg-info/SOURCES.txt +9 -0
- iflow_mcp_renqabs_x32dbg_mcp-0.1.0/iflow_mcp_renqabs_x32dbg_mcp.egg-info/dependency_links.txt +1 -0
- iflow_mcp_renqabs_x32dbg_mcp-0.1.0/iflow_mcp_renqabs_x32dbg_mcp.egg-info/entry_points.txt +2 -0
- iflow_mcp_renqabs_x32dbg_mcp-0.1.0/iflow_mcp_renqabs_x32dbg_mcp.egg-info/requires.txt +2 -0
- iflow_mcp_renqabs_x32dbg_mcp-0.1.0/iflow_mcp_renqabs_x32dbg_mcp.egg-info/top_level.txt +1 -0
- iflow_mcp_renqabs_x32dbg_mcp-0.1.0/pyproject.toml +14 -0
- iflow_mcp_renqabs_x32dbg_mcp-0.1.0/setup.cfg +4 -0
- iflow_mcp_renqabs_x32dbg_mcp-0.1.0/x32dbg_mcp_server.py +545 -0
|
@@ -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,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 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
x32dbg_mcp_server
|
|
@@ -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,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()
|