From 16d892fe2307777e63ec4bb54be36a5b827dde33 Mon Sep 17 00:00:00 2001 From: William Bowling Date: Sun, 7 Oct 2018 11:29:51 +1100 Subject: [PATCH 01/58] Search for cross references between sections (#332) Add command to scan mapping xrefs --- docs/commands.md | 1 + docs/commands/scan.md | 11 +++++++++ gef.py | 56 +++++++++++++++++++++++++++++++++++++++++++ mkdocs.yml | 1 + tests/runtests.py | 15 ++++++++++++ 5 files changed, 84 insertions(+) create mode 100644 docs/commands/scan.md diff --git a/docs/commands.md b/docs/commands.md index a2e4220a1..2f2a52d2b 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -40,6 +40,7 @@ improve it. |registers | Display full details on one, many or all registers value from current architecture.| |reset-cache | Reset cache of all stored data.| |ropper | Ropper (http://scoding.de/ropper) plugin| +|scan | Search for addresses that are located in a memory mapping (haystack) that belonging to another (needle). (alias: lookup)| |search-pattern | SearchPatternCommand: search a pattern in memory. (alias: grep)| |set-permission | Change a page permission. By default, it will change it to RWX. (alias: mprotect)| |shellcode | ShellcodeCommand uses @JonathanSalwan simple-yet-awesome shellcode API to download shellcodes.| diff --git a/docs/commands/scan.md b/docs/commands/scan.md new file mode 100644 index 000000000..65e590d67 --- /dev/null +++ b/docs/commands/scan.md @@ -0,0 +1,11 @@ +## Command scan ## + +`scan` Search for addresses that are located in a memory mapping (haystack) that +belonging to another (needle). + +![scan-example](https://i.imgur.com/Ua0VXRY.png) + +`scan` requires two arguments, the first is the memory section that will be +searched and the second is what will be searched for. The arguments are grepped +against the processes memory mappings (just like [vmmap](docs/commands/vmmap.md) +to determine the memory ranges to search. \ No newline at end of file diff --git a/gef.py b/gef.py index 94c7d9c84..2701709b0 100644 --- a/gef.py +++ b/gef.py @@ -4834,6 +4834,62 @@ def import_structures(self, structs): return +@register_command +class ScanSectionCommand(GenericCommand): + """Search for addresses that are located in a memory mapping (haystack) that belonging + to another (needle)""" + + _cmdline_ = "scan" + _syntax_ = "{:s} HAYSTACK NEEDLE".format(_cmdline_) + _aliases_ = ["lookup",] + _example_ = "\n{0:s} stack libc".format(_cmdline_) + + @only_if_gdb_running + def do_invoke(self, argv): + if len(argv) != 2: + self.usage() + return + + haystack = argv[0] + needle = argv[1] + + info("Searching for addresses in '{:s}' that point to '{:s}'" + .format(Color.yellowify(haystack), Color.yellowify(needle))) + + if haystack == "binary": + haystack = get_filepath() + + if needle == "binary": + needle = get_filepath() + + needle_sections = [] + haystack_sections = [] + + for sect in get_process_maps(): + if haystack in sect.path: + haystack_sections.append((sect.page_start, sect.page_end, os.path.basename(sect.path))) + if needle in sect.path: + needle_sections.append((sect.page_start, sect.page_end)) + + step = current_arch.ptrsize + fmt = "{}{}".format(endian_str(), "I" if step==4 else "Q") + + for hstart, hend, hname in haystack_sections: + try: + mem = read_memory(hstart, hend - hstart) + except gdb.MemoryError: + continue + + for i in range(0, len(mem), step): + target = struct.unpack(fmt, mem[i:i+step])[0] + for nstart, nend in needle_sections: + if target >= nstart and target < nend: + deref = DereferenceCommand.pprint_dereferenced(hstart, long(i / step)) + name = Color.colorify(hname, attrs="yellow") + gef_print("{:s}: {:s}".format(name, deref)) + + return + @register_command class SearchPatternCommand(GenericCommand): """SearchPatternCommand: search a pattern in memory. If given an hex value (starting with 0x) diff --git a/mkdocs.yml b/mkdocs.yml index c298d7b90..964e3ffba 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -41,6 +41,7 @@ pages: - Command registers: commands/registers.md - Command reset-cache: commands/reset-cache.md - Command ropper: commands/ropper.md +- Command scan: commands/scan.md - Command search-pattern: commands/search-pattern.md - Command set-permission: commands/set-permission.md - Command shellcode: commands/shellcode.md diff --git a/tests/runtests.py b/tests/runtests.py index d889171af..ab59f8066 100755 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -308,6 +308,21 @@ def test_cmd_ropper(self): self.assertTrue(len(res.splitlines()) > 2) return + def test_cmd_scan(self): + cmd = "scan libc stack" + target = "tests/binaries/checksec-no-pie.out" + self.assertFailIfInactiveSession(gdb_run_cmd(cmd)) + res = gdb_start_silent_cmd(cmd, target=target) + self.assertNoException(res) + self.assertIn(target.encode(), res) + + target = "tests/binaries/default.out" + res = gdb_start_silent_cmd("scan binary libc", target=target) + self.assertNoException(res) + self.assertIn(b"__libc_start_main", res) + + return + def test_cmd_search_pattern(self): self.assertFailIfInactiveSession(gdb_run_cmd("grep /bin/sh")) res = gdb_start_silent_cmd("grep /bin/sh") From 18667573f36b774b7c6aa6d6d3235435748893d7 Mon Sep 17 00:00:00 2001 From: William Bowling Date: Sun, 7 Oct 2018 16:12:35 +1100 Subject: [PATCH 02/58] Advance the address instead of repeating for memory commands (#333) Adds a repeat repeat_count attribute to GenericCommand that allows a command to determine if it's being called again by the user just pressing enter. This allows the command, for example to advance forward through memory, in the case of hexdump. Hexdump and Dereference now use this. --- gef.py | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/gef.py b/gef.py index 2701709b0..7efc5edf6 100644 --- a/gef.py +++ b/gef.py @@ -3592,6 +3592,9 @@ def __init__(self, *args, **kwargs): syntax = Color.yellowify("\nSyntax: ") + self._syntax_ example = Color.yellowify("\nExample: ") + self._example_ if self._example_ else "" self.__doc__ = self.__doc__.replace(" "*4, "") + syntax + example + self.repeat = False + self.repeat_count = 0 + self.__last_command = None command_type = kwargs.setdefault("command", gdb.COMMAND_OBSCURE) complete_type = kwargs.setdefault("complete", gdb.COMPLETE_NONE) prefix = kwargs.setdefault("prefix", False) @@ -3602,6 +3605,7 @@ def __init__(self, *args, **kwargs): def invoke(self, args, from_tty): try: argv = gdb.string_to_argv(args) + self.__set_repeat_count(from_tty) bufferize(self.do_invoke(argv)) except Exception as e: # Note: since we are intercepting cleaning exceptions here, commands preferably should avoid @@ -3666,6 +3670,18 @@ def del_setting(self, name): del __config__[key] return + def __set_repeat_count(self, from_tty): + if not from_tty: + self.repeat = False + self.repeat_count = 0 + return + + command = gdb.execute("show commands", to_string=True).strip().split("\n")[-1] + self.repeat = self.__last_command == command + self.repeat_count = self.repeat_count + 1 if self.repeat else 0 + + self.__last_command = command + # Copy/paste this template for new command # @register_command @@ -7563,10 +7579,11 @@ def do_invoke(self, argv): continue if fmt == "byte": + read_from += self.repeat_count * read_len mem = read_memory(read_from, read_len) lines = hexdump(mem, base=read_from).splitlines() else: - lines = self._hexdump(read_from, read_len, fmt) + lines = self._hexdump(read_from, read_len, fmt, self.repeat_count * read_len) if not up_to_down: lines.reverse() @@ -7575,7 +7592,7 @@ def do_invoke(self, argv): return - def _hexdump(self, start_addr, length, arrange_as): + def _hexdump(self, start_addr, length, arrange_as, offset=0): elf = get_elf_headers() if elf is None: return @@ -7594,12 +7611,12 @@ def _hexdump(self, start_addr, length, arrange_as): i = 0 while i < length: - cur_addr = start_addr + i * l + cur_addr = start_addr + (i + offset) * l sym = gdb_get_location_from_symbol(cur_addr) sym = "<{:s}+{:04x}>".format(*sym) if sym else '' mem = read_memory(cur_addr, l) val = struct.unpack(fmt_pack, mem)[0] - lines.append(fmt_str % (cur_addr, i * l, sym, val)) + lines.append(fmt_str % (cur_addr, (i + offset) * l, sym, val)) i += 1 return lines @@ -7748,12 +7765,12 @@ def do_invoke(self, argv): return if get_gef_setting("context.grow_stack_down") is True: - from_insnum = nb-1 - to_insnum = -1 + from_insnum = nb * (self.repeat_count + 1) - 1 + to_insnum = self.repeat_count * nb - 1 insnum_step = -1 else: - from_insnum = 0 - to_insnum = nb + from_insnum = 0 + self.repeat_count * nb + to_insnum = nb * (self.repeat_count + 1) insnum_step = 1 start_address = align_address(addr) From 668d2f087d0895cf348db57a7e7b85e4ce93ece2 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Sun, 7 Oct 2018 10:09:23 -0700 Subject: [PATCH 03/58] Make _syntax_ more consistent between commands --- gef.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/gef.py b/gef.py index 7efc5edf6..b40b47fc3 100644 --- a/gef.py +++ b/gef.py @@ -6308,7 +6308,7 @@ class ShellcodeCommand(GenericCommand): download shellcodes.""" _cmdline_ = "shellcode" - _syntax_ = "{:s} ".format(_cmdline_) + _syntax_ = "{:s} (search|get)".format(_cmdline_) def __init__(self): super(ShellcodeCommand, self).__init__(prefix=True) @@ -6325,7 +6325,7 @@ class ShellcodeSearchCommand(GenericCommand): """Search pattern in shell-storm's shellcode database.""" _cmdline_ = "shellcode search" - _syntax_ = "{:s} ".format(_cmdline_) + _syntax_ = "{:s} PATTERN1 PATTERN2".format(_cmdline_) _aliases_ = ["sc-search",] api_base = "http://shell-storm.org" @@ -6375,7 +6375,7 @@ class ShellcodeGetCommand(GenericCommand): """Download shellcode from shell-storm's shellcode database.""" _cmdline_ = "shellcode get" - _syntax_ = "{:s} ".format(_cmdline_) + _syntax_ = "{:s} SHELLCODE_ID".format(_cmdline_) _aliases_ = ["sc-get",] api_base = "http://shell-storm.org" @@ -7430,7 +7430,7 @@ def empty_extra_messages(self, event): class MemoryCommand(GenericCommand): """Add or remove address ranges to the memory view.""" _cmdline_ = "memory" - _syntax_ = "{:s}".format(_cmdline_) + _syntax_ = "{:s} (watch|unwatch|reset|list)".format(_cmdline_) def __init__(self): super(MemoryCommand, self).__init__(prefix=True) @@ -7628,7 +7628,7 @@ class PatchCommand(GenericCommand): _cmdline_ = "patch" _syntax_ = ("{0:s} \n" - "{0:s} string \"double-escaped string\"".format(_cmdline_)) + "{0:s} string LOCATION \"double-escaped string\"".format(_cmdline_)) SUPPORTED_SIZES = { "qword": (8, "Q"), "dword": (4, "L"), From 7191dde5027c35828606f2f3e99b343583551c32 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Mon, 8 Oct 2018 10:23:01 -0700 Subject: [PATCH 04/58] Make capstone-disassemble use repeat_count (#337) --- gef.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/gef.py b/gef.py index b40b47fc3..f05e37da6 100644 --- a/gef.py +++ b/gef.py @@ -1216,6 +1216,7 @@ def cs_insn_to_gef_insn(cs_insn): offset = location - page_start pc = current_arch.pc + skip = int(kwargs.get("skip", 0)) nb_prev = int(kwargs.get("nb_prev", 0)) if nb_prev > 0: location = gdb_get_nth_previous_instruction_address(pc, nb_prev) @@ -1225,6 +1226,9 @@ def cs_insn_to_gef_insn(cs_insn): code = bytes(code) for insn in cs.disasm(code, location): + if skip: + skip -= 1 + continue nb_insn -= 1 yield cs_insn_to_gef_insn(insn) if nb_insn==0: @@ -5784,7 +5788,7 @@ def do_invoke(self, argv): location = location or current_arch.pc length = int(kwargs.get("length", get_gef_setting("context.nb_lines_code"))) - for insn in capstone_disassemble(location, length, **kwargs): + for insn in capstone_disassemble(location, length, skip=length*self.repeat_count, **kwargs): text_insn = str(insn) msg = "" From 9ca8386b7de66e032c978302e7641710a1570ea6 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Mon, 8 Oct 2018 10:23:18 -0700 Subject: [PATCH 05/58] hexdump: Colourize bytes based on value (#336) --- gef.py | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/gef.py b/gef.py index f05e37da6..baf6d40b8 100644 --- a/gef.py +++ b/gef.py @@ -954,6 +954,29 @@ def is_exe(fpath): raise FileNotFoundError("Missing file `{:s}`".format(program)) +def style_byte(b, color=True): + style = { + 'nonprintable': "yellow", + 'printable': "white", + '00': "gray", + '0a': "blue", + 'ff': "green", + } + sbyte = "{:02x}".format(b) + if not color: + return sbyte + + if sbyte in style: + st = style[sbyte] + elif chr(b) in (string.ascii_letters + string.digits + string.punctuation + ' '): + st = style.get('printable') + else: + st = style.get('nonprintable') + if st: + sbyte = Color.colorify(sbyte, attrs=st) + return sbyte + + def hexdump(source, length=0x10, separator=".", show_raw=False, base=0x00): """Return the hexdump of `src` argument. @param source *MUST* be of type bytes or bytearray @@ -961,14 +984,14 @@ def hexdump(source, length=0x10, separator=".", show_raw=False, base=0x00): @param separator is the default character to use if one byte is not printable @param show_raw if True, do not add the line nor the text translation @param base is the start address of the block being hexdump - @param func is the function to use to parse bytes (int for Py3, chr for Py2) @return a string with the hexdump """ result = [] align = get_memory_alignment()*2+2 if is_alive() else 18 for i in range(0, len(source), length): chunk = bytearray(source[i:i + length]) - hexa = " ".join(["{:02x}".format(b) for b in chunk]) + hexa = " ".join([style_byte(b, color=not show_raw) for b in chunk]) + if show_raw: result.append(hexa) From 4f1385508354bd50fca1ca98b9c3e4965ff47dcb Mon Sep 17 00:00:00 2001 From: Grazfather Date: Tue, 9 Oct 2018 11:38:49 -0700 Subject: [PATCH 06/58] Make hexdump look more like deref (#334) --- gef.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/gef.py b/gef.py index baf6d40b8..2f0537eff 100644 --- a/gef.py +++ b/gef.py @@ -7625,6 +7625,8 @@ def _hexdump(self, start_addr, length, arrange_as, offset=0): return endianness = endian_str() + base_address_color = get_gef_setting("theme.dereference_base_address") + formats = { "qword": ("Q", 8), "dword": ("I", 4), @@ -7632,7 +7634,7 @@ def _hexdump(self, start_addr, length, arrange_as, offset=0): } r, l = formats[arrange_as] - fmt_str = "%#x+%.4x %s {:s} %#.{:s}x".format(VERTICAL_LINE, str(l * 2)) + fmt_str = "{{base}}{v}+{{offset:#06x}} {{sym}}{{val:#0{prec}x}}".format(v=VERTICAL_LINE, prec=l*2+2) fmt_pack = endianness + r lines = [] @@ -7640,10 +7642,11 @@ def _hexdump(self, start_addr, length, arrange_as, offset=0): while i < length: cur_addr = start_addr + (i + offset) * l sym = gdb_get_location_from_symbol(cur_addr) - sym = "<{:s}+{:04x}>".format(*sym) if sym else '' + sym = "<{:s}+{:04x}> ".format(*sym) if sym else '' mem = read_memory(cur_addr, l) val = struct.unpack(fmt_pack, mem)[0] - lines.append(fmt_str % (cur_addr, (i + offset) * l, sym, val)) + lines.append(fmt_str.format(base=Color.colorify(format_address(cur_addr), attrs=base_address_color), + offset=(i + offset) * l, sym=sym, val=val)) i += 1 return lines @@ -7749,7 +7752,7 @@ def pprint_dereferenced(addr, off): addrs = DereferenceCommand.dereference_from(current_address) l = "" addr_l = format_address(long(addrs[0], 16)) - l += "{:s}{:s}+{:#04x}: {:{ma}s}".format(Color.colorify(addr_l, attrs=base_address_color), + l += "{:s}{:s}+{:#06x}: {:{ma}s}".format(Color.colorify(addr_l, attrs=base_address_color), VERTICAL_LINE, offset, sep.join(addrs[1:]), ma=(memalign*2 + 2)) From 7ce356e83aac5b5eaaa0c21045c7ccdccd48b295 Mon Sep 17 00:00:00 2001 From: William Bowling Date: Wed, 10 Oct 2018 07:01:52 +1100 Subject: [PATCH 07/58] Add gdb convenience functions for working with heap and other randomised section addresses (#339) Add new gdb.Functions that can be used in gdb expressions. Added a set of 'offset' functions that can be used to add an offset to its argument, making it easy to get an address relative to a section. --- docs/commands.md | 1 + docs/commands/functions.md | 25 +++++++ gef.py | 133 ++++++++++++++++++++++++++++++++++++- mkdocs.yml | 1 + tests/binaries/bss.c | 19 ++++++ tests/runtests.py | 63 ++++++++++++++++++ 6 files changed, 241 insertions(+), 1 deletion(-) create mode 100644 docs/commands/functions.md create mode 100644 tests/binaries/bss.c diff --git a/docs/commands.md b/docs/commands.md index 2f2a52d2b..5760a6368 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -21,6 +21,7 @@ improve it. |elf-info | Display ELF header informations.| |entry-break | Tries to find best entry point and sets a temporary breakpoint on it. (alias: start-break)| |format-string-helper | Exploitable format-string helper: this command will set up specific breakpoints at well-known dangerous functions (printf, snprintf, etc.), and check if the pointer holding the format string is writable, and susceptible to format string attacks if an attacker can control its content. (alias: fmtstr-helper)| +|functions | List the convenience functions provided by GEF.| |gef-remote | gef wrapper for the `target remote` command. This command will automatically download the target binary in the local temporary directory (defaut /tmp) and then source it. Additionally, it will fetch all the /proc/PID/maps and loads all its information.| |heap | Base command to get information about the Glibc heap structure.| |heap-analysis-helper | Tracks dynamic heap allocation through malloc/free to try to detect heap vulnerabilities.| diff --git a/docs/commands/functions.md b/docs/commands/functions.md new file mode 100644 index 000000000..7524423dd --- /dev/null +++ b/docs/commands/functions.md @@ -0,0 +1,25 @@ +## Command functions ## + +The `functions` command will list all of the [convenience functions](https://sourceware.org/gdb/onlinedocs/gdb/Convenience-Funs.html) provided by GEF. + +* `$_bss([offset])` -- Return the current bss base address plus the given offset. +* `$_got([offset])` -- Return the current bss base address plus the given offset. +* `$_heap([offset])` -- Return the current heap base address plus an optional offset. +* `$_pie([offset])` -- Return the current pie base address plus an optional offset. +* `$_stack([offset])` -- Return the current stack base address plus an optional offset. + + +These functions can be used as arguments to other commands to dynamically calculate values. + +``` +gef➤ deref $_heap() l4 +0x0000000000602000│+0x00: 0x0000000000000000 ← $r8 +0x0000000000602008│+0x08: 0x0000000000000021 ("!"?) +0x0000000000602010│+0x10: 0x0000000000000000 ← $rax, $rdx +0x0000000000602018│+0x18: 0x0000000000000000 +gef➤ deref $_heap(0x20) l4 +0x0000000000602020│+0x00: 0x0000000000000000 ← $rsi +0x0000000000602028│+0x08: 0x0000000000020fe1 +0x0000000000602030│+0x10: 0x0000000000000000 +0x0000000000602038│+0x18: 0x0000000000000000 +``` diff --git a/gef.py b/gef.py index 2f0537eff..4ccf5b7e2 100644 --- a/gef.py +++ b/gef.py @@ -189,6 +189,7 @@ def update_gef(argv): __gef__ = None __commands__ = [] +__functions__ = [] __aliases__ = [] __config__ = {} __watches__ = {} @@ -2557,6 +2558,13 @@ def process_lookup_path(name, perm=Permission.ALL): return None +def file_lookup_name_path(name, path): + """Look up a file by name and path. + Return a Zone object if found, None otherwise.""" + for xfile in get_info_files(): + if path == xfile.filename and name == xfile.name: + return xfile + return None def file_lookup_address(address): """Look up for a file by its address. @@ -3609,6 +3617,11 @@ def register_priority_command(cls): __commands__.insert(0, cls) return cls +def register_function(cls): + """Decorator for registering a new convenience function to GDB.""" + global __functions__ + __functions__.append(cls) + return cls class GenericCommand(gdb.Command): """This is an abstract class for invoking commands, should not be instantiated.""" @@ -8633,6 +8646,119 @@ def get_settings_path(self): return path if os.path.isdir(path) else None +class GenericOffsetFunction(gdb.Function): + """This is an abstract class for invoking offset functions, should not be instantiated.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractproperty + def _section_(self): pass + @abc.abstractproperty + def _zone_(self): pass + @abc.abstractproperty + def _function_(self): pass + @property + def _syntax_(self): + return "${}([offset])".format(self._function_) + + def __init__ (self): + super(GenericOffsetFunction, self).__init__(self._function_) + + def invoke(self, offset=gdb.Value(0)): + if not is_alive(): + raise gdb.GdbError("No debugging session active") + + base_address = self.get_base_address() + if not base_address: + raise gdb.GdbError("No {} section".format(self._section_ or self._zone_)) + + addr = long(offset) if offset.address is None else long(offset.address) + return long(base_address + addr) + + def get_base_address(self): + if self._section_: + section = process_lookup_path(self._section_) + if section: + return section.page_start + elif self._zone_: + zone = file_lookup_name_path(self._zone_, get_filepath()) + if zone: + return zone.zone_start + return None + +@register_function +class StackOffsetFunction(GenericOffsetFunction): + """Return the current stack base address plus an optional offset.""" + _function_ = "_stack" + _section_ = "[stack]" + _zone_ = None + +@register_function +class HeapBaseFunction(GenericOffsetFunction): + """Return the current heap base address plus an optional offset.""" + _function_ = "_heap" + _section_ = "[heap]" + _zone_ = None + +@register_function +class PieBaseFunction(GenericOffsetFunction): + """Return the current pie base address plus an optional offset.""" + _function_ = "_pie" + _zone_ = None + @property + def _section_(self): + return get_filepath() + +@register_function +class BssBaseFunction(GenericOffsetFunction): + """Return the current bss base address plus the given offset.""" + _function_ = "_bss" + _zone_ = ".bss" + _section_ = None + +@register_function +class GotBaseFunction(GenericOffsetFunction): + """Return the current bss base address plus the given offset.""" + _function_ = "_got" + _zone_ = ".got" + _section_ = None + +@register_command +class GefFunctionsCommand(GenericCommand): + """List the convenience functions provided by GEF.""" + _cmdline_ = "functions" + _syntax_ = _cmdline_ + + def __init__(self): + super(GefFunctionsCommand, self).__init__() + self.docs = [] + self.setup() + return + + def setup(self): + global __gef__ + for function in __gef__.loaded_functions: + self.add_function_to_doc(function) + self.__doc__ = "\n".join(sorted(self.docs)) + + def add_function_to_doc(self, function): + """Add function to documentation.""" + doc = getattr(function, "__doc__", "").lstrip() + doc = "\n ".join(doc.split("\n")) + syntax = getattr(function, "_syntax_", "").lstrip() + w = max(1, get_terminal_size()[1] - 29) # use max() to avoid zero or negative numbers + msg = "{syntax:<25s} -- {help:{w}s}".format(syntax=syntax, help=Color.greenify(doc), w=w) + self.docs.append(msg) + return + + def do_invoke(self, argv): + self.dont_repeat() + gef_print(titlify("GEF - Convenience Functions")) + gef_print("These functions can be used as arguments to other " + "commands to dynamically calculate values, eg: {:s}\n" + .format(Color.colorify("deref $_heap(0x20)", attrs="yellow"))) + gef_print(self.__doc__) + return + class GefCommand(gdb.Command): """GEF main command: view all new commands by typing `gef`""" @@ -8651,6 +8777,7 @@ def __init__(self): set_gef_setting("gef.extra_plugins_dir", "", str, "Autoload additional GEF commands from external directory") set_gef_setting("gef.disable_color", False, bool, "Disable all colors in GEF") self.loaded_commands = [] + self.loaded_functions = [] self.missing_commands = {} return @@ -8734,10 +8861,14 @@ def invoke(self, args, from_tty): def load(self, initial=False): - """Load all the commands defined by GEF into GDB.""" + """Load all the commands and functions defined by GEF into GDB.""" nb_missing = 0 self.commands = [(x._cmdline_, x) for x in __commands__] + # load all of the functions + for function_class_name in __functions__: + self.loaded_functions.append(function_class_name()) + def is_loaded(x): return any(filter(lambda u: x == u[0], self.loaded_commands)) diff --git a/mkdocs.yml b/mkdocs.yml index 964e3ffba..6e0a4adda 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -21,6 +21,7 @@ pages: - Command entry-break: commands/entry-break.md - Command eval: commands/eval.md - Command format-string-helper: commands/format-string-helper.md +- Command functions: commands/functions.md - Command gef-remote: commands/gef-remote.md - Command heap: commands/heap.md - Command heap-analysis-helper: commands/heap-analysis-helper.md diff --git a/tests/binaries/bss.c b/tests/binaries/bss.c new file mode 100644 index 000000000..1107f5411 --- /dev/null +++ b/tests/binaries/bss.c @@ -0,0 +1,19 @@ +/** + * default.c + * -*- mode: c -*- + * -*- coding: utf-8 -*- + */ + +#include +#include +#include +#include + +char msg[0x100]; + +int main(int argc, char** argv, char** envp) +{ + strncpy(msg, "Hello world!", sizeof(msg)); + __asm__ volatile("int3;" : : : ); + return EXIT_SUCCESS; +} diff --git a/tests/runtests.py b/tests/runtests.py index ab59f8066..243222942 100755 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -127,6 +127,13 @@ def test_cmd_format_string_helper(self): self.assertIn(b"Possible insecure format string:", res) return + def test_cmd_functions(self): + cmd = "functions" + res = gdb_run_cmd(cmd) + self.assertNoException(res) + self.assertIn(b"$_heap", res) + return + def test_cmd_heap_arenas(self): cmd = "heap arenas" target = "tests/binaries/heap.out" @@ -501,6 +508,61 @@ def test_func_get_pid(self): self.assertTrue(int(res.splitlines()[-1])) return +class TestGdbFunctions(GefUnitTestGeneric): + """Tests gdb convenience functions added by GEF.""" + + def test_func_pie(self): + cmd = "x/s $_pie()" + self.assertFailIfInactiveSession(gdb_run_cmd(cmd)) + res = gdb_start_silent_cmd(cmd) + self.assertNoException(res) + self.assertIn(b"\\177ELF", res) + + cmd = "x/s $_pie(1)" + res = gdb_start_silent_cmd(cmd) + self.assertNoException(res) + self.assertNotIn(b"\\177ELF", res) + self.assertIn(b"ELF", res) + return + + def test_func_heap(self): + cmd = "deref $_heap()" + self.assertFailIfInactiveSession(gdb_run_cmd(cmd, target="tests/binaries/heap.out")) + res = gdb_run_silent_cmd(cmd, target="tests/binaries/heap.out") + self.assertNoException(res) + self.assertIn(b"0x0000000000000021", res) + self.assertIn(b"0x0000000000020fe1", res) + + cmd = "deref $_heap(0x10+0x10)" + res = gdb_run_silent_cmd(cmd, target="tests/binaries/heap.out") + self.assertNoException(res) + self.assertNotIn(b"0x0000000000000021", res) + self.assertIn(b"0x0000000000020fe1", res) + return + + def test_func_got(self): + cmd = "deref $_got()" + self.assertFailIfInactiveSession(gdb_run_cmd(cmd, target="tests/binaries/heap.out")) + res = gdb_run_silent_cmd(cmd, target="tests/binaries/heap.out") + self.assertNoException(res) + self.assertIn(b"malloc", res) + return + + def test_func_bss(self): + cmd = "deref $_bss()" + self.assertFailIfInactiveSession(gdb_run_cmd(cmd, target="tests/binaries/bss.out")) + res = gdb_run_silent_cmd(cmd, target="tests/binaries/bss.out") + self.assertNoException(res) + self.assertIn(b"Hello world!", res) + return + + def test_func_stack(self): + cmd = "deref $_stack()" + self.assertFailIfInactiveSession(gdb_run_cmd(cmd)) + res = gdb_start_silent_cmd(cmd) + self.assertNoException(res) + self.assertIn(b"+0x20: 0x0000000000000000", res) + return class TestGefMisc(GefUnitTestGeneric): """Tests external functionality.""" @@ -517,6 +579,7 @@ def run_tests(): test_instances = [ TestGefCommands, TestGefFunctions, + TestGdbFunctions, TestGefMisc, ] From ecd25e0d02dc7bf0a7711cff57c742ba555ef4b9 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Tue, 9 Oct 2018 13:46:19 -0700 Subject: [PATCH 08/58] hexdump: Allow shorter form for size and direction (#340) --- gef.py | 14 ++++++++++---- tests/runtests.py | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/gef.py b/gef.py index 4ccf5b7e2..e5fb8d12f 100644 --- a/gef.py +++ b/gef.py @@ -7590,8 +7590,14 @@ def do_invoke(self, argv): self.usage() return - fmt, argv = argv[0], argv[1:] - if fmt not in {"qword", "dword", "word", "byte"}: + arg0, argv = argv[0].lower(), argv[1:] + valid_formats = ["byte", "word", "dword", "qword"] + fmt = None + for valid_format in valid_formats: + if valid_format.startswith(arg0): + fmt = valid_format + break + if not fmt: self.usage() return @@ -7611,10 +7617,10 @@ def do_invoke(self, argv): except ValueError: pass - if arg == "up": + if arg in {"up", "u"}: up_to_down = True continue - elif arg == "down": + elif arg in {"down", "d"}: up_to_down = False continue diff --git a/tests/runtests.py b/tests/runtests.py index 243222942..0ccc250e2 100755 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -561,7 +561,7 @@ def test_func_stack(self): self.assertFailIfInactiveSession(gdb_run_cmd(cmd)) res = gdb_start_silent_cmd(cmd) self.assertNoException(res) - self.assertIn(b"+0x20: 0x0000000000000000", res) + self.assertRegex(res.decode(), r"\+0x0*20: *0x0000000000000000\n") return class TestGefMisc(GefUnitTestGeneric): From ea3c54ba3bc5b1ef4d3d64b071e139b953e3d19e Mon Sep 17 00:00:00 2001 From: hugsy Date: Tue, 9 Oct 2018 18:56:10 -0700 Subject: [PATCH 09/58] Simple fix for issue #318 : in EntryPointBreakCommand the only case where the warning is irrelevant is when debugging in qemu compat mode. We can safely ignore that case if the qemu flag is set. --- gef.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gef.py b/gef.py index 8517d6c0b..ac30de22e 100644 --- a/gef.py +++ b/gef.py @@ -6785,7 +6785,7 @@ def do_invoke(self, argv): warn("The file '{}' is not executable.".format(fpath)) return - if is_alive(): + if is_alive() and not __gef_qemu_mode__: warn("gdb is already running") return From 5707f5d65259a4fdd25472623e90f81eb8a7cc97 Mon Sep 17 00:00:00 2001 From: William Bowling Date: Wed, 10 Oct 2018 23:39:12 +1100 Subject: [PATCH 10/58] set mode based on current thumb flag --- gef.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gef.py b/gef.py index ac30de22e..0fd9aeff7 100644 --- a/gef.py +++ b/gef.py @@ -1520,7 +1520,6 @@ def get_ra(self, insn, frame): class ARM(Architecture): arch = "ARM" - mode = "ARM" all_registers = ["$r0", "$r1", "$r2", "$r3", "$r4", "$r5", "$r6", "$r7", "$r8", "$r9", "$r10", "$r11", "$r12", "$sp", @@ -1544,6 +1543,10 @@ class ARM(Architecture): syscall_register = "$r7" syscall_instructions = ["swi 0x0", "swi NR"] + @property + def mode(self): + return "THUMB" if is_arm_thumb() else "ARM" + @property def instruction_length(self): # Thumb instructions have variable-length (2 or 4-byte) From b1473af3eea4d5a9eb0968d17f39185d45892765 Mon Sep 17 00:00:00 2001 From: crazy rabbidz Date: Wed, 10 Oct 2018 08:33:57 -0700 Subject: [PATCH 11/58] Update CONTRIBUTING.md --- .github/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 9050b2830..35706a100 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -18,7 +18,7 @@ to release the code written by you under the said license. * Comment your code * If you add a new feature/GDB command, also write the adequate documentation (in [`docs/`](https://github.com/hugsy/gef/docs)) -1. Submit a pull request. +1. Submit a pull request, **make sure it is made against the `dev` branch (not `master`, which only contains tested and stable features)** 1. The contributors will review your patch. If it is approved, the change will be merged via the GitHub, and you will be seen as contributors. If it needs additional work, the repo owner will respond with useful comments. From 9b6ca7a6ec1bcccc289c66d78d2811b781bbf491 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Wed, 10 Oct 2018 16:37:12 +0000 Subject: [PATCH 12/58] Add commit to title of context_times.sh's csv output --- tests/perf/context_times.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/perf/context_times.sh b/tests/perf/context_times.sh index 1e76ca92a..bce9665dd 100755 --- a/tests/perf/context_times.sh +++ b/tests/perf/context_times.sh @@ -16,6 +16,8 @@ get_context_time() { log_this_revision() { rev=`git log -1 --pretty="format:%h"` printf $rev + title=`git log -1 --pretty="format:%s"` + printf ",\"$title\"" rv=`$1` echo ,$rv } From cfdd62d2c0e6283192edc24f37a652f8dfc472f8 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Wed, 10 Oct 2018 11:14:47 -0700 Subject: [PATCH 13/58] Add cyan colour --- gef.py | 1 + 1 file changed, 1 insertion(+) diff --git a/gef.py b/gef.py index 0fd9aeff7..71769d052 100644 --- a/gef.py +++ b/gef.py @@ -333,6 +333,7 @@ class Color: "yellow" : "\033[33m", "blue" : "\033[34m", "pink" : "\033[35m", + "cyan" : "\033[36m", "bold" : "\033[1m", "underline" : "\033[4m", "underline_off" : "\033[24m", From c02677021c53fb4ce01c9aaca1d5ce2ace9a6a80 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Wed, 10 Oct 2018 11:45:59 -0700 Subject: [PATCH 14/58] context_args: colorize arg key --- gef.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gef.py b/gef.py index 71769d052..5cfcbe617 100644 --- a/gef.py +++ b/gef.py @@ -7217,6 +7217,7 @@ def __get_current_block_start_address(): use_capstone = self.has_setting("use_capstone") and self.get_setting("use_capstone") instruction_iterator = capstone_disassemble if use_capstone else gef_disassemble function_parameters = current_arch.function_parameters + arg_key_color = get_gef_setting("theme.registers_register_name") for insn in instruction_iterator(block_start, pc-block_start): if not insn.operands: @@ -7252,7 +7253,7 @@ def __get_current_block_start_address(): for i in range(nb_argument): _key, _value = self.get_ith_parameter(i) _value = RIGHT_ARROW.join(DereferenceCommand.dereference_from(_value)) - args.append("{} = {}".format(_key, _value)) + args.append("{} = {}".format(Color.colorify(_key, attrs=arg_key_color), _value)) self.context_title("arguments (guessed)") gef_print("{} (".format(function_name)) From 002f33bdfd9cdcb0c445576051690ecce9cbc4e6 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Wed, 10 Oct 2018 11:49:08 -0700 Subject: [PATCH 15/58] colorify: Don't use 'attr=', it's not a kwarg --- gef.py | 202 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 101 insertions(+), 101 deletions(-) diff --git a/gef.py b/gef.py index 5cfcbe617..af73a9172 100644 --- a/gef.py +++ b/gef.py @@ -344,25 +344,25 @@ class Color: } @staticmethod - def redify(msg): return Color.colorify(msg, attrs="red") + def redify(msg): return Color.colorify(msg, "red") @staticmethod - def greenify(msg): return Color.colorify(msg, attrs="green") + def greenify(msg): return Color.colorify(msg, "green") @staticmethod - def blueify(msg): return Color.colorify(msg, attrs="blue") + def blueify(msg): return Color.colorify(msg, "blue") @staticmethod - def yellowify(msg): return Color.colorify(msg, attrs="yellow") + def yellowify(msg): return Color.colorify(msg, "yellow") @staticmethod - def grayify(msg): return Color.colorify(msg, attrs="gray") + def grayify(msg): return Color.colorify(msg, "gray") @staticmethod - def pinkify(msg): return Color.colorify(msg, attrs="pink") + def pinkify(msg): return Color.colorify(msg, "pink") @staticmethod - def boldify(msg): return Color.colorify(msg, attrs="bold") + def boldify(msg): return Color.colorify(msg, "bold") @staticmethod - def underlinify(msg): return Color.colorify(msg, attrs="underline") + def underlinify(msg): return Color.colorify(msg, "underline") @staticmethod - def highlightify(msg): return Color.colorify(msg, attrs="highlight") + def highlightify(msg): return Color.colorify(msg, "highlight") @staticmethod - def blinkify(msg): return Color.colorify(msg, attrs="blink") + def blinkify(msg): return Color.colorify(msg, "blink") @staticmethod def colorify(text, attrs): @@ -394,11 +394,11 @@ def __str__(self): 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) + return Color.colorify(value, code_color) if self.is_in_heap_segment(): - return Color.colorify(value, attrs=heap_color) + return Color.colorify(value, heap_color) if self.is_in_stack_segment(): - return Color.colorify(value, attrs=stack_color) + return Color.colorify(value, stack_color) return value def is_in_text_segment(self): @@ -809,15 +809,15 @@ def str_as_freed(self): def flags_as_string(self): flags = [] if self.has_p_bit(): - flags.append(Color.colorify("PREV_INUSE", attrs="red bold")) + flags.append(Color.colorify("PREV_INUSE", "red bold")) if self.has_m_bit(): - flags.append(Color.colorify("IS_MMAPPED", attrs="red bold")) + flags.append(Color.colorify("IS_MMAPPED", "red bold")) if self.has_n_bit(): - flags.append(Color.colorify("NON_MAIN_ARENA", attrs="red bold")) + flags.append(Color.colorify("NON_MAIN_ARENA", "red bold")) return "|".join(flags) def __str__(self): - msg = "{:s}(addr={:#x}, size={:#x}, flags={:s})".format(Color.colorify("Chunk", attrs="yellow bold underline"), + msg = "{:s}(addr={:#x}, size={:#x}, flags={:s})".format(Color.colorify("Chunk", "yellow bold underline"), long(self.address),self.get_chunk_size(), self.flags_as_string()) return msg @@ -853,9 +853,9 @@ def titlify(text, color=None, msg_color=None): msg_color = __config__.get("theme.default_title_message")[0] msg = [] - msg.append(Color.colorify(HORIZONTAL_LINE * nb + '[ ', attrs=color)) - msg.append(Color.colorify(text, attrs=msg_color)) - msg.append(Color.colorify(' ]' + HORIZONTAL_LINE * nb, attrs=color)) + msg.append(Color.colorify(HORIZONTAL_LINE * nb + '[ ', color)) + msg.append(Color.colorify(text, msg_color)) + msg.append(Color.colorify(' ]' + HORIZONTAL_LINE * nb, color)) return "".join(msg) @@ -868,10 +868,10 @@ def _xlog(text, stream, cr=True): return 0 -def err(msg, cr=True): return _xlog("{} {}".format(Color.colorify("[!]", attrs="bold red"), msg), gdb.STDERR, cr) -def warn(msg, cr=True): return _xlog("{} {}".format(Color.colorify("[*]", attrs="bold yellow"), msg), gdb.STDLOG, cr) -def ok(msg, cr=True): return _xlog("{} {}".format(Color.colorify("[+]", attrs="bold green"), msg), gdb.STDLOG, cr) -def info(msg, cr=True): return _xlog("{} {}".format(Color.colorify("[+]", attrs="bold blue"), msg), gdb.STDLOG, cr) +def err(msg, cr=True): return _xlog("{} {}".format(Color.colorify("[!]", "bold red"), msg), gdb.STDERR, cr) +def warn(msg, cr=True): return _xlog("{} {}".format(Color.colorify("[*]", "bold yellow"), msg), gdb.STDLOG, cr) +def ok(msg, cr=True): return _xlog("{} {}".format(Color.colorify("[+]", "bold green"), msg), gdb.STDLOG, cr) +def info(msg, cr=True): return _xlog("{} {}".format(Color.colorify("[+]", "bold blue"), msg), gdb.STDLOG, cr) def push_context_message(level, message): @@ -896,7 +896,7 @@ def _show_code_line(fname, idx): exc_type, exc_value, exc_traceback = sys.exc_info() gef_print(" Exception raised ".center(80, HORIZONTAL_LINE)) - gef_print("{}: {}".format(Color.colorify(exc_type.__name__, attrs="bold underline red"), exc_value)) + gef_print("{}: {}".format(Color.colorify(exc_type.__name__, "bold underline red"), exc_value)) gef_print(" Detailed stacktrace ".center(80, HORIZONTAL_LINE)) for fs in traceback.extract_tb(exc_traceback)[::-1]: @@ -975,7 +975,7 @@ def style_byte(b, color=True): else: st = style.get('nonprintable') if st: - sbyte = Color.colorify(sbyte, attrs=st) + sbyte = Color.colorify(sbyte, st) return sbyte @@ -2115,7 +2115,7 @@ class MIPS(Architecture): syscall_instructions = ["syscall"] def flag_register_to_human(self, val=None): - return Color.colorify("No flag register", attrs="yellow underline") + return Color.colorify("No flag register", "yellow underline") def is_call(self, insn): return False @@ -3265,7 +3265,7 @@ def stop(self): if addr.section.permission.value & Permission.WRITE: content = read_cstring_from_memory(addr.value) name = addr.info.name if addr.info else addr.section.path - msg.append(Color.colorify("Format string helper", attrs="yellow bold")) + msg.append(Color.colorify("Format string helper", "yellow bold")) msg.append("Possible insecure format string: {:s}('{:s}' {:s} {:#x}: '{:s}')".format(self.location, ptr, RIGHT_ARROW, addr.value, content)) msg.append("Reason: Call to '{:s}()' with format string argument in position " "#{:d} is in page {:#x} ({:s}) that has write permission".format(self.location, self.num_args, addr.section.page_start, name)) @@ -3353,7 +3353,7 @@ def stop(self): loc = to_unsigned_long(gdb.parse_and_eval(current_arch.return_register)) size = self.size - ok("{} - malloc({})={:#x}".format(Color.colorify("Heap-Analysis", attrs="yellow bold"), size, loc)) + ok("{} - malloc({})={:#x}".format(Color.colorify("Heap-Analysis", "yellow bold"), size, loc)) check_heap_overlap = get_gef_setting("heap-analysis-helper.check_heap_overlap") # pop from free-ed list if it was in it @@ -3391,7 +3391,7 @@ def stop(self): offset = loc - chunk_addr - 2*align if offset < 0: continue # false positive, discard - msg.append(Color.colorify("Heap-Analysis", attrs="yellow bold")) + msg.append(Color.colorify("Heap-Analysis", "yellow bold")) msg.append("Possible heap overlap detected") msg.append("Reason {} new allocated chunk {:#x} (of size {:d}) overlaps in-used chunk {:#x} (of size {:#x})".format(RIGHT_ARROW, loc, size, chunk_addr, current_chunk_size)) msg.append("Writing {0:d} bytes from {1:#x} will reach chunk {2:#x}".format(offset, chunk_addr, loc)) @@ -3443,13 +3443,13 @@ def stop(self): newloc = to_unsigned_long(gdb.parse_and_eval(current_arch.return_register)) if newloc != self: - ok("{} - realloc({:#x}, {})={}".format(Color.colorify("Heap-Analysis", attrs="yellow bold"), + ok("{} - realloc({:#x}, {})={}".format(Color.colorify("Heap-Analysis", "yellow bold"), self.ptr, self.size, - Color.colorify("{:#x}".format(newloc), attrs="green"),)) + Color.colorify("{:#x}".format(newloc), "green"),)) else: - ok("{} - realloc({:#x}, {})={}".format(Color.colorify("Heap-Analysis", attrs="yellow bold"), + ok("{} - realloc({:#x}, {})={}".format(Color.colorify("Heap-Analysis", "yellow bold"), self.ptr, self.size, - Color.colorify("{:#x}".format(newloc), attrs="red"),)) + Color.colorify("{:#x}".format(newloc), "red"),)) item = (newloc, self.size) @@ -3488,10 +3488,10 @@ def stop(self): check_weird_free = get_gef_setting("heap-analysis-helper.check_weird_free") check_uaf = get_gef_setting("heap-analysis-helper.check_uaf") - ok("{} - free({:#x})".format(Color.colorify("Heap-Analysis", attrs="yellow bold"), addr)) + ok("{} - free({:#x})".format(Color.colorify("Heap-Analysis", "yellow bold"), addr)) if addr==0: if check_free_null: - msg.append(Color.colorify("Heap-Analysis", attrs="yellow bold")) + msg.append(Color.colorify("Heap-Analysis", "yellow bold")) msg.append("Attempting to free(NULL) at {:#x}".format(current_arch.pc)) msg.append("Reason: if NULL page is allocatable, this can lead to code execution.") push_context_message("warn", "\n".join(msg)) @@ -3501,7 +3501,7 @@ def stop(self): if addr in [x for (x,y) in __heap_freed_list__]: if check_double_free: - msg.append(Color.colorify("Heap-Analysis", attrs="yellow bold")) + msg.append(Color.colorify("Heap-Analysis", "yellow bold")) msg.append("Double-free detected {} free({:#x}) is called at {:#x} but is already in the free-ed list".format(RIGHT_ARROW, addr, current_arch.pc)) msg.append("Execution will likely crash...") push_context_message("warn", "\n".join(msg)) @@ -3517,7 +3517,7 @@ def stop(self): except ValueError: if check_weird_free: - msg.append(Color.colorify("Heap-Analysis", attrs="yellow bold")) + msg.append(Color.colorify("Heap-Analysis", "yellow bold")) msg.append("Heap inconsistency detected:") msg.append("Attempting to free an unknown value: {:#x}".format(addr)) push_context_message("warn", "\n".join(msg)) @@ -3546,7 +3546,7 @@ def __init__(self, addr): def stop(self): wp = UafWatchpoint(self.addr) __heap_uaf_watchpoints__.append(wp) - ok("{} - watching {:#x}".format(Color.colorify("Heap-Analysis", attrs="yellow bold"), self.addr)) + ok("{} - watching {:#x}".format(Color.colorify("Heap-Analysis", "yellow bold"), self.addr)) return False @@ -3572,7 +3572,7 @@ def stop(self): pc = gdb_get_nth_previous_instruction_address(current_arch.pc, 2) insn = gef_current_instruction(pc) msg = [] - msg.append(Color.colorify("Heap-Analysis", attrs="yellow bold")) + msg.append(Color.colorify("Heap-Analysis", "yellow bold")) msg.append("Possible Use-after-Free in '{:s}': pointer {:#x} was freed, but is attempted to be used at {:#x}" .format(get_filepath(), self.address, pc)) msg.append("{:#x} {:s} {:s}".format(insn.address, insn.mnemonic, Color.yellowify(", ".join(insn.operands)))) @@ -4332,7 +4332,7 @@ def do_invoke(self, args): if argc==0: for setting in sorted(self.settings): value = self.get_setting(setting) - value = Color.colorify(value, attrs=value) + value = Color.colorify(value, value) gef_print("{:40s}: {:s}".format(setting, value)) return @@ -4343,7 +4343,7 @@ def do_invoke(self, args): if argc==1: value = self.get_setting(setting) - value = Color.colorify(value, attrs=value) + value = Color.colorify(value, value) gef_print("{:40s}: {:s}".format(setting, value)) return @@ -4945,7 +4945,7 @@ def do_invoke(self, argv): for nstart, nend in needle_sections: if target >= nstart and target < nend: deref = DereferenceCommand.pprint_dereferenced(hstart, long(i / step)) - name = Color.colorify(hname, attrs="yellow") + name = Color.colorify(hname, "yellow") gef_print("{:s}: {:s}".format(name, deref)) return @@ -5833,7 +5833,7 @@ def do_invoke(self, argv): msg = "" if insn.address == current_arch.pc: - msg = Color.colorify("{} {}".format(RIGHT_ARROW, text_insn), attrs="bold red") + msg = Color.colorify("{} {}".format(RIGHT_ARROW, text_insn), "bold red") reason = self.capstone_analyze_pc(insn, length)[0] if reason: gef_print(msg) @@ -5850,10 +5850,10 @@ def capstone_analyze_pc(self, insn, nb_insn): is_taken, reason = current_arch.is_branch_taken(insn) if is_taken: reason = "[Reason: {:s}]".format(reason) if reason else "" - msg = Color.colorify("\tTAKEN {:s}".format(reason), attrs="bold green") + msg = Color.colorify("\tTAKEN {:s}".format(reason), "bold green") else: reason = "[Reason: !({:s})]".format(reason) if reason else "" - msg = Color.colorify("\tNOT taken {:s}".format(reason), attrs="bold red") + msg = Color.colorify("\tNOT taken {:s}".format(reason), "bold red") return (is_taken, msg) if current_arch.is_call(insn): @@ -6299,7 +6299,7 @@ def do_invoke(self, argv): if is_x86() and regname in current_arch.msr_registers: msr = set(current_arch.msr_registers) for r in set(regs) & msr: - line = "{}: ".format(Color.colorify(r, attrs=regname_color)) + line = "{}: ".format(Color.colorify(r, regname_color)) line+= "0x{:04x}".format(get_register(r)) gef_print(line, end=" ") regs.remove(r) @@ -6307,10 +6307,10 @@ def do_invoke(self, argv): continue padreg = regname.ljust(widest, " ") - line = "{}: ".format(Color.colorify(padreg, attrs=regname_color)) + line = "{}: ".format(Color.colorify(padreg, regname_color)) if str(reg) == "": - line += Color.colorify("no value", attrs="yellow underline") + line += Color.colorify("no value", "yellow underline") gef_print(line) continue @@ -6324,7 +6324,7 @@ def do_invoke(self, argv): if new_value == old_value: line += format_address_spaces(new_value) else: - line += Color.colorify(format_address_spaces(new_value), attrs=changed_register_value_color) + line += Color.colorify(format_address_spaces(new_value), changed_register_value_color) addrs = DereferenceCommand.dereference_from(new_value) if len(addrs) > 1: @@ -6338,7 +6338,7 @@ def do_invoke(self, argv): last_addr = int(addrs[-1],16) val = gef_pystring(struct.pack(fmt, last_addr)) if all([_ in charset for _ in val]): - line += ' ("{:s}"?)'.format(Color.colorify(val, attrs=string_color)) + line += ' ("{:s}"?)'.format(Color.colorify(val, string_color)) except ValueError: pass @@ -6910,11 +6910,11 @@ def show_legend(self): heap_addr_color = get_gef_setting("theme.address_heap") changed_register_color = get_gef_setting("theme.registers_value_changed") - gef_print("[ Legend: {} | {} | {} | {} | {} ]".format(Color.colorify("Modified register", attrs=changed_register_color), - 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) + gef_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 @@ -6966,10 +6966,10 @@ def context_title(self, m): title += Color.colorify("{:{padd}<{width}}[ ".format("", width=self.tty_columns - trail_len, padd=HORIZONTAL_LINE), - attrs=line_color) + line_color) title += Color.colorify(m, msg_color) title += Color.colorify(" ]{:{padd}<4}".format("", padd=HORIZONTAL_LINE), - attrs=line_color) + line_color) gef_print(title) return @@ -7016,7 +7016,7 @@ def context_regs(self): old_value = self.old_registers.get(reg, 0) padreg = reg.ljust(widest, " ") - line += "{}: ".format(Color.colorify(padreg, attrs=regname_color)) + line += "{}: ".format(Color.colorify(padreg, regname_color)) if new_value_type_flag: line += "{:s} ".format(str(new_value)) else: @@ -7025,7 +7025,7 @@ def context_regs(self): if new_value == old_value: line += "{:s} ".format(format_address_spaces(new_value)) else: - line += "{:s} ".format(Color.colorify(format_address_spaces(new_value), attrs=color)) + line += "{:s} ".format(Color.colorify(format_address_spaces(new_value), color)) if i % nb == 0 : gef_print(line) @@ -7086,17 +7086,17 @@ def context_code(self): line += Color.grayify(" {}".format(text)) elif insn.address == pc: - line += Color.colorify("{:s}{:s}".format(RIGHT_ARROW, text), attrs=cur_insn_color) + line += Color.colorify("{:s}{:s}".format(RIGHT_ARROW, text), cur_insn_color) if current_arch.is_conditional_branch(insn): is_taken, reason = 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), attrs="bold green") + 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), attrs="bold red") + line += Color.colorify("\tNOT taken {:s}".format(reason), "bold red") elif current_arch.is_call(insn) and self.get_setting("peek_calls") is True: target = insn.operands[-1].split()[0] elif current_arch.is_ret(insn) and self.get_setting("peek_ret") is True: @@ -7253,7 +7253,7 @@ def __get_current_block_start_address(): for i in range(nb_argument): _key, _value = self.get_ith_parameter(i) _value = RIGHT_ARROW.join(DereferenceCommand.dereference_from(_value)) - args.append("{} = {}".format(Color.colorify(_key, attrs=arg_key_color), _value)) + args.append("{} = {}".format(Color.colorify(_key, arg_key_color), _value)) self.context_title("arguments (guessed)") gef_print("{} (".format(function_name)) @@ -7295,7 +7295,7 @@ def context_source(self): extra_info = self.get_pc_context_info(pc, lines[i]) if extra_info: gef_print(extra_info) - gef_print(Color.colorify("{}{:4d}\t {:s}".format(RIGHT_ARROW, i + 1, lines[i]), attrs=cur_line_color)) + gef_print(Color.colorify("{}{:4d}\t {:s}".format(RIGHT_ARROW, i + 1, lines[i]), cur_line_color)) if i > line_num: try: @@ -7419,15 +7419,15 @@ def reason(): return for i, thread in enumerate(threads): - line = """[{:s}] Id {:d}, Name: "{:s}", """.format(Color.colorify("#{:d}".format(i), attrs="bold pink"), + 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", attrs="bold green") + line += Color.colorify("running", "bold green") elif thread.is_stopped(): - line += Color.colorify("stopped", attrs="bold red") - line += ", reason: {}".format(Color.colorify(reason(), attrs="bold pink")) + line += Color.colorify("stopped", "bold red") + line += ", reason: {}".format(Color.colorify(reason(), "bold pink")) elif thread.is_exited(): - line += Color.colorify("exited", attrs="bold yellow") + line += Color.colorify("exited", "bold yellow") gef_print(line) i += 1 return @@ -7669,7 +7669,7 @@ def _hexdump(self, start_addr, length, arrange_as, offset=0): sym = "<{:s}+{:04x}> ".format(*sym) if sym else '' mem = read_memory(cur_addr, l) val = struct.unpack(fmt_pack, mem)[0] - lines.append(fmt_str.format(base=Color.colorify(format_address(cur_addr), attrs=base_address_color), + lines.append(fmt_str.format(base=Color.colorify(format_address(cur_addr), base_address_color), offset=(i + offset) * l, sym=sym, val=val)) i += 1 @@ -7776,7 +7776,7 @@ def pprint_dereferenced(addr, off): addrs = DereferenceCommand.dereference_from(current_address) l = "" addr_l = format_address(long(addrs[0], 16)) - l += "{:s}{:s}+{:#06x}: {:{ma}s}".format(Color.colorify(addr_l, attrs=base_address_color), + l += "{:s}{:s}+{:#06x}: {:{ma}s}".format(Color.colorify(addr_l, base_address_color), VERTICAL_LINE, offset, sep.join(addrs[1:]), ma=(memalign*2 + 2)) @@ -7788,7 +7788,7 @@ def pprint_dereferenced(addr, off): if register_hints: m = "\t{:s}{:s}".format(LEFT_ARROW, ", ".join(list(register_hints))) - l += Color.colorify(m, attrs=registers_color) + l += Color.colorify(m, registers_color) offset += memalign return l @@ -7874,18 +7874,18 @@ def dereference_from(addr): if addr.section.is_executable() and addr.is_in_text_segment() and not is_readable_string(addr.value): insn = gef_current_instruction(addr.value) insn_str = "{} {} {}".format(insn.location, insn.mnemonic, ", ".join(insn.operands)) - msg.append(Color.colorify(insn_str, attrs=code_color)) + msg.append(Color.colorify(insn_str, code_color)) break elif addr.section.permission.value & Permission.READ: if is_readable_string(addr.value): s = read_cstring_from_memory(addr.value) if len(s) < get_memory_alignment(): - txt = '{:s} ("{:s}"?)'.format(format_address(deref), Color.colorify(s, attrs=string_color)) + txt = '{:s} ("{:s}"?)'.format(format_address(deref), Color.colorify(s, string_color)) elif len(s) >= 50: - txt = Color.colorify('"{:s}[...]"'.format(s[:50]), attrs=string_color) + txt = Color.colorify('"{:s}[...]"'.format(s[:50]), string_color) else: - txt = Color.colorify('"{:s}"'.format(s), attrs=string_color) + txt = Color.colorify('"{:s}"'.format(s), string_color) msg.append(txt) break @@ -7970,7 +7970,7 @@ def do_invoke(self, argv): return color = get_gef_setting("theme.xinfo_title_message") - headers = [Color.colorify(x, attrs=color) for x in ["Start", "End", "Offset", "Perm", "Path"]] + headers = [Color.colorify(x, color) for x in ["Start", "End", "Offset", "Perm", "Path"]] if is_elf64(): gef_print("{:<31s} {:<31s} {:<31s} {:<4s} {:s}".format(*headers)) else: @@ -7985,7 +7985,7 @@ def do_invoke(self, argv): l.append(format_address(entry.offset)) if entry.permission.value == (Permission.READ|Permission.WRITE|Permission.EXECUTE) : - l.append(Color.colorify(str(entry.permission), attrs="bold red")) + l.append(Color.colorify(str(entry.permission), "bold red")) else: l.append(str(entry.permission)) @@ -8008,7 +8008,7 @@ class XFilesCommand(GenericCommand): @only_if_gdb_running def do_invoke(self, argv): color = get_gef_setting("theme.xinfo_title_message") - headers = [Color.colorify(x, attrs=color) for x in ["Start", "End", "Name", "File",]] + headers = [Color.colorify(x, color) for x in ["Start", "End", "Name", "File",]] if is_elf64(): gef_print("{:<31s} {:<31s} {:<34s} {:s}".format(*headers)) else: @@ -8364,12 +8364,12 @@ def search(self, pattern, size): found = False off = cyclic_pattern.find(pattern_le) if off >= 0: - ok("Found at offset {:d} (little-endian search) {:s}".format(off, Color.colorify("likely", attrs="bold red") if is_little_endian() else "")) + ok("Found at offset {:d} (little-endian search) {:s}".format(off, Color.colorify("likely", "bold red") if is_little_endian() else "")) found = True off = cyclic_pattern.find(pattern_be) if off >= 0: - ok("Found at offset {:d} (big-endian search) {:s}".format(off, Color.colorify("likely", attrs="bold green") if is_big_endian() else "")) + ok("Found at offset {:d} (big-endian search) {:s}".format(off, Color.colorify("likely", "bold green") if is_big_endian() else "")) found = True if not found: @@ -8518,7 +8518,7 @@ def setup(self): gdb.execute("set can-use-hw-watchpoints 0") info("Dynamic breakpoints correctly setup, GEF will break execution if a possible vulnerabity is found.") - warn("{}: The heap analysis slows down noticeably the execution. ".format(Color.colorify("Note", attrs="bold underline yellow"))) + warn("{}: The heap analysis slows down noticeably the execution. ".format(Color.colorify("Note", "bold underline yellow"))) # when inferior quits, we need to clean everything for a next execution gef_on_exit_hook(self.clean) @@ -8543,7 +8543,7 @@ def dump_tracked_allocations(self): def clean(self, event): global __heap_allocated_list__, __heap_freed_list__, __heap_uaf_watchpoints__ - ok("{} - Cleaning up".format(Color.colorify("Heap-Analysis", attrs="yellow bold"),)) + ok("{} - Cleaning up".format(Color.colorify("Heap-Analysis", "yellow bold"),)) for bp in [self.bp_malloc, self.bp_calloc, self.bp_free, self.bp_realloc]: if hasattr(bp, "retbp") and bp.retbp: bp.retbp.delete() @@ -8556,7 +8556,7 @@ def clean(self, event): __heap_freed_list__ = [] __heap_uaf_watchpoints__ = [] - ok("{} - Re-enabling hardware watchpoints".format(Color.colorify("Heap-Analysis", attrs="yellow bold"),)) + ok("{} - Re-enabling hardware watchpoints".format(Color.colorify("Heap-Analysis", "yellow bold"),)) gdb.execute("set can-use-hw-watchpoints 1") gef_on_exit_unhook(self.clean) @@ -8618,10 +8618,10 @@ def do_invoke(self, argv): parameters = [s.param for s in syscall_entry.params] registers = [s.reg for s in syscall_entry.params] - info("Detected syscall {}".format(Color.colorify(syscall_entry.name, attrs=color))) + info("Detected syscall {}".format(Color.colorify(syscall_entry.name, color))) gef_print(" {}({})".format(syscall_entry.name, ', '.join(parameters))) - headers = [Color.colorify(x, attrs=color) for x in ["Parameter", "Register", "Value"]] + headers = [Color.colorify(x, color) for x in ["Parameter", "Register", "Value"]] param_names = [re.split(r' |\*', p)[-1] for p in parameters] info("{:<28} {:<28} {}".format(*headers)) for name, register, value in zip(param_names, registers, values): @@ -8766,7 +8766,7 @@ def do_invoke(self, argv): gef_print(titlify("GEF - Convenience Functions")) gef_print("These functions can be used as arguments to other " "commands to dynamically calculate values, eg: {:s}\n" - .format(Color.colorify("deref $_heap(0x20)", attrs="yellow"))) + .format(Color.colorify("deref $_heap(0x20)", "yellow"))) gef_print(self.__doc__) return @@ -8853,8 +8853,8 @@ def __load_extra_plugins(self): gdb.execute("source {:s}".format(fpath)) nb_added = len(self.loaded_commands) - nb_inital if nb_added > 0: - ok("{:s} extra commands added from '{:s}'".format(Color.colorify(str(nb_added), attrs="bold green"), - Color.colorify(directory, attrs="bold blue"))) + ok("{:s} extra commands added from '{:s}'".format(Color.colorify(str(nb_added), "bold green"), + Color.colorify(directory, "bold blue"))) except gdb.error as e: err("failed: {}".format(str(e))) return nb_added @@ -8905,21 +8905,21 @@ def is_loaded(x): if initial: gef_print("{:s} for {:s} ready, type `{:s}' to start, `{:s}' to configure" .format(Color.greenify("GEF"), get_os(), - Color.colorify("gef",attrs="underline yellow"), - Color.colorify("gef config", attrs="underline pink"))) + Color.colorify("gef","underline yellow"), + Color.colorify("gef config", "underline pink"))) ver = "{:d}.{:d}".format(sys.version_info.major, sys.version_info.minor) nb_cmds = len(self.loaded_commands) gef_print("{:s} commands loaded for GDB {:s} using Python engine {:s}" - .format(Color.colorify(str(nb_cmds), attrs="bold green"), - Color.colorify(gdb.VERSION, attrs="bold yellow"), - Color.colorify(ver, attrs="bold red"))) + .format(Color.colorify(str(nb_cmds), "bold green"), + Color.colorify(gdb.VERSION, "bold yellow"), + Color.colorify(ver, "bold red"))) if nb_missing: warn("{:s} command{} could not be loaded, run `{:s}` to know why." - .format(Color.colorify(str(nb_missing), attrs="bold red"), + .format(Color.colorify(str(nb_missing), "bold red"), "s" if nb_missing > 1 else "", - Color.colorify("gef missing", attrs="underline pink"))) + Color.colorify("gef missing", "underline pink"))) return @@ -9022,14 +9022,14 @@ def print_setting(self, plugin_name, show_description=False): return _value, _type, _desc = res - _setting = Color.colorify(plugin_name, attrs="pink bold underline") + _setting = Color.colorify(plugin_name, "pink bold underline") _type = _type.__name__ - _value = Color.colorify(str(_value), attrs="yellow") if _type!='str' else '"{:s}"'.format(Color.colorify(str(_value), attrs=string_color)) + _value = Color.colorify(str(_value), "yellow") if _type!='str' else '"{:s}"'.format(Color.colorify(str(_value), string_color)) gef_print("{:s} ({:s}) = {:s}".format(_setting, _type, _value)) if show_description: gef_print("") - gef_print(Color.colorify("Description:", attrs="bold underline")) + gef_print(Color.colorify("Description:", "bold underline")) gef_print("\t{:s}".format(_desc)) return @@ -9167,7 +9167,7 @@ def invoke(self, args, from_tty): pass if not quiet: - ok("Configuration from '{:s}' restored".format(Color.colorify(GEF_RC, attrs="bold blue"))) + ok("Configuration from '{:s}' restored".format(Color.colorify(GEF_RC, "bold blue"))) return From f69633c24c4cf82e83c6a38a64ae2a644da7b4dc Mon Sep 17 00:00:00 2001 From: Grazfather Date: Wed, 10 Oct 2018 14:47:20 -0700 Subject: [PATCH 16/58] Fix header alignment The format function doesn't ignore control codes, so we were padding assuming they were visible. --- gef.py | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/gef.py b/gef.py index af73a9172..e108c8126 100644 --- a/gef.py +++ b/gef.py @@ -6760,7 +6760,7 @@ def do_invoke(self, argv): ] for title, content in data: - gef_print("{:<30}: {}".format(Color.boldify(title), content)) + gef_print("{}: {}".format(Color.boldify("{:<22}".format(title)), content)) return @@ -7970,11 +7970,9 @@ def do_invoke(self, argv): return color = get_gef_setting("theme.xinfo_title_message") - headers = [Color.colorify(x, color) for x in ["Start", "End", "Offset", "Perm", "Path"]] - if is_elf64(): - gef_print("{:<31s} {:<31s} {:<31s} {:<4s} {:s}".format(*headers)) - else: - gef_print("{:<23s} {:<23s} {:<23s} {:<4s} {:s}".format(*headers)) + + headers = ["Start", "End", "Offset", "Perm", "Path"] + gef_print(Color.colorify("{:<{w}s}{:<{w}s}{:<{w}s}{:<4s} {:s}".format(*headers, w=get_memory_alignment()*2+3), color)) for entry in vmmap: if argv and not argv[0] in entry.path: @@ -8008,11 +8006,8 @@ class XFilesCommand(GenericCommand): @only_if_gdb_running def do_invoke(self, argv): color = get_gef_setting("theme.xinfo_title_message") - headers = [Color.colorify(x, color) for x in ["Start", "End", "Name", "File",]] - if is_elf64(): - gef_print("{:<31s} {:<31s} {:<34s} {:s}".format(*headers)) - else: - gef_print("{:<23s} {:<23s} {:<23s} {:s}".format(*headers)) + headers = ["Start", "End", "Name", "File"] + gef_print(Color.colorify("{:<{w}s}{:<{w}s}{:<21s} {:s}".format(*headers, w=get_memory_alignment()*2+3), color)) filter_by_file = argv[0] if argv and argv[0] else None filter_by_name = argv[1] if len(argv) > 1 and argv[1] else None @@ -8621,9 +8616,9 @@ def do_invoke(self, argv): info("Detected syscall {}".format(Color.colorify(syscall_entry.name, color))) gef_print(" {}({})".format(syscall_entry.name, ', '.join(parameters))) - headers = [Color.colorify(x, color) for x in ["Parameter", "Register", "Value"]] + headers = ["Parameter", "Register", "Value"] param_names = [re.split(r' |\*', p)[-1] for p in parameters] - info("{:<28} {:<28} {}".format(*headers)) + info(Color.colorify("{:<28} {:<28} {}".format(*headers), color)) for name, register, value in zip(param_names, registers, values): line = " {:<15} {:<15} 0x{:x}".format(name, register, value) @@ -8756,8 +8751,7 @@ def add_function_to_doc(self, function): doc = getattr(function, "__doc__", "").lstrip() doc = "\n ".join(doc.split("\n")) syntax = getattr(function, "_syntax_", "").lstrip() - w = max(1, get_terminal_size()[1] - 29) # use max() to avoid zero or negative numbers - msg = "{syntax:<25s} -- {help:{w}s}".format(syntax=syntax, help=Color.greenify(doc), w=w) + msg = "{syntax:<25s} -- {help:s}".format(syntax=syntax, help=Color.greenify(doc)) self.docs.append(msg) return @@ -8958,9 +8952,8 @@ def add_command_to_doc(self, command): return 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 "" - w = max(1, get_terminal_size()[1] - 29 - len(aliases)) # use max() to avoid zero or negative numbers - msg = "{cmd:<25s} -- {help:{w}s} {aliases:s}".format(cmd=cmd, help=Color.greenify(doc), w=w, aliases=aliases) + 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 From d106564661a14913bea2560f44752a9ad849dc0e Mon Sep 17 00:00:00 2001 From: Grazfather Date: Wed, 10 Oct 2018 15:05:01 -0700 Subject: [PATCH 17/58] Fix some docstrings --- gef.py | 10 +++++----- tests/runtests.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/gef.py b/gef.py index e108c8126..6e3c90f3f 100644 --- a/gef.py +++ b/gef.py @@ -5041,7 +5041,7 @@ def do_invoke(self, argv): @register_command class FlagsCommand(GenericCommand): - """Edit flags in a human friendly way""" + """Edit flags in a human friendly way.""" _cmdline_ = "edit-flags" _syntax_ = "{:s} [(+|-|~)FLAGNAME ...]".format(_cmdline_) @@ -6359,7 +6359,7 @@ def __init__(self): return def do_invoke(self, argv): - err("Missing sub-command ") + err("Missing sub-command (search|get)") self.usage() return @@ -7681,7 +7681,7 @@ class PatchCommand(GenericCommand): """Write specified values to the specified address.""" _cmdline_ = "patch" - _syntax_ = ("{0:s} \n" + _syntax_ = ("{0:s} (qword|dword|word|byte) LOCATION VALUES\n" "{0:s} string LOCATION \"double-escaped string\"".format(_cmdline_)) SUPPORTED_SIZES = { "qword": (8, "Q"), @@ -8098,7 +8098,7 @@ class XorMemoryCommand(GenericCommand): runtime at runtime.""" _cmdline_ = "xor-memory" - _syntax_ = "{:s} ADDRESS SIZE KEY".format(_cmdline_) + _syntax_ = "{:s} (display|patch) ADDRESS SIZE KEY".format(_cmdline_) def __init__(self): super(XorMemoryCommand, self).__init__(prefix=True) @@ -8262,7 +8262,7 @@ class PatternCommand(GenericCommand): used by pwntools, and can therefore be used in conjunction.""" _cmdline_ = "pattern" - _syntax_ = "{:s} (create|search) ".format(_cmdline_) + _syntax_ = "{:s} (create|search) ARGS".format(_cmdline_) def __init__(self, *args, **kwargs): super(PatternCommand, self).__init__(prefix=True) diff --git a/tests/runtests.py b/tests/runtests.py index 0ccc250e2..fcbefcc4a 100755 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -358,7 +358,7 @@ def test_cmd_set_permission(self): def test_cmd_shellcode(self): res = gdb_start_silent_cmd("shellcode") self.assertNoException(res) - self.assertIn(b"Missing sub-command ", res) + self.assertIn(b"Missing sub-command (search|get)", res) return def test_cmd_shellcode_search(self): From 401bcf7839ea0724f5c687ebcd3243bde9ed0edb Mon Sep 17 00:00:00 2001 From: Grazfather Date: Thu, 11 Oct 2018 09:37:17 -0700 Subject: [PATCH 18/58] New theme! (#347) --- gef.py | 49 +++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/gef.py b/gef.py index 6e3c90f3f..3c6087fa5 100644 --- a/gef.py +++ b/gef.py @@ -846,16 +846,16 @@ def get_main_arena(): def titlify(text, color=None, msg_color=None): """Print a title.""" cols = get_terminal_size()[1] - nb = (cols - len(text) - 4)//2 + nb = (cols - len(text) - 2)//2 if color is None: color = __config__.get("theme.default_title_line")[0] if msg_color is None: msg_color = __config__.get("theme.default_title_message")[0] msg = [] - msg.append(Color.colorify(HORIZONTAL_LINE * nb + '[ ', color)) + msg.append(Color.colorify(HORIZONTAL_LINE * nb + ' ', color)) msg.append(Color.colorify(text, msg_color)) - msg.append(Color.colorify(' ]' + HORIZONTAL_LINE * nb, color)) + msg.append(Color.colorify(' ' + HORIZONTAL_LINE * nb, color)) return "".join(msg) @@ -4307,22 +4307,23 @@ class GefThemeCommand(GenericCommand): def __init__(self, *args, **kwargs): super(GefThemeCommand, self).__init__(GefThemeCommand._cmdline_) - self.add_setting("context_title_line", "green bold", "Color of the borders in context window") - self.add_setting("context_title_message", "red bold", "Color of the title in context window") - self.add_setting("default_title_line", "green bold", "Default color of borders") - self.add_setting("default_title_message", "red bold", "Default color of title") - self.add_setting("xinfo_title_message", "blue bold", "Color of the title in xinfo window") - self.add_setting("disassemble_current_instruction", "bold red", "Color to use to highlight the current $pc when disassembling") - self.add_setting("dereference_string", "green", "Color of dereferenced string") - self.add_setting("dereference_code", "red", "Color of dereferenced code") - 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 register name in the register window") + 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", "yellow", "Color to use when a heap 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", "bold red", "Color to use for the current code line in the source window") + self.add_setting("source_current_line", "green", "Color to use for the current code line in the source window") + self.add_setting("xinfo_title_message", "blue bold", "Unused") return def do_invoke(self, args): @@ -6961,14 +6962,14 @@ def context_title(self, m): gef_print(Color.colorify(HORIZONTAL_LINE * self.tty_columns, line_color)) return - trail_len = len(m) + 8 + trail_len = len(m) + 6 title = "" - title += Color.colorify("{:{padd}<{width}}[ ".format("", - width=self.tty_columns - trail_len, - padd=HORIZONTAL_LINE), + title += Color.colorify("{:{padd}<{width}} ".format("", + width=self.tty_columns - trail_len, + padd=HORIZONTAL_LINE), line_color) title += Color.colorify(m, msg_color) - title += Color.colorify(" ]{:{padd}<4}".format("", padd=HORIZONTAL_LINE), + title += Color.colorify(" {:{padd}<4}".format("", padd=HORIZONTAL_LINE), line_color) gef_print(title) return @@ -7969,7 +7970,7 @@ def do_invoke(self, argv): err("No address mapping information found") return - color = get_gef_setting("theme.xinfo_title_message") + color = get_gef_setting("theme.table_heading") headers = ["Start", "End", "Offset", "Perm", "Path"] gef_print(Color.colorify("{:<{w}s}{:<{w}s}{:<{w}s}{:<4s} {:s}".format(*headers, w=get_memory_alignment()*2+3), color)) @@ -8005,7 +8006,7 @@ class XFilesCommand(GenericCommand): @only_if_gdb_running def do_invoke(self, argv): - color = get_gef_setting("theme.xinfo_title_message") + color = get_gef_setting("theme.table_heading") headers = ["Start", "End", "Name", "File"] gef_print(Color.colorify("{:<{w}s}{:<{w}s}{:<21s} {:s}".format(*headers, w=get_memory_alignment()*2+3), color)) @@ -8589,7 +8590,7 @@ def __init__(self): return def do_invoke(self, argv): - color = get_gef_setting("theme.xinfo_title_message") + color = get_gef_setting("theme.table_heading") path = self.get_settings_path() if path is None: From 5190f01085f84ecb84c309fb859b16b297b81abf Mon Sep 17 00:00:00 2001 From: Grazfather Date: Thu, 11 Oct 2018 09:50:21 -0700 Subject: [PATCH 19/58] Show changed reg colour in name and section in value (#346) In context_regs and 'registers' command, instead of colouring the value of the register based on whether it changed since the last stop, colour the name. This allows us to colour the value based on the section, as we do with `deref`. --- docs/index.md | 2 +- gef.py | 63 ++++++++++++++++++++++++++++++--------------------- 2 files changed, 38 insertions(+), 27 deletions(-) diff --git a/docs/index.md b/docs/index.md index 195f143cc..d2543fa37 100644 --- a/docs/index.md +++ b/docs/index.md @@ -10,7 +10,7 @@ API to assist during the process of dynamic analysis and exploit development. It has full support for both Python2 and Python3 indifferently (as more and more distros start pushing `gdb` compiled with Python3 support). -![gef-context](https://i.imgur.com/i0Hkw2C.png) +![gef-context](https://i.imgur.com/E3EuQPs.png) *Some* of `GEF` features include: diff --git a/gef.py b/gef.py index 3c6087fa5..f7123b223 100644 --- a/gef.py +++ b/gef.py @@ -6277,9 +6277,8 @@ class DetailRegistersCommand(GenericCommand): @only_if_gdb_running def do_invoke(self, argv): - regs = [] - regname_color = get_gef_setting("theme.registers_register_name") - changed_register_value_color = get_gef_setting("theme.registers_value_changed") + unchanged_color = get_gef_setting("theme.registers_register_name") + changed_color = get_gef_setting("theme.registers_value_changed") string_color = get_gef_setting("theme.dereference_string") if argv: @@ -6290,43 +6289,51 @@ def do_invoke(self, argv): memsize = current_arch.ptrsize endian = endian_str() charset = string.printable + widest = max(map(len, current_arch.all_registers)) for regname in regs: reg = gdb.parse_and_eval(regname) if reg.type.code == gdb.TYPE_CODE_VOID: continue - widest = max(map(len, current_arch.all_registers)) + padreg = regname.ljust(widest, " ") + + if str(reg) == "": + line = "{}: ".format(Color.colorify(padreg, unchanged_color)) + line += Color.colorify("no value", "yellow underline") + gef_print(line) + continue + + value = align_address(long(reg)) + old_value = ContextCommand.old_registers.get(regname, 0) + if value == old_value: + color = unchanged_color + else: + color = changed_color + if is_x86() and regname in current_arch.msr_registers: msr = set(current_arch.msr_registers) for r in set(regs) & msr: - line = "{}: ".format(Color.colorify(r, regname_color)) + line = "{}: ".format(Color.colorify(r, color)) line+= "0x{:04x}".format(get_register(r)) gef_print(line, end=" ") regs.remove(r) gef_print() continue - padreg = regname.ljust(widest, " ") - line = "{}: ".format(Color.colorify(padreg, regname_color)) - - if str(reg) == "": - line += Color.colorify("no value", "yellow underline") - gef_print(line) - continue + line = "{}: ".format(Color.colorify(padreg, color)) if regname == current_arch.flag_register: line += current_arch.flag_register_to_human() gef_print(line) continue - old_value = ContextCommand.old_registers.get(regname, 0) - new_value = align_address(long(reg)) - if new_value == old_value: - line += format_address_spaces(new_value) + addr = lookup_address(align_address(long(value))) + if addr.valid: + line += str(addr) else: - line += Color.colorify(format_address_spaces(new_value), changed_register_value_color) - addrs = DereferenceCommand.dereference_from(new_value) + line += format_address_spaces(value) + addrs = DereferenceCommand.dereference_from(value) if len(addrs) > 1: sep = " {:s} ".format(RIGHT_ARROW) @@ -6990,7 +6997,7 @@ def context_regs(self): nb = get_terminal_size()[1]//l i = 1 line = "" - color = get_gef_setting("theme.registers_value_changed") + changed_color = get_gef_setting("theme.registers_value_changed") regname_color = get_gef_setting("theme.registers_register_name") for reg in current_arch.all_registers: @@ -7017,16 +7024,20 @@ def context_regs(self): old_value = self.old_registers.get(reg, 0) padreg = reg.ljust(widest, " ") - line += "{}: ".format(Color.colorify(padreg, regname_color)) + value = align_address(new_value) + old_value = align_address(old_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(new_value)) + line += "{:s} ".format(str(value)) else: - new_value = align_address(new_value) - old_value = align_address(old_value) - if new_value == old_value: - line += "{:s} ".format(format_address_spaces(new_value)) + addr = lookup_address(align_address(long(value))) + if addr.valid: + line += "{:s} ".format(str(addr)) else: - line += "{:s} ".format(Color.colorify(format_address_spaces(new_value), color)) + line += "{:s} ".format(format_address_spaces(value)) if i % nb == 0 : gef_print(line) From abd07da9fe2d6a3873fb8cdb5c17e233a4d650b8 Mon Sep 17 00:00:00 2001 From: Dan Robertson Date: Thu, 11 Oct 2018 13:18:11 -0500 Subject: [PATCH 20/58] Update target function regex (#317) The ops for a `call` instruction may be something like the following. call The current regex will attempt to lookup the global symbol "func_name at filename.c:42". Instead we should lookup just "func_name". --- gef.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gef.py b/gef.py index f7123b223..89a5c5cd9 100644 --- a/gef.py +++ b/gef.py @@ -7157,7 +7157,7 @@ def context_args(self): ops = " ".join(insn.operands) if "<" in ops and ">" in ops: # extract it - target = re.sub(r".*<([^\(>]*).*", r"\1", ops) + 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) From 1b3cb40c968647bdc7b76d533d00efd9fe9f6e2e Mon Sep 17 00:00:00 2001 From: Dan Robertson Date: Thu, 20 Sep 2018 02:27:49 +0000 Subject: [PATCH 21/58] AARCH64: Add more ARM condition codes --- gef.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gef.py b/gef.py index 89a5c5cd9..c13b3aa6b 100644 --- a/gef.py +++ b/gef.py @@ -1719,6 +1719,10 @@ def is_branch_taken(self, insn): taken, reason = val&(1< Date: Fri, 12 Oct 2018 08:08:55 +1100 Subject: [PATCH 22/58] Correct bufferize use (#349) bufferize takes a function, but we were accidentally passing in the result of invoke, not invoke itself. --- gef.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gef.py b/gef.py index c13b3aa6b..aafeb0c74 100644 --- a/gef.py +++ b/gef.py @@ -311,14 +311,14 @@ def wrapper(*args, **kwargs): global __gef_int_stream_buffer__ if __gef_int_stream_buffer__: - f(*args, **kwargs) - return + return f(*args, **kwargs) __gef_int_stream_buffer__ = StringIO() - f(*args, **kwargs) + rv = f(*args, **kwargs) sys.stdout.write(__gef_int_stream_buffer__.getvalue()) sys.stdout.flush() __gef_int_stream_buffer__ = None + return rv return wrapper @@ -3654,7 +3654,7 @@ def invoke(self, args, from_tty): try: argv = gdb.string_to_argv(args) self.__set_repeat_count(from_tty) - bufferize(self.do_invoke(argv)) + bufferize(self.do_invoke)(argv) except Exception as e: # Note: since we are intercepting cleaning exceptions here, commands preferably should avoid # catching generic Exception, but rather specific ones. This is allows a much cleaner use. From 99025b57f2f8cd94f8b70ea6864bcb3983cd3b53 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Fri, 12 Oct 2018 14:45:04 -0700 Subject: [PATCH 23/58] Remove xinfo_title_message theme setting --- gef.py | 1 - 1 file changed, 1 deletion(-) diff --git a/gef.py b/gef.py index aafeb0c74..0c7828480 100644 --- a/gef.py +++ b/gef.py @@ -4327,7 +4327,6 @@ def __init__(self, *args, **kwargs): 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("xinfo_title_message", "blue bold", "Unused") return def do_invoke(self, args): From 860de4be555efd7abfd092599833ac1b1bd3f3cd Mon Sep 17 00:00:00 2001 From: Kyle Zeng Date: Thu, 11 Oct 2018 21:15:25 -0700 Subject: [PATCH 24/58] fallback to read memory when gdb api fails to retrieve a cstring (#338) --- gef.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/gef.py b/gef.py index 0c7828480..f7616cf36 100644 --- a/gef.py +++ b/gef.py @@ -2208,8 +2208,13 @@ def read_cstring_from_memory(address, max_length=GEF_MAX_STRING_LENGTH, encoding char_t = cached_lookup_type("char") char_ptr = char_t.pointer() - res = gdb.Value(address).cast(char_ptr).string(encoding=encoding).strip() - res2 = res.replace('\n','\\n').replace('\r','\\r').replace('\t','\\t') + try: + res = gdb.Value(address).cast(char_ptr).string(encoding=encoding).strip() + except gdb.error: + length = min(address|(DEFAULT_PAGE_SIZE-1), max_length+1) + mem = bytes(read_memory(address, length)).decode("utf-8") + res = mem.split("\x00", 1)[0] + res2 = res.encode("unicode_escape") if max_length and len(res) > max_length: return "{}[...]".format(res2[:max_length]) @@ -2954,7 +2959,7 @@ def cached_lookup_type(_type): def get_memory_alignment(in_bits=False): """Return sizeof(size_t). If `in_bits` is set to True, the result is returned in bits, otherwise in bytes.""" - res = cached_lookup_type('size_t') + res = cached_lookup_type("size_t") if res is not None: return res.sizeof if not in_bits else res.sizeof * 8 if is_elf32(): From 77a8bf8615040e586f0c9ee3665266331bdb74d2 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Sat, 13 Oct 2018 08:54:32 -0700 Subject: [PATCH 25/58] Fix read_cstring_from_memory --- gef.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gef.py b/gef.py index f7616cf36..f67fcf88c 100644 --- a/gef.py +++ b/gef.py @@ -2214,7 +2214,7 @@ def read_cstring_from_memory(address, max_length=GEF_MAX_STRING_LENGTH, encoding length = min(address|(DEFAULT_PAGE_SIZE-1), max_length+1) mem = bytes(read_memory(address, length)).decode("utf-8") res = mem.split("\x00", 1)[0] - res2 = res.encode("unicode_escape") + res2 = res.replace('\n','\\n').replace('\r','\\r').replace('\t','\\t') if max_length and len(res) > max_length: return "{}[...]".format(res2[:max_length]) From 3b0cc771f67a874521bb5c3dd28256b61d35bddf Mon Sep 17 00:00:00 2001 From: Grazfather Date: Sat, 13 Oct 2018 09:03:21 -0700 Subject: [PATCH 26/58] tests: Don't disable colour We missed an bug we introduced, because it only came up when a return value that should have been a string but was not was was colorized. Instead let's let the colour through, and strip it out after. --- tests/helpers.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/helpers.py b/tests/helpers.py index 88545b035..fc9245d4d 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -1,15 +1,20 @@ +import re import subprocess PATH_TO_DEFAULT_BINARY = "./tests/binaries/default.out" +def ansi_clean(s): + ansi_escape = re.compile(b"(\x9B|\x1B\\[)[0-?]*[ -/]*[@-~]") + return ansi_escape.sub(b"", s) + + def gdb_run_cmd(cmd, before=None, after=None, target=PATH_TO_DEFAULT_BINARY): """Execute a command inside GDB. `before` and `after` are lists of commands to be executed before (resp. after) the command to test.""" command = [ "gdb", "-q", "-nx", "-ex", "source /tmp/gef.py", - "-ex", "gef config gef.disable_color True", "-ex", "gef config gef.debug True" ] @@ -24,7 +29,7 @@ def gdb_run_cmd(cmd, before=None, after=None, target=PATH_TO_DEFAULT_BINARY): command += ["-ex", "quit", "--", target] lines = subprocess.check_output(command, stderr=subprocess.STDOUT).strip().splitlines() - return b"\n".join(lines) + return ansi_clean(b"\n".join(lines)) def gdb_run_silent_cmd(cmd, before=None, after=None, target=PATH_TO_DEFAULT_BINARY): From 3fb9ddfda1d67eea43c8ff78e019d5dece12a21f Mon Sep 17 00:00:00 2001 From: Grazfather Date: Fri, 12 Oct 2018 15:54:23 -0700 Subject: [PATCH 27/58] Add cyanify --- gef.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gef.py b/gef.py index f67fcf88c..7d3d51669 100644 --- a/gef.py +++ b/gef.py @@ -356,6 +356,8 @@ def grayify(msg): return Color.colorify(msg, "gray") @staticmethod def pinkify(msg): return Color.colorify(msg, "pink") @staticmethod + def cyanify(msg): return Color.colorify(msg, "cyan") + @staticmethod def boldify(msg): return Color.colorify(msg, "bold") @staticmethod def underlinify(msg): return Color.colorify(msg, "underline") @@ -844,7 +846,7 @@ def get_main_arena(): def titlify(text, color=None, msg_color=None): - """Print a title.""" + """Print a centered title.""" cols = get_terminal_size()[1] nb = (cols - len(text) - 2)//2 if color is None: From 8ee605f8e937b7a69c5adae623eb73a2b0c84e07 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Sat, 13 Oct 2018 09:24:21 -0700 Subject: [PATCH 28/58] tests: Convert to output to string --- tests/helpers.py | 6 +- tests/runtests.py | 136 +++++++++++++++++++++++----------------------- 2 files changed, 71 insertions(+), 71 deletions(-) diff --git a/tests/helpers.py b/tests/helpers.py index fc9245d4d..978776332 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -5,8 +5,8 @@ def ansi_clean(s): - ansi_escape = re.compile(b"(\x9B|\x1B\\[)[0-?]*[ -/]*[@-~]") - return ansi_escape.sub(b"", s) + ansi_escape = re.compile(r"(\x9B|\x1B\[)[0-?]*[ -/]*[@-~]") + return ansi_escape.sub("", s) def gdb_run_cmd(cmd, before=None, after=None, target=PATH_TO_DEFAULT_BINARY): @@ -29,7 +29,7 @@ def gdb_run_cmd(cmd, before=None, after=None, target=PATH_TO_DEFAULT_BINARY): command += ["-ex", "quit", "--", target] lines = subprocess.check_output(command, stderr=subprocess.STDOUT).strip().splitlines() - return ansi_clean(b"\n".join(lines)) + return ansi_clean(b"\n".join(lines).decode("utf-8")) def gdb_run_silent_cmd(cmd, before=None, after=None, target=PATH_TO_DEFAULT_BINARY): diff --git a/tests/runtests.py b/tests/runtests.py index fcbefcc4a..2cf2ea6c2 100755 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -25,16 +25,16 @@ class GefUnitTestGeneric(unittest.TestCase): @staticmethod def assertNoException(buf): #pylint: disable=invalid-name - if not (b"Python Exception <" not in buf - and b"Traceback" not in buf - and b"'gdb.error'" not in buf - and b"Exception raised" not in buf - and b"failed to execute properly, reason:" not in buf): + if not ("Python Exception <" not in buf + and "Traceback" not in buf + and "'gdb.error'" not in buf + and "Exception raised" not in buf + and "failed to execute properly, reason:" not in buf): raise AssertionError("Detected error in gdb output") @staticmethod def assertFailIfInactiveSession(buf): #pylint: disable=invalid-name - if b"No debugging session active" not in buf: + if "No debugging session active" not in buf: raise AssertionError("No debugging session inactive warning") @@ -45,8 +45,8 @@ def test_cmd_canary(self): self.assertFailIfInactiveSession(gdb_run_cmd("canary")) res = gdb_start_silent_cmd("canary", target="tests/binaries/canary.out") self.assertNoException(res) - self.assertIn(b"Found AT_RANDOM at", res) - self.assertIn(b"The canary of process ", res) + self.assertIn("Found AT_RANDOM at", res) + self.assertIn("The canary of process ", res) return def test_cmd_capstone_disassemble(self): @@ -63,15 +63,15 @@ def test_cmd_checksec(self): target = "tests/binaries/checksec-no-canary.out" res = gdb_run_cmd(cmd, target=target) - self.assertIn(b"Canary : No", res) + self.assertIn("Canary : No", res) target = "tests/binaries/checksec-no-nx.out" res = gdb_run_cmd(cmd, target=target) - self.assertIn(b"NX : No", res) + self.assertIn("NX : No", res) target = "tests/binaries/checksec-no-pie.out" res = gdb_run_cmd(cmd, target=target) - self.assertIn(b"PIE : No", res) + self.assertIn("PIE : No", res) return def test_cmd_dereference(self): @@ -80,22 +80,22 @@ def test_cmd_dereference(self): res = gdb_start_silent_cmd("dereference $sp") self.assertNoException(res) self.assertTrue(len(res.splitlines()) > 2) - self.assertIn(b"$rsp", res) + self.assertIn("$rsp", res) res = gdb_start_silent_cmd("dereference 0") self.assertNoException(res) - self.assertIn(b"Unmapped address", res) + self.assertIn("Unmapped address", res) return def test_cmd_edit_flags(self): # force enable flag res = gdb_start_silent_cmd_last_line("edit-flags +carry") self.assertNoException(res) - self.assertIn(b"CARRY ", res) + self.assertIn("CARRY ", res) # force disable flag res = gdb_start_silent_cmd_last_line("edit-flags -carry") self.assertNoException(res) - self.assertIn(b"carry ", res) + self.assertIn("carry ", res) # toggle flag before = gdb_start_silent_cmd_last_line("edit-flags") self.assertNoException(before) @@ -108,7 +108,7 @@ def test_cmd_edit_flags(self): def test_cmd_elf_info(self): res = gdb_run_cmd("elf-info") self.assertNoException(res) - self.assertIn(b"7f 45 4c 46", res) + self.assertIn("7f 45 4c 46", res) return def test_cmd_entry_break(self): @@ -124,14 +124,14 @@ def test_cmd_format_string_helper(self): "run",], target=target) self.assertNoException(res) - self.assertIn(b"Possible insecure format string:", res) + self.assertIn("Possible insecure format string:", res) return def test_cmd_functions(self): cmd = "functions" res = gdb_run_cmd(cmd) self.assertNoException(res) - self.assertIn(b"$_heap", res) + self.assertIn("$_heap", res) return def test_cmd_heap_arenas(self): @@ -140,7 +140,7 @@ def test_cmd_heap_arenas(self): self.assertFailIfInactiveSession(gdb_run_cmd(cmd, target=target)) res = gdb_start_silent_cmd(cmd, target=target) self.assertNoException(res) - self.assertIn(b"Arena (base=", res) + self.assertIn("Arena (base=", res) return def test_cmd_heap_set_arena(self): @@ -149,7 +149,7 @@ def test_cmd_heap_set_arena(self): self.assertFailIfInactiveSession(gdb_run_cmd(cmd, target=target)) res = gdb_run_silent_cmd(cmd, target=target, after=["heap arenas",]) self.assertNoException(res) - self.assertIn(b"Arena (base=", res) + self.assertIn("Arena (base=", res) return def test_cmd_heap_chunk(self): @@ -158,7 +158,7 @@ def test_cmd_heap_chunk(self): self.assertFailIfInactiveSession(gdb_run_cmd(cmd, target=target)) res = gdb_run_silent_cmd(cmd, target=target) self.assertNoException(res) - self.assertIn(b"NON_MAIN_ARENA flag: ", res) + self.assertIn("NON_MAIN_ARENA flag: ", res) return def test_cmd_heap_chunks(self): @@ -167,8 +167,8 @@ def test_cmd_heap_chunks(self): self.assertFailIfInactiveSession(gdb_run_cmd(cmd, target=target)) res = gdb_run_silent_cmd(cmd, target=target) self.assertNoException(res) - self.assertIn(b"Chunk(addr=", res) - self.assertIn(b"top chunk", res) + self.assertIn("Chunk(addr=", res) + self.assertIn("top chunk", res) return def test_cmd_heap_bins_fast(self): @@ -177,7 +177,7 @@ def test_cmd_heap_bins_fast(self): self.assertFailIfInactiveSession(gdb_run_cmd(cmd, target=target)) res = gdb_run_silent_cmd(cmd, target=target) self.assertNoException(res) - self.assertIn(b"Fastbins[idx=0, size=0x10]", res) + self.assertIn("Fastbins[idx=0, size=0x10]", res) return def test_cmd_heap_analysis(self): @@ -253,7 +253,7 @@ def test_cmd_patch_qword(self): def test_cmd_patch_string(self): res = gdb_start_silent_cmd_last_line("patch string $sp \"Gef!Gef!Gef!Gef!\"", after=["grep Gef!Gef!Gef!Gef!",]) self.assertNoException(res) - self.assertIn(b"Gef!Gef!Gef!Gef!", res) + self.assertIn("Gef!Gef!Gef!Gef!", res) return def test_cmd_pattern(self): @@ -261,43 +261,43 @@ def test_cmd_pattern(self): target = "tests/binaries/pattern.out" res = gdb_run_cmd(cmd, target=target) self.assertNoException(res) - self.assertIn(b"aaaaaaaabaaaaaaacaaaaaaadaaaaaaa", res) + self.assertIn("aaaaaaaabaaaaaaacaaaaaaadaaaaaaa", res) cmd = "pattern search $rbp" target = "tests/binaries/pattern.out" res = gdb_run_cmd(cmd, before=["set args aaaaaaaabaaaaaaacaaaaaaadaaaaaaa", "run"], target=target) self.assertNoException(res) - self.assertIn(b"Found at offset", res) + self.assertIn("Found at offset", res) return def test_cmd_print_format(self): self.assertFailIfInactiveSession(gdb_run_cmd("print-format")) res = gdb_start_silent_cmd("print-format $rsp") self.assertNoException(res) - self.assertTrue(b"buf = [" in res) + self.assertTrue("buf = [" in res) res = gdb_start_silent_cmd("print-format -f js $rsp") self.assertNoException(res) - self.assertTrue(b"var buf = [" in res) + self.assertTrue("var buf = [" in res) res = gdb_start_silent_cmd("print-format -f iDontExist $rsp") self.assertNoException(res) - self.assertTrue(b"Language must be :" in res) + self.assertTrue("Language must be :" in res) return def test_cmd_process_status(self): self.assertFailIfInactiveSession(gdb_run_cmd("process-status")) res = gdb_start_silent_cmd("process-status") self.assertNoException(res) - self.assertIn(b"Process Information", res) - self.assertIn(b"No child process", res) - self.assertIn(b"No open connections", res) + self.assertIn("Process Information", res) + self.assertIn("No child process", res) + self.assertIn("No open connections", res) return def test_cmd_registers(self): self.assertFailIfInactiveSession(gdb_run_cmd("registers")) res = gdb_start_silent_cmd("registers") self.assertNoException(res) - self.assertIn(b"$rax", res) - self.assertIn(b"$eflags", res) + self.assertIn("$rax", res) + self.assertIn("$eflags", res) return def test_cmd_reset_cache(self): @@ -311,7 +311,7 @@ def test_cmd_ropper(self): cmd = "ropper --search \"pop %; pop %; ret\"" res = gdb_run_silent_cmd(cmd) self.assertNoException(res) - self.assertNotIn(b": error:", res) + self.assertNotIn(": error:", res) self.assertTrue(len(res.splitlines()) > 2) return @@ -321,12 +321,12 @@ def test_cmd_scan(self): self.assertFailIfInactiveSession(gdb_run_cmd(cmd)) res = gdb_start_silent_cmd(cmd, target=target) self.assertNoException(res) - self.assertIn(target.encode(), res) + self.assertIn(target, res) target = "tests/binaries/default.out" res = gdb_start_silent_cmd("scan binary libc", target=target) self.assertNoException(res) - self.assertIn(b"__libc_start_main", res) + self.assertIn("__libc_start_main", res) return @@ -334,7 +334,7 @@ def test_cmd_search_pattern(self): self.assertFailIfInactiveSession(gdb_run_cmd("grep /bin/sh")) res = gdb_start_silent_cmd("grep /bin/sh") self.assertNoException(res) - self.assertIn(b"0x", res) + self.assertIn("0x", res) return def test_cmd_set_permission(self): @@ -343,35 +343,35 @@ def test_cmd_set_permission(self): res = gdb_run_silent_cmd("set-permission 0x1337000", after=["vmmap",], target=target) self.assertNoException(res) - line = [ l for l in res.splitlines() if b"0x0000000001337000" in l ][0] + line = [ l for l in res.splitlines() if "0x0000000001337000" in l ][0] line = line.split() - self.assertEqual(line[0], b"0x0000000001337000") - self.assertEqual(line[1], b"0x0000000001338000") - self.assertEqual(line[2], b"0x0000000000000000") - self.assertEqual(line[3], b"rwx") + self.assertEqual(line[0], "0x0000000001337000") + self.assertEqual(line[1], "0x0000000001338000") + self.assertEqual(line[2], "0x0000000000000000") + self.assertEqual(line[3], "rwx") res = gdb_run_silent_cmd("set-permission 0x1338000", target=target) self.assertNoException(res) - self.assertIn(b"Unmapped address", res) + self.assertIn("Unmapped address", res) return def test_cmd_shellcode(self): res = gdb_start_silent_cmd("shellcode") self.assertNoException(res) - self.assertIn(b"Missing sub-command (search|get)", res) + self.assertIn("Missing sub-command (search|get)", res) return def test_cmd_shellcode_search(self): cmd = "shellcode search execve /bin/sh" res = gdb_start_silent_cmd(cmd) self.assertNoException(res) - self.assertIn(b"setuid(0) + execve(/bin/sh) 49 bytes", res) + self.assertIn("setuid(0) + execve(/bin/sh) 49 bytes", res) return def test_cmd_shellcode_get(self): res = gdb_start_silent_cmd("shellcode get 77") self.assertNoException(res) - self.assertIn(b"Shellcode written to ", res) + self.assertIn("Shellcode written to ", res) return def test_cmd_stub(self): @@ -416,7 +416,7 @@ def test_cmd_trace_run(self): res = gdb_start_silent_cmd(cmd, before=["gef config trace-run.tracefile_prefix /tmp/gef-trace-"]) self.assertNoException(res) - self.assertIn(b"Tracing from", res) + self.assertIn("Tracing from", res) return def test_cmd_unicorn_emulate(self): @@ -426,7 +426,7 @@ def test_cmd_unicorn_emulate(self): res = gdb_start_silent_cmd(cmd) self.assertNoException(res) - self.assertIn(b"Final registers", res) + self.assertIn("Final registers", res) return def test_cmd_vmmap(self): @@ -450,7 +450,7 @@ def test_cmd_xfiles(self): def test_cmd_xinfo(self): self.assertFailIfInactiveSession(gdb_run_cmd("xinfo $sp")) res = gdb_start_silent_cmd("xinfo") - self.assertIn(b"At least one valid address must be specified", res) + self.assertIn("At least one valid address must be specified", res) res = gdb_start_silent_cmd("xinfo $sp") self.assertNoException(res) @@ -462,13 +462,13 @@ def test_cmd_xor_memory(self): self.assertFailIfInactiveSession(gdb_run_cmd(cmd)) res = gdb_start_silent_cmd(cmd) self.assertNoException(res) - self.assertIn(b"Original block", res) - self.assertIn(b"XOR-ed block", res) + self.assertIn("Original block", res) + self.assertIn("XOR-ed block", res) cmd = "xor-memory patch $sp 0x10 0x41" res = gdb_start_silent_cmd(cmd) self.assertNoException(res) - self.assertIn(b"Patching XOR-ing ", res) + self.assertIn("Patching XOR-ing ", res) return class TestGefFunctions(GefUnitTestGeneric): @@ -476,21 +476,21 @@ class TestGefFunctions(GefUnitTestGeneric): def test_func_get_memory_alignment(self): res = gdb_test_python_method("get_memory_alignment(in_bits=False)") - self.assertIn(res.splitlines()[-1], (b"4", b"8")) + self.assertIn(res.splitlines()[-1], ("4", "8")) return def test_func_set_arch(self): res = gdb_test_python_method("current_arch.arch, current_arch.mode", before="set_arch()") res = (res.splitlines()[-1]) - self.assertIn(b"X86", res) + self.assertIn("X86", res) return def test_func_which(self): res = gdb_test_python_method("which('gdb')") lines = res.splitlines() - self.assertIn(b"/gdb", lines[-1]) + self.assertIn("/gdb", lines[-1]) res = gdb_test_python_method("which('__IDontExist__')") - self.assertIn(b"Missing file `__IDontExist__`", res) + self.assertIn("Missing file `__IDontExist__`", res) return def test_func_get_filepath(self): @@ -516,13 +516,13 @@ def test_func_pie(self): self.assertFailIfInactiveSession(gdb_run_cmd(cmd)) res = gdb_start_silent_cmd(cmd) self.assertNoException(res) - self.assertIn(b"\\177ELF", res) + self.assertIn("\\177ELF", res) cmd = "x/s $_pie(1)" res = gdb_start_silent_cmd(cmd) self.assertNoException(res) - self.assertNotIn(b"\\177ELF", res) - self.assertIn(b"ELF", res) + self.assertNotIn("\\177ELF", res) + self.assertIn("ELF", res) return def test_func_heap(self): @@ -530,14 +530,14 @@ def test_func_heap(self): self.assertFailIfInactiveSession(gdb_run_cmd(cmd, target="tests/binaries/heap.out")) res = gdb_run_silent_cmd(cmd, target="tests/binaries/heap.out") self.assertNoException(res) - self.assertIn(b"0x0000000000000021", res) - self.assertIn(b"0x0000000000020fe1", res) + self.assertIn("0x0000000000000021", res) + self.assertIn("0x0000000000020fe1", res) cmd = "deref $_heap(0x10+0x10)" res = gdb_run_silent_cmd(cmd, target="tests/binaries/heap.out") self.assertNoException(res) - self.assertNotIn(b"0x0000000000000021", res) - self.assertIn(b"0x0000000000020fe1", res) + self.assertNotIn("0x0000000000000021", res) + self.assertIn("0x0000000000020fe1", res) return def test_func_got(self): @@ -545,7 +545,7 @@ def test_func_got(self): self.assertFailIfInactiveSession(gdb_run_cmd(cmd, target="tests/binaries/heap.out")) res = gdb_run_silent_cmd(cmd, target="tests/binaries/heap.out") self.assertNoException(res) - self.assertIn(b"malloc", res) + self.assertIn("malloc", res) return def test_func_bss(self): @@ -553,7 +553,7 @@ def test_func_bss(self): self.assertFailIfInactiveSession(gdb_run_cmd(cmd, target="tests/binaries/bss.out")) res = gdb_run_silent_cmd(cmd, target="tests/binaries/bss.out") self.assertNoException(res) - self.assertIn(b"Hello world!", res) + self.assertIn("Hello world!", res) return def test_func_stack(self): @@ -561,7 +561,7 @@ def test_func_stack(self): self.assertFailIfInactiveSession(gdb_run_cmd(cmd)) res = gdb_start_silent_cmd(cmd) self.assertNoException(res) - self.assertRegex(res.decode(), r"\+0x0*20: *0x0000000000000000\n") + self.assertRegex(res, r"\+0x0*20: *0x0000000000000000\n") return class TestGefMisc(GefUnitTestGeneric): From fd0674243f3516a08cff66856df18363a197be30 Mon Sep 17 00:00:00 2001 From: hugsy Date: Sun, 14 Oct 2018 19:21:58 -0700 Subject: [PATCH 29/58] - Fixed #348 - Fixed bug in ContextCommand that prevented "extra" messages to show correctly --- docs/index.md | 7 ++-- docs/screenshots.md | 83 +++++++++++++++++++++++++++++++++++---------- gef.py | 9 ++--- 3 files changed, 72 insertions(+), 27 deletions(-) diff --git a/docs/index.md b/docs/index.md index d2543fa37..4e47d0062 100644 --- a/docs/index.md +++ b/docs/index.md @@ -2,10 +2,7 @@ [![ReadTheDocs](https://readthedocs.org/projects/gef/badge/?version=master)](https://gef.readthedocs.org/en/master/) [![MIT](https://img.shields.io/packagist/l/doctrine/orm.svg?maxAge=2592000?style=plastic)](https://github.com/hugsy/gef/blob/master/LICENSE) [![Python 2 & 3](https://img.shields.io/badge/Python-2%20%26%203-green.svg)](https://github.com/hugsy/gef/) [![IRC](https://img.shields.io/badge/freenode-%23%23gef-yellowgreen.svg)](https://webchat.freenode.net/?channels=##gef) [![CircleCI status](https://circleci.com/gh/hugsy/gef/tree/master.svg?style=shield)](https://circleci.com/gh/hugsy/gef/tree/master) -`GEF` is a kick-ass set of commands for X86, ARM, MIPS, PowerPC and SPARC to -make GDB cool again for exploit dev. It is aimed to be used mostly by exploiters -and reverse-engineers, to provide additional features to GDB using the Python -API to assist during the process of dynamic analysis and exploit development. +`GEF` (pronounced ʤɛf - "Jeff") is a kick-ass set of commands for X86, ARM, MIPS, PowerPC and SPARC to make GDB cool again for exploit dev. It is aimed to be used mostly by exploit developers and reverse-engineers, to provide additional features to GDB using the Python API to assist during the process of dynamic analysis and exploit development. It has full support for both Python2 and Python3 indifferently (as more and more distros start pushing `gdb` compiled with Python3 support). @@ -166,7 +163,7 @@ There, you're now fully equipped epic pwnage with **all** GEF's goodness!! To discuss `gef`, `gdb`, exploitation or other topics, feel free to join the `##gef` channel on the Freenode IRC network. You can also talk to me (`hugsy`) on the channel. For those who do not have an IRC client (like `weechat` or `irssi`), -simply [click here](https://webchat.freenode.net/?channels=##gef). +simply [click here](https://webchat.freenode.net/?channels=##gef). For bugs or feature requests, just go [here](https://github.com/hugsy/gef/issues) and provide a thorough description diff --git a/docs/screenshots.md b/docs/screenshots.md index caed8de28..533fd0da4 100644 --- a/docs/screenshots.md +++ b/docs/screenshots.md @@ -1,34 +1,81 @@ -# GEF show cases # +# Screenshots -This shows a few examples of new features available to you when installing -`GEF`, with the supported architecture. + -#### Heap analysis #### +This page illustrates a few of the possibilities available to you when using `GEF`. -![heap-chunks](https://i.imgur.com/2Ew2fA6.png) -![uaf](https://i.imgur.com/NfV5Cu9.png) +## Multi-architecture support -#### Automatic vulnerable string detection #### +`GEF` was designed to support any architecture supported by GDB via an easily extensible architecture API. -![fmtstr-helper-example](https://i.imgur.com/INU3KGn.png) +Currently `GEF` supports the following architectures: -#### Code emulation with Unicorn-Engine (x86-64) #### + - Intel x86 (32b & 64b) + - ARM (v6/v7) + - AARCH64 + - MIPS/MIPS64 + - PowerPC + - SPARC/SPARCv9 -![gef-x86](https://i.imgur.com/emhEsol.png) -#### ELF information, memory mapping and code disassembly with Capstone/Keystone integration (ARM v6) #### +## Features -![gef-arm](http://i.imgur.com/qOL8CnL.png) +### Embedded hexdump view -#### Automatic dereferencing of registers values and identifying binary protections (PowerPC) #### +To this day, GDB doesn't come with a hexdump-like view. Well `GEF` fixes that for you via the `hexdump` command: -![gef-ppc](https://i.imgur.com/IN6x6lw.png) +![hexdump](https://i.imgur.com/mJUq6T2.png) -#### Context display on MIPS #### -![gef-mips](https://i.imgur.com/dBaB9os.png) +### Dereferencing data or registers -#### Capstone-Engine disassembly on SPARC v9 #### +No more endless manual pointer dereferencing `x/x` style. Just use `dereference` for that. Or for a comprehensive view of the registers, `registers` might become your best friend: -![gef-sparc](https://i.imgur.com/VD2FpDt.png) +![mipsel-deref-regs](https://i.imgur.com/f5ZaWDC.png) + + +### Heap analysis + +#### Detailed view of Glibc Chunks + +![x86-heap-chunks](https://i.imgur.com/zBSTUHb.png) + + +#### Automatic detection of UaF during runtime + +![x86-heap-helper-uaf](https://i.imgur.com/NfV5Cu9.png) + + +### Display ELF information + +#### ELF structure + +![arm-elf-info](https://i.imgur.com/qOL8CnL.png) + + +#### Security settings + +![mips-elf-checksec](https://i.imgur.com/aanY2uK.png) + + +### Automatic vulnerable string detection + +![aarch64-fmtstr](https://i.imgur.com/iF4l1R5.png) + + +### Code emulation with Unicorn-Engine (x86-64) + +![x86-unicorn](https://i.imgur.com/emhEsol.png) + + + +### Comprehensive address space layout display + +![mips-vmmap](https://i.imgur.com/TbC1kNa.png) + + + +### Defining arbitrary custom structures + +![sparc-arb-struct](https://i.imgur.com/dEMUuP7.png) diff --git a/gef.py b/gef.py index 7d3d51669..25183a4fd 100644 --- a/gef.py +++ b/gef.py @@ -7461,10 +7461,11 @@ def context_additional_information(self): self.context_title("extra") for level, text in __context_messages__: - if level=="error": err(text) - elif level=="warn": warn(text) - elif level=="success": ok(text) - else: info(text) + if level=="error": prefix = "[!] {}" + elif level=="warn": prefix = "[*] {}" + elif level=="success": prefix = "[+] {}" + else: prefix = "{}" + gef_print( prefix.format(text) ) return def context_memory(self): From 1cf4c4cf6de95c4d9912ba5f664d7182a5191898 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Sun, 14 Oct 2018 18:31:02 -0700 Subject: [PATCH 30/58] bufferize: fix stdout even on unhandled exception (#352) --- gef.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/gef.py b/gef.py index 25183a4fd..959fadddf 100644 --- a/gef.py +++ b/gef.py @@ -314,10 +314,12 @@ def wrapper(*args, **kwargs): return f(*args, **kwargs) __gef_int_stream_buffer__ = StringIO() - rv = f(*args, **kwargs) - sys.stdout.write(__gef_int_stream_buffer__.getvalue()) - sys.stdout.flush() - __gef_int_stream_buffer__ = None + try: + rv = f(*args, **kwargs) + finally: + sys.stdout.write(__gef_int_stream_buffer__.getvalue()) + sys.stdout.flush() + __gef_int_stream_buffer__ = None return rv return wrapper From ff3c1736f041e59206d5fc6fa5ba0db1d5f8589b Mon Sep 17 00:00:00 2001 From: Grazfather Date: Fri, 12 Oct 2018 15:27:18 -0700 Subject: [PATCH 31/58] Improve speed of get_memory_alignment --- gef.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/gef.py b/gef.py index 959fadddf..da87d119a 100644 --- a/gef.py +++ b/gef.py @@ -2960,16 +2960,27 @@ def cached_lookup_type(_type): return None +@lru_cache() def get_memory_alignment(in_bits=False): - """Return sizeof(size_t). If `in_bits` is set to True, the result is - returned in bits, otherwise in bytes.""" - res = cached_lookup_type("size_t") - if res is not None: - return res.sizeof if not in_bits else res.sizeof * 8 + """Try to determine the size of a pointer on this system. + First, try to parse it out of the ELF header. + Next, use the size of `size_t`. + Finally, try the size of $pc. + If `in_bits` is set to True, the result is returned in bits, otherwise in + bytes.""" if is_elf32(): return 4 if not in_bits else 32 elif is_elf64(): return 8 if not in_bits else 64 + + res = cached_lookup_type("size_t") + if res is not None: + return res.sizeof if not in_bits else res.sizeof * 8 + + try: + return gdb.parse_and_eval('$pc').type.sizeof + except: + pass raise EnvironmentError("GEF is running under an unsupported mode") @@ -5375,7 +5386,7 @@ def reset(): "True" if verbose else "False", current_arch.syscall_register, cs_arch, cs_mode, - 16 if is_elf64() else 8, + current_arch.ptrsize, emulate_segmentation_block if is_x86() else "", arch, mode, context_segmentation_block if is_x86() else "", @@ -7005,7 +7016,7 @@ def context_regs(self): widest = l = max(map(len, current_arch.all_registers)) l += 5 - l += 16 if is_elf64() else 8 + l += current_arch.ptrsize nb = get_terminal_size()[1]//l i = 1 line = "" From 332131f90e74da64eeefb4432ae8accc2ebc89ef Mon Sep 17 00:00:00 2001 From: Grazfather Date: Fri, 12 Oct 2018 15:42:51 -0700 Subject: [PATCH 32/58] Move is_arm_thumb into ARM class --- gef.py | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/gef.py b/gef.py index da87d119a..cd2099077 100644 --- a/gef.py +++ b/gef.py @@ -1548,14 +1548,26 @@ class ARM(Architecture): syscall_register = "$r7" syscall_instructions = ["swi 0x0", "swi NR"] + @lru_cache() + def is_thumb(self): + """Determine if the machine is currently in THUMB mode.""" + return is_alive() and get_register("$cpsr") & (1<<5) + + @property + def pc(self): + pc = get_register("$pc") + if self.is_thumb(): + pc += 1 + return pc + @property def mode(self): - return "THUMB" if is_arm_thumb() else "ARM" + return "THUMB" if self.is_thumb() else "ARM" @property def instruction_length(self): # Thumb instructions have variable-length (2 or 4-byte) - return None if is_arm_thumb() else 4 + return None if self.is_thumb() else 4 def is_call(self, insn): mnemo = insn.mnemonic @@ -2855,12 +2867,6 @@ def is_arm(filename=None): return elf.e_machine == Elf.ARM -@lru_cache() -def is_arm_thumb(): - """Checks if `filename` is an ARM (THUMB mode) ELF.""" - return is_arm() and is_alive() and get_register("$cpsr") & (1<<5) - - @lru_cache() def is_mips(): """Checks if `filename` is a MIPS ELF.""" @@ -7101,10 +7107,7 @@ def context_code(self): frame = gdb.selected_frame() arch = frame.architecture() - arch_name = arch.name().lower() - if is_arm_thumb(): - arch_name += ":thumb" - pc += 1 + arch_name = "{}:{}".format(current_arch.arch.lower(), current_arch.mode) self.context_title("code:{}".format(arch_name)) From 5af4b2ecee7300c1df5ff10c080447607adcff68 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Sat, 13 Oct 2018 09:48:50 -0700 Subject: [PATCH 33/58] Remove is_ funcs in favour of is_arch(arch) --- gef.py | 83 ++++++++++++++++++---------------------------------------- 1 file changed, 26 insertions(+), 57 deletions(-) diff --git a/gef.py b/gef.py index cd2099077..4f7d2647c 100644 --- a/gef.py +++ b/gef.py @@ -2072,7 +2072,6 @@ def mprotect_asm(cls, addr, size, perm): hi = (addr & 0xffff0000) >> 16 lo = (addr & 0x0000ffff) _NR_mprotect = 125 - syscall = "t 0x6d" if is_sparc64() else "t 0x10" insns = ["add %sp, -16, %sp", "st %g1, [ %sp ]", "st %o0, [ %sp + 4 ]", "st %o1, [ %sp + 8 ]", "st %o2, [ %sp + 12 ]", @@ -2081,7 +2080,7 @@ def mprotect_asm(cls, addr, size, perm): "clr %o1", "clr %o2", "mov {}, %g1".format(_NR_mprotect), - syscall, + "t 0x10", "ld [ %sp ], %g1", "ld [ %sp + 4 ], %o0", "ld [ %sp + 8 ], %o1", "ld [ %sp + 12 ], %o2", "add %sp, 16, %sp",] @@ -2113,6 +2112,25 @@ class SPARC64(SPARC): syscall_instructions = ["t 0x6d"] + @classmethod + def mprotect_asm(cls, addr, size, perm): + hi = (addr & 0xffff0000) >> 16 + lo = (addr & 0x0000ffff) + _NR_mprotect = 125 + insns = ["add %sp, -16, %sp", + "st %g1, [ %sp ]", "st %o0, [ %sp + 4 ]", + "st %o1, [ %sp + 8 ]", "st %o2, [ %sp + 12 ]", + "sethi %hi({}), %o0".format(hi), + "or %o0, {}, %o0".format(lo), + "clr %o1", + "clr %o2", + "mov {}, %g1".format(_NR_mprotect), + "t 0x6d", + "ld [ %sp ], %g1", "ld [ %sp + 4 ], %o0", + "ld [ %sp + 8 ], %o1", "ld [ %sp + 12 ], %o2", + "add %sp, 16, %sp",] + return "; ".join(insns) + class MIPS(Architecture): arch = "MIPS" @@ -2740,9 +2758,10 @@ def get_capstone_arch(arch=None, mode=None, endian=None, to_string=False): # hacky patch to unify capstone/ppc syntax with keystone & unicorn: # CS_MODE_PPC32 does not exist (but UC_MODE_32 & KS_MODE_32 do) - if is_alive() and (is_powerpc() or is_ppc64()): - if is_ppc64(): - raise OSError("Capstone not supported for PPC64 yet.") + if is_arch(Elf.POWERPC64): + raise OSError("Capstone not supported for PPC64 yet.") + + if is_alive() and is_arch(Elf.POWERPC): arch = "PPC" mode = "32" @@ -2861,59 +2880,9 @@ def is_x86(filename=None): @lru_cache() -def is_arm(filename=None): - """Checks if `filename` is an ARM ELF.""" - elf = current_elf or get_elf_headers(filename) - return elf.e_machine == Elf.ARM - - -@lru_cache() -def is_mips(): - """Checks if `filename` is a MIPS ELF.""" - elf = current_elf or get_elf_headers() - return elf.e_machine == Elf.MIPS - - -@lru_cache() -def is_powerpc(): - """Checks if `filename` is a PowerPC ELF.""" - elf = current_elf or get_elf_headers() - return elf.e_machine == Elf.POWERPC - - -@lru_cache() -def is_ppc64(): - """Checks if `filename` is a PowerPC64 ELF.""" - elf = current_elf or get_elf_headers() - return elf.e_machine == Elf.POWERPC64 - - -@lru_cache() -def is_sparc(): - """Checks if `filename` is a SPARC ELF.""" - elf = current_elf or get_elf_headers() - return elf.e_machine == Elf.SPARC - - -@lru_cache() -def is_sparc64(): - """Checks if `filename` is a SPARC64 ELF.""" - elf = current_elf or get_elf_headers() - return elf.e_machine == Elf.SPARC64 - - -@lru_cache() -def is_aarch64(): - """Checks if `filename` is an AARCH64 ELF.""" - elf = current_elf or get_elf_headers() - return elf.e_machine == Elf.AARCH64 - - -@lru_cache() -def is_riscv(): - """Checks if `filename` is a RISCV ELF.""" +def is_arch(arch): elf = current_elf or get_elf_headers() - return elf.e_machine == Elf.RISCV + return elf.e_machine == arch def set_arch(arch=None, default=None): From 850d66d263ea6730727487cb218509fbf45d3fc7 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Sun, 14 Oct 2018 12:40:03 -0700 Subject: [PATCH 34/58] Make colorify do the string conversion --- gef.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/gef.py b/gef.py index 4f7d2647c..d369990ab 100644 --- a/gef.py +++ b/gef.py @@ -375,7 +375,7 @@ def colorify(text, attrs): colors = Color.colors msg = [colors[attr] for attr in attrs.split() if attr in colors] - msg.append(text) + msg.append(str(text)) if colors["highlight"] in msg : msg.append(colors["highlight_off"]) if colors["underline"] in msg : msg.append(colors["underline_off"]) if colors["blink"] in msg : msg.append(colors["blink_off"]) @@ -8855,7 +8855,7 @@ def __load_extra_plugins(self): gdb.execute("source {:s}".format(fpath)) nb_added = len(self.loaded_commands) - nb_inital if nb_added > 0: - ok("{:s} extra commands added from '{:s}'".format(Color.colorify(str(nb_added), "bold green"), + ok("{:s} extra commands added from '{:s}'".format(Color.colorify(nb_added, "bold green"), Color.colorify(directory, "bold blue"))) except gdb.error as e: err("failed: {}".format(str(e))) @@ -8913,13 +8913,13 @@ def is_loaded(x): ver = "{:d}.{:d}".format(sys.version_info.major, sys.version_info.minor) nb_cmds = len(self.loaded_commands) gef_print("{:s} commands loaded for GDB {:s} using Python engine {:s}" - .format(Color.colorify(str(nb_cmds), "bold green"), + .format(Color.colorify(nb_cmds, "bold green"), Color.colorify(gdb.VERSION, "bold yellow"), Color.colorify(ver, "bold red"))) if nb_missing: warn("{:s} command{} could not be loaded, run `{:s}` to know why." - .format(Color.colorify(str(nb_missing), "bold red"), + .format(Color.colorify(nb_missing, "bold red"), "s" if nb_missing > 1 else "", Color.colorify("gef missing", "underline pink"))) return @@ -9025,7 +9025,11 @@ def print_setting(self, plugin_name, show_description=False): _value, _type, _desc = res _setting = Color.colorify(plugin_name, "pink bold underline") _type = _type.__name__ - _value = Color.colorify(str(_value), "yellow") if _type!='str' else '"{:s}"'.format(Color.colorify(str(_value), string_color)) + if _type == "str": + _value = '"{:s}"'.format(Color.colorify(_value, string_color)) + else: + _value = Color.colorify(_value, "yellow") + gef_print("{:s} ({:s}) = {:s}".format(_setting, _type, _value)) if show_description: From 2c63447f993e98d3f57f686805506ac3eb213f70 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Sun, 14 Oct 2018 12:49:52 -0700 Subject: [PATCH 35/58] Use themes in print_setting --- gef.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/gef.py b/gef.py index d369990ab..be7380b82 100644 --- a/gef.py +++ b/gef.py @@ -9017,18 +9017,19 @@ def invoke(self, args, from_tty): def print_setting(self, plugin_name, show_description=False): res = __config__.get(plugin_name) - string_color = __config__.get("theme.dereference_string")[0] + string_color = get_gef_setting("theme.dereference_string") + misc_color = get_gef_setting("theme.dereference_base_address") if not res: return _value, _type, _desc = res - _setting = Color.colorify(plugin_name, "pink bold underline") + _setting = Color.colorify(plugin_name, "green") _type = _type.__name__ if _type == "str": _value = '"{:s}"'.format(Color.colorify(_value, string_color)) else: - _value = Color.colorify(_value, "yellow") + _value = Color.colorify(_value, misc_color) gef_print("{:s} ({:s}) = {:s}".format(_setting, _type, _value)) From 9bd82fb21eae382e0c3938a288c5692e0b556f2a Mon Sep 17 00:00:00 2001 From: Grazfather Date: Sun, 14 Oct 2018 12:50:33 -0700 Subject: [PATCH 36/58] Cache get_gef_setting --- gef.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gef.py b/gef.py index be7380b82..3216c7f2d 100644 --- a/gef.py +++ b/gef.py @@ -1044,6 +1044,7 @@ def disable_redirect_output(): return +@lru_cache() def get_gef_setting(name): """Read global gef settings. Return None if not found. A valid config setting can never return None, @@ -9071,6 +9072,7 @@ def set_setting(self, argc, argv): err("{} expects type '{}'".format(argv[0], _type.__name__)) return + reset_all_caches() __config__[argv[0]][0] = _newval return From 4125fe3cbd23e7d03fd91c6ee74305d2512a64c5 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Sun, 14 Oct 2018 12:53:16 -0700 Subject: [PATCH 37/58] misc tidyup --- gef.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/gef.py b/gef.py index 3216c7f2d..10c42c0ac 100644 --- a/gef.py +++ b/gef.py @@ -7340,7 +7340,7 @@ def get_pc_context_info(self, pc, line): current_block = current_block.superblock if m: - return "\t\t// " + ", ".join(["{:s}={:s}".format(Color.yellowify(a),b) for a, b in m.items()]) + return "\t\t// " + ", ".join(["{}={}".format(Color.yellowify(a), b) for a, b in m.items()]) except Exception: pass return "" @@ -7370,8 +7370,9 @@ def context_trace(self): items.append("{:#x}".format(pc)) if name: frame_args = gdb.FrameDecorator.FrameDecorator(current_frame).frame_args() or [] - m = "Name: {:s}({:s})".format(Color.greenify(name), - ", ".join(["{!s}={!s}".format(x.sym, x.sym.value(current_frame)) for x in frame_args])) + 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: @@ -7380,8 +7381,8 @@ def context_trace(self): break items.append(Color.redify("{} {}".format(insn.mnemonic, ', '.join(insn.operands)))) - gef_print("[{:s}] {:s}".format(Color.colorify("#{:d}".format(i), "bold pink"), - RIGHT_ARROW.join(items))) + gef_print("[{}] {}".format(Color.colorify("#{}".format(i), "bold pink"), + RIGHT_ARROW.join(items))) current_frame = current_frame.older() i += 1 nb_backtrace -= 1 @@ -7891,7 +7892,7 @@ def dereference_from(addr): s = read_cstring_from_memory(addr.value) if len(s) < get_memory_alignment(): txt = '{:s} ("{:s}"?)'.format(format_address(deref), Color.colorify(s, string_color)) - elif len(s) >= 50: + elif len(s) > 50: txt = Color.colorify('"{:s}[...]"'.format(s[:50]), string_color) else: txt = Color.colorify('"{:s}"'.format(s), string_color) @@ -9007,7 +9008,7 @@ def invoke(self, args, from_tty): if names: if len(names)==1: gef_print(titlify("GEF configuration setting: {:s}".format(names[0]))) - self.print_setting(names[0], show_description=True) + self.print_setting(names[0], verbose=True) else: gef_print(titlify("GEF configuration settings matching '{:s}'".format(argv[0]))) for name in names: self.print_setting(name) @@ -9016,7 +9017,7 @@ def invoke(self, args, from_tty): self.set_setting(argc, argv) return - def print_setting(self, plugin_name, show_description=False): + def print_setting(self, plugin_name, verbose=False): res = __config__.get(plugin_name) string_color = get_gef_setting("theme.dereference_string") misc_color = get_gef_setting("theme.dereference_base_address") @@ -9034,9 +9035,8 @@ def print_setting(self, plugin_name, show_description=False): gef_print("{:s} ({:s}) = {:s}".format(_setting, _type, _value)) - if show_description: - gef_print("") - gef_print(Color.colorify("Description:", "bold underline")) + if verbose: + gef_print(Color.colorify("\nDescription:", "bold underline")) gef_print("\t{:s}".format(_desc)) return From bdf029f793c084295132811848f53859ad23c452 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Mon, 15 Oct 2018 15:54:30 -0700 Subject: [PATCH 38/58] remove _xlog and just use gef_print --- gef.py | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/gef.py b/gef.py index 10c42c0ac..b420b44a2 100644 --- a/gef.py +++ b/gef.py @@ -863,19 +863,10 @@ def titlify(text, color=None, msg_color=None): return "".join(msg) -def _xlog(text, stream, cr=True): - """Logging core function.""" - text += "\n" if cr else "" - gdb.write(text, stream) - if cr: - gdb.flush() - return 0 - - -def err(msg, cr=True): return _xlog("{} {}".format(Color.colorify("[!]", "bold red"), msg), gdb.STDERR, cr) -def warn(msg, cr=True): return _xlog("{} {}".format(Color.colorify("[*]", "bold yellow"), msg), gdb.STDLOG, cr) -def ok(msg, cr=True): return _xlog("{} {}".format(Color.colorify("[+]", "bold green"), msg), gdb.STDLOG, cr) -def info(msg, cr=True): return _xlog("{} {}".format(Color.colorify("[+]", "bold blue"), msg), gdb.STDLOG, cr) +def err(msg): return gef_print("{} {}".format(Color.colorify("[!]", "bold red"), msg)) +def warn(msg): return gef_print("{} {}".format(Color.colorify("[*]", "bold yellow"), msg)) +def ok(msg): return gef_print("{} {}".format(Color.colorify("[+]", "bold green"), msg)) +def info(msg): return gef_print("{} {}".format(Color.colorify("[+]", "bold blue"), msg)) def push_context_message(level, message): @@ -7448,11 +7439,10 @@ def context_additional_information(self): self.context_title("extra") for level, text in __context_messages__: - if level=="error": prefix = "[!] {}" - elif level=="warn": prefix = "[*] {}" - elif level=="success": prefix = "[+] {}" - else: prefix = "{}" - gef_print( prefix.format(text) ) + if level=="error": err(text) + elif level=="warn": warn(text) + elif level=="success": ok(text) + else: info(text) return def context_memory(self): From 1fa16415c2cef338390bc02663b7900335e293f8 Mon Sep 17 00:00:00 2001 From: hugsy Date: Tue, 16 Oct 2018 00:18:06 -0700 Subject: [PATCH 39/58] Fixed #357 by reverting back to original behavior when showing non ascii value in `grep` command --- gef.py | 54 ++++++++++++++++++++++++------------------- tests/binaries/heap.c | 2 +- tests/runtests.py | 6 ++--- 3 files changed, 33 insertions(+), 29 deletions(-) diff --git a/gef.py b/gef.py index b420b44a2..d4233f5ca 100644 --- a/gef.py +++ b/gef.py @@ -2221,7 +2221,7 @@ def read_memory(addr, length=0x10): def read_int_from_memory(addr): """Return an integer read from memory.""" - sz = get_memory_alignment() + sz = current_arch.ptrsize mem = read_memory(addr, sz) fmt = "{}{}".format(endian_str(), "I" if sz==4 else "Q") return struct.unpack(fmt, mem)[0] @@ -2229,34 +2229,40 @@ def read_int_from_memory(addr): def read_cstring_from_memory(address, max_length=GEF_MAX_STRING_LENGTH, encoding=None): """Return a C-string read from memory.""" + if not encoding: encoding = "unicode_escape" if PYTHON_MAJOR==3 else "ascii" - char_t = cached_lookup_type("char") - char_ptr = char_t.pointer() + char_ptr = cached_lookup_type("char").pointer() + try: res = gdb.Value(address).cast(char_ptr).string(encoding=encoding).strip() except gdb.error: length = min(address|(DEFAULT_PAGE_SIZE-1), max_length+1) mem = bytes(read_memory(address, length)).decode("utf-8") res = mem.split("\x00", 1)[0] - res2 = res.replace('\n','\\n').replace('\r','\\r').replace('\t','\\t') + + ustr = res.replace('\n','\\n').replace('\r','\\r').replace('\t','\\t') if max_length and len(res) > max_length: - return "{}[...]".format(res2[:max_length]) + return "{}[...]".format(ustr[:max_length]) + + return ustr + - return res2 +def read_ascii_string(address): + """Read an ASCII string from memory""" + cstr = read_cstring_from_memory(address) + if isinstance(cstr, unicode) and cstr and all([x in string.printable for x in cstr]): + return cstr + return None -def is_readable_string(address): - """Try to determine if the content pointed by `address` is - a readable string by checking if: - * it ends with a null byte (i.e. it is a C-string) - * each byte is printable""" +def is_ascii_string(address): + """Helper function to determine if the buffer pointed by `address` is an ASCII string (in GDB)""" try: - cstr = read_cstring_from_memory(address) - return isinstance(cstr, unicode) and cstr and all([x in string.printable for x in cstr]) - except UnicodeDecodeError: + return read_ascii_string(address) is not None + except Exception: return False @@ -2264,7 +2270,7 @@ def is_alive(): """Check if GDB is running.""" try: return gdb.selected_inferior().pid > 0 - except: + except Exception: return False return False @@ -4967,13 +4973,13 @@ def search_pattern_by_address(self, pattern, start_address, end_address): for match in re.finditer(pattern, mem): start = chunk_addr + match.start() - try: - string = read_cstring_from_memory(start) - end = start + len(string) - except UnicodeError: - string = gef_pystring(pattern)+"[...]" - end = start + len(pattern) - locations.append((start, end, string)) + if is_ascii_string(start): + ustr = read_ascii_string(start) + end = start + len(ustr) + else : + ustr = gef_pystring(pattern)+"[...]" + end = start + len(pattern) + locations.append((start, end, ustr)) del mem @@ -7871,14 +7877,14 @@ def dereference_from(addr): # -- Otherwise try to parse the value if addr.section: - if addr.section.is_executable() and addr.is_in_text_segment() and not is_readable_string(addr.value): + if addr.section.is_executable() and addr.is_in_text_segment() and not is_ascii_string(addr.value): insn = gef_current_instruction(addr.value) insn_str = "{} {} {}".format(insn.location, insn.mnemonic, ", ".join(insn.operands)) msg.append(Color.colorify(insn_str, code_color)) break elif addr.section.permission.value & Permission.READ: - if is_readable_string(addr.value): + if is_ascii_string(addr.value): s = read_cstring_from_memory(addr.value) if len(s) < get_memory_alignment(): txt = '{:s} ("{:s}"?)'.format(format_address(deref), Color.colorify(s, string_color)) diff --git a/tests/binaries/heap.c b/tests/binaries/heap.c index 3dd53bd60..257920dde 100644 --- a/tests/binaries/heap.c +++ b/tests/binaries/heap.c @@ -16,7 +16,7 @@ int main(int argc, char** argv, char** envp) { - void* p1 = malloc(0x10); + void* p1 = malloc(0x20); __asm__ volatile("int3;" : : : ); (void)p1; return EXIT_SUCCESS; diff --git a/tests/runtests.py b/tests/runtests.py index 2cf2ea6c2..0a71c1572 100755 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -530,14 +530,12 @@ def test_func_heap(self): self.assertFailIfInactiveSession(gdb_run_cmd(cmd, target="tests/binaries/heap.out")) res = gdb_run_silent_cmd(cmd, target="tests/binaries/heap.out") self.assertNoException(res) - self.assertIn("0x0000000000000021", res) - self.assertIn("0x0000000000020fe1", res) + self.assertIn("+0x0048:", res) cmd = "deref $_heap(0x10+0x10)" res = gdb_run_silent_cmd(cmd, target="tests/binaries/heap.out") self.assertNoException(res) - self.assertNotIn("0x0000000000000021", res) - self.assertIn("0x0000000000020fe1", res) + self.assertIn("+0x0048:", res) return def test_func_got(self): From a94f7f0bc391636e97c2fdbb10c98b68cae4a7a2 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Mon, 15 Oct 2018 15:39:13 -0700 Subject: [PATCH 40/58] Make Zone a namedtuple --- gef.py | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/gef.py b/gef.py index d4233f5ca..3e33c4646 100644 --- a/gef.py +++ b/gef.py @@ -481,17 +481,14 @@ def from_process_maps(perm_str): class Section: """GEF representation of process memory sections.""" - page_start = None - page_end = None - offset = None - permission = None - inode = None - path = None def __init__(self, *args, **kwargs): - for attr in ["page_start", "page_end", "offset", "permission", "inode", "path", ]: - value = kwargs.get(attr) - setattr(self, attr, value) + self.page_start = kwargs.get("page_start") + self.page_end = kwargs.get("page_end") + self.offset = kwargs.get("offset") + self.permission = kwargs.get("permission") + self.inode = kwargs.get("inode") + self.path = kwargs.get("path") return def is_readable(self): @@ -515,11 +512,7 @@ def realpath(self): return self.path if __gef_remote__ is None else "/tmp/gef/{:d}/{:s}".format(__gef_remote__, self.path) -class Zone: - name = None - zone_start = None - zone_end = None - filename = None +Zone = collections.namedtuple("Zone", ["name", "zone_start", "zone_end", "filename"]) class Elf: @@ -2561,11 +2554,7 @@ def get_info_files(): else: filename = get_filepath() - info = Zone() - info.name = section_name - info.zone_start = addr_start - info.zone_end = addr_end - info.filename = filename + info = Zone(section_name, addr_start, addr_end, filename) __infos_files__.append(info) From 92592525e76d4f15bc2860482ff5f4c3cc31f60d Mon Sep 17 00:00:00 2001 From: Grazfather Date: Mon, 15 Oct 2018 15:40:38 -0700 Subject: [PATCH 41/58] GlibcChunk: Replace pprint with psprint --- gef.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/gef.py b/gef.py index 3e33c4646..3d63286a4 100644 --- a/gef.py +++ b/gef.py @@ -815,10 +815,12 @@ def flags_as_string(self): def __str__(self): msg = "{:s}(addr={:#x}, size={:#x}, flags={:s})".format(Color.colorify("Chunk", "yellow bold underline"), - long(self.address),self.get_chunk_size(), self.flags_as_string()) + long(self.address), + self.get_chunk_size(), + self.flags_as_string()) return msg - def pprint(self): + def psprint(self): msg = [] msg.append(str(self)) if self.is_used(): @@ -826,9 +828,7 @@ def pprint(self): else: msg.append(self.str_as_freed()) - gdb.write("\n".join(msg) + "\n") - gdb.flush() - return + return "\n".join(msg) + "\n" @lru_cache() @@ -5951,7 +5951,7 @@ def do_invoke(self, argv): addr = to_unsigned_long(gdb.parse_and_eval(argv[0])) chunk = GlibcChunk(addr) - chunk.pprint() + gef_print(chunk.psprint()) return @register_command From 9580cc49c3e595823bdfc424a8522004f74abc48 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Mon, 15 Oct 2018 15:41:32 -0700 Subject: [PATCH 42/58] misc docstrings --- gef.py | 57 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/gef.py b/gef.py index 3d63286a4..315fde4f7 100644 --- a/gef.py +++ b/gef.py @@ -225,7 +225,7 @@ def update_gef(argv): def lru_cache(maxsize = 128): """Port of the Python3 LRU cache mechanism provided by itertools.""" class GefLruCache(object): - """Local LRU cache for Python2""" + """Local LRU cache for Python2.""" def __init__(self, input_func, max_size): self._input_func = input_func self._max_size = max_size @@ -516,7 +516,7 @@ def realpath(self): class Elf: - """ Basic ELF parsing. + """Basic ELF parsing. Ref: - http://www.skyfree.org/linux/references/ELF_Format.pdf - http://refspecs.freestandards.org/elf/elfspec_ppc.pdf @@ -672,7 +672,7 @@ def __str__(self): class GlibcChunk: """Glibc chunk class. - Ref: https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/""" + Ref: https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/.""" def __init__(self, addr, from_base=False): self.ptrsize = current_arch.ptrsize @@ -974,7 +974,7 @@ def hexdump(source, length=0x10, separator=".", show_raw=False, base=0x00): @param separator is the default character to use if one byte is not printable @param show_raw if True, do not add the line nor the text translation @param base is the start address of the block being hexdump - @return a string with the hexdump """ + @return a string with the hexdump""" result = [] align = get_memory_alignment()*2+2 if is_alive() else 18 @@ -1448,7 +1448,7 @@ def is_conditional_branch(self, insn): def is_branch_taken(self, insn): def long_to_twos_complement(v): - """Convert a python long value to its two's complement""" + """Convert a python long value to its two's complement.""" if is_elf32(): if v & 0x80000000: return v - 0x100000000 @@ -1945,7 +1945,7 @@ def get_ra(self, insn, frame): @classmethod def mprotect_asm(cls, addr, size, perm): - """Ref: http://www.ibm.com/developerworks/library/l-ppc/index.html""" + # Ref: http://www.ibm.com/developerworks/library/l-ppc/index.html _NR_mprotect = 125 insns = ["addi 1, 1, -16", # 1 = r1 = sp "stw 0, 0(1)", "stw 3, 4(1)", # r0 = syscall_code | r3, r4, r5 = args @@ -2435,7 +2435,7 @@ def download_file(target, use_cache=False, local_name=None): def open_file(path, use_cache=False): """Attempt to open the given file, if remote debugging is active, download - it first to the mirror in /tmp/""" + it first to the mirror in /tmp/.""" if is_remote_debug(): lpath = download_file(path, use_cache) if not lpath: @@ -3224,7 +3224,7 @@ def destroy(self): # class FormatStringBreakpoint(gdb.Breakpoint): - """Inspect stack for format string""" + """Inspect stack for format string.""" def __init__(self, spec, num_args): super(FormatStringBreakpoint, self).__init__(spec, type=gdb.BP_BREAKPOINT, internal=False) self.num_args = num_args @@ -3262,7 +3262,7 @@ def stop(self): class StubBreakpoint(gdb.Breakpoint): - """Create a breakpoint to permanently disable a call (fork/alarm/signal/etc.)""" + """Create a breakpoint to permanently disable a call (fork/alarm/signal/etc.).""" def __init__(self, func, retval): super(StubBreakpoint, self).__init__(func, gdb.BP_BREAKPOINT, internal=False) @@ -3302,7 +3302,7 @@ def stop(self): class TraceMallocBreakpoint(gdb.Breakpoint): - """Track allocations done with malloc() or calloc.""" + """Track allocations done with malloc() or calloc().""" def __init__(self, name): super(TraceMallocBreakpoint, self).__init__(name, gdb.BP_BREAKPOINT, internal=True) @@ -3521,7 +3521,7 @@ def stop(self): class TraceFreeRetBreakpoint(gdb.FinishBreakpoint): - """Internal temporary breakpoint to track free-ed values.""" + """Internal temporary breakpoint to track free()d values.""" def __init__(self, addr): super(TraceFreeRetBreakpoint, self).__init__(gdb.newest_frame(), internal=True) @@ -3537,7 +3537,7 @@ def stop(self): class UafWatchpoint(gdb.Breakpoint): - """Custom watchpoints set TraceFreeBreakpoint() to monitor free-ed pointers being used.""" + """Custom watchpoints set TraceFreeBreakpoint() to monitor free()d pointers being used.""" def __init__(self, addr): super(UafWatchpoint, self).__init__("*{:#x}".format(addr), gdb.BP_WATCHPOINT, internal=True) @@ -3728,7 +3728,7 @@ def __set_repeat_count(self, from_tty): @register_command class PrintFormatCommand(GenericCommand): - """Print bytes format in high level languages""" + """Print bytes format in high level languages.""" _cmdline_ = "print-format" _syntax_ = "{:s} [-f FORMAT] [-b BITSIZE] [-l LENGTH] [-c] [-h] LOCATION".format(_cmdline_) @@ -3777,7 +3777,7 @@ def clip(self, data): @only_if_gdb_running def do_invoke(self, argv): - """Default value for print-format command""" + """Default value for print-format command.""" lang = 'py' length = 256 bitlen = 8 @@ -4770,7 +4770,7 @@ def parsed_arglist(arglist): def synchronize(self): - """Submit all active breakpoint addresses to IDA/BN""" + """Submit all active breakpoint addresses to IDA/BN.""" pc = current_arch.pc vmmap = get_process_maps() base_address = min([x.page_start for x in vmmap if x.path == get_filepath()]) @@ -4883,7 +4883,7 @@ def import_structures(self, structs): @register_command class ScanSectionCommand(GenericCommand): """Search for addresses that are located in a memory mapping (haystack) that belonging - to another (needle)""" + to another (needle).""" _cmdline_ = "scan" _syntax_ = "{:s} HAYSTACK NEEDLE".format(_cmdline_) @@ -4992,7 +4992,6 @@ def search_pattern(self, pattern, endian): for loc in self.search_pattern_by_address(pattern, start, end): addr_loc_start = lookup_address(loc[0]) - section = "" if addr_loc_start and addr_loc_start.section: if old_section != addr_loc_start.section: title = "In " @@ -5552,7 +5551,7 @@ def do_invoke(self, argv): return def new_objfile_handler(self, event): - """Hook that handles new_objfile events, will update remote environment accordingly""" + """Hook that handles new_objfile events, will update remote environment accordingly.""" if not is_remote_debug(): return @@ -5608,7 +5607,7 @@ def connect_target(self, target, is_extended_remote): def load_from_remote_proc(self, pid, info): - """Download one item from /proc/pid""" + """Download one item from /proc/pid.""" remote_name = "/proc/{:d}/{:s}".format(pid, info) return download_file(remote_name, use_cache=False) @@ -5930,7 +5929,7 @@ def do_invoke(self, argv): @register_command class GlibcHeapChunkCommand(GenericCommand): """Display information on a heap chunk. - See https://github.com/sploitfun/lsploits/blob/master/glibc/malloc/malloc.c#L1123""" + See https://github.com/sploitfun/lsploits/blob/master/glibc/malloc/malloc.c#L1123.""" _cmdline_ = "heap chunk" _syntax_ = "{:s} LOCATION".format(_cmdline_) @@ -6023,7 +6022,7 @@ def do_invoke(self, argv): @register_command class GlibcHeapBinsCommand(GenericCommand): """Display information on the bins on an arena (default: main_arena). - See https://github.com/sploitfun/lsploits/blob/master/glibc/malloc/malloc.c#L1123""" + See https://github.com/sploitfun/lsploits/blob/master/glibc/malloc/malloc.c#L1123.""" _bin_types_ = ["fast", "unsorted", "small", "large"] _cmdline_ = "heap bins" @@ -6077,7 +6076,7 @@ def pprint_bin(arena_addr, index, _type=""): @register_command class GlibcHeapFastbinsYCommand(GenericCommand): """Display information on the fastbinsY on an arena (default: main_arena). - See https://github.com/sploitfun/lsploits/blob/master/glibc/malloc/malloc.c#L1123""" + See https://github.com/sploitfun/lsploits/blob/master/glibc/malloc/malloc.c#L1123.""" _cmdline_ = "heap bins fast" _syntax_ = "{:s} [ARENA_ADDRESS]".format(_cmdline_) @@ -6137,7 +6136,7 @@ def fastbin_index(sz): @register_command class GlibcHeapUnsortedBinsCommand(GenericCommand): """Display information on the Unsorted Bins of an arena (default: main_arena). - See: https://github.com/sploitfun/lsploits/blob/master/glibc/malloc/malloc.c#L1689""" + See: https://github.com/sploitfun/lsploits/blob/master/glibc/malloc/malloc.c#L1689.""" _cmdline_ = "heap bins unsorted" _syntax_ = "{:s} [ARENA_ADDRESS]".format(_cmdline_) @@ -6453,7 +6452,7 @@ def get_shellcode(self, sid): @register_command class RopperCommand(GenericCommand): - """Ropper (http://scoding.de/ropper) plugin""" + """Ropper (http://scoding.de/ropper) plugin.""" _cmdline_ = "ropper" _syntax_ = "{:s} [ROPPER_OPTIONS]".format(_cmdline_) @@ -8760,7 +8759,7 @@ def do_invoke(self, argv): return class GefCommand(gdb.Command): - """GEF main command: view all new commands by typing `gef`""" + """GEF main command: view all new commands by typing `gef`.""" _cmdline_ = "gef" _syntax_ = "{:s} (missing|config|save|restore|set|run)".format(_cmdline_) @@ -9077,8 +9076,8 @@ def complete(self, text, word): class GefSaveCommand(gdb.Command): - """GEF save sub-command - Saves the current configuration of GEF to disk (by default in file '~/.gef.rc')""" + """GEF save sub-command. + Saves the current configuration of GEF to disk (by default in file '~/.gef.rc').""" _cmdline_ = "gef save" _syntax_ = _cmdline_ @@ -9117,8 +9116,8 @@ def invoke(self, args, from_tty): class GefRestoreCommand(gdb.Command): - """GEF restore sub-command - Loads settings from file '~/.gef.rc' and apply them to the configuration of GEF""" + """GEF restore sub-command. + Loads settings from file '~/.gef.rc' and apply them to the configuration of GEF.""" _cmdline_ = "gef restore" _syntax_ = _cmdline_ From d3c3ac02187939f0cbfc8c9577d152df2da1102e Mon Sep 17 00:00:00 2001 From: Grazfather Date: Mon, 15 Oct 2018 19:18:50 -0700 Subject: [PATCH 43/58] Remove a few unnecessary byte strings --- gef.py | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/gef.py b/gef.py index 315fde4f7..85286a5b6 100644 --- a/gef.py +++ b/gef.py @@ -4531,7 +4531,7 @@ def create_or_edit_structure(self, mod_name, struct_name): fullname = self.pcustom_filepath(mod_name) if not self.is_valid_struct(mod_name): info("Creating '{:s}' from template".format(fullname)) - with open(fullname, "wb") as f: + with open(fullname, "w") as f: f.write(self.get_template(struct_name)) f.flush() else: @@ -4545,13 +4545,11 @@ def create_or_edit_structure(self, mod_name, struct_name): def get_template(self, structname): d = [ - b"from ctypes import *\n\n", - b"class ", - gef_pybytes(structname), - b"(Structure):\n", - b" _fields_ = []\n" + "from ctypes import *\n\n", + "class ", structname, "(Structure):\n", + " _fields_ = []\n" ] - return b"".join(d) + return "".join(d) def list_custom_structures(self): @@ -4859,22 +4857,22 @@ def import_structures(self, structs): for struct_name in structs: fullpath = os.path.join(path, "{}.py".format(struct_name)) - with open(fullpath, "wb") as f: - f.write(b"from ctypes import *\n\n") - f.write(b"class ") - f.write(bytes(str(struct_name), encoding="utf-8")) - f.write(b"(Structure):\n") - f.write(b" _fields_ = [\n") + with open(fullpath, "w") as f: + f.write("from ctypes import *\n\n") + f.write("class ") + f.write(struct_name) + f.write("(Structure):\n") + f.write(" _fields_ = [\n") for _, name, size in structs[struct_name]: name = bytes(name, encoding="utf-8") - if size == 1: csize = b"c_uint8" - elif size == 2: csize = b"c_uint16" - elif size == 4: csize = b"c_uint32" - elif size == 8: csize = b"c_uint64" - else: csize = b"c_byte * " + bytes(str(size), encoding="utf-8") - m = [b' ("', name, b'", ', csize, b'),\n'] - f.write(b"".join(m)) - f.write(b"]\n") + if size == 1: csize = "c_uint8" + elif size == 2: csize = "c_uint16" + elif size == 4: csize = "c_uint32" + elif size == 8: csize = "c_uint64" + else: csize = "c_byte * {}".format(size) + m = " (\"{}\", {}),\n".format(name, csize) + f.write(m) + f.write("]\n") ok("Success, {:d} structure{:s} imported".format(len(structs), "s" if len(structs)>1 else "")) return From 3a7931b50b58c7585666bb4aaef85d68df99287d Mon Sep 17 00:00:00 2001 From: Grazfather Date: Mon, 15 Oct 2018 20:11:55 -0700 Subject: [PATCH 44/58] use .format in titlify --- gef.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gef.py b/gef.py index 85286a5b6..9068d1e6f 100644 --- a/gef.py +++ b/gef.py @@ -850,9 +850,9 @@ def titlify(text, color=None, msg_color=None): msg_color = __config__.get("theme.default_title_message")[0] msg = [] - msg.append(Color.colorify(HORIZONTAL_LINE * nb + ' ', color)) + msg.append(Color.colorify("{} ".format(HORIZONTAL_LINE * nb), color)) msg.append(Color.colorify(text, msg_color)) - msg.append(Color.colorify(' ' + HORIZONTAL_LINE * nb, color)) + msg.append(Color.colorify(" {}".format(HORIZONTAL_LINE * nb), color)) return "".join(msg) From 8143cc4623518a88325de7020bbc548ddafd23f7 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Mon, 15 Oct 2018 20:11:45 -0700 Subject: [PATCH 45/58] Convert single quotes to double --- gef.py | 111 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 55 insertions(+), 56 deletions(-) diff --git a/gef.py b/gef.py index 9068d1e6f..291bd96f0 100644 --- a/gef.py +++ b/gef.py @@ -544,7 +544,7 @@ class Elf: ET_CORE = 4 - e_magic = b'\x7fELF' + e_magic = b"\x7fELF" e_class = ELF_32_BITS e_endianness = LITTLE_ENDIAN e_eiversion = None @@ -914,7 +914,7 @@ def _show_code_line(fname, idx): def gef_pystring(x): """Python 2 & 3 compatibility function for strings handling.""" res = str(x, encoding="utf-8") if PYTHON_MAJOR == 3 else x - substs = [('\n','\\n'), ('\r','\\r'), ('\t','\\t'), ('\v','\\v'), ('\b','\\b'), ] + substs = [("\n","\\n"), ("\r","\\r"), ("\t","\\t"), ("\v","\\v"), ("\b","\\b"), ] for x,y in substs: res = res.replace(x,y) return res @@ -946,11 +946,11 @@ def is_exe(fpath): def style_byte(b, color=True): style = { - 'nonprintable': "yellow", - 'printable': "white", - '00': "gray", - '0a': "blue", - 'ff': "green", + "nonprintable": "yellow", + "printable": "white", + "00": "gray", + "0a": "blue", + "ff": "green", } sbyte = "{:02x}".format(b) if not color: @@ -958,10 +958,10 @@ def style_byte(b, color=True): if sbyte in style: st = style[sbyte] - elif chr(b) in (string.ascii_letters + string.digits + string.punctuation + ' '): - st = style.get('printable') + elif chr(b) in (string.ascii_letters + string.digits + string.punctuation + " "): + st = style.get("printable") else: - st = style.get('nonprintable') + st = style.get("nonprintable") if st: sbyte = Color.colorify(sbyte, st) return sbyte @@ -989,7 +989,7 @@ def hexdump(source, length=0x10, separator=".", show_raw=False, base=0x00): text = "".join([chr(b) if 0x20 <= b < 0x7F else separator for b in chunk]) sym = gdb_get_location_from_symbol(base+i) - sym = "<{:s}+{:04x}>".format(*sym) if sym else '' + sym = "<{:s}+{:04x}>".format(*sym) if sym else "" result.append("{addr:#0{aw}x} {sym} {data:<{dw}} {text}".format(aw=align, addr=base+i, @@ -1218,7 +1218,7 @@ def capstone_disassemble(location, nb_insn, **kwargs): def cs_insn_to_gef_insn(cs_insn): sym_info = gdb_get_location_from_symbol(cs_insn.address) loc = "<{}+{}>".format(*sym_info) if sym_info else "" - ops = [] + cs_insn.op_str.split(', ') + ops = [] + cs_insn.op_str.split(", ") return Instruction(cs_insn.address, loc, cs_insn.mnemonic, ops) capstone = sys.modules["capstone"] @@ -2235,8 +2235,7 @@ def read_cstring_from_memory(address, max_length=GEF_MAX_STRING_LENGTH, encoding mem = bytes(read_memory(address, length)).decode("utf-8") res = mem.split("\x00", 1)[0] - ustr = res.replace('\n','\\n').replace('\r','\\r').replace('\t','\\t') - + ustr = res.replace("\n","\\n").replace("\r","\\r").replace("\t","\\t") if max_length and len(res) > max_length: return "{}[...]".format(ustr[:max_length]) @@ -2940,7 +2939,7 @@ def get_memory_alignment(in_bits=False): return res.sizeof if not in_bits else res.sizeof * 8 try: - return gdb.parse_and_eval('$pc').type.sizeof + return gdb.parse_and_eval("$pc").type.sizeof except: pass raise EnvironmentError("GEF is running under an unsupported mode") @@ -3735,9 +3734,9 @@ class PrintFormatCommand(GenericCommand): _aliases_ = ["pf",] _example_ = "{0:s} -f py -b 8 -l 256 $rsp".format(_cmdline_) - bitformat = {8: ' Date: Tue, 16 Oct 2018 23:10:54 -0700 Subject: [PATCH 46/58] Improve test for heap-analysis-helper --- gef.py | 4 +++- tests/binaries/heap-analysis.c | 23 +++++++++++++++++++++++ tests/helpers.py | 2 +- tests/runtests.py | 12 ++++++++++-- 4 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 tests/binaries/heap-analysis.c diff --git a/gef.py b/gef.py index 291bd96f0..491b691d7 100644 --- a/gef.py +++ b/gef.py @@ -8504,7 +8504,9 @@ def setup(self): gdb.execute("set can-use-hw-watchpoints 0") info("Dynamic breakpoints correctly setup, GEF will break execution if a possible vulnerabity is found.") - warn("{}: The heap analysis slows down noticeably the execution. ".format(Color.colorify("Note", "bold underline yellow"))) + warn("{}: The heap analysis slows down the execution noticeably.".format( + Color.colorify("Note", "bold underline yellow"))) + # when inferior quits, we need to clean everything for a next execution gef_on_exit_hook(self.clean) diff --git a/tests/binaries/heap-analysis.c b/tests/binaries/heap-analysis.c new file mode 100644 index 000000000..d615d179c --- /dev/null +++ b/tests/binaries/heap-analysis.c @@ -0,0 +1,23 @@ +/** + * -*- mode: c -*- + * -*- coding: utf-8 -*- + * + * heap-analysis.c + * + * @author: @Grazfather + * @licence: WTFPL v.2 + */ + +#include +#include + +int main(int argc, char** argv, char** envp) +{ + void* p1 = malloc(0x10); + void* p2 = calloc(0x20, 1); + memset(p1, 'A', 0x10); + memset(p2, 'B', 0x20); + p1 = realloc(p1, 0x30); + free(p2); + return EXIT_SUCCESS; +} diff --git a/tests/helpers.py b/tests/helpers.py index 978776332..f87899119 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -50,7 +50,7 @@ def gdb_run_cmd_last_line(cmd, before=None, after=None, target=PATH_TO_DEFAULT_B def gdb_start_silent_cmd(cmd, before=None, after=None, target=PATH_TO_DEFAULT_BINARY): """Execute a command in GDB by starting an execution context. This command disables the `context` - and set a tbreak at the most convenient entry point.""" + and sets a tbreak at the most convenient entry point.""" if not before: before = [] diff --git a/tests/runtests.py b/tests/runtests.py index 0a71c1572..bd6ecea5c 100755 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -182,9 +182,17 @@ def test_cmd_heap_bins_fast(self): def test_cmd_heap_analysis(self): cmd = "heap-analysis-helper" + target = "tests/binaries/heap-analysis.out" self.assertFailIfInactiveSession(gdb_run_cmd(cmd)) - res = gdb_start_silent_cmd(cmd) - self.assertNoException(res) + res = gdb_start_silent_cmd(cmd, after=["continue"], target=target) + self.assertNoException(res) + self.assertIn("Tracking", res) + self.assertIn("correctly setup", res) + self.assertIn("malloc(16)=", res) + self.assertIn("malloc(32)=", res) # Actually calloc + addr = int(res.split("malloc(32)=")[1].split("\n")[0], 0) + self.assertRegex(res, r"realloc\(.+, 48") + self.assertIn("free({:#x}".format(addr), res) return def test_cmd_hexdump(self): From 89ea5d63ce0cae063c4fef20caa35ee8fe1dac2d Mon Sep 17 00:00:00 2001 From: Grazfather Date: Wed, 17 Oct 2018 14:28:41 -0700 Subject: [PATCH 47/58] Add some minor fixes --- gef.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/gef.py b/gef.py index 491b691d7..d814dea25 100644 --- a/gef.py +++ b/gef.py @@ -1199,9 +1199,9 @@ def gef_disassemble(addr, nb_insn, nb_prev=0): Return an iterator of Instruction objects.""" count = nb_insn + 1 if nb_insn & 1 else nb_insn - if nb_prev > 0: + if nb_prev: start_addr = gdb_get_nth_previous_instruction_address(addr, nb_prev) - if start_addr > 0: + if start_addr: for insn in gdb_disassemble(start_addr, count=nb_prev): if insn.address == addr: break yield insn @@ -2637,7 +2637,7 @@ def is_hex(pattern): def ida_synchronize_handler(event): - gdb.execute("ida-interact Sync", from_tty=True, to_string=True) + gdb.execute("ida-interact sync", from_tty=True) return @@ -4726,7 +4726,7 @@ def parsed_arglist(arglist): self.usage(method_name) return - method_name = argv[0] + method_name = argv[0].lower() if method_name == "version": self.version = self.sock.version() info("Enhancing {:s} with {:s} (v.{:s})".format(Color.greenify("gef"), @@ -4742,7 +4742,7 @@ def parsed_arglist(arglist): main_end_address = max([x.page_end for x in vmmap if x.realpath == get_filepath()]) try: - if method_name == "Sync": + if method_name == "sync": self.synchronize() else: method = getattr(self.sock, method_name) From bdbbdbc17e87af19eb5372f16435d4d9e2907019 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Tue, 16 Oct 2018 22:11:51 -0700 Subject: [PATCH 48/58] Move get_ith_parameter to Arch --- gef.py | 68 +++++++++++++++++++++------------------------------------- 1 file changed, 25 insertions(+), 43 deletions(-) diff --git a/gef.py b/gef.py index d814dea25..4065f9bc1 100644 --- a/gef.py +++ b/gef.py @@ -1401,6 +1401,13 @@ def fp(self): def ptrsize(self): return get_memory_alignment() + def get_ith_parameter(self, i): + """Retrieves the correct parameter used for the current function call.""" + reg = current_arch.function_parameters[i] + val = get_register(reg) + key = reg + return key, val + class RISCV(Architecture): arch = "RISCV" @@ -1847,6 +1854,14 @@ def mprotect_asm(cls, addr, size, perm): "popad",] return "; ".join(insns) + def get_ith_parameter(self, i): + sp = current_arch.sp + sz = current_arch.ptrsize + loc = sp + (i * sz) + val = read_int_from_memory(loc) + key = "[sp + {:#x}]".format(i * sz) + return key, val + class X86_64(X86): arch = "X86" @@ -1860,6 +1875,8 @@ class X86_64(X86): function_parameters = ["$rdi", "$rsi", "$rdx", "$rcx", "$r8", "$r9"] syscall_register = "$rax" syscall_instructions = ["syscall"] + # We don't want to inherit x86's stack based param getter + get_ith_parameter = Architecture.get_ith_parameter @classmethod def mprotect_asm(cls, addr, size, perm): @@ -3232,17 +3249,8 @@ def __init__(self, spec, num_args): def stop(self): msg = [] - if is_x86_32(): - sp = current_arch.sp - sz = get_memory_alignment() - val = sp + (self.num_args * sz) + sz - ptr = read_int_from_memory(val) - addr = lookup_address(ptr) - ptr = hex(ptr) - else: - regs = current_arch.function_parameters - ptr = regs[self.num_args] - addr = lookup_address(get_register(ptr)) + ptr, addr = current_arch.get_ith_parameter(self.num_args) + addr = lookup_address(addr) if not addr.valid: return False @@ -3309,11 +3317,7 @@ def __init__(self, name): return def stop(self): - if is_x86_32(): - # if intel x32, the malloc size is in the stack, so we need to dereference $sp - size = to_unsigned_long(dereference(current_arch.sp+4)) - else: - size = get_register(current_arch.function_parameters[0]) + _, size = current_arch.get_ith_parameter(0) self.retbp = TraceMallocRetBreakpoint(size) return False @@ -3399,12 +3403,8 @@ def __init__(self): return def stop(self): - if is_x86_32(): - ptr = to_unsigned_long(dereference(current_arch.sp+4)) - size = to_unsigned_long(dereference(current_arch.sp+8)) - else: - ptr = get_register(current_arch.function_parameters[0]) - size = get_register(current_arch.function_parameters[1]) + _, ptr = current_arch.get_ith_parameter(0) + _, size = current_arch.get_ith_parameter(1) self.retbp = TraceReallocRetBreakpoint(ptr, size) return False @@ -3462,11 +3462,7 @@ def __init__(self): return def stop(self): - if is_x86_32(): - # if intel x32, the free address is in the stack, so we need to dereference $sp - addr = to_unsigned_long(dereference(current_arch.sp+4)) - else: - addr = long(gdb.parse_and_eval(current_arch.function_parameters[0])) + _, addr = current_arch.get_ith_parameter(0) msg = [] check_free_null = get_gef_setting("heap-analysis-helper.check_free_null") check_double_free = get_gef_setting("heap-analysis-helper.check_double_free") @@ -7152,26 +7148,12 @@ def context_args(self): self.print_arguments_from_symbol(target, sym) return - def get_ith_parameter(self, i): - """Retrieves the correct parameter used for the current function call.""" - if is_x86_32(): - sp = current_arch.sp - sz = current_arch.ptrsize - loc = sp + (i * sz) - val = read_int_from_memory(loc) - key = "[sp + {:#x}]".format(i * sz) - else: - reg = current_arch.function_parameters[i] - val = get_register(reg) - key = reg - return (key, val) - def print_arguments_from_symbol(self, function_name, symbol): """If symbols were found, parse them and print the argument adequately.""" args = [] for i, f in enumerate(symbol.type.fields()): - _value = self.get_ith_parameter(i)[1] + _value = current_arch.get_ith_parameter(i)[1] _value = RIGHT_ARROW.join(DereferenceCommand.dereference_from(_value)) _name = f.name or "var_{}".format(i) _type = f.type.name or self.size2type[f.type.sizeof] @@ -7241,7 +7223,7 @@ def __get_current_block_start_address(): args = [] for i in range(nb_argument): - _key, _value = self.get_ith_parameter(i) + _key, _value = current_arch.get_ith_parameter(i) _value = RIGHT_ARROW.join(DereferenceCommand.dereference_from(_value)) args.append("{} = {}".format(Color.colorify(_key, arg_key_color), _value)) From 0a46e0e639f0c252d44d6951878719fb163361a7 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Tue, 16 Oct 2018 21:46:55 -0700 Subject: [PATCH 49/58] Make DetailRegisters not have x86-specific code --- gef.py | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/gef.py b/gef.py index 4065f9bc1..b5675711e 100644 --- a/gef.py +++ b/gef.py @@ -1385,6 +1385,10 @@ def is_branch_taken(self, insn): pass @abc.abstractmethod def get_ra(self, insn, frame): pass + @property + def special_registers(self): + return [] + @property def pc(self): return get_register("$pc") @@ -1740,9 +1744,9 @@ class X86(Architecture): nop_insn = b"\x90" flag_register = "$eflags" - msr_registers = ["$cs", "$ss", "$ds", "$es", "$fs", "$gs", ] + special_registers = ["$cs", "$ss", "$ds", "$es", "$fs", "$gs", ] gpr_registers = ["$eax", "$ebx", "$ecx", "$edx", "$esp", "$ebp", "$esi", "$edi", "$eip", ] - all_registers = gpr_registers + [ flag_register, ] + msr_registers + all_registers = gpr_registers + [ flag_register, ] + special_registers instruction_length = None return_register = "$eax" function_parameters = ["$esp", ] @@ -1870,7 +1874,7 @@ class X86_64(X86): gpr_registers = [ "$rax", "$rbx", "$rcx", "$rdx", "$rsp", "$rbp", "$rsi", "$rdi", "$rip", "$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$r14", "$r15", ] - all_registers = gpr_registers + [ X86.flag_register, ] + X86.msr_registers + all_registers = gpr_registers + [ X86.flag_register, ] + X86.special_registers return_register = "$rax" function_parameters = ["$rdi", "$rsi", "$rdx", "$rcx", "$r8", "$r9"] syscall_register = "$rax" @@ -6260,13 +6264,17 @@ def do_invoke(self, argv): if argv: regs = [reg for reg in current_arch.all_registers if reg in argv] + if not regs: + warn("No matching registers found") else: regs = current_arch.all_registers + memsize = current_arch.ptrsize endian = endian_str() charset = string.printable widest = max(map(len, current_arch.all_registers)) + special_line = "" for regname in regs: reg = gdb.parse_and_eval(regname) @@ -6288,14 +6296,10 @@ def do_invoke(self, argv): else: color = changed_color - if is_x86() and regname in current_arch.msr_registers: - msr = set(current_arch.msr_registers) - for r in set(regs) & msr: - line = "{}: ".format(Color.colorify(r, color)) - line+= "0x{:04x}".format(get_register(r)) - gef_print(line, end=" ") - regs.remove(r) - gef_print() + # Special (e.g. segment) registers go on their own line + if regname in current_arch.special_registers: + special_line += "{}: ".format(Color.colorify(regname, color)) + special_line += "0x{:04x} ".format(get_register(regname)) continue line = "{}: ".format(Color.colorify(padreg, color)) @@ -6328,6 +6332,9 @@ def do_invoke(self, argv): pass gef_print(line) + + if special_line: + gef_print(special_line) return From d1ccf72b386db743722599bed5451ad040ba9111 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Tue, 16 Oct 2018 23:47:14 -0700 Subject: [PATCH 50/58] special_registers as class attr not property --- gef.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/gef.py b/gef.py index b5675711e..e041f6859 100644 --- a/gef.py +++ b/gef.py @@ -1385,9 +1385,7 @@ def is_branch_taken(self, insn): pass @abc.abstractmethod def get_ra(self, insn, frame): pass - @property - def special_registers(self): - return [] + special_registers = [] @property def pc(self): From 57446c7ee2e1b19e8ae20b7165c55f48439e7f3c Mon Sep 17 00:00:00 2001 From: William Bowling Date: Tue, 23 Oct 2018 14:47:26 +1100 Subject: [PATCH 51/58] Refactor offset functions and create section offset helpers (#366) --- gef.py | 100 +++++++++++++++++++++++++++++++++------------------------ 1 file changed, 58 insertions(+), 42 deletions(-) diff --git a/gef.py b/gef.py index e041f6859..f48ab7206 100644 --- a/gef.py +++ b/gef.py @@ -8632,14 +8632,26 @@ def get_settings_path(self): return path if os.path.isdir(path) else None -class GenericOffsetFunction(gdb.Function): - """This is an abstract class for invoking offset functions, should not be instantiated.""" +@lru_cache() +def get_section_base_address(name): + section = process_lookup_path(name) + if section: + return section.page_start + + return None + +@lru_cache() +def get_zone_base_address(name): + zone = file_lookup_name_path(name, get_filepath()) + if zone: + return zone.zone_start + + return None + +class GenericFunction(gdb.Function): + """This is an abstract class for invoking convenience functions, should not be instantiated.""" __metaclass__ = abc.ABCMeta - @abc.abstractproperty - def _section_(self): pass - @abc.abstractproperty - def _zone_(self): pass @abc.abstractproperty def _function_(self): pass @property @@ -8647,66 +8659,70 @@ def _syntax_(self): return "${}([offset])".format(self._function_) def __init__ (self): - super(GenericOffsetFunction, self).__init__(self._function_) + super(GenericFunction, self).__init__(self._function_) - def invoke(self, offset=gdb.Value(0)): + def invoke(self, *args): if not is_alive(): raise gdb.GdbError("No debugging session active") + return long(self.do_invoke(args)) + + def arg_to_long(self, args, index, default=0): + try: + addr = args[index] + return long(addr) if addr.address is None else long(addr.address) + except IndexError: + return default + + @abc.abstractmethod + def do_invoke(self, args): pass - base_address = self.get_base_address() - if not base_address: - raise gdb.GdbError("No {} section".format(self._section_ or self._zone_)) - - addr = long(offset) if offset.address is None else long(offset.address) - return long(base_address + addr) - - def get_base_address(self): - if self._section_: - section = process_lookup_path(self._section_) - if section: - return section.page_start - elif self._zone_: - zone = file_lookup_name_path(self._zone_, get_filepath()) - if zone: - return zone.zone_start - return None @register_function -class StackOffsetFunction(GenericOffsetFunction): +class StackOffsetFunction(GenericFunction): """Return the current stack base address plus an optional offset.""" _function_ = "_stack" - _section_ = "[stack]" - _zone_ = None + + def do_invoke(self, args): + return self.arg_to_long(args, 0) + get_section_base_address("[stack]") @register_function -class HeapBaseFunction(GenericOffsetFunction): +class HeapBaseFunction(GenericFunction): """Return the current heap base address plus an optional offset.""" _function_ = "_heap" - _section_ = "[heap]" - _zone_ = None + + def do_invoke(self, args): + return self.arg_to_long(args, 0) + HeapBaseFunction.heap_base() + + @staticmethod + def heap_base(): + try: + return long(gdb.parse_and_eval("mp_->sbrk_base")) + except gdb.error: + return get_section_base_address("[heap]") @register_function -class PieBaseFunction(GenericOffsetFunction): +class PieBaseFunction(GenericFunction): """Return the current pie base address plus an optional offset.""" _function_ = "_pie" - _zone_ = None - @property - def _section_(self): - return get_filepath() + + def do_invoke(self, args): + return self.arg_to_long(args, 0) + get_section_base_address(get_filepath()) @register_function -class BssBaseFunction(GenericOffsetFunction): +class BssBaseFunction(GenericFunction): """Return the current bss base address plus the given offset.""" _function_ = "_bss" - _zone_ = ".bss" - _section_ = None + + def do_invoke(self, args): + return self.arg_to_long(args, 0) + get_zone_base_address(".bss") @register_function -class GotBaseFunction(GenericOffsetFunction): +class GotBaseFunction(GenericFunction): """Return the current bss base address plus the given offset.""" _function_ = "_got" - _zone_ = ".got" - _section_ = None + + def do_invoke(self, args): + return self.arg_to_long(args, 0) + get_zone_base_address(".got") @register_command class GefFunctionsCommand(GenericCommand): From b5cfcde852d35286e50963f6105d1417c5b3aa43 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Wed, 17 Oct 2018 15:09:56 -0700 Subject: [PATCH 52/58] Correct missing conditionals and add missing On ARM, AARCH64, and x86 we were not correctly comparing if two flags were equal: Because we were comparing the masked value instead of whether the bit was set, these would NEVER be true. For example for `ge` on x86, the branch is taken is SF==OF, but we were comparing flags&(1<<7) == flags&(1<<11). Even if both flags were set, we'd be comparing 0b10000000 with 0b100000000000, which would never be true. --- gef.py | 89 ++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 52 insertions(+), 37 deletions(-) diff --git a/gef.py b/gef.py index f48ab7206..83e34245b 100644 --- a/gef.py +++ b/gef.py @@ -1588,9 +1588,8 @@ def flag_register_to_human(self, val=None): return flags_to_human(val, self.flags_table) def is_conditional_branch(self, insn): - branch_mnemos = {"beq", "bne", "bleq", "blt", "bgt", "bgez", "bvs", "bvc", - "jeq", "jne", "jleq", "jlt", "jgt", "jgez", "jvs", "jvc"} - return insn.mnemonic in branch_mnemos + conditions = {"eq", "ne", "lt", "le", "gt", "ge", "vs", "vc", "mi", "pl", "hi", "ls"} + return insn.mnemonic[-2:] in conditions def is_branch_taken(self, insn): mnemo = insn.mnemonic @@ -1599,20 +1598,28 @@ def is_branch_taken(self, insn): val = get_register(self.flag_register) taken, reason = False, "" - if mnemo.endswith("eq"): taken, reason = val&(1< Date: Fri, 19 Oct 2018 19:15:25 -0700 Subject: [PATCH 53/58] Avoid duplicated is_branch_taken logic in AARCH64 --- gef.py | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/gef.py b/gef.py index 83e34245b..ed5388aae 100644 --- a/gef.py +++ b/gef.py @@ -1726,28 +1726,8 @@ def is_branch_taken(self, insn): if (op & 1< Date: Mon, 22 Oct 2018 21:59:28 -0700 Subject: [PATCH 54/58] Add tcache support to heap bins command (#363) --- gef.py | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 97 insertions(+), 5 deletions(-) diff --git a/gef.py b/gef.py index ed5388aae..06367b85a 100644 --- a/gef.py +++ b/gef.py @@ -624,6 +624,8 @@ def is_valid(self): class GlibcArena: """Glibc arena class Ref: https://github.com/sploitfun/lsploits/blob/master/glibc/malloc/malloc.c#L1671 """ + TCACHE_MAX_BINS = 0x40 + def __init__(self, addr, name=__gef_default_main_arena__): arena = gdb.parse_and_eval(addr) malloc_state_t = cached_lookup_type("struct malloc_state") @@ -641,7 +643,16 @@ def __getattr__(self, item): def __int__(self): return self.__addr + def tcachebin(self, i): + """Return head chunk in tcache[i].""" + heap_base = HeapBaseFunction.heap_base() + addr = dereference(heap_base + 2*current_arch.ptrsize + self.TCACHE_MAX_BINS + i*current_arch.ptrsize) + if not addr: + return None + return GlibcChunk(long(addr)) + def fastbin(self, i): + """Return head chunk in fastbinsY[i].""" addr = dereference_as_long(self.fastbinsY[i]) if addr == 0: return None @@ -831,6 +842,22 @@ def psprint(self): return "\n".join(msg) + "\n" +@lru_cache() +def get_libc_version(): + sections = get_process_maps() + try: + for section in sections: + if "libc-" in section.path: + libc_version = tuple(int(_) for _ in + re.search(r"libc-(\d+)\.(\d+)\.so", section.path).groups()) + break + else: + libc_version = 0, 0 + except AttributeError: + libc_version = 0, 0 + return libc_version + + @lru_cache() def get_main_arena(): try: @@ -6014,7 +6041,7 @@ class GlibcHeapBinsCommand(GenericCommand): """Display information on the bins on an arena (default: main_arena). See https://github.com/sploitfun/lsploits/blob/master/glibc/malloc/malloc.c#L1123.""" - _bin_types_ = ["fast", "unsorted", "small", "large"] + _bin_types_ = ["tcache", "fast", "unsorted", "small", "large"] _cmdline_ = "heap bins" _syntax_ = "{:s} [{:s}]".format(_cmdline_, "|".join(_bin_types_)) @@ -6060,9 +6087,74 @@ def pprint_bin(arena_addr, index, _type=""): fw = chunk.fwd nb_chunk += 1 - gef_print(" ".join(m)) + if m: + gef_print(" ".join(m)) return nb_chunk + +@register_command +class GlibcHeapTcachebinsCommand(GenericCommand): + """Display information on the Tcachebins on an arena (default: main_arena). + See https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=d5c3fafc4307c9b7a4c7d5cb381fcdbfad340bcc.""" + + _cmdline_ = "heap bins tcache" + _syntax_ = "{:s} [ARENA_ADDRESS]".format(_cmdline_) + + def __init__(self): + super(GlibcHeapTcachebinsCommand, self).__init__(complete=gdb.COMPLETE_LOCATION) + return + + @only_if_gdb_running + def do_invoke(self, argv): + # Determine if we are using libc with tcache built in (2.26+) + if get_libc_version() < (2, 26): + info("No Tcache in this version of libc") + return + + arena = GlibcArena("*{:s}".format(argv[0])) if len(argv) == 1 else get_main_arena() + + if arena is None: + err("Invalid Glibc arena") + return + + # Get tcache_perthread_struct for this arena + addr = HeapBaseFunction.heap_base() + 0x10 + + gef_print(titlify("Tcachebins for arena {:#x}".format(int(arena)))) + for i in range(GlibcArena.TCACHE_MAX_BINS): + count = ord(read_memory(addr + i, 1)) + chunk = arena.tcachebin(i) + chunks = set() + m = [] + + # Only print the entry if there are valid chunks. Don't trust count + while True: + if chunk is None: + break + + try: + m.append("{:s} {:s} ".format(LEFT_ARROW, str(chunk))) + if chunk.address in chunks: + m.append("{:s} [loop detected]".format(RIGHT_ARROW)) + break + + chunks.add(chunk.address) + + next_chunk = chunk.get_fwd_ptr() + if next_chunk == 0: + break + + chunk = GlibcChunk(next_chunk) + except gdb.MemoryError: + m.append("{:s} [Corrupted chunk at {:#x}]".format(LEFT_ARROW, chunk.address)) + break + if m: + gef_print("Tcachebins[idx={:d}, size={:#x}] count={:d} ".format(i, (i+1)*(current_arch.ptrsize)*2, count), end="") + gef_print("".join(m)) + return + + + @register_command class GlibcHeapFastbinsYCommand(GenericCommand): """Display information on the fastbinsY on an arena (default: main_arena). @@ -6094,7 +6186,7 @@ def fastbin_index(sz): for i in range(NFASTBINS): gef_print("Fastbins[idx={:d}, size={:#x}] ".format(i, (i+1)*SIZE_SZ*2), end="") chunk = arena.fastbin(i) - chunks = [] + chunks = set() while True: if chunk is None: @@ -6110,7 +6202,7 @@ def fastbin_index(sz): if fastbin_index(chunk.get_chunk_size()) != i: gef_print("[incorrect fastbin_index] ", end="") - chunks.append(chunk.address) + chunks.add(chunk.address) next_chunk = chunk.get_fwd_ptr() if next_chunk == 0: @@ -6199,7 +6291,7 @@ def do_invoke(self, argv): bins = {} for i in range(63, 126): nb_chunk = GlibcHeapBinsCommand.pprint_bin(arena_addr, i, "large_") - if nb_chunk < 0: + if nb_chunk <= 0: break if nb_chunk > 0: bins[i] = nb_chunk From fa80001dca38523e5a3732ac331f70dc117c4500 Mon Sep 17 00:00:00 2001 From: Grazfather Date: Tue, 23 Oct 2018 08:38:24 -0700 Subject: [PATCH 55/58] use self in get_ith_parameter --- gef.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gef.py b/gef.py index 06367b85a..6f88a7e27 100644 --- a/gef.py +++ b/gef.py @@ -1432,7 +1432,7 @@ def ptrsize(self): def get_ith_parameter(self, i): """Retrieves the correct parameter used for the current function call.""" - reg = current_arch.function_parameters[i] + reg = self.function_parameters[i] val = get_register(reg) key = reg return key, val From 7dd838aa810f2b3f4f4d14ed77d725aa0a499ef7 Mon Sep 17 00:00:00 2001 From: William Bowling Date: Thu, 25 Oct 2018 11:21:01 +1100 Subject: [PATCH 56/58] Allow the patch command to take an expression as the value (#368) --- gef.py | 2 +- tests/runtests.py | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/gef.py b/gef.py index 6f88a7e27..0a8576b5c 100644 --- a/gef.py +++ b/gef.py @@ -7776,7 +7776,7 @@ def do_invoke(self, argv): d = "<" if is_little_endian() else ">" for value in values: - value = int(value, 0) & ((1 << size * 8) - 1) + value = parse_address(value) & ((1 << size * 8) - 1) vstr = struct.pack(d + fcode, value) write_memory(addr, vstr, length=size) addr += size diff --git a/tests/runtests.py b/tests/runtests.py index bd6ecea5c..e997b1fa0 100755 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -258,6 +258,16 @@ def test_cmd_patch_qword(self): self.assertTrue( r > 0.50 ) return + def test_cmd_patch_qword_symbol(self): + target = "tests/binaries/bss.out" + before = gdb_run_silent_cmd("deref $sp 1", target=target) + after = gdb_run_silent_cmd("patch qword $sp &msg", after=["deref $sp 1",], target=target) + self.assertNoException(before) + self.assertNoException(after) + self.assertNotIn("Hello world!", before) + self.assertIn("Hello world!", after) + return + def test_cmd_patch_string(self): res = gdb_start_silent_cmd_last_line("patch string $sp \"Gef!Gef!Gef!Gef!\"", after=["grep Gef!Gef!Gef!Gef!",]) self.assertNoException(res) From 8cb42d05a9cb153a00d47fd9252130cf435f3525 Mon Sep 17 00:00:00 2001 From: William Bowling Date: Fri, 26 Oct 2018 16:30:46 +1100 Subject: [PATCH 57/58] Return None instead of -1 (#370) --- gef.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gef.py b/gef.py index 0a8576b5c..b52bd22c0 100644 --- a/gef.py +++ b/gef.py @@ -1186,7 +1186,7 @@ def gdb_get_nth_previous_instruction_address(addr, n): if insns[-1].address==cur_insn_addr: return insns[0].address - return -1 + return None def gdb_get_nth_next_instruction_address(addr, n): From d871e47292f081fca87ded2145b96af981e84981 Mon Sep 17 00:00:00 2001 From: JaeRyoung Oh Date: Fri, 26 Oct 2018 14:34:03 +0900 Subject: [PATCH 58/58] Add pyenv support (#331) --- gef.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/gef.py b/gef.py index b52bd22c0..2e83ba216 100644 --- a/gef.py +++ b/gef.py @@ -72,6 +72,7 @@ import platform import re import shutil +import site import socket import string import struct @@ -9472,6 +9473,16 @@ def __gef_prompt__(current_prompt): "Consider updating to GDB {} or higher.".format(".".join(GDB_MIN_VERSION))) else: + try: + pyenv = which("pyenv") + PYENV_ROOT = subprocess.check_output([pyenv, "root"]).strip() + PYENV_VERSION = subprocess.check_output([pyenv, "version-name"]).strip() + site_packages_dir = os.path.join(PYENV_ROOT, "versions", PYENV_VERSION, "lib", + "python{}".format(PYENV_VERSION[:3]), "site-packages") + site.addsitedir(site_packages_dir) + except FileNotFoundError: + pass + # setup prompt gdb.prompt_hook = __gef_prompt__