def context_code(self): nb_insn = self.get_setting("nb_lines_code") nb_insn_prev = self.get_setting("nb_lines_code_prev") cur_insn_color = unigdb.config.get("theme.disassemble_current_instruction") pc = int(unigdb.arch.CURRENT_ARCH.pc) # frame = gdb.selected_frame() arch_name = "{}:{}".format(unigdb.arch.CURRENT_ARCH.arch.lower(), unigdb.arch.CURRENT_ARCH.mode) self.context_title("code:{}".format(arch_name)) try: instruction_iterator = disass.capstone_disassemble # instruction_iterator = disass.ida_disassemble if use_ida else instruction_iterator for insn in instruction_iterator(pc, nb_insn, nb_prev=nb_insn_prev): line = [] is_taken = False target = None text = str(insn) if insn.address < pc: line += Color.grayify(" {}".format(text)) elif insn.address == pc: line += Color.colorify("{:s}{:s}".format(config_arrow_right.rjust(3), text), cur_insn_color) if unigdb.arch.CURRENT_ARCH.is_conditional_branch(insn): is_taken, reason = unigdb.arch.CURRENT_ARCH.is_branch_taken(insn) if is_taken: target = insn.operands[-1].split()[0] reason = "[Reason: {:s}]".format(reason) if reason else "" line += Color.colorify("\tTAKEN {:s}".format(reason), "bold green") else: reason = "[Reason: !({:s})]".format(reason) if reason else "" line += Color.colorify("\tNOT taken {:s}".format(reason), "bold red") elif unigdb.arch.CURRENT_ARCH.is_call(insn) and self.get_setting("peek_calls") is True: target = insn.operands[-1].split()[0] elif unigdb.arch.CURRENT_ARCH.is_ret(insn) and self.get_setting("peek_ret") is True: target = int(unigdb.arch.CURRENT_ARCH.get_ra(insn)) else: line += " {}".format(text) print("".join(line)) if target: try: target = int(target, 0) except TypeError: # Already an int pass except ValueError: # If the operand isn't an address right now we can't parse it continue for i, tinsn in enumerate(instruction_iterator(target, nb_insn)): text = " {} {}".format(unigdb.config.DOWN_ARROW if i == 0 else " ", str(tinsn)) print(text) break # except Exception as e: # message.error("Cannot disassemble from $PC: %s" % e) except Exception: import traceback print(traceback.format_exc()) return None
def print_registers(registers, ignored_registers=[], old_registers={}, flags=False): '''Print dereferenced registers Arguments: registers(list): List of printed registers ignored_registers(list): List of registers witch didn't printed old_registers(list): Old registers, needed for check if registers was changed flags(bool): Print flags Returns: A string representing pointers of each address and reference REG_NAME: 0x0804a10 —▸ 0x08061000 —▸ AAAA ''' widest = max(map(len, registers)) changed_color = unigdb.config.get("theme.registers_value_changed") regname_color = unigdb.config.get("theme.registers_register_name") line = '' # Print registers value for reg in registers: if reg in ignored_registers: continue try: r = unigdb.regs.get_register(reg) # if r.type.code == gdb.TYPE_CODE_VOID: # continue new_value_type_flag = False new_value = int(r) if r >= 0 else unigdb.arch.ptrmask + int(r) + 1 except Exception: # If this exception is triggered, it means that the current register # is corrupted. Just use the register "raw" value (not eval-ed) new_value = unigdb.regs.get_register(reg) new_value_type_flag = False except Exception: new_value = 0 new_value_type_flag = False old_value = old_registers.get(reg, 0) padreg = reg.ljust(widest, " ") value = new_value if value == old_value: line += "{}: ".format(Color.colorify(padreg, regname_color)) else: line += "{}: ".format(Color.colorify(padreg, changed_color)) if new_value_type_flag: line += "{:s} ".format(str(value)) else: line += unigdb.chain.format(value) print(line) line = "" # Print Flags if flags and unigdb.arch.CURRENT_ARCH.flags_table: print("Flags: {:s}".format( unigdb.arch.CURRENT_ARCH.flag_register_to_human()))
def initial_message(): msg = "{:s} for {:s} ready, type `{:s}' to start, `{:s}' to configure\n".format( Color.greenify("UniGDB"), get_os(), Color.colorify("self", "underline yellow"), Color.colorify("self config", "underline pink") ) ver = "{:d}.{:d}".format(sys.version_info.major, sys.version_info.minor) nb_cmds = len(unigdb.commands.__commands__) msg += "{:s} commands loaded using Python engine {:s}".format( Color.colorify(nb_cmds, "bold green"), Color.colorify(ver, "bold red")) return msg
class NextInstCommand(GenericCommand): """Step one instruction, but proceed through subroutine calls.""" _cmdline_ = 'nexti' _aliases_ = [ "ni", ] def __init__(self, cls): super(NextInstCommand, self).__init__(cls) next_parser = argparse.ArgumentParser(description=Color.yellowify(__doc__), add_help=False) next_parser.add_argument('n', type=int, metavar='N', nargs=argparse.OPTIONAL, help='Step N times') @cmd2.with_argparser(next_parser) def do_nexti(self, args: argparse.Namespace): pc = int(unigdb.arch.CURRENT_ARCH.pc) insn = disass.get_current_instruction(pc) if not args.n: step_over = int(disass.get_next_instruction(pc).address) - pc else: step_over = unigdb.arch.CURRENT_ARCH.instruction_length * args.n if unigdb.arch.CURRENT_ARCH.is_call(insn): if unigdb.arch.CURRENT_ARCH.arch == 'MIPS': step_over = unigdb.arch.CURRENT_ARCH.instruction_length * 2 setBreakpoint(pc + step_over, temporary=True) unigdb.proc.alive = True unigdb.arch.UC.emu_start(begin=pc, until=pc + 100)
class DetailRegistersCommand(GenericCommand): """Display full details on one, many or all registers value from current architecture.""" _cmdline_ = "registers" _aliases_ = [ "regs", ] def __init__(self, cls): super(DetailRegistersCommand, self).__init__(cls) reg_parser = argparse.ArgumentParser(description=Color.yellowify(__doc__), add_help=False) reg_parser.add_argument('regs', nargs='*', help='Registers for show') @unigdb.proc.OnlyWhenInit @cmd2.with_argparser(reg_parser) def do_registers(self, args: argparse.Namespace): if args.regs: regs = [ reg for reg in unigdb.arch.CURRENT_ARCH.all_registers if reg in args.regs ] if not regs: message.warn("No matching registers found") else: regs = unigdb.arch.CURRENT_ARCH.all_registers print_registers(registers=regs)
def invoke(self, args, from_tty): self.dont_repeat() if not os.access(unigdb.config.UNIGDB_RC, os.R_OK): return None quiet = args.lower() == "quiet" cfg = configparser.ConfigParser() cfg.read(unigdb.config.UNIGDB_RC) for section in cfg.sections(): if section == "aliases": # load the aliases for key in cfg.options(section): SelfAlias(key, cfg.get(section, key)) continue # load the other options for optname in cfg.options(section): try: key = "{:s}.{:s}".format(section, optname) _value, _doc = unigdb.config.get(key, get_all=True) new_value = cfg.get(section, optname) if isinstance(_value, bool): new_value = True if new_value == "True" else False new_value = int(new_value) if new_value.isdigit( ) or isinstance(_value, int) else new_value unigdb.config.set(key, new_value, _doc) except Exception: pass if not quiet: message.success("Configuration from '{:s}' restored".format( Color.colorify(unigdb.config.UNIGDB_RC, "bold blue"))) return None
class BreakCommand(GenericCommand): """Set breakpoint at specified location.""" _cmdline_ = 'break' _aliases_ = [ "b", ] def __init__(self, cls): super(BreakCommand, self).__init__(cls) break_parser = argparse.ArgumentParser( description=Color.yellowify(__doc__), add_help=False) break_parser.add_argument('location', metavar='LOCATION', nargs=argparse.OPTIONAL, help='Address for breakpoint') @cmd2.with_argparser(break_parser) def do_break(self, args: argparse.Namespace): if args.location: args.location = parse_and_eval(args.location) setBreakpoint(args.location, temporary=False) message.success('Set breakpoint to %#08x' % args.location) else: message.hint('Current breakpoints:') print('Address\tTemporary') for item in unigdb.commands.breakpoint._breakpoints_: print('%#x\t%s' % (item, hasBreakpoint(item)))
def show_legend(self): if unigdb.config.get("self.disable_colors") is not True: str_color = unigdb.config.get("theme.dereference_string") code_addr_color = unigdb.config.get("theme.address_code") stack_addr_color = unigdb.config.get("theme.address_stack") heap_addr_color = unigdb.config.get("theme.address_heap") changed_register_color = unigdb.config.get("theme.registers_value_changed") print("[ Legend: {} | {} | {} | {} | {} ]".format( Color.colorify("Modified register", changed_register_color), Color.colorify("Code", code_addr_color), Color.colorify("Heap", heap_addr_color), Color.colorify("Stack", stack_addr_color), Color.colorify("String", str_color) )) return None
class LoadCommand(GenericCommand): """Dynamically load FILE into the enviroment for access from UniGDB.""" _cmdline_ = "load" def __init__(self, cls): super(LoadCommand, self).__init__(cls) load_parser = cmd2.Cmd2ArgumentParser(description=Color.yellowify(__doc__), add_help=False) load_parser.add_argument('code', choices=['code', 'data'], help='Set $pc if needed') load_parser.add_argument('mode', choices=['binary', 'hex'], help='File mode (binary file or hex file)') load_parser.add_argument('file', metavar='FILE', completer_method=cmd2.Cmd.path_complete, help='Path to file for load in memory') load_parser.add_argument('offset', metavar='OFFSET', help='Address in mapped spaces for insert file data') @cmd2.with_argparser(load_parser) def do_load(self, args: argparse.Namespace): args.offset = parse_and_eval(args.offset) if not os.path.exists(args.file): message.error('File not found: %s' % args.file) if args.mode == 'binary': data = open(args.file, 'rb').read() else: data = binascii.unhexlify(open(args.file).read()) unigdb.memory.write(args.offset, data) if args.code == 'code': self.cls.do_set('$pc %#08x' % args.offset)
def flags_to_human(reg_value, value_table): """Return a human readable string showing the flag states.""" flags = [] for i in value_table: flag_str = Color.boldify(value_table[i].upper()) if reg_value & ( 1 << i) else value_table[i].lower() flags.append(flag_str) return "[{}]".format(" ".join(flags))
def load(self, initial=False): """Load all the commands and functions defined by UNIGDB into GDB.""" nb_missing = 0 self.commands = [(x._cmdline_, x) for x in unigdb.commands.__commands__] # load all of the functions for function_class_name in unigdb.functions.__functions__: self.loaded_functions.append(function_class_name()) def is_loaded(x): return any(filter(lambda u: x == u[0], self.loaded_commands)) for cmd, class_name in self.commands: if is_loaded(cmd): continue try: self.loaded_commands.append((cmd, class_name, class_name())) if hasattr(class_name, "_aliases_"): aliases = getattr(class_name, "_aliases_") for alias in aliases: SelfAlias(alias, cmd) except Exception as reason: self.missing_commands[cmd] = reason nb_missing += 1 # sort by command name self.loaded_commands = sorted(self.loaded_commands, key=lambda x: x[1]._cmdline_) if initial: print( "{:s} for {:s} ready, type `{:s}' to start, `{:s}' to configure" .format(Color.greenify("UNIGDB"), get_os(), Color.colorify("self", "underline yellow"), Color.colorify("self config", "underline pink"))) ver = "{:d}.{:d}".format(sys.version_info.major, sys.version_info.minor) nb_cmds = len(self.loaded_commands) print("{:s} commands loaded for GDB {:s} using Python engine {:s}". format(Color.colorify(nb_cmds, "bold green"), Color.colorify(gdb.VERSION, "bold yellow"), Color.colorify(ver, "bold red"))) if nb_missing: message.warn( "{:s} command{} could not be loaded, run `{:s}` to know why." .format(Color.colorify(nb_missing, "bold red"), "s" if nb_missing > 1 else "", Color.colorify("self missing", "underline pink"))) return None
def format(value, limit=LIMIT, code=True): """ Recursively dereferences an address into string representation, or convert the list representation of address dereferences into string representation. Arguments: value(int|list): Either the starting address to be sent to get, or the result of get (a list) limit(int): Number of valid pointers code(bool): Hint that indicates the value may be an instruction offset(int): Offset into the address to get the next pointer hard_stop(int): Value to stop on hard_end: Value to append when hard_stop is reached: null, value of hard stop, a string. Returns: A string representing pointers of each address and reference Strings format: 0x0804a10 —▸ 0x08061000 ◂— 0x41414141 """ limit = int(limit) # Get config params base_address_color = unigdb.config.get("theme.dereference_base_address") string_color = unigdb.config.get("theme.dereference_string") config_arrow_right = unigdb.config.get("theme.chain_arrow_right") # Allow results from get function to be passed to format if isinstance(value, list): chain = value else: chain = examine_mem_value(value, limit) # Set arrow separate arrow_right = ' %s ' % config_arrow_right # Colorize the chain rest = [] for link in chain: if isinstance(link, int): rest.append(Color.colorify('%#x' % link, base_address_color)) if isinstance(link, str): rest.append(Color.colorify('"{:s}"'.format(link), string_color)) # symbol = unigdb.symbol.get(link) or None # if symbol: # symbol = '%#x (%s)' % (link, symbol) # rest.append(M.get(link, symbol)) return arrow_right.join(rest)
def do_theme(self, args: argparse.Namespace): if not args.key: for setting in sorted(self.settings): value = self.get_setting(setting) value = Color.colorify(value, value) print("{:40s}: {:s}".format(setting, value)) return None setting = args.key if not self.has_setting(setting): message.error("Invalid key") return None if not args.value: value = self.get_setting(setting) value = Color.colorify(value, value) print("{:40s}: {:s}".format(setting, value)) return None val = [x for x in args.value.split() if x in Color.colors] self.add_setting(setting, " ".join(val)) return None
def print_setting(self, plugin_name, verbose=False): res = unigdb.config.get(plugin_name, get_all=True) string_color = unigdb.config.get("theme.dereference_string") misc_color = unigdb.config.get("theme.dereference_base_address") if not res: return None _value, _desc = res _setting = Color.colorify(plugin_name, "green") _type = type(_value).__name__ if isinstance(_value, str): _value = '"{:s}"'.format(Color.colorify(_value, string_color)) else: _value = Color.colorify(_value, misc_color) print("{:s} ({:s}) = {:s}".format(_setting, _type, _value)) if verbose: print(Color.colorify("\nDescription:", "bold underline")) print("\t{:s}".format(_desc)) return None
def context_title(self, m): line_color = unigdb.config.get("theme.context_title_line") msg_color = unigdb.config.get("theme.context_title_message") if not m: print(Color.colorify(unigdb.config.HORIZONTAL_LINE * self.tty_columns, line_color)) return None trail_len = len(m) + 6 title = "" title += Color.colorify( "{:{padd}<{width}} ".format( "", width=max(self.tty_columns - trail_len, 0), padd=unigdb.config.HORIZONTAL_LINE ), line_color ) title += Color.colorify(m, msg_color) title += Color.colorify(" {:{padd}<4}".format("", padd=unigdb.config.HORIZONTAL_LINE), line_color) print(title) return None
def add_command_to_doc(self, command): """Add command to UNIGDB documentation.""" cmd, class_name, _ = command if " " in cmd: # do not print subcommands in gef help return None doc = getattr(class_name, "__doc__", "").lstrip() doc = "\n ".join(doc.split("\n")) aliases = " (alias: {:s})".format(", ".join( class_name._aliases_)) if hasattr(class_name, "_aliases_") else "" msg = "{cmd:<25s} -- {help:s}{aliases:s}".format( cmd=cmd, help=Color.greenify(doc), aliases=aliases) self.docs.append(msg) return None
def __load_extra_plugins(self): nb_added = -1 try: nb_inital = len(self.loaded_commands) directories = unigdb.config.get("self.extra_plugins_dir") if directories: for directory in directories.split(";"): directory = os.path.realpath(os.path.expanduser(directory)) if os.path.isdir(directory): sys.path.append(directory) for fname in os.listdir(directory): if not fname.endswith(".py"): continue fpath = "{:s}/{:s}".format(directory, fname) if os.path.isfile(fpath): gdb.execute("source {:s}".format(fpath)) nb_added = len(self.loaded_commands) - nb_inital if nb_added > 0: message.success("{:s} extra commands added from '{:s}'".format( Color.colorify(nb_added, "bold green"), Color.colorify(directory, "bold blue"))) except gdb.error as e: message.error("failed: {}".format(str(e))) return nb_added
class ContinueCommand(GenericCommand): """Continue program being debugged, after signal or breakpoint.""" _cmdline_ = "continue" _aliases_ = ["c", ] def __init__(self, cls): super(ContinueCommand, self).__init__(cls) con_parser = argparse.ArgumentParser(description=Color.yellowify(__doc__), add_help=False) con_parser.add_argument('n', metavar='N', nargs=argparse.OPTIONAL, help='Step N times') @cmd2.with_argparser(con_parser) def do_continue(self, args: argparse.Namespace): pc = int(unigdb.arch.CURRENT_ARCH.pc) unigdb.proc.alive = True unigdb.arch.UC.emu_start(begin=pc, until=pc + 10000)
class HexDumpCommand(GenericCommand): '''Hexdumps data at the specified address (or at $sp)''' _cmdline_ = "hexdump" _syntax_ = "{:s} [address|reg] [count]".format(_cmdline_) def __init__(self, cls): super(HexDumpCommand, self).__init__(cls) self.add_setting("hexdump_width", 16, "line width of hexdump command") self.add_setting('hexdump_bytes', 64, 'number of bytes printed by hexdump command') self.add_setting( 'hexdump_colorize_ascii', True, 'whether to colorize the hexdump command ascii section') self.add_setting('hexdump_ascii_block_separator', '│', 'block separator char of the hexdump command') hexdump_parser = cmd2.Cmd2ArgumentParser( description=Color.yellowify(__doc__), add_help=False) hexdump_parser.add_argument('address', nargs=argparse.OPTIONAL, help='Address for dump') hexdump_parser.add_argument('count', nargs=argparse.OPTIONAL, help='Count bytes of read') @cmd2.with_argparser(hexdump_parser) def do_hexdump(self, args: argparse.Namespace): width = self.get_setting('hexdump_width') count = parse_and_eval( args.count) if args.count else self.get_setting('hexdump_bytes') if not args.address: address = int(unigdb.arch.CURRENT_ARCH.sp) else: address = parse_and_eval(args.address) data = unigdb.memory.read(address, count) for _, line in enumerate( unigdb.hexdump.hexdump(data, address=address, width=width)): print(line) return None
def __init__(self, cls): syntax = 'Usage: %s\n\n' % self._cmdline_ self.__doc__ = syntax + Color.yellowify( self.__doc__.replace(" " * 4, "")) + '\n' self.statement_parser = cls.statement_parser self.cls = cls
class ContextCommand(GenericCommand): """Disp+lays a comprehensive and modular summary of runtime context. Unless setting `enable` is set to False, this command will be spawned automatically every time GDB hits a breakpoint, a watchpoint, or any kind of interrupt. By default, it will show panes that contain the register states, the stack, and the disassembly code around $pc.""" _cmdline_ = "context" _aliases_ = ["ctx", ] old_registers = {} def __init__(self, cls): super(ContextCommand, self).__init__(cls) self.add_setting("enable", True, "Enable/disable printing the context when breaking") self.add_setting("show_stack_raw", False, "Show the stack pane as raw hexdump (no dereference)") self.add_setting("show_registers_raw", False, "Show the registers pane with raw values (no dereference)") self.add_setting("peek_calls", True, "Peek into calls") self.add_setting("peek_ret", True, "Peek at return address") self.add_setting("nb_lines_stack", 8, "Number of line in the stack pane") self.add_setting("grow_stack_down", False, "Order of stack downward starts at largest down to stack pointer") self.add_setting("nb_lines_backtrace", 10, "Number of line in the backtrace pane") self.add_setting("nb_lines_threads", -1, "Number of line in the threads pane") self.add_setting("nb_lines_code", 6, "Number of instruction after $pc") self.add_setting("nb_lines_code_prev", 3, "Number of instruction before $pc") self.add_setting("ignore_registers", "", "Space-separated list of registers not to display (e.g. '$cs $ds $gs')") self.add_setting("clear_screen", False, "Clear the screen before printing the context") self.add_setting("layout", "legend regs code stack args memory", "Change the order/presence of the context sections") self.add_setting("redirect", "", "Redirect the context information to another TTY") self.layout_mapping = { "legend": self.show_legend, "regs": self.context_regs, "stack": self.context_stack, "code": self.context_code, "args": self.context_args, "memory": self.context_memory, } return None def post_load(self): unigdb.events.cont(self.update_registers) return None def show_legend(self): if unigdb.config.get("self.disable_colors") is not True: str_color = unigdb.config.get("theme.dereference_string") code_addr_color = unigdb.config.get("theme.address_code") stack_addr_color = unigdb.config.get("theme.address_stack") heap_addr_color = unigdb.config.get("theme.address_heap") changed_register_color = unigdb.config.get("theme.registers_value_changed") print("[ Legend: {} | {} | {} | {} | {} ]".format( Color.colorify("Modified register", changed_register_color), Color.colorify("Code", code_addr_color), Color.colorify("Heap", heap_addr_color), Color.colorify("Stack", stack_addr_color), Color.colorify("String", str_color) )) return None context_parser = argparse.ArgumentParser(description=Color.yellowify(__doc__), add_help=False) context_parser.add_argument('subcommand', nargs='*', default=['legend', 'regs', 'code']) @unigdb.proc.OnlyWhenRunning @cmd2.with_argparser(context_parser) def do_context(self, args: argparse.Namespace): if not self.get_setting("enable") or context_hidden: return None if len(args.subcommand) > 0: current_layout = args.subcommand else: current_layout = self.get_setting("layout").strip().split() if not current_layout: return None self.tty_rows, self.tty_columns = unigdb.ui.get_window_size() redirect = self.get_setting("redirect") if redirect and os.access(redirect, os.W_OK): unigdb.ui.enable_redirect_output(to_file=redirect) if self.get_setting("clear_screen") and len(args.subcommand) == 0: clear_screen(redirect) for section in current_layout: if section[0] == "-": continue try: self.layout_mapping[section]() except Exception as e: # a MemoryError will happen when $pc is corrupted (invalid address) message.error(str(e)) self.context_title("") if redirect and os.access(redirect, os.W_OK): unigdb.ui.disable_redirect_output() return None def context_title(self, m): line_color = unigdb.config.get("theme.context_title_line") msg_color = unigdb.config.get("theme.context_title_message") if not m: print(Color.colorify(unigdb.config.HORIZONTAL_LINE * self.tty_columns, line_color)) return None trail_len = len(m) + 6 title = "" title += Color.colorify( "{:{padd}<{width}} ".format( "", width=max(self.tty_columns - trail_len, 0), padd=unigdb.config.HORIZONTAL_LINE ), line_color ) title += Color.colorify(m, msg_color) title += Color.colorify(" {:{padd}<4}".format("", padd=unigdb.config.HORIZONTAL_LINE), line_color) print(title) return None def context_regs(self): self.context_title("registers") ignored_registers = set(self.get_setting("ignore_registers").split()) if self.get_setting("show_registers_raw") is True: regs = set(unigdb.arch.CURRENT_ARCH.all_registers) printable_registers = " ".join(list(regs - ignored_registers)) self.cls.onecmd_plus_hooks("registers {}".format(printable_registers)) return None unigdb.commands.registers.print_registers( registers=unigdb.arch.CURRENT_ARCH.all_registers, old_registers=self.old_registers, ignored_registers=ignored_registers, flags=unigdb.arch.CURRENT_ARCH.flags_table ) return None def context_stack(self): self.context_title("stack") show_raw = self.get_setting("show_stack_raw") nb_lines = self.get_setting("nb_lines_stack") try: sp = int(unigdb.arch.CURRENT_ARCH.sp) if show_raw is True: mem = unigdb.memory.read(sp, 0x10 * nb_lines) for _, line in enumerate(unigdb.hexdump.hexdump(mem, address=sp)): print(line) else: for offset in range(nb_lines): print(unigdb.chain.format(sp + (offset * unigdb.arch.ptrsize))) # gdb.execute("dereference {:#x} l{:d}".format(sp, nb_lines)) except Exception: message.error("Cannot read memory from $SP (corrupted stack pointer?)") return None def context_code(self): nb_insn = self.get_setting("nb_lines_code") nb_insn_prev = self.get_setting("nb_lines_code_prev") cur_insn_color = unigdb.config.get("theme.disassemble_current_instruction") pc = int(unigdb.arch.CURRENT_ARCH.pc) # frame = gdb.selected_frame() arch_name = "{}:{}".format(unigdb.arch.CURRENT_ARCH.arch.lower(), unigdb.arch.CURRENT_ARCH.mode) self.context_title("code:{}".format(arch_name)) try: instruction_iterator = disass.capstone_disassemble # instruction_iterator = disass.ida_disassemble if use_ida else instruction_iterator for insn in instruction_iterator(pc, nb_insn, nb_prev=nb_insn_prev): line = [] is_taken = False target = None text = str(insn) if insn.address < pc: line += Color.grayify(" {}".format(text)) elif insn.address == pc: line += Color.colorify("{:s}{:s}".format(config_arrow_right.rjust(3), text), cur_insn_color) if unigdb.arch.CURRENT_ARCH.is_conditional_branch(insn): is_taken, reason = unigdb.arch.CURRENT_ARCH.is_branch_taken(insn) if is_taken: target = insn.operands[-1].split()[0] reason = "[Reason: {:s}]".format(reason) if reason else "" line += Color.colorify("\tTAKEN {:s}".format(reason), "bold green") else: reason = "[Reason: !({:s})]".format(reason) if reason else "" line += Color.colorify("\tNOT taken {:s}".format(reason), "bold red") elif unigdb.arch.CURRENT_ARCH.is_call(insn) and self.get_setting("peek_calls") is True: target = insn.operands[-1].split()[0] elif unigdb.arch.CURRENT_ARCH.is_ret(insn) and self.get_setting("peek_ret") is True: target = int(unigdb.arch.CURRENT_ARCH.get_ra(insn)) else: line += " {}".format(text) print("".join(line)) if target: try: target = int(target, 0) except TypeError: # Already an int pass except ValueError: # If the operand isn't an address right now we can't parse it continue for i, tinsn in enumerate(instruction_iterator(target, nb_insn)): text = " {} {}".format(unigdb.config.DOWN_ARROW if i == 0 else " ", str(tinsn)) print(text) break # except Exception as e: # message.error("Cannot disassemble from $PC: %s" % e) except Exception: import traceback print(traceback.format_exc()) return None def context_args(self): insn = disass.get_current_instruction(int(unigdb.arch.CURRENT_ARCH.pc)) if not unigdb.arch.CURRENT_ARCH.is_call(insn): return None self.size2type = { 1: "BYTE", 2: "WORD", 4: "DWORD", 8: "QWORD", } if insn.operands[-1].startswith(self.size2type[unigdb.arch.CURRENT_ARCH.ptrsize] + " PTR"): target = "*" + insn.operands[-1].split()[-1] elif "$" + insn.operands[0] in unigdb.arch.CURRENT_ARCH.all_registers: target = "*{:#x}".format(int(unigdb.regs.get_register("$" + insn.operands[0]))) else: # is there a symbol? ops = " ".join(insn.operands) if "<" in ops and ">" in ops: # extract it target = re.sub(r".*<([^\(> ]*).*", r"\1", ops) else: # it's an address, just use as is target = re.sub(r".*(0x[a-fA-F0-9]*).*", r"\1", ops) self.print_guessed_arguments(target) return None def print_guessed_arguments(self, function_name): """When no symbol, read the current basic block and look for "interesting" instructions.""" def __get_current_block_start_address(): pc = int(unigdb.arch.CURRENT_ARCH.pc) try: block_start = gdb.block_for_pc(pc).start except RuntimeError: # if stripped, let's roll back 5 instructions block_start = disass.gdb_get_nth_previous_instruction_address(pc, 5) return block_start parameter_set = set() pc = int(unigdb.arch.CURRENT_ARCH.pc) block_start = __get_current_block_start_address() instruction_iterator = disass.capstone_disassemble function_parameters = unigdb.arch.CURRENT_ARCH.function_parameters arg_key_color = unigdb.config.get("theme.registers_register_name") insn_count = (pc - block_start) // unigdb.arch.CURRENT_ARCH.instruction_length if unigdb.arch.current == 'mips': insn_count += 1 # for branch delay slot for insn in instruction_iterator(block_start, insn_count): if not insn.operands: continue if unigdb.arch.current == 'i386': if insn.mnemonic == "push": parameter_set.add(insn.operands[0]) else: op = "$" + insn.operands[0] if op in function_parameters: parameter_set.add(op) if unigdb.arch.current == 'x86-64': # also consider extended registers extended_registers = {"$rdi": ["$edi", "$di"], "$rsi": ["$esi", "$si"], "$rdx": ["$edx", "$dx"], "$rcx": ["$ecx", "$cx"], } for exreg in extended_registers: if op in extended_registers[exreg]: parameter_set.add(exreg) # cicle end if unigdb.arch.current == 'i386': nb_argument = len(parameter_set) else: nb_argument = 0 for p in parameter_set: nb_argument = max(nb_argument, function_parameters.index(p) + 1) args = [] for i in range(nb_argument): _key, _value = unigdb.arch.CURRENT_ARCH.get_ith_parameter(i) _value = unigdb.chain.format(int(_value)) args.append("{} = {}".format(Color.colorify(_key, arg_key_color), _value)) self.context_title("arguments (guessed)") print("{} (".format(function_name)) if args: print(" " + ",\n ".join(args)) print(")") return None def context_memory(self): global __watches__ for address, opt in sorted(__watches__.items()): self.context_title("memory:{:#x}".format(address)) self.cls.onecmd_plus_hooks("hexdump {fmt:s} {address:d} {size:d}".format( address=address, size=opt[0], fmt=opt[1] )) @classmethod def update_registers(cls, event): for reg in unigdb.arch.CURRENT_ARCH.all_registers: try: cls.old_registers[reg] = unigdb.regs.get_register(reg) except Exception: cls.old_registers[reg] = 0 return None
def print_guessed_arguments(self, function_name): """When no symbol, read the current basic block and look for "interesting" instructions.""" def __get_current_block_start_address(): pc = int(unigdb.arch.CURRENT_ARCH.pc) try: block_start = gdb.block_for_pc(pc).start except RuntimeError: # if stripped, let's roll back 5 instructions block_start = disass.gdb_get_nth_previous_instruction_address(pc, 5) return block_start parameter_set = set() pc = int(unigdb.arch.CURRENT_ARCH.pc) block_start = __get_current_block_start_address() instruction_iterator = disass.capstone_disassemble function_parameters = unigdb.arch.CURRENT_ARCH.function_parameters arg_key_color = unigdb.config.get("theme.registers_register_name") insn_count = (pc - block_start) // unigdb.arch.CURRENT_ARCH.instruction_length if unigdb.arch.current == 'mips': insn_count += 1 # for branch delay slot for insn in instruction_iterator(block_start, insn_count): if not insn.operands: continue if unigdb.arch.current == 'i386': if insn.mnemonic == "push": parameter_set.add(insn.operands[0]) else: op = "$" + insn.operands[0] if op in function_parameters: parameter_set.add(op) if unigdb.arch.current == 'x86-64': # also consider extended registers extended_registers = {"$rdi": ["$edi", "$di"], "$rsi": ["$esi", "$si"], "$rdx": ["$edx", "$dx"], "$rcx": ["$ecx", "$cx"], } for exreg in extended_registers: if op in extended_registers[exreg]: parameter_set.add(exreg) # cicle end if unigdb.arch.current == 'i386': nb_argument = len(parameter_set) else: nb_argument = 0 for p in parameter_set: nb_argument = max(nb_argument, function_parameters.index(p) + 1) args = [] for i in range(nb_argument): _key, _value = unigdb.arch.CURRENT_ARCH.get_ith_parameter(i) _value = unigdb.chain.format(int(_value)) args.append("{} = {}".format(Color.colorify(_key, arg_key_color), _value)) self.context_title("arguments (guessed)") print("{} (".format(function_name)) if args: print(" " + ",\n ".join(args)) print(")") return None
class GefThemeCommand(GenericCommand): """Customize UniGDB appearance.""" _cmdline_ = "theme" def __init__(self, cls): super(GefThemeCommand, self).__init__(cls) self.add_setting("context_title_line", "gray", "Color of the borders in context window") self.add_setting("context_title_message", "cyan", "Color of the title in context window") self.add_setting("default_title_line", "gray", "Default color of borders") self.add_setting("default_title_message", "cyan", "Default color of title") self.add_setting( "table_heading", "blue", "Color of the column headings to tables (e.g. vmmap)") self.add_setting( "disassemble_current_instruction", "green", "Color to use to highlight the current $pc when disassembling") self.add_setting("dereference_string", "yellow", "Color of dereferenced string") self.add_setting("dereference_code", "gray", "Color of dereferenced code") self.add_setting("dereference_base_address", "cyan", "Color of dereferenced address") self.add_setting("dereference_register_value", "bold blue", "Color of dereferenced register") self.add_setting("registers_register_name", "blue", "Color of the register name in the register window") self.add_setting( "registers_value_changed", "bold red", "Color of the changed register in the register window") self.add_setting("address_stack", "pink", "Color to use when a stack address is found") self.add_setting("address_heap", "green", "Color to use when a heap address is found") self.add_setting("address_code", "red", "Color to use when a code address is found") self.add_setting( "source_current_line", "green", "Color to use for the current code line in the source window") self.add_setting('chain_arrow_left', '◂—', 'left arrow of chain formatting') self.add_setting('chain_arrow_right', '—▸', 'right arrow of chain formatting') return None theme_parser = argparse.ArgumentParser( description=Color.yellowify(__doc__), add_help=False) theme_parser.add_argument('key', nargs=argparse.OPTIONAL, help='Theme param name') theme_parser.add_argument('value', nargs=argparse.OPTIONAL, help='Theme param value') @cmd2.with_argparser(theme_parser) def do_theme(self, args: argparse.Namespace): if not args.key: for setting in sorted(self.settings): value = self.get_setting(setting) value = Color.colorify(value, value) print("{:40s}: {:s}".format(setting, value)) return None setting = args.key if not self.has_setting(setting): message.error("Invalid key") return None if not args.value: value = self.get_setting(setting) value = Color.colorify(value, value) print("{:40s}: {:s}".format(setting, value)) return None val = [x for x in args.value.split() if x in Color.colors] self.add_setting(setting, " ".join(val)) return None
def flag_register_to_human(self, val=None): return Color.colorify("No flag register", "yellow underline")