miasm/analysis/debugging.py
from __future__ import print_function
from builtins import map
from builtins import range
import cmd
from future.utils import viewitems
from miasm.core.utils import hexdump
from miasm.core.interval import interval
import miasm.jitter.csts as csts
from miasm.jitter.jitload import ExceptionHandle
class DebugBreakpoint(object):
"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 DebugBreakpointTerminate(DebugBreakpoint):
"Stand for an execution termination"
def __init__(self, status):
self.status = status
def __str__(self):
return "Terminate with %s" % self.status
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 viewitems(self.type2str):
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 viewitems(cls.type2str):
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.mem_bp_list = [] # DebugBreakpointMemory list
self.mem_watched = [] # Memory areas watched
self.init_memory_breakpoint()
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_MEMORY"
def exception_memory_breakpoint(jitter):
"Stop the execution and return an identifier"
return ExceptionHandle.memoryBreakpoint()
self.myjit.add_exception_handler(csts.EXCEPT_BREAKPOINT_MEMORY,
exception_memory_breakpoint)
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.mem_bp_list.append(dbm)
self.myjit.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.mem_bp_list.remove(dbm)
self.myjit.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.mem_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 not res:
# A breakpoint has stopped the execution
return DebugBreakpointTerminate(res)
if isinstance(res, DebugBreakpointSoft):
print("Breakpoint reached @0x%08x" % res.addr)
elif isinstance(res, ExceptionHandle):
if res == ExceptionHandle.memoryBreakpoint():
print("Memory breakpoint reached @0x%08x" % self.myjit.pc)
memory_read = self.myjit.vm.get_memory_read()
if len(memory_read) > 0:
print("Read:")
for start_address, end_address in memory_read:
print("- from 0x%08x to 0x%08x" % (start_address, end_address))
memory_write = self.myjit.vm.get_memory_write()
if len(memory_write) > 0:
print("Write:")
for start_address, end_address in memory_write:
print("- from 0x%08x to 0x%08x" % (start_address, end_address))
# Remove flag
except_flag = self.myjit.vm.get_exception()
self.myjit.vm.set_exception(except_flag ^ res.except_flag)
# Clean memory access data
self.myjit.vm.reset_memory_access()
else:
raise NotImplementedError("Unknown Except")
else:
raise NotImplementedError("type res")
# Repropagate res
return res
def step(self):
"Step in jit"
self.myjit.jit.set_options(jit_maxline=1)
# Reset all jitted blocks
self.myjit.jit.clear_jitted_blocks()
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):
status = self.myjit.continue_run()
return self.handle_exception(status)
def get_mem(self, addr, size=0xF):
"hexdump @addr, size"
hexdump(self.myjit.vm.get_mem(addr, size))
def get_mem_raw(self, addr, size=0xF):
"hexdump @addr, size"
return self.myjit.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.lifter.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.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_memory_breakpoints(self):
bp_list = self.dbg.mem_bp_list
if len(bp_list) == 0:
print("No memory breakpoints.")
else:
for _, bp in enumerate(bp_list):
print(str(bp))
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, list(regs) + [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(viewitems(regs), 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:
addr = int(addr, 0)
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",
"m": "memory_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:
size = int(args[1], 0)
else:
size = 0xF
addr = int(args[0], 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",
"memory_breakpoint",
"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("m"):
# Memory breakpoints
self.print_memory_breakpoints()
if arg.startswith("d"):
# Display
self.update_display_mode()
for k, v in viewitems(self.display_mode):
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_memory_breakpoint(self, arg):
if arg == "":
self.help_memory_breakpoint()
return
args = arg.split(" ")
if len(args) > 3 or len(args) <= 1:
self.help_memory_breakpoint()
return
address = int(args[0], 0)
size = int(args[1], 0)
if len(args) == 2:
self.dbg.add_memory_breakpoint(address, size, read=True, write=True)
else:
self.dbg.add_memory_breakpoint(address,
size,
read=('r' in args[2]),
write=('w' in args[2]))
def help_memory_breakpoint(self):
print("Add memory breakpoints to memory space defined by a starting")
print("address and a size on specified access type (default is 'rw').")
print("Example:")
print("\tmemory_breakpoint 0x11223344 0x100 r")
print("\tmemory_breakpoint 1122 10")
def do_step(self, arg):
if arg == "":
nb = 1
else:
nb = int(arg)
for _ in range(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:
size = int(args[1], 0)
else:
size = 0xF
addr = int(args[0], 0)
self.dbg.get_mem(addr, size)
def help_dump(self):
print("Dump <addr> [size]. Dump size bytes at addr.")
def do_run(self, _):
self.dbg.run()
def help_run(self):
print("Launch or continue the current program")
def do_exit(self, _):
return True
def do_exec(self, line):
try:
print(eval(line))
except Exception as error:
print("*** Error: %s" % error)
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