Miasm2
 All Classes Namespaces Files Functions Variables Typedefs Properties Macros
debugging.py
Go to the documentation of this file.
1 import cmd
2 from miasm2.core.utils import hexdump
3 from miasm2.core.interval import interval
4 import miasm2.jitter.csts as csts
5 from miasm2.jitter.jitload import ExceptionHandle
6 
7 
9 
10  "Debug Breakpoint parent class"
11  pass
12 
13 
15 
16  "Stand for software breakpoint"
17 
18  def __init__(self, addr):
19  self.addr = addr
20 
21  def __str__(self):
22  return "Soft BP @0x%08x" % self.addr
23 
24 
26  "Stand for an execution termination"
27 
28  def __init__(self, status):
29  self.status = status
30 
31  def __str__(self):
32  return "Terminate with %s" % self.status
33 
34 
36 
37  "Stand for memory breakpoint"
38 
39  type2str = {csts.BREAKPOINT_READ: "R",
40  csts.BREAKPOINT_WRITE: "W"}
41 
42  def __init__(self, addr, size, access_type):
43  self.addr = addr
44  self.access_type = access_type
45  self.size = size
46 
47  def __str__(self):
48  bp_type = ""
49  for k, v in self.type2str.items():
50  if k & self.access_type != 0:
51  bp_type += v
52  return "Memory BP @0x%08x, Size 0x%08x, Type %s" % (self.addr,
53  self.size,
54  bp_type)
55 
56  @classmethod
57  def get_access_type(cls, read=False, write=False):
58  value = 0
59  for k, v in cls.type2str.items():
60  if v == "R" and read is True:
61  value += k
62  if v == "W" and write is True:
63  value += k
64  return value
65 
66 
68 
69  "Debugguer linked with a Jitter instance"
70 
71  def __init__(self, myjit):
72  "myjit : jitter instance"
73  self.myjit = myjit
74  self.bp_list = [] # DebugBreakpointSoft list
75  self.hw_bp_list = [] # DebugBreakpointHard list
76  self.mem_watched = [] # Memory areas watched
77 
78  def init_run(self, addr):
79  self.myjit.init_run(addr)
80 
81  def add_breakpoint(self, addr):
82  "Add bp @addr"
83  bp = DebugBreakpointSoft(addr)
84  func = lambda x: bp
85  bp.func = func
86  self.bp_list.append(bp)
87  self.myjit.add_breakpoint(addr, func)
88 
90  "Set exception handler on EXCEPT_BREAKPOINT_INTERN"
91  raise NotImplementedError("Not implemented")
92 
93  def add_memory_breakpoint(self, addr, size, read=False, write=False):
94  "add mem bp @[addr, addr + size], on read/write/both"
95  access_type = DebugBreakpointMemory.get_access_type(read=read,
96  write=write)
97  dbm = DebugBreakpointMemory(addr, size, access_type)
98  self.hw_bp_list.append(dbm)
99  self.myjit.vm.add_memory_breakpoint(addr, size, access_type)
100 
101  def remove_breakpoint(self, dbs):
102  "remove the DebugBreakpointSoft instance"
103  self.bp_list.remove(dbs)
104  self.myjit.remove_breakpoints_by_callback(dbs.func)
105 
106  def remove_breakpoint_by_addr(self, addr):
107  "remove breakpoints @ addr"
108  for bp in self.get_breakpoint_by_addr(addr):
109  self.remove_breakpoint(bp)
110 
111  def remove_memory_breakpoint(self, dbm):
112  "remove the DebugBreakpointMemory instance"
113  self.hw_bp_list.remove(dbm)
114  self.myjit.vm.remove_memory_breakpoint(dbm.addr, dbm.access_type)
115 
116  def remove_memory_breakpoint_by_addr_access(self, addr, read=False,
117  write=False):
118  "remove breakpoints @ addr"
119  access_type = DebugBreakpointMemory.get_access_type(read=read,
120  write=write)
121  for bp in self.hw_bp_list:
122  if bp.addr == addr and bp.access_type == access_type:
123  self.remove_memory_breakpoint(bp)
124 
125  def get_breakpoint_by_addr(self, addr):
126  ret = []
127  for dbgsoft in self.bp_list:
128  if dbgsoft.addr == addr:
129  ret.append(dbgsoft)
130  return ret
131 
132  def get_breakpoints(self):
133  return self.bp_list
134 
135  def active_trace(self, mn=None, regs=None, newbloc=None):
136  if mn is not None:
137  self.myjit.jit.log_mn = mn
138  if regs is not None:
139  self.myjit.jit.log_regs = regs
140  if newbloc is not None:
141  self.myjit.jit.log_newbloc = newbloc
142 
143  def handle_exception(self, res):
144  if not res:
145  # A breakpoint has stopped the execution
146  return DebugBreakpointTerminate(res)
147 
148  if isinstance(res, DebugBreakpointSoft):
149  print "Breakpoint reached @0x%08x" % res.addr
150  elif isinstance(res, ExceptionHandle):
151  if res == ExceptionHandle.memoryBreakpoint():
152  print "Memory breakpoint reached!"
153 
154  # Remove flag
155  except_flag = self.myjit.vm.get_exception()
156  self.myjit.vm.set_exception(except_flag ^ res.except_flag)
157 
158  else:
159  raise NotImplementedError("Unknown Except")
160  else:
161  raise NotImplementedError("type res")
162 
163  # Repropagate res
164  return res
165 
166  def step(self):
167  "Step in jit"
168 
169  self.myjit.jit.set_options(jit_maxline=1)
170  self.myjit.jit.addr_mod = interval([(self.myjit.pc, self.myjit.pc)])
171  self.myjit.jit.updt_automod_code(self.myjit.vm)
172 
173  res = self.myjit.continue_run(step=True)
174  self.handle_exception(res)
175 
176  self.myjit.jit.set_options(jit_maxline=50)
177  self.on_step()
178 
179  return res
180 
181  def run(self):
182  status = self.myjit.continue_run()
183  return self.handle_exception(status)
184 
185  def get_mem(self, addr, size=0xF):
186  "hexdump @addr, size"
187 
188  hexdump(self.myjit.vm.get_mem(addr, size))
189 
190  def get_mem_raw(self, addr, size=0xF):
191  "hexdump @addr, size"
192  return self.myjit.vm.get_mem(addr, size)
193 
194  def watch_mem(self, addr, size=0xF):
195  self.mem_watched.append((addr, size))
196 
197  def on_step(self):
198  for addr, size in self.mem_watched:
199  print "@0x%08x:" % addr
200  self.get_mem(addr, size)
201 
202  def get_reg_value(self, reg_name):
203  return getattr(self.myjit.cpu, reg_name)
204 
205  def set_reg_value(self, reg_name, value):
206 
207  # Handle PC case
208  if reg_name == self.myjit.ir_arch.pc.name:
209  self.init_run(value)
210 
211  setattr(self.myjit.cpu, reg_name, value)
212 
213  def get_gpreg_all(self):
214  "Return general purposes registers"
215  return self.myjit.cpu.get_gpreg()
216 
217 
218 class DebugCmd(cmd.Cmd, object):
219 
220  "CommandLineInterpreter for Debugguer instance"
221 
222  color_g = '\033[92m'
223  color_e = '\033[0m'
224  color_b = '\033[94m'
225  color_r = '\033[91m'
226 
227  intro = color_g + "=== Miasm2 Debugging shell ===\nIf you need help, "
228  intro += "type 'help' or '?'" + color_e
229  prompt = color_b + "$> " + color_e
230 
231  def __init__(self, dbg):
232  "dbg : Debugguer"
233  self.dbg = dbg
234  super(DebugCmd, self).__init__()
235 
236  # Debug methods
237 
238  def print_breakpoints(self):
239  bp_list = self.dbg.bp_list
240  if len(bp_list) == 0:
241  print "No breakpoints."
242  else:
243  for i, b in enumerate(bp_list):
244  print "%d\t0x%08x" % (i, b.addr)
245 
246  def print_watchmems(self):
247  watch_list = self.dbg.mem_watched
248  if len(watch_list) == 0:
249  print "No memory watchpoints."
250  else:
251  print "Num\tAddress \tSize"
252  for i, w in enumerate(watch_list):
253  addr, size = w
254  print "%d\t0x%08x\t0x%08x" % (i, addr, size)
255 
256  def print_registers(self):
257  regs = self.dbg.get_gpreg_all()
258 
259  # Display settings
260  title1 = "Registers"
261  title2 = "Values"
262  max_name_len = max(map(len, regs.keys() + [title1]))
263 
264  # Print value table
265  s = "%s%s | %s" % (
266  title1, " " * (max_name_len - len(title1)), title2)
267  print s
268  print "-" * len(s)
269  for name, value in sorted(regs.items(), key=lambda x: x[0]):
270  print "%s%s | %s" % (name,
271  " " * (max_name_len - len(name)),
272  hex(value).replace("L", ""))
273 
274  def add_breakpoints(self, bp_addr):
275  for addr in bp_addr:
276  addr = int(addr, 0)
277 
278  good = True
279  for i, dbg_obj in enumerate(self.dbg.bp_list):
280  if dbg_obj.addr == addr:
281  good = False
282  break
283  if good is False:
284  print "Breakpoint 0x%08x already set (%d)" % (addr, i)
285  else:
286  l = len(self.dbg.bp_list)
287  self.dbg.add_breakpoint(addr)
288  print "Breakpoint 0x%08x successfully added ! (%d)" % (addr, l)
289 
290  display_mode = {"mn": None,
291  "regs": None,
292  "newbloc": None}
293 
295  self.display_mode = {"mn": self.dbg.myjit.jit.log_mn,
296  "regs": self.dbg.myjit.jit.log_regs,
297  "newbloc": self.dbg.myjit.jit.log_newbloc}
298 
299  # Command line methods
300  def print_warning(self, s):
301  print self.color_r + s + self.color_e
302 
303  def onecmd(self, line):
304  cmd_translate = {"h": "help",
305  "q": "exit",
306  "e": "exit",
307  "!": "exec",
308  "r": "run",
309  "i": "info",
310  "b": "breakpoint",
311  "s": "step",
312  "d": "dump"}
313 
314  if len(line) >= 2 and \
315  line[1] == " " and \
316  line[:1] in cmd_translate:
317  line = cmd_translate[line[:1]] + line[1:]
318 
319  if len(line) == 1 and line in cmd_translate:
320  line = cmd_translate[line]
321 
322  r = super(DebugCmd, self).onecmd(line)
323  return r
324 
325  def can_exit(self):
326  return True
327 
328  def do_display(self, arg):
329  if arg == "":
330  self.help_display()
331  return
332 
333  args = arg.split(" ")
334  if args[-1].lower() not in ["on", "off"]:
335  self.print_warning("/!\ %s not in 'on' / 'off'" % args[-1])
336  return
337  mode = args[-1].lower() == "on"
338  d = {}
339  for a in args[:-1]:
340  d[a] = mode
341  self.dbg.active_trace(**d)
342  self.update_display_mode()
343 
344  def help_display(self):
345  print "Enable/Disable tracing."
346  print "Usage: display <mode1> <mode2> ... on|off"
347  print "Available modes are:"
348  for k in self.display_mode:
349  print "\t%s" % k
350  print "Use 'info display' to get current values"
351 
352  def do_watchmem(self, arg):
353  if arg == "":
354  self.help_watchmem()
355  return
356 
357  args = arg.split(" ")
358  if len(args) >= 2:
359  size = int(args[1], 0)
360  else:
361  size = 0xF
362 
363  addr = int(args[0], 0)
364 
365  self.dbg.watch_mem(addr, size)
366 
367  def help_watchmem(self):
368  print "Add a memory watcher."
369  print "Usage: watchmem <addr> [size]"
370  print "Use 'info watchmem' to get current memory watchers"
371 
372  def do_info(self, arg):
373  av_info = ["registers",
374  "display",
375  "breakpoints",
376  "watchmem"]
377 
378  if arg == "":
379  print "'info' must be followed by the name of an info command."
380  print "List of info subcommands:"
381  for k in av_info:
382  print "\t%s" % k
383 
384  if arg.startswith("b"):
385  # Breakpoint
386  self.print_breakpoints()
387 
388  if arg.startswith("d"):
389  # Display
390  self.update_display_mode()
391  for k, v in self.display_mode.items():
392  print "%s\t\t%s" % (k, v)
393 
394  if arg.startswith("w"):
395  # Watchmem
396  self.print_watchmems()
397 
398  if arg.startswith("r"):
399  # Registers
400  self.print_registers()
401 
402  def help_info(self):
403  print "Generic command for showing things about the program being"
404  print "debugged. Use 'info' without arguments to get the list of"
405  print "available subcommands."
406 
407  def do_breakpoint(self, arg):
408  if arg == "":
409  self.help_breakpoint()
410  else:
411  addrs = arg.split(" ")
412  self.add_breakpoints(addrs)
413 
414  def help_breakpoint(self):
415  print "Add breakpoints to argument addresses."
416  print "Example:"
417  print "\tbreakpoint 0x11223344"
418  print "\tbreakpoint 1122 0xabcd"
419 
420  def do_step(self, arg):
421  if arg == "":
422  nb = 1
423  else:
424  nb = int(arg)
425  for _ in xrange(nb):
426  self.dbg.step()
427 
428  def help_step(self):
429  print "Step program until it reaches a different source line."
430  print "Argument N means do this N times (or till program stops"
431  print "for another reason)."
432 
433  def do_dump(self, arg):
434  if arg == "":
435  self.help_dump()
436  else:
437  args = arg.split(" ")
438  if len(args) >= 2:
439  size = int(args[1], 0)
440  else:
441  size = 0xF
442  addr = int(args[0], 0)
443 
444  self.dbg.get_mem(addr, size)
445 
446  def help_dump(self):
447  print "Dump <addr> [size]. Dump size bytes at addr."
448 
449  def do_run(self, _):
450  self.dbg.run()
451 
452  def help_run(self):
453  print "Launch or continue the current program"
454 
455  def do_exit(self, _):
456  return True
457 
458  def do_exec(self, line):
459  try:
460  print eval(line)
461  except Exception, error:
462  print "*** Error: %s" % error
463 
464  def help_exec(self):
465  print "Exec a python command."
466  print "You can also use '!' shortcut."
467 
468  def help_exit(self):
469  print "Exit the interpreter."
470  print "You can also use the Ctrl-D shortcut."
471 
472  def help_help(self):
473  print "Print help"
474 
475  def postloop(self):
476  print '\nGoodbye !'
477  super(DebugCmd, self).postloop()
478 
479  do_EOF = do_exit
480  help_EOF = help_exit