ghidraxdbg 12.0__py3-none-any.whl
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.
- ghidraxdbg/__init__.py +17 -0
- ghidraxdbg/arch.py +236 -0
- ghidraxdbg/commands.py +1349 -0
- ghidraxdbg/hooks.py +419 -0
- ghidraxdbg/methods.py +680 -0
- ghidraxdbg/py.typed +0 -0
- ghidraxdbg/schema.xml +317 -0
- ghidraxdbg/util.py +297 -0
- ghidraxdbg-12.0.dist-info/METADATA +20 -0
- ghidraxdbg-12.0.dist-info/RECORD +13 -0
- ghidraxdbg-12.0.dist-info/WHEEL +5 -0
- ghidraxdbg-12.0.dist-info/licenses/LICENSE +11 -0
- ghidraxdbg-12.0.dist-info/top_level.txt +1 -0
ghidraxdbg/methods.py
ADDED
|
@@ -0,0 +1,680 @@
|
|
|
1
|
+
## ###
|
|
2
|
+
# IP: GHIDRA
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
##
|
|
16
|
+
from concurrent.futures import Future, ThreadPoolExecutor
|
|
17
|
+
from contextlib import redirect_stdout
|
|
18
|
+
from io import StringIO
|
|
19
|
+
import re
|
|
20
|
+
import sys
|
|
21
|
+
from typing import Annotated, Any, Dict, Optional
|
|
22
|
+
|
|
23
|
+
from ghidratrace import sch
|
|
24
|
+
from ghidratrace.client import (MethodRegistry, ParamDesc, Address,
|
|
25
|
+
AddressRange, Schedule, TraceObject)
|
|
26
|
+
|
|
27
|
+
from x64dbg_automate.events import *
|
|
28
|
+
from x64dbg_automate.models import BreakpointType, HardwareBreakpointType, MemoryBreakpointType
|
|
29
|
+
from . import util, commands
|
|
30
|
+
|
|
31
|
+
REGISTRY = MethodRegistry(ThreadPoolExecutor(
|
|
32
|
+
max_workers=1, thread_name_prefix='MethodRegistry'))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def extre(base: re.Pattern, ext: str) -> re.Pattern:
|
|
36
|
+
return re.compile(base.pattern + ext)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
WATCHPOINT_PATTERN = re.compile('Watchpoints\\[(?P<watchnum>\\d*)\\]')
|
|
40
|
+
BREAKPOINT_PATTERN = re.compile('Breakpoints\\[(?P<breaknum>\\d*)\\]')
|
|
41
|
+
BREAK_LOC_PATTERN = extre(BREAKPOINT_PATTERN, '\\[(?P<locnum>\\d*)\\]')
|
|
42
|
+
SESSIONS_PATTERN = re.compile('Sessions')
|
|
43
|
+
SESSION_PATTERN = extre(SESSIONS_PATTERN, '\\[(?P<snum>\\d*)\\]')
|
|
44
|
+
AVAILABLE_PATTERN = extre(SESSION_PATTERN, '\\.Available\\[(?P<pid>\\d*)\\]')
|
|
45
|
+
PROCESSES_PATTERN = extre(SESSION_PATTERN, '\\.Processes')
|
|
46
|
+
PROCESS_PATTERN = extre(PROCESSES_PATTERN, '\\[(?P<procnum>\\d*)\\]')
|
|
47
|
+
PROC_DEBUG_PATTERN = extre(PROCESS_PATTERN, '.Debug')
|
|
48
|
+
PROC_SBREAKS_PATTERN = extre(PROC_DEBUG_PATTERN, '\\.Software Breakpoints')
|
|
49
|
+
PROC_HBREAKS_PATTERN = extre(PROC_DEBUG_PATTERN, '\\.Hardware Breakpoints')
|
|
50
|
+
PROC_MBREAKS_PATTERN = extre(PROC_DEBUG_PATTERN, '\\.Memory Breakpoints')
|
|
51
|
+
PROC_SBREAKBPT_PATTERN = extre(PROC_SBREAKS_PATTERN, '\\[(?P<breaknum>\\d*)\\]')
|
|
52
|
+
PROC_HBREAKBPT_PATTERN = extre(PROC_HBREAKS_PATTERN, '\\[(?P<breaknum>\\d*)\\]')
|
|
53
|
+
PROC_MBREAKBPT_PATTERN = extre(PROC_MBREAKS_PATTERN, '\\[(?P<breaknum>\\d*)\\]')
|
|
54
|
+
ENV_PATTERN = extre(PROCESS_PATTERN, '\\.Environment')
|
|
55
|
+
THREADS_PATTERN = extre(PROCESS_PATTERN, '\\.Threads')
|
|
56
|
+
THREAD_PATTERN = extre(THREADS_PATTERN, '\\[(?P<tnum>\\d*)\\]')
|
|
57
|
+
STACK_PATTERN = extre(THREAD_PATTERN, '\\.Stack.Frames')
|
|
58
|
+
#FRAME_PATTERN = extre(STACK_PATTERN, '\\[(?P<level>\\d*)\\]')
|
|
59
|
+
REGS_PATTERN0 = extre(THREAD_PATTERN, '\\.Registers')
|
|
60
|
+
#REGS_PATTERN = extre(FRAME_PATTERN, '\\.Registers')
|
|
61
|
+
MEMORY_PATTERN = extre(PROCESS_PATTERN, '\\.Memory')
|
|
62
|
+
MODULES_PATTERN = extre(PROCESS_PATTERN, '\\.Modules')
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def find_availpid_by_pattern(pattern: re.Pattern, object: TraceObject,
|
|
66
|
+
err_msg: str) -> int:
|
|
67
|
+
mat = pattern.fullmatch(object.path)
|
|
68
|
+
if mat is None:
|
|
69
|
+
raise TypeError(f"{object} is not {err_msg}")
|
|
70
|
+
pid = int(mat['pid'])
|
|
71
|
+
return pid
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def find_availpid_by_obj(object: TraceObject) -> int:
|
|
75
|
+
return find_availpid_by_pattern(AVAILABLE_PATTERN, object, "an Attachable")
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def find_proc_by_num(id: int) -> int:
|
|
79
|
+
return util.selected_process()
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def find_proc_by_pattern(object: TraceObject, pattern: re.Pattern,
|
|
83
|
+
err_msg: str) -> int:
|
|
84
|
+
mat = pattern.fullmatch(object.path)
|
|
85
|
+
if mat is None:
|
|
86
|
+
raise TypeError(f"{object} is not {err_msg}")
|
|
87
|
+
procnum = int(mat['procnum'])
|
|
88
|
+
return find_proc_by_num(procnum)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def find_proc_by_obj(object: TraceObject) -> int:
|
|
92
|
+
return find_proc_by_pattern(object, PROCESS_PATTERN, "an Process")
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def find_proc_by_env_obj(object: TraceObject) -> int:
|
|
96
|
+
return find_proc_by_pattern(object, ENV_PATTERN, "an Environment")
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def find_proc_by_threads_obj(object: TraceObject) -> int:
|
|
100
|
+
return find_proc_by_pattern(object, THREADS_PATTERN, "a ThreadContainer")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def find_proc_by_mem_obj(object: TraceObject) -> int:
|
|
104
|
+
return find_proc_by_pattern(object, MEMORY_PATTERN, "a Memory")
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def find_proc_by_modules_obj(object: TraceObject) -> int:
|
|
108
|
+
return find_proc_by_pattern(object, MODULES_PATTERN, "a ModuleContainer")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def find_thread_by_num(id: int) -> Optional[int]:
|
|
112
|
+
if id != util.selected_thread():
|
|
113
|
+
util.select_thread(id)
|
|
114
|
+
return util.selected_thread()
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def find_thread_by_pattern(pattern: re.Pattern, object: TraceObject,
|
|
118
|
+
err_msg: str) -> Optional[int]:
|
|
119
|
+
mat = pattern.fullmatch(object.path)
|
|
120
|
+
if mat is None:
|
|
121
|
+
raise TypeError(f"{object} is not {err_msg}")
|
|
122
|
+
pnum = int(mat['procnum'])
|
|
123
|
+
tnum = int(mat['tnum'])
|
|
124
|
+
find_proc_by_num(pnum)
|
|
125
|
+
return find_thread_by_num(tnum)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def find_thread_by_obj(object: TraceObject) -> Optional[int]:
|
|
129
|
+
return find_thread_by_pattern(THREAD_PATTERN, object, "a Thread")
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def find_thread_by_stack_obj(object: TraceObject) -> Optional[int]:
|
|
133
|
+
return find_thread_by_pattern(STACK_PATTERN, object, "a Stack")
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def find_thread_by_regs_obj(object: TraceObject) -> Optional[int]:
|
|
137
|
+
return find_thread_by_pattern(REGS_PATTERN0, object,
|
|
138
|
+
"a RegisterValueContainer")
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
# TODO: if eventually exposed...
|
|
142
|
+
# def find_frame_by_level(level: int) -> DbgEng._DEBUG_STACK_FRAME:
|
|
143
|
+
# for f in util.dbg.client.backtrace_list():
|
|
144
|
+
# if f.FrameNumber == level:
|
|
145
|
+
# return f
|
|
146
|
+
# # return dbg().backtrace_list()[level]
|
|
147
|
+
#
|
|
148
|
+
#
|
|
149
|
+
# def find_frame_by_pattern(pattern: re.Pattern, object: TraceObject,
|
|
150
|
+
# err_msg: str) -> DbgEng._DEBUG_STACK_FRAME:
|
|
151
|
+
# mat = pattern.fullmatch(object.path)
|
|
152
|
+
# if mat is None:
|
|
153
|
+
# raise TypeError(f"{object} is not {err_msg}")
|
|
154
|
+
# pnum = int(mat['procnum'])
|
|
155
|
+
# tnum = int(mat['tnum'])
|
|
156
|
+
# level = int(mat['level'])
|
|
157
|
+
# find_proc_by_num(pnum)
|
|
158
|
+
# find_thread_by_num(tnum)
|
|
159
|
+
# return find_frame_by_level(level)
|
|
160
|
+
#
|
|
161
|
+
#
|
|
162
|
+
# def find_frame_by_obj(object: TraceObject) -> DbgEng._DEBUG_STACK_FRAME:
|
|
163
|
+
# return find_frame_by_pattern(FRAME_PATTERN, object, "a StackFrame")
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def find_bpt_by_pattern(pattern: re.Pattern, object: TraceObject,
|
|
167
|
+
err_msg: str) -> int:
|
|
168
|
+
mat = pattern.fullmatch(object.path)
|
|
169
|
+
if mat is None:
|
|
170
|
+
return -1 #raise TypeError(f"{object} is not {err_msg}")
|
|
171
|
+
breaknum = int(mat['breaknum'])
|
|
172
|
+
return breaknum
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def find_sbpt_by_obj(object: TraceObject) -> int:
|
|
176
|
+
return find_bpt_by_pattern(PROC_SBREAKBPT_PATTERN, object, "a BreakpointSpec")
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def find_hbpt_by_obj(object: TraceObject) -> int:
|
|
180
|
+
return find_bpt_by_pattern(PROC_HBREAKBPT_PATTERN, object, "a BreakpointSpec")
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def find_mbpt_by_obj(object: TraceObject) -> int:
|
|
184
|
+
return find_bpt_by_pattern(PROC_MBREAKBPT_PATTERN, object, "a BreakpointSpec")
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
shared_globals: Dict[str, Any] = dict()
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
class Session(TraceObject):
|
|
191
|
+
pass
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
class AvailableContainer(TraceObject):
|
|
195
|
+
pass
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
class BreakpointContainer(TraceObject):
|
|
199
|
+
pass
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
class ProcessContainer(TraceObject):
|
|
203
|
+
pass
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
class Environment(TraceObject):
|
|
207
|
+
pass
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
class ThreadContainer(TraceObject):
|
|
211
|
+
pass
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
class Stack(TraceObject):
|
|
215
|
+
pass
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
class RegisterValueContainer(TraceObject):
|
|
219
|
+
pass
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
class Memory(TraceObject):
|
|
223
|
+
pass
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
class ModuleContainer(TraceObject):
|
|
227
|
+
pass
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
class State(TraceObject):
|
|
231
|
+
pass
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
class Process(TraceObject):
|
|
235
|
+
pass
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
class Thread(TraceObject):
|
|
239
|
+
pass
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
class StackFrame(TraceObject):
|
|
243
|
+
pass
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
class Attachable(TraceObject):
|
|
247
|
+
pass
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
class BreakpointSpec(TraceObject):
|
|
251
|
+
pass
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
class EventContainer(TraceObject):
|
|
255
|
+
pass
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
class ExceptionContainer(TraceObject):
|
|
259
|
+
pass
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
class ContinueOption(TraceObject):
|
|
263
|
+
pass
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
class ExecutionOption(TraceObject):
|
|
267
|
+
pass
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
@REGISTRY.method()
|
|
271
|
+
def execute(cmd: str, to_string: bool=False):
|
|
272
|
+
"""Execute a Python3 command or script."""
|
|
273
|
+
# print("***{}***".format(cmd))
|
|
274
|
+
# sys.stderr.flush()
|
|
275
|
+
# sys.stdout.flush()
|
|
276
|
+
if to_string:
|
|
277
|
+
data = StringIO()
|
|
278
|
+
with redirect_stdout(data):
|
|
279
|
+
exec(cmd, shared_globals)
|
|
280
|
+
return data.getvalue()
|
|
281
|
+
else:
|
|
282
|
+
exec(cmd, shared_globals)
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
@REGISTRY.method(action='evaluate', display='Evaluate')
|
|
286
|
+
def evaluate(
|
|
287
|
+
session: Session,
|
|
288
|
+
expr: Annotated[str, ParamDesc(display='Expr')]) -> str:
|
|
289
|
+
"""Evaluate a Python3 expression."""
|
|
290
|
+
return str(eval(expr, shared_globals))
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
@REGISTRY.method(action='refresh', display='Refresh Available')
|
|
294
|
+
def refresh_available(node: AvailableContainer) -> None:
|
|
295
|
+
"""List processes on x64dbg's host system."""
|
|
296
|
+
with commands.open_tracked_tx('Refresh Available'):
|
|
297
|
+
commands.ghidra_trace_put_available()
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
@REGISTRY.method(action='refresh', display='Refresh Breakpoints')
|
|
301
|
+
def refresh_breakpoints(node: BreakpointContainer) -> None:
|
|
302
|
+
"""Refresh the list of breakpoints (including locations for the current
|
|
303
|
+
process)."""
|
|
304
|
+
with commands.open_tracked_tx('Refresh Breakpoints'):
|
|
305
|
+
commands.ghidra_trace_put_breakpoints()
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
@REGISTRY.method(action='refresh', display='Refresh Processes')
|
|
309
|
+
def refresh_processes(node: ProcessContainer) -> None:
|
|
310
|
+
"""Refresh the list of processes."""
|
|
311
|
+
with commands.open_tracked_tx('Refresh Processes'):
|
|
312
|
+
commands.ghidra_trace_put_processes()
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
@REGISTRY.method(action='refresh', display='Refresh Environment')
|
|
316
|
+
def refresh_environment(node: Environment) -> None:
|
|
317
|
+
"""Refresh the environment descriptors (arch, os, endian)."""
|
|
318
|
+
with commands.open_tracked_tx('Refresh Environment'):
|
|
319
|
+
commands.ghidra_trace_put_environment()
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
@REGISTRY.method(action='refresh', display='Refresh Threads')
|
|
323
|
+
def refresh_threads(node: ThreadContainer) -> None:
|
|
324
|
+
"""Refresh the list of threads in the process."""
|
|
325
|
+
with commands.open_tracked_tx('Refresh Threads'):
|
|
326
|
+
commands.ghidra_trace_put_threads()
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
# TODO: if eventually exposed...
|
|
330
|
+
# @REGISTRY.method(action='refresh', display='Refresh Stack')
|
|
331
|
+
# def refresh_stack(node: Stack) -> None:
|
|
332
|
+
# """Refresh the backtrace for the thread."""
|
|
333
|
+
# tnum = find_thread_by_stack_obj(node)
|
|
334
|
+
# util.reset_frames()
|
|
335
|
+
# with commands.open_tracked_tx('Refresh Stack'):
|
|
336
|
+
# commands.ghidra_trace_put_frames()
|
|
337
|
+
# with commands.open_tracked_tx('Refresh Registers'):
|
|
338
|
+
# commands.ghidra_trace_putreg()
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
@REGISTRY.method(action='refresh', display='Refresh Registers')
|
|
342
|
+
def refresh_registers(node: RegisterValueContainer) -> None:
|
|
343
|
+
"""Refresh the register values for the selected frame."""
|
|
344
|
+
tnum = find_thread_by_regs_obj(node)
|
|
345
|
+
with commands.open_tracked_tx('Refresh Registers'):
|
|
346
|
+
commands.ghidra_trace_putreg()
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
@REGISTRY.method(action='refresh', display='Refresh Memory')
|
|
350
|
+
def refresh_mappings(node: Memory) -> None:
|
|
351
|
+
"""Refresh the list of memory regions for the process."""
|
|
352
|
+
with commands.open_tracked_tx('Refresh Memory Regions'):
|
|
353
|
+
commands.ghidra_trace_put_regions()
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
@REGISTRY.method(action='refresh', display='Refresh Modules')
|
|
357
|
+
def refresh_modules(node: ModuleContainer) -> None:
|
|
358
|
+
"""Refresh the modules and sections list for the process.
|
|
359
|
+
|
|
360
|
+
This will refresh the sections for all modules, not just the
|
|
361
|
+
selected one.
|
|
362
|
+
"""
|
|
363
|
+
with commands.open_tracked_tx('Refresh Modules'):
|
|
364
|
+
commands.ghidra_trace_put_modules()
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
@REGISTRY.method(action='activate')
|
|
368
|
+
def activate_process(process: Process,
|
|
369
|
+
time: Optional[str]=None) -> None:
|
|
370
|
+
"""Switch to the process."""
|
|
371
|
+
find_proc_by_obj(process)
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
@REGISTRY.method(action='activate')
|
|
375
|
+
def activate_thread(thread: Thread,
|
|
376
|
+
time: Optional[str]=None) -> None:
|
|
377
|
+
"""Switch to the thread."""
|
|
378
|
+
find_thread_by_obj(thread)
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
# TODO: if eventually exposed...
|
|
382
|
+
# @REGISTRY.method(action='activate')
|
|
383
|
+
# def activate_frame(frame: StackFrame,
|
|
384
|
+
# time: Optional[str]=None) -> None:
|
|
385
|
+
# """Select the frame."""
|
|
386
|
+
# do_maybe_activate_time(time)
|
|
387
|
+
# f = find_frame_by_obj(frame)
|
|
388
|
+
# util.select_frame(f.FrameNumber)
|
|
389
|
+
# with commands.open_tracked_tx('Refresh Stack'):
|
|
390
|
+
# commands.ghidra_trace_put_frames()
|
|
391
|
+
# with commands.open_tracked_tx('Refresh Registers'):
|
|
392
|
+
# commands.ghidra_trace_putreg()
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
@REGISTRY.method(action='delete')
|
|
396
|
+
def remove_process(process: Process) -> None:
|
|
397
|
+
"""Remove the process."""
|
|
398
|
+
dbg().detach()
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
@REGISTRY.method(action='attach', display='Attach')
|
|
402
|
+
def attach_obj(target: Attachable) -> None:
|
|
403
|
+
"""Attach the process to the given target."""
|
|
404
|
+
pid = find_availpid_by_obj(target)
|
|
405
|
+
dbg().attach(pid)
|
|
406
|
+
#dbg().wait_until_debugging()
|
|
407
|
+
commands.ghidra_trace_stop()
|
|
408
|
+
commands.ghidra_trace_start(str(pid))
|
|
409
|
+
commands.ghidra_trace_sync_enable()
|
|
410
|
+
with commands.open_tracked_tx('Put all'):
|
|
411
|
+
commands.ghidra_trace_put_all()
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
@REGISTRY.method(action='attach', display='Attach by pid')
|
|
415
|
+
def attach_pid(session: Session,
|
|
416
|
+
pid: Annotated[int, ParamDesc(display='PID')]) -> None:
|
|
417
|
+
"""Attach the process to the given target."""
|
|
418
|
+
commands.ghidra_trace_stop()
|
|
419
|
+
commands.ghidra_trace_start(str(pid))
|
|
420
|
+
commands.ghidra_trace_sync_enable()
|
|
421
|
+
with commands.open_tracked_tx('Put all'):
|
|
422
|
+
commands.ghidra_trace_put_all()
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
@REGISTRY.method(action='detach', display='Detach')
|
|
426
|
+
def detach(process: Process) -> None:
|
|
427
|
+
"""Detach the process's target."""
|
|
428
|
+
dbg().detach()
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
@REGISTRY.method(action='launch', display='Launch')
|
|
432
|
+
def launch(
|
|
433
|
+
Session: Session,
|
|
434
|
+
file: Annotated[str, ParamDesc(display='Image')],
|
|
435
|
+
args: Annotated[str, ParamDesc(display='Arguments')]='',
|
|
436
|
+
initial_dir: Annotated[str, ParamDesc(
|
|
437
|
+
display='Initial Directory')]='',
|
|
438
|
+
wait: Annotated[bool, ParamDesc(
|
|
439
|
+
display='Wait',
|
|
440
|
+
description='Perform the initial WaitForEvents')]=False) -> None:
|
|
441
|
+
"""Run a native process with the given command line."""
|
|
442
|
+
commands.ghidra_trace_stop()
|
|
443
|
+
commands.ghidra_trace_create(command=file, args=args, initdir=initial_dir, start_trace=True, wait=wait)
|
|
444
|
+
commands.ghidra_trace_sync_enable()
|
|
445
|
+
with commands.open_tracked_tx('Put all'):
|
|
446
|
+
commands.ghidra_trace_put_all()
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
@REGISTRY.method()
|
|
450
|
+
def kill(process: Process) -> None:
|
|
451
|
+
"""Kill execution of the process."""
|
|
452
|
+
commands.ghidra_trace_kill()
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
@REGISTRY.method(action='resume', display='Go')
|
|
456
|
+
def go(process: Process) -> None:
|
|
457
|
+
"""Continue execution of the process."""
|
|
458
|
+
dbg().go()
|
|
459
|
+
proc = util.selected_process()
|
|
460
|
+
trace = commands.STATE.require_trace()
|
|
461
|
+
with trace.client.batch():
|
|
462
|
+
with trace.open_tx("Go proc {}".format(proc)):
|
|
463
|
+
commands.put_state(proc)
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
@REGISTRY.method()
|
|
467
|
+
def interrupt(process: Process) -> None:
|
|
468
|
+
"""Interrupt the execution of the debugged program."""
|
|
469
|
+
# SetInterrupt is reentrant, so bypass the thread checks
|
|
470
|
+
dbg().pause()
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
@REGISTRY.method(action='Hide PEB')
|
|
474
|
+
def hide_peb(process: Process) -> None:
|
|
475
|
+
"""Interrupt the execution of the debugged program."""
|
|
476
|
+
# SetInterrupt is reentrant, so bypass the thread checks
|
|
477
|
+
dbg().hide_debugger_peb()
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
@REGISTRY.method(action='step_into')
|
|
481
|
+
def step_into(thread: Thread,
|
|
482
|
+
n: Annotated[int, ParamDesc(display='N')]=1) -> None:
|
|
483
|
+
"""Step one instruction exactly."""
|
|
484
|
+
# find_thread_by_obj(thread)
|
|
485
|
+
find_thread_by_obj(thread)
|
|
486
|
+
dbg().stepi(n)
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
@REGISTRY.method(action='step_over')
|
|
490
|
+
def step_over(thread: Thread,
|
|
491
|
+
n: Annotated[int, ParamDesc(display='N')]=1) -> None:
|
|
492
|
+
"""Step one instruction, but proceed through subroutine calls."""
|
|
493
|
+
# find_thread_by_obj(thread)
|
|
494
|
+
find_thread_by_obj(thread)
|
|
495
|
+
dbg().stepo(n)
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
@REGISTRY.method(action='skip', display='Skip')
|
|
499
|
+
def skip(thread: Thread,
|
|
500
|
+
n: Annotated[int, ParamDesc(display='N')]=1) -> None:
|
|
501
|
+
"""Step one instruction, but proceed through subroutine calls."""
|
|
502
|
+
# find_thread_by_obj(thread)
|
|
503
|
+
find_thread_by_obj(thread)
|
|
504
|
+
dbg().skip(n)
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
@REGISTRY.method(action='step_out')
|
|
508
|
+
def step_out(thread: Thread) -> None:
|
|
509
|
+
"""Execute until the current stack frame returns."""
|
|
510
|
+
find_thread_by_obj(thread)
|
|
511
|
+
dbg().ret()
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
@REGISTRY.method(action='pause_thread', display='Pause')
|
|
515
|
+
def pause_thread(thread: Thread) -> None:
|
|
516
|
+
"""Execute until the current stack frame returns."""
|
|
517
|
+
tid = find_thread_by_obj(thread)
|
|
518
|
+
if tid is not None:
|
|
519
|
+
dbg().thread_pause(tid)
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
@REGISTRY.method(action='resume_thread', display='Resume')
|
|
523
|
+
def resume_thread(thread: Thread) -> None:
|
|
524
|
+
"""Execute until the current stack frame returns."""
|
|
525
|
+
tid = find_thread_by_obj(thread)
|
|
526
|
+
if tid is not None:
|
|
527
|
+
dbg().thread_resume(tid)
|
|
528
|
+
|
|
529
|
+
|
|
530
|
+
@REGISTRY.method(action='kill_thread', display='Kill')
|
|
531
|
+
def kill_thread(thread: Thread) -> None:
|
|
532
|
+
"""Execute until the current stack frame returns."""
|
|
533
|
+
tid = find_thread_by_obj(thread)
|
|
534
|
+
if tid is not None:
|
|
535
|
+
dbg().thread_terminate(tid)
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
@REGISTRY.method(action='break_sw_execute')
|
|
539
|
+
def break_address(process: Process, address: Address) -> None:
|
|
540
|
+
"""Set a breakpoint."""
|
|
541
|
+
find_proc_by_obj(process)
|
|
542
|
+
dbg().set_breakpoint(address_or_symbol=address.offset)
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
@REGISTRY.method(action='break_ext', display='Set Breakpoint')
|
|
546
|
+
def break_expression(expression: str) -> None:
|
|
547
|
+
"""Set a breakpoint."""
|
|
548
|
+
# TODO: Escape?
|
|
549
|
+
dbg().set_breakpoint(address_or_symbol=expression)
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
@REGISTRY.method(action='break_hw_execute')
|
|
553
|
+
def break_hw_address(process: Process, address: Address) -> None:
|
|
554
|
+
"""Set a hardware-assisted breakpoint."""
|
|
555
|
+
find_proc_by_obj(process)
|
|
556
|
+
dbg().set_hardware_breakpoint(address_or_symbol=address.offset)
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
@REGISTRY.method(action='break_ext', display='Set Hardware Breakpoint')
|
|
560
|
+
def break_hw_expression(expression: str) -> None:
|
|
561
|
+
"""Set a hardware-assisted breakpoint."""
|
|
562
|
+
dbg().set_hardware_breakpoint(address_or_symbol=expression)
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
@REGISTRY.method(action='break_read')
|
|
566
|
+
def break_read_address(process: Process, address: Address, size: int) -> None:
|
|
567
|
+
"""Set a read breakpoint."""
|
|
568
|
+
find_proc_by_obj(process)
|
|
569
|
+
dbg().set_hardware_breakpoint(address_or_symbol=address.offset, bp_type=HardwareBreakpointType.r, size=size)
|
|
570
|
+
|
|
571
|
+
|
|
572
|
+
@REGISTRY.method(action='break_ext', display='Set Read Breakpoint')
|
|
573
|
+
def break_read_expression(expression: str) -> None:
|
|
574
|
+
"""Set a read breakpoint."""
|
|
575
|
+
dbg().set_hardware_breakpoint(address_or_symbol=expression, bp_type=HardwareBreakpointType.r)
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
@REGISTRY.method(action='break_write')
|
|
579
|
+
def break_write_address(process: Process, address: Address, size: int) -> None:
|
|
580
|
+
"""Set a write breakpoint."""
|
|
581
|
+
find_proc_by_obj(process)
|
|
582
|
+
dbg().set_hardware_breakpoint(address_or_symbol=address.offset, bp_type=HardwareBreakpointType.w, size=size)
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
@REGISTRY.method(action='break_ext', display='Set Write Breakpoint')
|
|
586
|
+
def break_write_expression(expression: str) -> None:
|
|
587
|
+
"""Set a write breakpoint."""
|
|
588
|
+
dbg().set_hardware_breakpoint(address_or_symbol=expression, bp_type=HardwareBreakpointType.w)
|
|
589
|
+
|
|
590
|
+
|
|
591
|
+
@REGISTRY.method(action='break_access')
|
|
592
|
+
def break_access_address(process: Process, address: Address) -> None:
|
|
593
|
+
"""Set an access breakpoint."""
|
|
594
|
+
find_proc_by_obj(process)
|
|
595
|
+
dbg().set_memory_breakpoint(address_or_symbol=address.offset, bp_type=MemoryBreakpointType.a)
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
@REGISTRY.method(action='break_ext', display='Set Access Breakpoint')
|
|
599
|
+
def break_access_expression(expression: str) -> None:
|
|
600
|
+
"""Set an access breakpoint."""
|
|
601
|
+
dbg().set_memory_breakpoint(address_or_symbol=expression, bp_type=MemoryBreakpointType.a)
|
|
602
|
+
|
|
603
|
+
|
|
604
|
+
@REGISTRY.method(action='toggle', display='Toggle Breakpoint')
|
|
605
|
+
def toggle_breakpoint(breakpoint: BreakpointSpec, enabled: bool) -> None:
|
|
606
|
+
"""Toggle a breakpoint."""
|
|
607
|
+
bpt = find_sbpt_by_obj(breakpoint)
|
|
608
|
+
if bpt >= 0:
|
|
609
|
+
dbg().toggle_breakpoint(address_name_symbol_or_none=bpt, on=enabled)
|
|
610
|
+
with commands.open_tracked_tx('Toggle Breakpoints'):
|
|
611
|
+
commands.put_breakpoints(BreakpointType.BpNormal)
|
|
612
|
+
return
|
|
613
|
+
bpt = find_hbpt_by_obj(breakpoint)
|
|
614
|
+
if bpt >= 0:
|
|
615
|
+
dbg().toggle_breakpoint(address_name_symbol_or_none=bpt, on=enabled)
|
|
616
|
+
with commands.open_tracked_tx('Toggle Breakpoints'):
|
|
617
|
+
commands.put_breakpoints(BreakpointType.BpHardware)
|
|
618
|
+
return
|
|
619
|
+
bpt = find_mbpt_by_obj(breakpoint)
|
|
620
|
+
if bpt >= 0:
|
|
621
|
+
dbg().toggle_breakpoint(address_name_symbol_or_none=bpt, on=enabled)
|
|
622
|
+
with commands.open_tracked_tx('Toggle Breakpoints'):
|
|
623
|
+
commands.put_breakpoints(BreakpointType.BpMemory)
|
|
624
|
+
|
|
625
|
+
|
|
626
|
+
@REGISTRY.method(action='delete', display='Delete Breakpoint')
|
|
627
|
+
def delete_breakpoint(breakpoint: BreakpointSpec) -> None:
|
|
628
|
+
"""Delete a breakpoint."""
|
|
629
|
+
bpt = find_sbpt_by_obj(breakpoint)
|
|
630
|
+
if bpt >= 0:
|
|
631
|
+
dbg().clear_breakpoint(address_name_symbol_or_none=bpt)
|
|
632
|
+
with commands.open_tracked_tx('Delete Breakpoints'):
|
|
633
|
+
commands.put_breakpoints(BreakpointType.BpNormal)
|
|
634
|
+
return
|
|
635
|
+
bpt = find_hbpt_by_obj(breakpoint)
|
|
636
|
+
if bpt >= 0:
|
|
637
|
+
dbg().clear_hardware_breakpoint(address_symbol_or_none=bpt)
|
|
638
|
+
with commands.open_tracked_tx('Delete Breakpoints'):
|
|
639
|
+
commands.put_breakpoints(BreakpointType.BpHardware)
|
|
640
|
+
return
|
|
641
|
+
bpt = find_mbpt_by_obj(breakpoint)
|
|
642
|
+
if bpt >= 0:
|
|
643
|
+
dbg().clear_memory_breakpoint(address_symbol_or_none=bpt)
|
|
644
|
+
with commands.open_tracked_tx('Delete Breakpoints'):
|
|
645
|
+
commands.put_breakpoints(BreakpointType.BpMemory)
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
@REGISTRY.method()
|
|
649
|
+
def read_mem(process: Process, range: AddressRange) -> None:
|
|
650
|
+
"""Read memory."""
|
|
651
|
+
# print("READ_MEM: process={}, range={}".format(process, range))
|
|
652
|
+
nproc = find_proc_by_obj(process)
|
|
653
|
+
offset_start = process.trace.extra.require_mm().map_back(
|
|
654
|
+
nproc, Address(range.space, range.min))
|
|
655
|
+
with commands.open_tracked_tx('Read Memory'):
|
|
656
|
+
result = commands.put_bytes(
|
|
657
|
+
offset_start, offset_start + range.length() - 1, pages=True,
|
|
658
|
+
display_result=False)
|
|
659
|
+
if result['count'] == 0:
|
|
660
|
+
commands.putmem_state(
|
|
661
|
+
offset_start, offset_start + range.length() - 1, 'error')
|
|
662
|
+
|
|
663
|
+
|
|
664
|
+
@REGISTRY.method()
|
|
665
|
+
def write_mem(process: Process, address: Address, data: bytes) -> None:
|
|
666
|
+
"""Write memory."""
|
|
667
|
+
nproc = find_proc_by_obj(process)
|
|
668
|
+
offset = process.trace.extra.required_mm().map_back(nproc, address)
|
|
669
|
+
dbg().write_memory(offset, data)
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+
@REGISTRY.method(action='set_reg', display='Set Register')
|
|
673
|
+
def write_reg(reg: RegisterValueContainer, name: str, value: int) -> None:
|
|
674
|
+
"""Write a register."""
|
|
675
|
+
nproc = util.selected_process()
|
|
676
|
+
dbg().set_reg(name, value)
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
def dbg():
|
|
680
|
+
return util.dbg.client
|
ghidraxdbg/py.typed
ADDED
|
File without changes
|