From 3fc237c6587a239ed1bee77484935b74c99a0a5e Mon Sep 17 00:00:00 2001 From: theguy147 <37738506+theguy147@users.noreply.github.com> Date: Sat, 11 Sep 2021 19:27:55 +0200 Subject: [PATCH] Fix 'heap chunks' command for non-main arenas (#706) (#709) * Fix 'heap chunks' command for non-main arenas (#706) * Fix 'heap chunks' cmd for non-main arenas with debug symbols enabled * Rename arena global * Visually realign globals * Fix 'heap chunks' command for non-main arenas (#706) * Fix 'heap chunks' cmd for non-main arenas with debug symbols enabled * Rename arena global * Visually realign globals * Fix address parsing --- gef.py | 97 ++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 53 insertions(+), 44 deletions(-) diff --git a/gef.py b/gef.py index f23d61434..6ee3951fb 100644 --- a/gef.py +++ b/gef.py @@ -151,7 +151,7 @@ def update_gef(argv): __pie_counter__ = 1 __gef_remote__ = None __gef_qemu_mode__ = False -__gef_default_main_arena__ = "main_arena" +__gef_current_arena__ = "main_arena" __gef_int_stream_buffer__ = None __gef_redirect_output_fd__ = None @@ -176,14 +176,14 @@ def update_gef(argv): def reset_all_caches(): """Free all caches. If an object is cached, it will have a callable attribute `cache_clear` which will be invoked to purge the function cache.""" - global __gef_default_main_arena__ + global __gef_current_arena__ for mod in dir(sys.modules["__main__"]): obj = getattr(sys.modules["__main__"], mod) if hasattr(obj, "cache_clear"): obj.cache_clear() - __gef_default_main_arena__ = "main_arena" + __gef_current_arena__ = "main_arena" return @@ -616,7 +616,7 @@ def is_valid(self): @lru_cache() def search_for_main_arena(): - global __gef_default_main_arena__ + global __gef_current_arena__ malloc_hook_addr = to_unsigned_long(gdb.parse_and_eval("(void *)&__malloc_hook")) if is_x86(): @@ -626,7 +626,7 @@ def search_for_main_arena(): else: raise OSError("Cannot find main_arena for {}".format(current_arch.arch)) - __gef_default_main_arena__ = "*0x{:x}".format(addr) + __gef_current_arena__ = "*0x{:x}".format(addr) return addr @@ -746,12 +746,13 @@ class GlibcArena: Ref: https://github.com/sploitfun/lsploits/blob/master/glibc/malloc/malloc.c#L1671""" def __init__(self, addr, name=None): - self.__name = name or __gef_default_main_arena__ + self.__name = name or __gef_current_arena__ try: arena = gdb.parse_and_eval(addr) malloc_state_t = cached_lookup_type("struct malloc_state") self.__arena = arena.cast(malloc_state_t) self.__addr = int(arena.address) + self.struct_size = malloc_state_t.sizeof except: self.__arena = MallocStateStruct(addr) self.__addr = self.__arena.addr @@ -794,6 +795,17 @@ def get_next(self): return None return GlibcArena("*{:#x} ".format(addr_next)) + def heap_addr(self): + main_arena_addr = to_unsigned_long(gdb.parse_and_eval("&main_arena")) + if int(self) == main_arena_addr: + heap_section = HeapBaseFunction.heap_base() + if not heap_section: + err("Heap not initialized") + return None + return heap_section + _addr = int(self) + self.struct_size + return malloc_align_address(_addr) + def __str__(self): fmt = "Arena (base={:#x}, top={:#x}, last_remainder={:#x}, next={:#x}, next_free={:#x}, system_mem={:#x})" return fmt.format(self.__addr, self.top, self.last_remainder, self.n, self.nfree, self.sysmem) @@ -811,32 +823,13 @@ def __init__(self, addr, from_base=False, allow_unaligned=True): else: self.data_address = addr if not allow_unaligned: - self.align_data_address() + self.data_address = malloc_align_address(self.data_address) self.base_address = addr - 2 * self.ptrsize self.size_addr = int(self.data_address - self.ptrsize) self.prev_size_addr = self.base_address return - def align_data_address(self): - """Align chunk data addresses according to glibc's MALLOC_ALIGNMENT. See also Issue #689 on Github""" - __default_malloc_alignment = 0x10 - if is_x86_32() and get_libc_version() >= (2, 26): - # Special case introduced in Glibc 2.26: - # https://elixir.bootlin.com/glibc/glibc-2.26/source/sysdeps/i386/malloc-alignment.h#L22 - malloc_alignment = __default_malloc_alignment - else: - # Generic case: - # https://elixir.bootlin.com/glibc/glibc-2.26/source/sysdeps/generic/malloc-alignment.h#L22 - __alignof__long_double = int(safe_parse_and_eval("_Alignof(long double)") or __default_malloc_alignment) # fallback to default if the expression fails to evaluate - malloc_alignment = max(__alignof__long_double, 2 * self.ptrsize) - - ceil = lambda n: int(-1 * n // 1 * -1) - # align data_address to nearest next multiple of malloc_alignment - self.data_address = malloc_alignment * ceil(self.data_address / malloc_alignment) - return - - def get_chunk_size(self): return read_int_from_memory(self.size_addr) & (~0x07) @@ -1010,7 +1003,7 @@ def get_libc_version(): def get_main_arena(): try: - return GlibcArena(__gef_default_main_arena__) + return GlibcArena(__gef_current_arena__) except Exception as e: err( "Failed to get the main arena, heap commands may not work properly: {}".format( @@ -3587,6 +3580,23 @@ def align_address_to_page(address): a = align_address(address) >> DEFAULT_PAGE_ALIGN_SHIFT return a << DEFAULT_PAGE_ALIGN_SHIFT +def malloc_align_address(address): + """Align addresses according to glibc's MALLOC_ALIGNMENT. See also Issue #689 on Github""" + __default_malloc_alignment = 0x10 + if is_x86_32() and get_libc_version() >= (2, 26): + # Special case introduced in Glibc 2.26: + # https://elixir.bootlin.com/glibc/glibc-2.26/source/sysdeps/i386/malloc-alignment.h#L22 + malloc_alignment = __default_malloc_alignment + else: + # Generic case: + # https://elixir.bootlin.com/glibc/glibc-2.26/source/sysdeps/generic/malloc-alignment.h#L22 + __alignof__long_double = int(safe_parse_and_eval("_Alignof(long double)") or __default_malloc_alignment) # fallback to default if the expression fails to evaluate + malloc_alignment = max(__alignof__long_double, 2 * current_arch.ptrsize) + + ceil = lambda n: int(-1 * n // 1 * -1) + # align address to nearest next multiple of malloc_alignment + return malloc_alignment * ceil((address / malloc_alignment)) + def parse_address(address): """Parse an address and return it as an Integer.""" @@ -6605,10 +6615,10 @@ def __init__(self): @only_if_gdb_running def do_invoke(self, argv): - global __gef_default_main_arena__ + global __gef_current_arena__ if not argv: - ok("Current main_arena set to: '{}'".format(__gef_default_main_arena__)) + ok("Current arena set to: '{}'".format(__gef_current_arena__)) return new_arena = safe_parse_and_eval(argv[0]) @@ -6622,9 +6632,9 @@ def do_invoke(self, argv): err("Invalid address") return - __gef_default_main_arena__ = "*{:s}".format(format_address(new_arena.value)) + __gef_current_arena__ = "*{:s}".format(format_address(new_arena.value)) else: - __gef_default_main_arena__ = argv[0] + __gef_current_arena__ = argv[0] return @@ -6638,7 +6648,7 @@ class GlibcHeapArenaCommand(GenericCommand): @only_if_gdb_running def do_invoke(self, argv): try: - arena = GlibcArena(__gef_default_main_arena__) + arena = GlibcArena(__gef_current_arena__) except gdb.error: err("Could not find Glibc main arena") return @@ -6700,21 +6710,20 @@ def __init__(self): def do_invoke(self, *args, **kwargs): args = kwargs["arguments"] - if not args.address: - heap_section = HeapBaseFunction.heap_base() - if not heap_section: - err("Heap not initialized") - return - else: - heap_section = parse_address(args.address) - arena = get_main_arena() if arena is None: err("No valid arena") return + if not args.address: + heap_addr = arena.heap_addr() + if heap_addr is None: + return + else: + heap_addr = parse_address(args.address) + nb = self.get_setting("peek_nb_byte") - current_chunk = GlibcChunk(heap_section, from_base=True, allow_unaligned=args.allow_unaligned) + current_chunk = GlibcChunk(heap_addr, from_base=True, allow_unaligned=args.allow_unaligned) while True: if current_chunk.base_address == arena.top: gef_print("{} {} {}".format(str(current_chunk), LEFT_ARROW, Color.greenify("top chunk"))) @@ -7034,7 +7043,7 @@ def do_invoke(self, argv): err("Invalid Glibc arena") return - arena_addr = "*{:s}".format(argv[0]) if len(argv) == 1 else __gef_default_main_arena__ + arena_addr = "*{:s}".format(argv[0]) if len(argv) == 1 else __gef_current_arena__ gef_print(titlify("Unsorted Bin for arena '{:s}'".format(arena_addr))) nb_chunk = GlibcHeapBinsCommand.pprint_bin(arena_addr, 0, "unsorted_") if nb_chunk >= 0: @@ -7058,7 +7067,7 @@ def do_invoke(self, argv): err("Invalid Glibc arena") return - arena_addr = "*{:s}".format(argv[0]) if len(argv) == 1 else __gef_default_main_arena__ + arena_addr = "*{:s}".format(argv[0]) if len(argv) == 1 else __gef_current_arena__ gef_print(titlify("Small Bins for arena '{:s}'".format(arena_addr))) bins = {} for i in range(1, 63): @@ -7087,7 +7096,7 @@ def do_invoke(self, argv): err("Invalid Glibc arena") return - arena_addr = "*{:s}".format(argv[0]) if len(argv) == 1 else __gef_default_main_arena__ + arena_addr = "*{:s}".format(argv[0]) if len(argv) == 1 else __gef_current_arena__ gef_print(titlify("Large Bins for arena '{:s}'".format(arena_addr))) bins = {} for i in range(63, 126):