Skip to content

Commit

Permalink
Fix 'heap chunks' command for non-main arenas (#706) (#709)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
theguy147 committed Sep 11, 2021
1 parent 3bc42ef commit 3fc237c
Showing 1 changed file with 53 additions and 44 deletions.
97 changes: 53 additions & 44 deletions gef.py
Expand Up @@ -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

Expand All @@ -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


Expand Down Expand Up @@ -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():
Expand All @@ -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


Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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)

Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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."""
Expand Down Expand Up @@ -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])
Expand All @@ -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


Expand All @@ -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
Expand Down Expand Up @@ -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")))
Expand Down Expand Up @@ -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:
Expand All @@ -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):
Expand Down Expand Up @@ -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):
Expand Down

0 comments on commit 3fc237c

Please sign in to comment.