From b3b8868624f9525341b900dfac8cb2a81d7262ee Mon Sep 17 00:00:00 2001 From: Grazfather Date: Thu, 28 Dec 2017 13:39:57 -0800 Subject: [PATCH 1/9] Fix arg parsing in capstone-disassemble Because we were removing the parsed arg from the dictionary while iterating it, we were skipping over other args, ignoring them completely. --- gef.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/gef.py b/gef.py index 37f977a80..08ae218d2 100644 --- a/gef.py +++ b/gef.py @@ -5322,12 +5322,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 @@ -5337,7 +5335,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) @@ -5362,7 +5359,6 @@ def do_invoke(self, argv): print(msg) return - def capstone_analyze_pc(self, insn, nb_insn): cs = sys.modules["capstone"] From b4f1349b92b14f605935e3633e909f8cb9eeeff1 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Thu, 28 Dec 2017 13:43:26 -0800 Subject: [PATCH 2/9] Split config nb_lines_code to add a prev option Add a new option "nb_lines_code_prev" to select how many previous instructions should be shown on the code context pane. Set the default to 3 previous and 6 after. --- gef.py | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/gef.py b/gef.py index 08ae218d2..22af36164 100644 --- a/gef.py +++ b/gef.py @@ -1119,7 +1119,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 +1133,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 +1150,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 +1169,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) @@ -5375,7 +5374,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)) @@ -6274,7 +6273,8 @@ def __init__(self): self.add_setting("peek_calls", True, "Peek into calls") self.add_setting("nb_lines_stack", 8, "Number of line in the stack pane") 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") @@ -6431,6 +6431,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 @@ -6446,7 +6447,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 @@ -6482,7 +6483,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 @@ -6592,7 +6593,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)))) From 0a52bb94bf659b04cb67b9724e80ef474f026011 Mon Sep 17 00:00:00 2001 From: hugsy Date: Thu, 28 Dec 2017 18:29:08 -0800 Subject: [PATCH 3/9] issue #174: adding an additional check for supporting the types defined by golang --- gef.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/gef.py b/gef.py index 37f977a80..8077d7745 100644 --- a/gef.py +++ b/gef.py @@ -2022,9 +2022,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)) @@ -2754,7 +2762,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 From 27032d7ada24c1ed1a42e2bd6c0f9e939d945393 Mon Sep 17 00:00:00 2001 From: hugsy Date: Sat, 30 Dec 2017 10:32:38 -0800 Subject: [PATCH 4/9] private request: added colors and legend to the context --- gef.py | 63 ++++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 15 deletions(-) diff --git a/gef.py b/gef.py index 8077d7745..69f8de1fc 100644 --- a/gef.py +++ b/gef.py @@ -53,6 +53,8 @@ # # +# x86 aggregate selectors + from __future__ import print_function, division, absolute_import import abc @@ -363,16 +365,23 @@ def __init__(self, *args, **kwargs): return def __str__(self): - return hex(self.value) + value = format_address( self.value ) + if self.is_in_text_segment(): + return Color.redify(value) + if self.is_in_heap_segment(): + return Color.yellowify(value) + if self.is_in_stack_segment(): + return Color.pinkify(value) + return value def is_in_text_segment(self): return hasattr(self.info, "name") and ".text" in self.info.name def is_in_stack_segment(self): - return hasattr(self.info, "name") and "[stack]" in self.info.name + return hasattr(self.section, "path") and "[stack]" in 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]" in self.section.path def dereference(self): addr = align_address(long(self.value)) @@ -1489,14 +1498,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", @@ -1611,11 +1623,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"] @@ -5724,9 +5736,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") @@ -6331,6 +6351,19 @@ 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") + print("[ Legend: {} | {} | {} | {} | {} ]".format( Color.colorify("Modified register", + attrs="bold red"), + Color.colorify("Code", + attrs=code_color), + Color.yellowify("Heap"), + Color.pinkify("Stack"), + Color.colorify("String", + attrs=str_color) + )) + for section in current_layout: if section[0] == "-": continue @@ -7080,13 +7113,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 From 5e1cf82c0e846b814359fcddd0d8e4d971c0c3b9 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Sat, 30 Dec 2017 12:59:35 -0800 Subject: [PATCH 5/9] Add more info to context command docs --- docs/commands/config.md | 7 ++++--- docs/commands/context.md | 30 +++++++++++++++++++++--------- 2 files changed, 25 insertions(+), 12 deletions(-) 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 index 90906d155..c8eff75e3 100644 --- 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,17 @@ 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. ### Redirecting context output to another tty/file ### @@ -94,9 +101,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: @@ -118,3 +125,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" +``` From 07a8676db83387b05630b1f02967380ecaad31e8 Mon Sep 17 00:00:00 2001 From: hugsy Date: Sat, 30 Dec 2017 16:58:39 -0800 Subject: [PATCH 6/9] Colors for code/stack/heap addresses can be tweaked directly via theme command --- gef.py | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/gef.py b/gef.py index a77b61c45..3d4397a4f 100644 --- a/gef.py +++ b/gef.py @@ -366,12 +366,15 @@ def __init__(self, *args, **kwargs): def __str__(self): 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.redify(value) + return Color.colorify(value, attrs=code_color) if self.is_in_heap_segment(): - return Color.yellowify(value) + return Color.colorify(value, attrs=heap_color) if self.is_in_stack_segment(): - return Color.pinkify(value) + return Color.colorify(value, attrs=stack_color) return value def is_in_text_segment(self): @@ -3842,6 +3845,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): @@ -3849,7 +3855,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)) @@ -6350,14 +6356,15 @@ def do_invoke(self, argv): if get_gef_setting("gef.disable_color")!=True: code_color = get_gef_setting("theme.dereference_code") str_color = get_gef_setting("theme.dereference_string") - print("[ Legend: {} | {} | {} | {} | {} ]".format( Color.colorify("Modified register", - attrs="bold red"), - Color.colorify("Code", - attrs=code_color), - Color.yellowify("Heap"), - Color.pinkify("Stack"), - Color.colorify("String", - attrs=str_color) + 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: From edbadbef7813fb96827572fcac3db35c9550beaa Mon Sep 17 00:00:00 2001 From: hugsy Date: Sun, 31 Dec 2017 16:50:25 -0800 Subject: [PATCH 7/9] minor fix in class Address --- gef.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/gef.py b/gef.py index 3d4397a4f..7cf1c15a5 100644 --- a/gef.py +++ b/gef.py @@ -378,13 +378,14 @@ def __str__(self): 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.section, "path") and "[stack]" in self.section.path + return hasattr(self.section, "path") and "[stack]" == self.section.path def is_in_heap_segment(self): - return hasattr(self.section, "path") and "[heap]" in self.section.path + return hasattr(self.section, "path") and "[heap]" == self.section.path def dereference(self): addr = align_address(long(self.value)) From 265ac2650be0ce7323969d2b358489038bdeab32 Mon Sep 17 00:00:00 2001 From: hugsy Date: Sun, 31 Dec 2017 16:51:15 -0800 Subject: [PATCH 8/9] gef turns 5!! they grow so fast! --- gef.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gef.py b/gef.py index 7cf1c15a5..a9eced611 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 From c758bb54b86df7f2b7e23ab9cd34771d657227bf Mon Sep 17 00:00:00 2001 From: etrickel Date: Thu, 28 Dec 2017 14:41:52 -0700 Subject: [PATCH 9/9] Add some simple improvements to stack display - Added the option context.grow_stack_down, when enabled the stack will grow up on the display. - Added the option context.show_saved_ip, when enabled the context shows the saved ip and the ebp, it puts ... (XX btyes) between the base pointer and where the stack display begins. - Both options are disabled by default. - Changed stack alias and dereference function so that it would work without requiring the L prefix. --- docs/commands/context.md | 28 +++++++++++++ gef.py | 87 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 111 insertions(+), 4 deletions(-) diff --git a/docs/commands/context.md b/docs/commands/context.md index c8eff75e3..2081293c0 100644 --- a/docs/commands/context.md +++ b/docs/commands/context.md @@ -69,6 +69,34 @@ The size of most sections are also customizable: * `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: +``` +gef➤ gef config context.grow_stack_down True +``` + +To always display the saved instruction pointer in the stack view, enable the following: +``` +gef➤ gef config context.show_saved_ip True +``` + +If the saved instruction pointer is not within the portion of the stack being displayed, +then a section is created that includes the saved ip and depending on the architecture +the frame pointer. Since not all the addresses can be displayed, the missing addresses +are squished into an ellipsis. +``` +0x00007fffffffc9e8│+0x00: 0x00007ffff7a2d830 → <__main+240> mov edi, eax ← savedip +0x00007fffffffc9e0│+0x00: 0x00000000004008c0 → <__init+0> push r15 ← $rbp +. . . (440 bytes skipped) +0x00007fffffffc7e8│+0x38: 0x0000000000000000 +0x00007fffffffc7e0│+0x30: 0x0000000000000026 ("&"?) +0x00007fffffffc7d8│+0x28: 0x0000000001958ac0 +0x00007fffffffc7d0│+0x20: 0x00007ffff7ffa2b0 → 0x5f6f7364765f5f00 +0x00007fffffffc7c8│+0x18: 0x00007fff00000000 +0x00007fffffffc7c0│+0x10: 0x00007fffffffc950 → 0x0000000000000000 +0x00007fffffffc7b8│+0x08: 0x0000000000000000 +0x00007fffffffc7b0│+0x00: 0x00007fffffffc7e4 → 0x0000000000000000 ← $rsp +``` ### Redirecting context output to another tty/file ### diff --git a/gef.py b/gef.py index a9eced611..c451c689c 100644 --- a/gef.py +++ b/gef.py @@ -1337,6 +1337,10 @@ def pc(self): def sp(self): return get_register("$sp") + @property + def fp(self): + return get_register("$fp") + @property def ptrsize(self): return get_memory_alignment() @@ -2254,6 +2258,24 @@ def get_info_sections(): return +def get_saved_ip(): + """Retrieves the saved ip using the 'info frame' command.""" + + lines = gdb.execute("info frame", to_string=True).splitlines() + if lines: + saved_regs = lines[-1].split() + saved_ip_str = saved_regs[-1] + + try: + saved_ip = int(saved_ip_str, 16) + return saved_ip + except ValueError: + # ignore + pass + + return 0 + + def get_info_files(): """Retrieves all the files loaded by debuggee.""" lines = gdb.execute("info files", to_string=True).splitlines() @@ -6309,6 +6331,8 @@ def __init__(self): 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("nb_lines_stack", 8, "Number of line in the stack pane") + 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", 6, "Number of instruction after $pc") self.add_setting("nb_lines_code_prev", 3, "Number of instruction before $pc") @@ -7042,7 +7066,7 @@ def __init__(self): return def post_load(self): - GefAlias("stack", "dereference $sp L10") + GefAlias("stack", "dereference $sp") GefAlias("dps", "dereference", completer_class=gdb.COMPLETE_LOCATION) return @@ -7051,6 +7075,10 @@ def pprint_dereferenced(self, addr, off): registers_color = get_gef_setting("theme.dereference_register_value") regs = [(k.strip(), get_register(k)) for k in current_arch.all_registers] + + if get_gef_setting("context.show_saved_ip"): + regs.append(("savedip", get_saved_ip())) + sep = " {:s} ".format(right_arrow) memalign = current_arch.ptrsize @@ -7064,6 +7092,7 @@ def pprint_dereferenced(self, addr, off): sep.join(addrs[1:]), ma=(memalign*2 + 2)) values = [] + for regname, regvalue in regs: if current_address == regvalue: values.append(regname) @@ -7077,18 +7106,68 @@ def pprint_dereferenced(self, addr, off): @only_if_gdb_running def do_invoke(self, argv): + grow_stack_down = get_gef_setting("context.grow_stack_down") + if len(argv) < 1: err("Missing location.") return nb = 10 - if len(argv)==2 and argv[1][0] in ("l", "L") and argv[1][1:].isdigit(): + + if len(argv) == 2 and argv[1][0] in ("l", "L") and argv[1][1:].isdigit(): nb = int(argv[1][1:]) + elif len(argv) == 2 and argv[0] == "$sp" and argv[1].isdigit(): + nb = int(argv[1]) start_address = align_address(long(gdb.parse_and_eval(argv[0]))) + largest_addresss_to_be_shown = start_address + (current_arch.ptrsize * nb) + + stackoffs = range(0, nb) + + # adjusts order of stack so that it grows downward + if grow_stack_down: + stackoffs = reversed(stackoffs) + + stack_str = "" + for i in stackoffs: + stack_str += self.pprint_dereferenced(start_address, i) + "\n" + stack_str = stack_str[:-1] + + # prints saved instruction pointer + if get_gef_setting("context.show_saved_ip"): + + frame_ptr = current_arch.fp + saved_ip = get_saved_ip() + + # only add saved_ip if it isn't already shown and we are displaying the stack + if saved_ip > largest_addresss_to_be_shown and start_address == current_arch.sp: + bytes_skipped_label = Color.grayify(". . . ({:d} bytes skipped)".format(saved_ip - largest_addresss_to_be_shown)) + + saved_ip_str = self.pprint_dereferenced(saved_ip, 0) + out_info_str = saved_ip_str + if abs(frame_ptr - saved_ip) == current_arch.ptrsize: + frame_str = self.pprint_dereferenced(frame_ptr, 0) + if frame_str: + if (saved_ip > frame_ptr and grow_stack_down) or (saved_ip < frame_ptr and not grow_stack_down): + out_info_str += "\n" + frame_str + else: + out_info_str = frame_str + "\n" + out_info_str += saved_ip_str + + if grow_stack_down: + print(out_info_str) + print(bytes_skipped_label) + print(stack_str) + else: + print(stack_str) + print(bytes_skipped_label) + print(out_info_str) + + else: + print(stack_str) + else: + print(stack_str) - for i in range(0, nb): - print(self.pprint_dereferenced(start_address, i)) return