Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add feature to print heap chunks of all arenas #722

Merged
merged 3 commits into from
Sep 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion docs/commands/heap.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ gef➤ heap chunks [arena_address]

![heap-chunks-arena](https://i.imgur.com/y1fybRx.png)

In order to display the chunks of all the available arenas at once use

```
gef➤ heap chunks -a
```

![heap-chunks-all](https://i.imgur.com/pTjRJFo.png)

Because usually the heap chunks are aligned to a certain number of bytes in
memory GEF automatically re-aligns the chunks data start addresses to match
Glibc's behavior. To be able to view unaligned chunks as well, you can disable
Expand Down Expand Up @@ -59,7 +67,7 @@ Multi-threaded programs have different arenas, and the knowledge of the
to help you list all the arenas allocated in your program **at the moment you
call the command**.

![heap-arenas](https://i.imgur.com/ajbLiCF.png)
![heap-arenas](https://i.imgur.com/RUTiADa.png)

### `heap set-arena` command ###

Expand Down
44 changes: 24 additions & 20 deletions gef.py
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,12 @@ def __getattr__(self, item):
def __int__(self):
return self.__addr

def __iter__(self):
arena = self
while arena is not None:
yield arena
arena = arena.get_next()

def fastbin(self, i):
"""Return head chunk in fastbinsY[i]."""
addr = int(self.fastbinsY[i])
Expand Down Expand Up @@ -1056,8 +1062,11 @@ def get_heap_for_ptr(ptr):
return ptr & ~(heap_max_size - 1)

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)
fmt = "{:s}(base={:#x}, top={:#x}, last_remainder={:#x}, next={:#x}, next_free={:#x}, system_mem={:#x})"
return fmt.format(
Color.colorify("Arena", "blue bold underline"),
self.__addr, self.top, self.last_remainder, self.n, self.nfree, self.sysmem
)


class GlibcChunk:
Expand Down Expand Up @@ -1263,6 +1272,11 @@ def get_glibc_arena(addr=None):
return None


def get_glibc_arenas(addr=None, get_all=True):
arena = get_glibc_arena(addr)
return [arena for arena in iter(arena)] if get_all else [arena]


def titlify(text, color=None, msg_color=None):
"""Print a centered title."""
cols = get_terminal_size()[1]
Expand Down Expand Up @@ -6924,17 +6938,9 @@ class GlibcHeapArenaCommand(GenericCommand):

@only_if_gdb_running
def do_invoke(self, argv):
try:
arena = GlibcArena(__gef_current_arena__)
except gdb.error:
err("Could not find Glibc main arena")
return

while True:
gef_print("{}".format(arena))
arena = arena.get_next()
if arena is None:
break
arenas = get_glibc_arenas()
for arena in arenas:
gef_print(str(arena))
return


Expand Down Expand Up @@ -6974,24 +6980,22 @@ class GlibcHeapChunksCommand(GenericCommand):
the base address of a different arena can be passed"""

_cmdline_ = "heap chunks"
_syntax_ = "{0} [-h] [--allow-unaligned] [arena_address]".format(_cmdline_)
_syntax_ = "{0} [-h] [--all] [--allow-unaligned] [arena_address]".format(_cmdline_)
_example_ = "\n{0}\n{0} 0x555555775000".format(_cmdline_)

def __init__(self):
super().__init__(complete=gdb.COMPLETE_LOCATION)
self.add_setting("peek_nb_byte", 16, "Hexdump N first byte(s) inside the chunk data (0 to disable)")
return

@parse_arguments({"arena_address": ""}, {"--allow-unaligned": True})
@parse_arguments({"arena_address": ""}, {("--all", "-a"): True, "--allow-unaligned": True})
@only_if_gdb_running
def do_invoke(self, *args, **kwargs):
args = kwargs["arguments"]

arena = get_glibc_arena(addr=args.arena_address)
if arena is None:
err("No valid arena")
return
self.dump_chunks_arena(arena, allow_unaligned=args.allow_unaligned)
arenas = get_glibc_arenas(addr=args.arena_address, get_all=args.all)
for arena in arenas:
self.dump_chunks_arena(arena, print_arena=args.all, allow_unaligned=args.allow_unaligned)

def dump_chunks_arena(self, arena, print_arena=False, allow_unaligned=False):
top_chunk_addr = arena.top
Expand Down
4 changes: 2 additions & 2 deletions tests/runtests.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,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("Arena (base=", res)
self.assertIn("Arena(base=", res)
return

def test_cmd_heap_set_arena(self):
Expand All @@ -202,7 +202,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("Arena (base=", res)
self.assertIn("Arena(base=", res)
return

def test_cmd_heap_chunk(self):
Expand Down