def context_title(self, m): line_color = pwngef.config.get("theme.context_title_line") msg_color = pwngef.config.get("theme.context_title_message") if not m: print( Color.colorify( pwngef.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=pwngef.config.HORIZONTAL_LINE), line_color) title += Color.colorify(m, msg_color) title += Color.colorify( " {:{padd}<4}".format("", padd=pwngef.config.HORIZONTAL_LINE), line_color) print(title) return None
def do_invoke(self, args): self.dont_repeat() argc = len(args) if argc == 0: 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[0] if not self.has_setting(setting): message.error("Invalid key") return None if argc == 1: value = self.get_setting(setting) value = Color.colorify(value, value) print("{:40s}: {:s}".format(setting, value)) return None val = [x for x in args[1:] if x in Color.colors] self.add_setting(setting, " ".join(val)) return None
def flags_as_string(self): flags = [] if self.has_p_bit(): flags.append(Color.colorify("PREV_INUSE", "red bold")) if self.has_m_bit(): flags.append(Color.colorify("IS_MMAPPED", "red bold")) if self.has_n_bit(): flags.append(Color.colorify("NON_MAIN_ARENA", "red bold")) return "|".join(flags)
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 = pwngef.config.get("theme.registers_value_changed") regname_color = pwngef.config.get("theme.registers_register_name") line = '' # Print registers value for reg in registers: if reg in ignored_registers: continue try: r = pwngef.regs.get_register(reg) if r.type.code == gdb.TYPE_CODE_VOID: continue new_value_type_flag = (r.type.code == gdb.TYPE_CODE_FLAGS) new_value = int(r) if r > 0 else pwngef.arch.ptrmask + int(r) + 1 except (gdb.MemoryError, gdb.error): # If this exception is triggered, it means that the current register # is corrupted. Just use the register "raw" value (not eval-ed) new_value = pwngef.regs.get_register(reg) new_value_type_flag = False except Exception: new_value = 0 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 += pwngef.chain.format(value) print(line) line = "" # Print Flags if flags and pwngef.arch.CURRENT_ARCH.flags_table: print("Flags: {:s}".format( pwngef.arch.CURRENT_ARCH.flag_register_to_human()))
def context_threads(self): def reason(): res = gdb.execute("info program", to_string=True).splitlines() if not res: return "NOT RUNNING" for line in res: line = line.strip() if line.startswith("It stopped with signal "): return line.replace("It stopped with signal ", "").split(",", 1)[0] if line == "The program being debugged is not being run.": return "NOT RUNNING" if line == "It stopped at a breakpoint that has since been deleted.": return "TEMPORARY BREAKPOINT" if line.startswith("It stopped at breakpoint "): return "BREAKPOINT" if line == "It stopped after being stepped.": return "SINGLE STEP" return "STOPPED" self.context_title("threads") threads = gdb.selected_inferior().threads()[::-1] idx = self.get_setting("nb_lines_threads") if idx > 0: threads = threads[0:idx] if idx == 0: return None if not threads: message.error("No thread selected") return None for i, thread in enumerate(threads): line = """[{:s}] Id {:d}, Name: "{:s}", """.format( Color.colorify("#{:d}".format(i), "bold pink"), thread.num, thread.name or "") if thread.is_running(): line += Color.colorify("running", "bold green") elif thread.is_stopped(): line += Color.colorify("stopped", "bold red") line += ", reason: {}".format( Color.colorify(reason(), "bold pink")) elif thread.is_exited(): line += Color.colorify("exited", "bold yellow") print(line) i += 1 return None
def show_legend(self): if pwngef.config.get("self.disable_colors") is not True: str_color = pwngef.config.get("theme.dereference_string") code_addr_color = pwngef.config.get("theme.address_code") stack_addr_color = pwngef.config.get("theme.address_stack") heap_addr_color = pwngef.config.get("theme.address_heap") changed_register_color = pwngef.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
def invoke(self, args, from_tty): self.dont_repeat() if not os.access(pwngef.config.PWNGEF_RC, os.R_OK): return None quiet = args.lower() == "quiet" cfg = configparser.ConfigParser() cfg.read(pwngef.config.PWNGEF_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 = pwngef.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 pwngef.config.set(key, new_value, _doc) except Exception: pass if not quiet: message.success("Configuration from '{:s}' restored".format( Color.colorify(pwngef.config.PWNGEF_RC, "bold blue"))) return None
def context_source(self): try: pc = pwngef.arch.CURRENT_ARCH.pc symtabline = gdb.find_pc_line(pc) symtab = symtabline.symtab line_num = symtabline.line - 1 # we substract one because line number returned by gdb start at 1 if not symtab.is_valid(): return None fpath = symtab.fullname() with open(fpath, "r") as f: lines = [l.rstrip() for l in f.readlines()] except Exception: return None nb_line = self.get_setting("nb_lines_code") fn = symtab.filename if len(fn) > 20: fn = "{}[...]{}".format(fn[:15], os.path.splitext(fn)[1]) title = "source:{}+{}".format(fn, line_num + 1) cur_line_color = pwngef.config.get("theme.source_current_line") self.context_title(title) for i in range(line_num - nb_line + 1, line_num + nb_line): if i < 0: continue if i < line_num: print( Color.grayify(" {:4d}\t {:s}".format( i + 1, lines[i], ))) if i == line_num: extra_info = self.get_pc_context_info(pc, lines[i]) prefix = "{}{:4d}\t ".format(config_arrow_right, i + 1) leading = len(lines[i]) - len(lines[i].lstrip()) if extra_info: print("{}{}".format(" " * (len(prefix) + leading), extra_info)) print( Color.colorify("{}{:s}".format(prefix, lines[i]), cur_line_color)) if i > line_num: try: print(" {:4d}\t {:s}".format( i + 1, lines[i], )) except IndexError: break 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 = pwngef.config.get("theme.dereference_base_address") string_color = pwngef.config.get("theme.dereference_string") config_arrow_right = pwngef.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 = pwngef.symbol.get(link) or None # if symbol: # symbol = '%#x (%s)' % (link, symbol) # rest.append(M.get(link, symbol)) return arrow_right.join(rest)
def context_trace(self): self.context_title("trace") nb_backtrace = self.get_setting("nb_lines_backtrace") if nb_backtrace <= 0: return None orig_frame = current_frame = gdb.selected_frame() i = 0 # backward compat for gdb (gdb < 7.10) if not hasattr(gdb, "FrameDecorator"): gdb.execute("backtrace {:d}".format(nb_backtrace)) return None while current_frame: current_frame.select() if not current_frame.is_valid(): continue pc = current_frame.pc() name = current_frame.name() items = [] items.append("{:#x}".format(pc)) if name: frame_args = gdb.FrameDecorator.FrameDecorator( current_frame).frame_args() or [] m = "{}({})".format( Color.greenify(name), ", ".join([ "{}={!s}".format(Color.yellowify(x.sym), x.sym.value(current_frame)) for x in frame_args ])) items.append(m) else: try: insn = next(disass.gef_disassemble(pc, 1)) except gdb.MemoryError: break items.append( Color.redify("{} {}".format(insn.mnemonic, ", ".join(insn.operands)))) print("[{}] {}".format( Color.colorify("#{}".format(i), "bold pink"), config_arrow_right.join(items))) current_frame = current_frame.older() i += 1 nb_backtrace -= 1 if nb_backtrace == 0: break orig_frame.select() return None
def load(self, initial=False): """Load all the commands and functions defined by PWNGEF into GDB.""" nb_missing = 0 self.commands = [(x._cmdline_, x) for x in pwngef.commands.__commands__] # load all of the functions for function_class_name in pwngef.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("PWNGEF"), 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 print_setting(self, plugin_name, verbose=False): res = pwngef.config.get(plugin_name, get_all=True) string_color = pwngef.config.get("theme.dereference_string") misc_color = pwngef.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 __load_extra_plugins(self): nb_added = -1 try: nb_inital = len(self.loaded_commands) directories = pwngef.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
def context_code(self): nb_insn = self.get_setting("nb_lines_code") nb_insn_prev = self.get_setting("nb_lines_code_prev") use_capstone = self.has_setting("use_capstone") and self.get_setting( "use_capstone") use_ida = self.get_setting("use_ida") cur_insn_color = pwngef.config.get( "theme.disassemble_current_instruction") pc = int(pwngef.arch.CURRENT_ARCH.pc) frame = gdb.selected_frame() arch_name = "{}:{}".format(pwngef.arch.CURRENT_ARCH.arch.lower(), pwngef.arch.CURRENT_ARCH.mode) self.context_title("code:{}".format(arch_name)) try: instruction_iterator = disass.capstone_disassemble if use_capstone else disass.gef_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 pwngef.arch.CURRENT_ARCH.is_conditional_branch(insn): is_taken, reason = pwngef.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 pwngef.arch.CURRENT_ARCH.is_call( insn) and self.get_setting("peek_calls") is True: target = insn.operands[-1].split()[0] elif pwngef.arch.CURRENT_ARCH.is_ret( insn) and self.get_setting("peek_ret") is True: target = int( pwngef.arch.CURRENT_ARCH.get_ra(insn, frame)) 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( pwngef.config.DOWN_ARROW if i == 0 else " ", str(tinsn)) print(text) break except gdb.MemoryError: message.error("Cannot disassemble from $PC") except Exception: import traceback print(traceback.format_exc()) 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(pwngef.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(pwngef.arch.CURRENT_ARCH.pc) block_start = __get_current_block_start_address() use_capstone = self.has_setting("use_capstone") and self.get_setting( "use_capstone") instruction_iterator = disass.capstone_disassemble if use_capstone else disass.gef_disassemble function_parameters = pwngef.arch.CURRENT_ARCH.function_parameters arg_key_color = pwngef.config.get("theme.registers_register_name") insn_count = ( pc - block_start) // pwngef.arch.CURRENT_ARCH.instruction_length if pwngef.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 pwngef.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 pwngef.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 pwngef.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 = pwngef.arch.CURRENT_ARCH.get_ith_parameter(i) _value = pwngef.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 flag_register_to_human(self, val=None): return Color.colorify("No flag register", "yellow underline")
def __str__(self): msg = "{:s}(addr={:#x}, size={:#x}, flags={:s})".format( Color.colorify("Chunk", "yellow bold underline"), int(self.address), self.get_chunk_size(), self.flags_as_string()) return msg