Beispiel #1
0
    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
Beispiel #2
0
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()))
Beispiel #3
0
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
Beispiel #4
0
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)
Beispiel #5
0
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)
Beispiel #6
0
    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
Beispiel #7
0
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)))
Beispiel #8
0
    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
Beispiel #9
0
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)
Beispiel #10
0
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))
Beispiel #11
0
    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
Beispiel #12
0
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)
Beispiel #13
0
    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
Beispiel #14
0
    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
Beispiel #15
0
    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
Beispiel #16
0
 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
Beispiel #17
0
 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
Beispiel #18
0
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)
Beispiel #19
0
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
Beispiel #20
0
 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
Beispiel #21
0
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
Beispiel #22
0
    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
Beispiel #23
0
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
Beispiel #24
0
 def flag_register_to_human(self, val=None):
     return Color.colorify("No flag register", "yellow underline")