diff --git a/docs/commands/config.md b/docs/commands/config.md index a3db07edc..aae77f305 100644 --- a/docs/commands/config.md +++ b/docs/commands/config.md @@ -14,8 +14,8 @@ Or get one setting value: gef➤ gef config pcustom.struct_path ``` -Feel free to edit the values. For example, if you want the screen to be cleared -before displaying the current context when reaching a breakpoing: +Of course you can edit the values. For example, if you want the screen to be +cleared before displaying the current context when reaching a breakpoing: ``` gef➤ gef config context.clear_screen 1 ``` @@ -36,4 +36,5 @@ gef➤ gef restore [+] Configuration from '/home/hugsy/.gef.rc' restored ``` -You can tweak this configuration outside your `gdb` session to suit your needs. +You can tweak this configuration file outside your `gdb` session to suit your +needs. diff --git a/docs/commands/context.md b/docs/commands/context.md old mode 100644 new mode 100755 index 44eaebf9e..f58638bfa --- a/docs/commands/context.md +++ b/docs/commands/context.md @@ -42,13 +42,13 @@ There are currently 6 sections that can be displayed: heap vulnerability, etc.) it will be displayed in this pane * `memory` : peek into arbitrary memory locations -To prevent one section to be displayed, simply use the `context.layout` setting, -and prepend the section name with `-` or just omit it. +To hide a section, simply use the `context.layout` setting, and prepend the +section name with `-` or just omit it. ``` gef➤ gef config context.layout "regs stack code -source -threads -trace extra memory" ``` -Will not display the `source`, `threads`, and `trace` sections. +This configuration will not display the `source`, `threads`, and `trace` sections. The `memory` pane will display the content of all locations specified by the `memory` command. For instance, @@ -57,10 +57,18 @@ The `memory` pane will display the content of all locations specified by the gef➤ memory watch $sp 0x40 byte ``` -will print an -hexdump version of 0x40 byte of the stack. This command makes it convenient for -tracking the evolution of arbitrary locations in memory. Tracked locations can -be removed one by one using `memory unwatch`, or altogether with `memory reset`. +will print a hexdump version of 0x40 bytes of the stack. This command makes it +convenient for tracking the evolution of arbitrary locations in memory. Tracked +locations can be removed one by one using `memory unwatch`, or altogether with +`memory reset`. + +======= +The size of most sections are also customizable: + +* `nb_lines_stack` configures how many lines of the stack to show. +* `nb_lines_backtrack` configures how many lines of the backtrace to show. +* `nb_lines_code` and `nb_lines_code_prev` configure how many lines to show + after and before the PC, respectively. To have the stack displayed with the largest stack addresses on top (i.e., grow the stack downward), enable the following setting: @@ -122,9 +130,9 @@ gef➤ gef config context.redirect "" ### Examples ### -* Display the code section first, then register, and stack: +* Display the code section first, then register, and stack, hiding everything else: ``` -gef➤ gef config context.layout "code regs stack -source -threads -trace" +gef➤ gef config context.layout "code regs stack" ``` * Stop showing the context sections when breaking: @@ -146,3 +154,8 @@ gef➤ gef config context.show_registers_raw 0 ``` gef➤ gef config context.peek_calls False ``` + +* Hide specific registers from the registers view. +``` +gef➤ gef config context.ignore_registers "$cs $ds $gs" +``` diff --git a/gef.py b/gef.py index 1bdf9ecaa..c451c689c 100644 --- a/gef.py +++ b/gef.py @@ -31,7 +31,7 @@ ####################################################################################### # # gef is distributed under the MIT License (MIT) -# Copyright (c) 2013-2017 crazy rabbidz +# Copyright (c) 2013-2018 crazy rabbidz # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -53,6 +53,8 @@ # # +# x86 aggregate selectors + from __future__ import print_function, division, absolute_import import abc @@ -363,16 +365,27 @@ def __init__(self, *args, **kwargs): return def __str__(self): - return hex(self.value) + value = format_address( self.value ) + code_color = get_gef_setting("theme.address_code") + stack_color = get_gef_setting("theme.address_stack") + heap_color = get_gef_setting("theme.address_heap") + if self.is_in_text_segment(): + return Color.colorify(value, attrs=code_color) + if self.is_in_heap_segment(): + return Color.colorify(value, attrs=heap_color) + if self.is_in_stack_segment(): + return Color.colorify(value, attrs=stack_color) + return value def is_in_text_segment(self): - return hasattr(self.info, "name") and ".text" in self.info.name + return (hasattr(self.info, "name") and ".text" in self.info.name) or \ + (get_filepath() == self.section.path and self.section.is_executable()) def is_in_stack_segment(self): - return hasattr(self.info, "name") and "[stack]" in self.info.name + return hasattr(self.section, "path") and "[stack]" == self.section.path def is_in_heap_segment(self): - return hasattr(self.info, "name") and "[heap]" in self.info.name + return hasattr(self.section, "path") and "[heap]" == self.section.path def dereference(self): addr = align_address(long(self.value)) @@ -1119,7 +1132,7 @@ def gef_instruction_n(addr, n): def gef_get_instruction_at(addr): """Return the full Instruction found at the specified address.""" - insn = list(gef_disassemble(addr, 1, from_top=True))[0] + insn = next(gef_disassemble(addr, 1)) return insn @@ -1133,16 +1146,15 @@ def gef_next_instruction(addr): return gef_instruction_n(addr, 1) -def gef_disassemble(addr, nb_insn, from_top=False): - """Disassemble `nb_insn` instructions after `addr`. If `from_top` is False (default), it will - also disassemble the `nb_insn` instructions before `addr`. +def gef_disassemble(addr, nb_insn, nb_prev=0): + """Disassemble `nb_insn` instructions after `addr` and `nb_prev` before `addr`. Return an iterator of Instruction objects.""" count = nb_insn + 1 if nb_insn & 1 else nb_insn - if not from_top: - start_addr = gdb_get_nth_previous_instruction_address(addr, count) + if nb_prev > 0: + start_addr = gdb_get_nth_previous_instruction_address(addr, nb_prev) if start_addr > 0: - for insn in gdb_disassemble(start_addr, count=count): + for insn in gdb_disassemble(start_addr, count=nb_prev): if insn.address == addr: break yield insn @@ -1151,9 +1163,9 @@ def gef_disassemble(addr, nb_insn, from_top=False): def capstone_disassemble(location, nb_insn, **kwargs): - """Disassemble `nb_insn` instructions after `addr` using the Capstone-Engine disassembler, if available. - If `kwargs["from_top"]` is False (default), it will also disassemble the `nb_insn` instructions before - `addr`. Return an iterator of Instruction objects.""" + """Disassemble `nb_insn` instructions after `addr` and `nb_prev` before + `addr` using the Capstone-Engine disassembler, if available. + Return an iterator of Instruction objects.""" def cs_insn_to_gef_insn(cs_insn): sym_info = gdb_get_location_from_symbol(cs_insn.address) @@ -1170,10 +1182,10 @@ def cs_insn_to_gef_insn(cs_insn): offset = location - page_start pc = current_arch.pc - from_top = kwargs.get("from_top", True) - if from_top in (False, "0", "false", "False"): - location = gdb_get_nth_previous_instruction_address(pc, nb_insn) - nb_insn *= 2 + nb_prev = int(kwargs.get("nb_prev", 0)) + if nb_prev > 0: + location = gdb_get_nth_previous_instruction_address(pc, nb_prev) + nb_insn += nb_prev code = kwargs.get("code", read_memory(location, gef_getpagesize() - offset - 1)) code = bytes(code) @@ -1493,14 +1505,17 @@ class X86(Architecture): mode = "32" nop_insn = b"\x90" - all_registers = [ + flag_register = "$eflags" + msr_registers = [ + "$cs ", "$ss ", "$ds ", "$es ", "$fs ", "$gs ", + ] + gpr_registers = [ "$eax ", "$ebx ", "$ecx ", "$edx ", "$esp ", "$ebp ", "$esi ", - "$edi ", "$eip ", "$cs ", "$ss ", "$ds ", "$es ", - "$fs ", "$gs ", "$eflags",] + "$edi ", "$eip ", ] + all_registers = gpr_registers + [ flag_register, ] + msr_registers instruction_length = None return_register = "$eax" function_parameters = ["$esp", ] - flag_register = "$eflags" flags_table = { 6: "zero", 0: "carry", @@ -1615,11 +1630,11 @@ class X86_64(X86): arch = "X86" mode = "64" - all_registers = [ + gpr_registers = [ "$rax ", "$rbx ", "$rcx ", "$rdx ", "$rsp ", "$rbp ", "$rsi ", "$rdi ", "$rip ", "$r8 ", "$r9 ", "$r10 ", "$r11 ", "$r12 ", - "$r13 ", "$r14 ", "$r15 ", - "$cs ", "$ss ", "$ds ", "$es ", "$fs ", "$gs ", "$eflags",] + "$r13 ", "$r14 ", "$r15 ", ] + all_registers = gpr_registers + [ X86.flag_register, ] + X86.msr_registers return_register = "$rax" function_parameters = ["$rdi", "$rsi", "$rdx", "$rcx", "$r8", "$r9"] @@ -2026,9 +2041,17 @@ def use_default_type(): return "unsigned short" +def use_golang_type(): + if is_elf32(): return "uint32" + elif is_elf64(): return "uint64" + return "uint16" + + def to_unsigned_long(v): """Cast a gdb.Value to unsigned long.""" - unsigned_long_t = cached_lookup_type(use_stdtype()) or cached_lookup_type(use_default_type()) + unsigned_long_t = cached_lookup_type(use_stdtype()) \ + or cached_lookup_type(use_default_type()) \ + or cached_lookup_type(use_golang_type()) return long(v.cast(unsigned_long_t)) @@ -2776,7 +2799,9 @@ def safe_parse_and_eval(value): def dereference(addr): """GEF wrapper for gdb dereference function.""" try: - ulong_t = cached_lookup_type(use_stdtype()) or cached_lookup_type(use_default_type()) + ulong_t = cached_lookup_type(use_stdtype()) or \ + cached_lookup_type(use_default_type()) or \ + cached_lookup_type(use_golang_type()) unsigned_long_type = ulong_t.pointer() res = gdb.Value(addr).cast(unsigned_long_type).dereference() # GDB does lazy fetch, so we need to force access to the value @@ -3843,6 +3868,9 @@ def __init__(self, *args, **kwargs): self.add_setting("dereference_base_address", "bold green", "Color of dereferenced address") self.add_setting("dereference_register_value", "bold green" , "Color of dereferenced register") self.add_setting("registers_register_name", "bold red", "Color of the changed register in register window") + self.add_setting("address_stack", "pink", "Color to use when a stack address is found") + self.add_setting("address_heap", "yellow", "Color to use when a heap address is found") + self.add_setting("address_code", "red", "Color to use when a code address is found") return def do_invoke(self, args): @@ -3850,7 +3878,7 @@ def do_invoke(self, args): argc = len(args) if argc==0: - for item in self.settings: + for item in sorted(self.settings): value = self.settings[item][0] value = Color.colorify(value, attrs=value) print("{:40s}: {:s}".format(item, value)) @@ -5344,12 +5372,10 @@ def pre_load(self): raise ImportWarning(msg) return - def __init__(self): super(CapstoneDisassembleCommand, self).__init__(complete=gdb.COMPLETE_LOCATION) return - @only_if_gdb_running def do_invoke(self, argv): location = None @@ -5359,7 +5385,6 @@ def do_invoke(self, argv): if '=' in arg: key, value = arg.split('=', 1) kwargs[key] = value - argv.remove(arg) elif location is None: location = parse_address(arg) @@ -5384,7 +5409,6 @@ def do_invoke(self, argv): print(msg) return - def capstone_analyze_pc(self, insn, nb_insn): cs = sys.modules["capstone"] @@ -5401,7 +5425,7 @@ def capstone_analyze_pc(self, insn, nb_insn): if current_arch.is_call(insn): target_address = int(insn.operands[-1].split()[0], 16) msg = [] - for i, new_insn in enumerate(capstone_disassemble(target_address, nb_insn, from_top=True)): + for i, new_insn in enumerate(capstone_disassemble(target_address, nb_insn)): msg.append(" {} {}".format (down_arrow if i==0 else " ", str(new_insn))) return (True, "\n".join(msg)) @@ -5736,9 +5760,17 @@ def do_invoke(self, argv): if reg.type.code == gdb.TYPE_CODE_VOID: continue - line = "" - line+= Color.colorify(regname, attrs=regname_color) - line+= ": " + if ( is_x86_64() or is_x86_32() ) and regname in current_arch.msr_registers: + msr = set(current_arch.msr_registers) + for r in set(regs) & msr: + line = "{}: ".format( Color.colorify(r.strip(), attrs=regname_color) ) + line+= "0x{:04x}".format(get_register(r)) + print(line, end=" ") + regs.remove(r) + print() + continue + + line = "{}: ".format( Color.colorify(regname, attrs=regname_color) ) if str(reg) == "": line += Color.colorify("no value", attrs="yellow underline") @@ -6302,7 +6334,8 @@ def __init__(self): self.add_setting("show_saved_ip", False, "Show saved IP and EBP even if outside of nb_lines_stack") 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_code", 5, "Number of instruction before and after $pc") + 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", "regs stack code source memory threads trace extra", "Change the order/presence of the context sections") @@ -6345,6 +6378,20 @@ def do_invoke(self, argv): if self.get_setting("clear_screen"): clear_screen(redirect) + if get_gef_setting("gef.disable_color")!=True: + code_color = get_gef_setting("theme.dereference_code") + str_color = get_gef_setting("theme.dereference_string") + code_addr_color = get_gef_setting("theme.address_code") + stack_addr_color = get_gef_setting("theme.address_stack") + heap_addr_color = get_gef_setting("theme.address_heap") + + print("[ Legend: {} | {} | {} | {} | {} ]".format( Color.colorify("Modified register", attrs="bold red"), + Color.colorify("Code", attrs=code_addr_color), + Color.colorify("Heap", attrs=heap_addr_color), + Color.colorify("Stack", attrs=stack_addr_color), + Color.colorify("String", attrs=str_color) + )) + for section in current_layout: if section[0] == "-": continue @@ -6459,6 +6506,7 @@ def context_stack(self): 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") pc = current_arch.pc @@ -6474,7 +6522,7 @@ def context_code(self): try: instruction_iterator = capstone_disassemble if use_capstone else gef_disassemble - for insn in instruction_iterator(pc, nb_insn, from_top=False): + for insn in instruction_iterator(pc, nb_insn, nb_prev=nb_insn_prev): line = [] is_branch = False is_taken = False @@ -6510,7 +6558,7 @@ def context_code(self): # If the operand isn't an address right now we can't parse it is_taken = False continue - for i, insn in enumerate(instruction_iterator(target, nb_insn, from_top=True)): + for i, insn in enumerate(instruction_iterator(target, nb_insn)): text= " {} {}".format (down_arrow if i==0 else " ", str(insn)) print(text) break @@ -6620,7 +6668,7 @@ def context_trace(self): items.append(m) else: try: - insn = next(gef_disassemble(pc, 1, from_top=True)) + insn = next(gef_disassemble(pc, 1)) except gdb.MemoryError: break items.append(Color.redify("{} {}".format(insn.mnemo, ', '.join(insn.operands)))) @@ -7149,13 +7197,13 @@ def dereference_from(addr): deref = addr.dereference() if deref is None: # if here, dereferencing addr has triggered a MemoryError, no need to go further - msg.append(format_address(addr.value)) + msg.append(str(addr)) break new_addr = lookup_address(deref) if new_addr.valid: addr = new_addr - msg.append(format_address(addr.value)) + msg.append(str(addr)) continue # -- Otherwise try to parse the value