diff options
Diffstat (limited to 'miasm2/analysis/debugging.py')
| -rw-r--r-- | miasm2/analysis/debugging.py | 479 |
1 files changed, 479 insertions, 0 deletions
diff --git a/miasm2/analysis/debugging.py b/miasm2/analysis/debugging.py new file mode 100644 index 00000000..473f20f1 --- /dev/null +++ b/miasm2/analysis/debugging.py @@ -0,0 +1,479 @@ +import cmd +from miasm2.core.utils import hexdump +import miasm2.jitter.csts as csts +from miasm2.jitter.jitload import ExceptionHandle + + +class DebugBreakpoint: + + "Debug Breakpoint parent class" + pass + + +class DebugBreakpointSoft(DebugBreakpoint): + + "Stand for software breakpoint" + + def __init__(self, addr): + self.addr = addr + + def __str__(self): + return "Soft BP @0x%08x" % self.addr + + +class DebugBreakpointMemory(DebugBreakpoint): + + "Stand for memory breakpoint" + + type2str = {csts.BREAKPOINT_READ: "R", + csts.BREAKPOINT_WRITE: "W"} + + def __init__(self, addr, size, access_type): + self.addr = addr + self.access_type = access_type + self.size = size + + def __str__(self): + bp_type = "" + for k, v in self.type2str.items(): + if k & self.access_type != 0: + bp_type += v + return "Memory BP @0x%08x, Size 0x%08x, Type %s" % (self.addr, + self.size, + bp_type) + + @classmethod + def get_access_type(cls, read=False, write=False): + value = 0 + for k, v in cls.type2str.items(): + if v == "R" and read is True: + value += k + if v == "W" and write is True: + value += k + return value + + +class Debugguer(object): + + "Debugguer linked with a Jitter instance" + + def __init__(self, myjit): + "myjit : jitter instance" + self.myjit = myjit + self.bp_list = [] # DebugBreakpointSoft list + self.hw_bp_list = [] # DebugBreakpointHard list + self.mem_watched = [] # Memory areas watched + + def init_run(self, addr): + self.myjit.init_run(addr) + + def add_breakpoint(self, addr): + "Add bp @addr" + bp = DebugBreakpointSoft(addr) + func = lambda x: bp + bp.func = func + self.bp_list.append(bp) + self.myjit.add_breakpoint(addr, func) + + def init_memory_breakpoint(self): + "Set exception handler on EXCEPT_BREAKPOINT_INTERN" + self.myjit.exception_handler + + def add_memory_breakpoint(self, addr, size, read=False, write=False): + "add mem bp @[addr, addr + size], on read/write/both" + access_type = DebugBreakpointMemory.get_access_type(read=read, + write=write) + dbm = DebugBreakpointMemory(addr, size, access_type) + self.hw_bp_list.append(dbm) + self.myjit.vm.vm_add_memory_breakpoint(addr, size, access_type) + + def remove_breakpoint(self, dbs): + "remove the DebugBreakpointSoft instance" + self.bp_list.remove(dbs) + self.myjit.remove_breakpoints_by_callback(dbs.func) + + def remove_breakpoint_by_addr(self, addr): + "remove breakpoints @ addr" + for bp in self.get_breakpoint_by_addr(addr): + self.remove_breakpoint(bp) + + def remove_memory_breakpoint(self, dbm): + "remove the DebugBreakpointMemory instance" + self.hw_bp_list.remove(dbm) + self.myjit.vm.vm_remove_memory_breakpoint(dbm.addr, dbm.access_type) + + def remove_memory_breakpoint_by_addr_access(self, addr, read=False, + write=False): + "remove breakpoints @ addr" + access_type = DebugBreakpointMemory.get_access_type(read=read, + write=write) + for bp in self.hw_bp_list: + if bp.addr == addr and bp.access_type == access_type: + self.remove_memory_breakpoint(bp) + + def get_breakpoint_by_addr(self, addr): + ret = [] + for dbgsoft in self.bp_list: + if dbgsoft.addr == addr: + ret.append(dbgsoft) + return ret + + def get_breakpoints(self): + return self.bp_list + + def active_trace(self, mn=None, regs=None, newbloc=None): + if mn is not None: + self.myjit.jit.log_mn = mn + if regs is not None: + self.myjit.jit.log_regs = regs + if newbloc is not None: + self.myjit.jit.log_newbloc = newbloc + + def handle_exception(self, res): + if res is None: + return + + if isinstance(res, DebugBreakpointSoft): + print "Breakpoint reached @0x%08x" % res.addr + elif isinstance(res, ExceptionHandle): + if res == ExceptionHandle.memoryBreakpoint(): + print "Memory breakpoint reached!" + + # Remove flag + except_flag = self.myjit.vm.vm_get_exception() + self.myjit.vm.vm_set_exception(except_flag ^ res.except_flag) + + else: + raise NotImplementedError("Unknown Except") + else: + raise NotImplementedError("type res") + + def step(self): + "Step in jit" + + self.myjit.jit.set_options(jit_maxline=1) + self.myjit.jit.updt_automod_code(self.myjit.vm, self.myjit.pc, 8) + + res = self.myjit.continue_run(step=True) + self.handle_exception(res) + + self.myjit.jit.set_options(jit_maxline=50) + self.on_step() + + return res + + def run(self): + res = self.myjit.continue_run() + self.handle_exception(res) + return res + + def get_mem(self, addr, size=0xF): + "hexdump @addr, size" + + hexdump(self.myjit.vm.vm_get_mem(addr, size)) + + def get_mem_raw(self, addr, size=0xF): + "hexdump @addr, size" + return self.myjit.vm.vm_get_mem(addr, size) + + def watch_mem(self, addr, size=0xF): + self.mem_watched.append((addr, size)) + + def on_step(self): + for addr, size in self.mem_watched: + print "@0x%08x:" % addr + self.get_mem(addr, size) + + def get_reg_value(self, reg_name): + return getattr(self.myjit.cpu, reg_name) + + def set_reg_value(self, reg_name, value): + + # Handle PC case + if reg_name == self.myjit.my_ir.pc.name: + self.init_run(value) + + setattr(self.myjit.cpu, reg_name, value) + + def get_gpreg_all(self): + "Return general purposes registers" + return self.myjit.cpu.vm_get_gpreg() + + +class DebugCmd(cmd.Cmd, object): + + "CommandLineInterpreter for Debugguer instance" + + color_g = '\033[92m' + color_e = '\033[0m' + color_b = '\033[94m' + color_r = '\033[91m' + + intro = color_g + "=== Miasm2 Debugging shell ===\nIf you need help, " + intro += "type 'help' or '?'" + color_e + prompt = color_b + "$> " + color_e + + def __init__(self, dbg): + "dbg : Debugguer" + self.dbg = dbg + super(DebugCmd, self).__init__() + + # Debug methods + + def print_breakpoints(self): + bp_list = self.dbg.bp_list + if len(bp_list) == 0: + print "No breakpoints." + else: + for i, b in enumerate(bp_list): + print "%d\t0x%08x" % (i, b.addr) + + def print_watchmems(self): + watch_list = self.dbg.mem_watched + if len(watch_list) == 0: + print "No memory watchpoints." + else: + print "Num\tAddress \tSize" + for i, w in enumerate(watch_list): + addr, size = w + print "%d\t0x%08x\t0x%08x" % (i, addr, size) + + def print_registers(self): + regs = self.dbg.get_gpreg_all() + + # Display settings + title1 = "Registers" + title2 = "Values" + max_name_len = max(map(len, regs.keys() + [title1])) + + # Print value table + s = "%s%s | %s" % ( + title1, " " * (max_name_len - len(title1)), title2) + print s + print "-" * len(s) + for name, value in sorted(regs.items(), key=lambda x: x[0]): + print "%s%s | %s" % (name, + " " * (max_name_len - len(name)), + hex(value).replace("L", "")) + + def add_breakpoints(self, bp_addr): + for addr in bp_addr: + if "0x" in addr: + addr = int(addr, 16) + else: + addr = int(addr) + + good = True + for i, dbg_obj in enumerate(self.dbg.bp_list): + if dbg_obj.addr == addr: + good = False + break + if good is False: + print "Breakpoint 0x%08x already set (%d)" % (addr, i) + else: + l = len(self.dbg.bp_list) + self.dbg.add_breakpoint(addr) + print "Breakpoint 0x%08x successfully added ! (%d)" % (addr, l) + + display_mode = {"mn": None, + "regs": None, + "newbloc": None} + + def update_display_mode(self): + self.display_mode = {"mn": self.dbg.myjit.jit.log_mn, + "regs": self.dbg.myjit.jit.log_regs, + "newbloc": self.dbg.myjit.jit.log_newbloc} + + # Command line methods + def print_warning(self, s): + print self.color_r + s + self.color_e + + def onecmd(self, line): + cmd_translate = {"h": "help", + "q": "exit", + "e": "exit", + "!": "exec", + "r": "run", + "i": "info", + "b": "breakpoint", + "s": "step", + "d": "dump"} + + if len(line) >= 2 and \ + line[1] == " " and \ + line[:1] in cmd_translate: + line = cmd_translate[line[:1]] + line[1:] + + if len(line) == 1 and line in cmd_translate: + line = cmd_translate[line] + + r = super(DebugCmd, self).onecmd(line) + return r + + def can_exit(self): + return True + + def do_display(self, arg): + if arg == "": + self.help_display() + return + + args = arg.split(" ") + if args[-1].lower() not in ["on", "off"]: + self.print_warning("/!\ %s not in 'on' / 'off'" % args[-1]) + return + mode = args[-1].lower() == "on" + d = {} + for a in args[:-1]: + d[a] = mode + self.dbg.active_trace(**d) + self.update_display_mode() + + def help_display(self): + print "Enable/Disable tracing." + print "Usage: display <mode1> <mode2> ... on|off" + print "Available modes are:" + for k in self.display_mode: + print "\t%s" % k + print "Use 'info display' to get current values" + + def do_watchmem(self, arg): + if arg == "": + self.help_watchmem() + return + + args = arg.split(" ") + if len(args) >= 2: + if "0x" in args[1]: + size = int(args[1], 16) + else: + size = int(args[1]) + else: + size = 0xF + if "0x" in args[0]: + addr = int(args[0], 16) + else: + addr = int(args[0]) + + self.dbg.watch_mem(addr, size) + + def help_watchmem(self): + print "Add a memory watcher." + print "Usage: watchmem <addr> [size]" + print "Use 'info watchmem' to get current memory watchers" + + def do_info(self, arg): + av_info = ["registers", + "display", + "breakpoints", + "watchmem"] + + if arg == "": + print "'info' must be followed by the name of an info command." + print "List of info subcommands:" + for k in av_info: + print "\t%s" % k + + if arg.startswith("b"): + # Breakpoint + self.print_breakpoints() + + if arg.startswith("d"): + # Display + self.update_display_mode() + for k, v in self.display_mode.items(): + print "%s\t\t%s" % (k, v) + + if arg.startswith("w"): + # Watchmem + self.print_watchmems() + + if arg.startswith("r"): + # Registers + self.print_registers() + + def help_info(self): + print "Generic command for showing things about the program being" + print "debugged. Use 'info' without arguments to get the list of" + print "available subcommands." + + def do_breakpoint(self, arg): + if arg == "": + self.help_breakpoint() + else: + addrs = arg.split(" ") + self.add_breakpoints(addrs) + + def help_breakpoint(self): + print "Add breakpoints to argument addresses." + print "Example:" + print "\tbreakpoint 0x11223344" + print "\tbreakpoint 1122 0xabcd" + + def do_step(self, arg): + if arg == "": + nb = 1 + else: + nb = int(arg) + for _ in xrange(nb): + self.dbg.step() + + def help_step(self): + print "Step program until it reaches a different source line." + print "Argument N means do this N times (or till program stops" + print "for another reason)." + + def do_dump(self, arg): + if arg == "": + self.help_dump() + else: + args = arg.split(" ") + if len(args) >= 2: + if "0x" in args[1]: + size = int(args[1], 16) + else: + size = int(args[1]) + else: + size = 0xF + if "0x" in args[0]: + addr = int(args[0], 16) + else: + addr = int(args[0]) + + self.dbg.get_mem(addr, size) + + def help_dump(self): + print "Dump <addr> [size]. Dump size bytes at addr." + + def do_run(self, arg): + self.dbg.run() + + def help_run(self): + print "Launch or continue the current program" + + def do_exit(self, s): + return True + + def do_exec(self, l): + try: + print eval(l) + except Exception, e: + print "*** Error: %s" % e + + def help_exec(self): + print "Exec a python command." + print "You can also use '!' shortcut." + + def help_exit(self): + print "Exit the interpreter." + print "You can also use the Ctrl-D shortcut." + + def help_help(self): + print "Print help" + + def postloop(self): + print '\nGoodbye !' + super(DebugCmd, self).postloop() + + do_EOF = do_exit + help_EOF = help_exit |