Added heap operations (#140)

* Added support for using different malloc algorithms and implemented
ptmalloc commands

    - Added support for the mp_ structure from glibc
    - Fixed the bug when the base of [heap] is not sbrk_base (occurs
      when bss is very large).
    - Added an interface for interacting with ptmalloc heaps

* Added fastbins, unsortedbins, smallbins, largebins

    - Implemented the bin functions both in the ptmalloc interface in
      the heap/ directory and the commands function
    - Completed the bins command which prints all the commands
    - Added option to bin commands `verbose` specifies the users desire
      to print empty bins - linked lists with out any nodes from malloc
    - Changed bin representation to OrderedDict

* remove old bins command

* Ptmalloc bug fixes

    - switched constants.ptmalloc from using pwndbg.typeinfo to a gdb
      api function to prevent bugs when libc-dbg isn't installed
    - Other minor changes

* Added some functionality for when libc-dbg is not present

    - Added checks for finding symbols

* Ran isort on pwndbg

* Made few changes based on feedback from pull requests
    - Cleaned up spaces_table
    - Syntax changes

* Moved formatting code away from logic

    - Moved format_bin from pwndbg.heap.ptmalloc to pwndbg.commands.heap

* Fixed bug in spaces_table for 32 bit archs

* Changed malloc_chunk command to display FASTBIN instead of PREV_INUSE

    - Fixed local isort version problems

* Small bug fixes, tweaks, and added features list.

    - Changed pwndbg.chain.format to use the offset for the very last
      index in the chain.
    - Fixed bug with ptmalloc's main_arena property.
    - Added images to cap and links to FEATURES.md.
    - Changed bin commands to print hex instead of base 10.

* Modified docstring to match other docstrings in pwndbg

* Memoize properties, fix merge conflicst, fix constants

    - Moved the macros from the constants directory into the Heap
      class, left the constants there.
    - Switched from lazy loading to the memoize events.

* Fixed double spaces after images from a previous pull request.

* Added more examples to FEATURES.md for heaps

* Fix typos in FEATURES.md
pull/146/head
blendin 9 years ago committed by Zach Riggle
parent 0d3952f1ae
commit d6a03b3c98

@ -6,18 +6,18 @@ Pwndbg has a great deal of useful features. You can a list of all available com
All function call sites are annotated with the arguments to those functions. This works best with debugging symbols, but also works in the most common case where an imported function (e.g. libc function via GOT or PLT) is used.
![](caps/arguments_getenv.png)
![](caps/arguments_memcpy.png)
![](caps/arguments_sigsetjmp.png)
![](caps/arguments_strcpy.png)
![](caps/arguments_syscall.png)
![](caps/arguments_xtrace_init.png)
![](caps/arguments_getenv.png)
![](caps/arguments_memcpy.png)
![](caps/arguments_sigsetjmp.png)
![](caps/arguments_strcpy.png)
![](caps/arguments_syscall.png)
![](caps/arguments_xtrace_init.png)
## Context
A useful summary of the current execution context is printed every time GDB stops (e.g. breakpoint or single-step), displaying all registers, the stack, call frames, disassembly, and additionally recursively dereferencing all pointers. All memory addresses are color-coded to the type of memory they represent.
![](caps/context.png)
![](caps/context.png)
## Disassembly
@ -29,9 +29,9 @@ All absolute jumps are folded away, only displaying relevant instructions.
Additionally, if the current instruction is conditional, Pwndbg displays whether or not it is evaluated with a green check or a red X, and folds away instructions as necessary.
![](caps/disasm_taken_after.png)
![](caps/disasm_taken_before.png)
![](caps/disasn_taken_false.png)
![](caps/disasm_taken_after.png)
![](caps/disasm_taken_before.png)
![](caps/disasn_taken_false.png)
## Emulation
@ -39,20 +39,25 @@ Pwndbg leverages Unicorn Engine in order to only show instructions which will ac
This is incredibly useful when stepping through jump tables, PLT entries, and even while ROPping!
![](caps/emulate_vs_disasm.png)
![](caps/emulation_plt.png)
![](caps/emulation_rop.png)
![](caps/emulate_vs_disasm.png)
![](caps/emulation_plt.png)
![](caps/emulation_rop.png)
## Heap Inspection
Pwndbg enables introspection of the glibc allocator, ptmalloc2, via a handful of introspection functions.
![](caps/heap_arena.png)
![](caps/heap_bins.png)
![](caps/heap_heap.png)
![](caps/heap_heap2.png)
![](caps/heap_mallocchunk.png)
![](caps/heap_topchunk.png)
![](caps/heap_arena.png)
![](caps/heap_mp.png)
![](caps/heap_bins.png)
![](caps/heap_fastbins.png)
![](caps/heap_unsorted.png)
![](caps/heap_smallbins.png)
![](caps/heap_largebins.png)
![](caps/heap_heap.png)
![](caps/heap_heap2.png)
![](caps/heap_mallocchunk.png)
![](caps/heap_topchunk.png)
## IDA Pro Integration
@ -60,9 +65,9 @@ Pwndbg flips traditional IDA Pro integration on its head. Rather than sticking
This allows extraction of comments, decompiled lines of source, breakpoints, and synchronized debugging (single-steps update the cursor in IDA).
![](caps/ida_comments.png)
![](caps/ida_function.png)
![](caps/ida_integration.png)
![](caps/ida_comments.png)
![](caps/ida_function.png)
![](caps/ida_integration.png)
Since the complete IDA API is exposed, new tools can be built on this functionality to further enhance Pwndbg's usefulness.
@ -75,10 +80,10 @@ You can also connect to Ida Pro XMLRPC server hosted on different machine. In or
There are two commands to set various options:
* `theme` - to set particular output color/style
![](caps/theme.png)
![](caps/theme.png)
* `config` - to set parameters like whether to emulate code near current instruction, ida rpc connection info, hexdump bytes/width (and more)
![](caps/config.png)
![](caps/config.png)
Of course you can generate and put it in `.gdbinit` after pwndbg initialization to keep it persistent between pwngdb sessions.
@ -92,27 +97,27 @@ Vanilla GDB, PEDA, and GEF all fail terribly in this scenario.
#### GEF
![](caps/qemu_gef.png)
![](caps/qemu_gef.png)
#### PEDA
![](caps/qemu_peda.png)
![](caps/qemu_peda.png)
#### Vanilla GDB
![](caps/qemu_vanilla.png)
![](caps/qemu_vanilla.png)
#### Pwndbg
However, Pwndbg works around the limitations of the GDB stub to give you the best debugger environment possible.
![](caps/qemu_pwndbg.png)
![](caps/qemu_pwndbg.png)
## Process State Inspection
Use the `procinfo` command in order to inspect the current process state, like UID, GID, Groups, SELinux context, and open file descriptors! Pwndbg works particularly well with remote GDB debugging like with Android phones, which PEDA, GEF, and vanilla GDB choke on.
![](caps/procinfo.png)
![](caps/procinfo.png)
## ROP Gadgets
@ -120,13 +125,13 @@ Pwndbg makes using ROPGadget easy with the actual addresses in the process.
Just use the `rop` command!
![](caps/rop_grep.png)
![](caps/rop_grep.png)
## Search
Pwndbg makes searching the target memory space easy, with a complete and easy-to-use interface. Whether you're searching for bytes, strings, or various sizes of integer values or pointers, it's a simple command away.
![](caps/search.png)
![](caps/search.png)
## Telescope
@ -136,14 +141,14 @@ Inspecting memory dumps is easy with the `telescope` command. It recursively de
Pwndbg enhances the standard memory map listing, and allows easy searching.
![](caps/vmmap.png)
![](caps/vmmap2.png)
![](caps/vmmap_pc.png)
![](caps/vmmap_register.png)
![](caps/vmmap_stack.png)
![](caps/vmmap.png)
![](caps/vmmap2.png)
![](caps/vmmap_pc.png)
![](caps/vmmap_register.png)
![](caps/vmmap_stack.png)
## Windbg Compatibility
Pwndbg has a complete windbg compatibility layer. You can `dd`, `dps`, `eq`, and even `eb eip 90` to your heart's content.
![](caps/windbg.png)
![](caps/windbg.png)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

@ -55,6 +55,7 @@ import pwndbg.disasm.sparc
import pwndbg.disasm.x86
import pwndbg.dt
import pwndbg.elf
import pwndbg.heap
import pwndbg.inthook
import pwndbg.memory
import pwndbg.net
@ -85,6 +86,7 @@ __all__ = [
'events',
'file',
'function',
'heap',
'hexdump',
'ida',
'info',

@ -18,19 +18,31 @@ import pwndbg.vmmap
LIMIT = 5
def get(address, limit=LIMIT, offset=0):
def get(address, limit=LIMIT, offset=0, hard_stop=None, hard_end=0):
"""
Recursively dereferences an address.
Arguments:
address(int): the first address to begin dereferencing
limit(int): number of valid pointers
offset(int): offset into the address to get the next pointer
hard_stop(int): address to stop at
hard_end: value to append when hard_stop is reached
Returns:
A list containing ``address``, followed by up to ``limit`` valid pointers.
A list representing pointers of each ```address``` and reference
"""
result = []
for i in range(limit):
# Don't follow cycles, except to stop at the second occurrence.
if result.count(address) >= 2:
break
if hard_stop is not None and address == hard_stop:
result.append(hard_end)
break
result.append(address)
try:
address = int(pwndbg.memory.poi(pwndbg.typeinfo.ppvoid, address + offset))
@ -44,8 +56,30 @@ config_arrow_left = theme.Parameter('chain-arrow-left', '◂—', 'left arrow o
config_arrow_right = theme.Parameter('chain-arrow-right', '—▸', 'right arrow of chain formatting')
config_contiguous = theme.Parameter('chain-contiguous-marker', '...', 'contiguous marker of chain formatting')
def format(value, limit=LIMIT, code=True, offset=0):
chain = get(value, limit, offset)
def format(value, limit=LIMIT, code=True, offset=0, hard_stop=None, hard_end=0):
"""
Recursively dereferences an address into string representation, or convert the list representation
of address dereferences into string representation.
Arguments:
value(int|list): Either the starting address to be sent to get, or the result of get (a list)
limit(int): Number of valid pointers
code(bool): Hint that indicates the value may be an instruction
offset(int): Offset into the address to get the next pointer
hard_stop(int): Value to stop on
hard_end: Value to append when hard_stop is reached: null, value of hard stop, a string.
Returns:
A string representing pointers of each address and reference
Strings format: 0x0804a10 0x08061000 0x41414141
"""
# Allow results from get function to be passed to format
if type(value) == list:
chain = value
else:
chain = get(value, limit, offset, hard_stop, hard_end)
arrow_left = C.arrow(' %s ' % config_arrow_left)
arrow_right = C.arrow(' %s ' % config_arrow_right)
@ -56,9 +90,10 @@ def format(value, limit=LIMIT, code=True, offset=0):
enhanced = pwndbg.enhance.enhance(chain[-1], code=code)
# Otherwise, the last element in the chain is the non-pointer value.
# We want to enhance the last pointer value.
# We want to enhance the last pointer value. If an offset was used
# chain failed at that offset, so display that offset.
elif len(chain) < limit:
enhanced = pwndbg.enhance.enhance(chain[-2], code=code)
enhanced = pwndbg.enhance.enhance(chain[-2] + offset, code=code)
else:
enhanced = C.contiguous('%s' % config_contiguous)

@ -1,6 +1,5 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#!/usr/bin/env python
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
@ -16,40 +15,32 @@ from pwndbg.color import red
from pwndbg.color import underline
from pwndbg.color import yellow
PREV_INUSE = 1
IS_MMAPED = 2
NON_MAIN_ARENA = 4
def value_from_type(type_name, addr):
gdb_type = pwndbg.typeinfo.load(type_name)
return gdb.Value(addr).cast(gdb_type.pointer()).dereference()
def get_main_arena(addr=None):
if addr == None:
main_arena = gdb.lookup_symbol('main_arena')[0]
if main_arena is not None:
main_arena = main_arena.value()
else:
main_arena = value_from_type('struct malloc_state', addr)
def format_bin(bins, verbose=False):
main_heap = pwndbg.heap.current
fd_offset = main_heap.chunk_key_offset('fd')
if main_arena == None:
print(red('Symbol \'main_arena\' not found. Try installing libc ' \
'debugging symbols or specifying the main arena address ' \
'and try again'))
result = []
for size in bins:
chain = bins[size]
return main_arena
if not verbose and chain == [0]:
continue
def get_heap_bounds():
page = None
for m in pwndbg.vmmap.get():
if m.objfile == '[heap]':
page = m
break
formatted_chain = pwndbg.chain.format(chain, offset=fd_offset)
if m != None:
return (m.vaddr, m.vaddr + m.memsz)
else:
return (None, None)
if isinstance(size, int):
size = hex(size)
result.append((bold(size) + ': ').ljust(13) + formatted_chain)
if not result:
result.append(bold('empty'))
return result
@pwndbg.commands.ParsedCommand
@pwndbg.commands.OnlyWhenRunning
@ -57,11 +48,13 @@ def heap(addr=None):
"""
Prints out all chunks in the main_arena, or the arena specified by `addr`.
"""
main_arena = get_main_arena(addr)
main_heap = pwndbg.heap.current
main_arena = main_heap.get_arena(addr)
if main_arena == None:
return
heap_base = get_heap_bounds()[0]
heap_base = main_heap.get_bounds()[0]
if heap_base == None:
print(red('Could not find the heap'))
return
@ -92,7 +85,9 @@ def arena(addr=None):
"""
Prints out the main arena or the arena at the specified by address.
"""
main_arena = get_main_arena(addr)
main_heap = pwndbg.heap.current
main_arena = main_heap.get_arena(addr)
if main_arena == None:
return
@ -100,31 +95,13 @@ def arena(addr=None):
@pwndbg.commands.ParsedCommand
@pwndbg.commands.OnlyWhenRunning
def bins(addr=None):
def mp():
"""
Prints out the contents of the fastbins of the main arena or the arena
at the specified address.
Prints out the mp_ structure from glibc
"""
main_arena = get_main_arena(addr)
if main_arena == None:
return
main_heap = pwndbg.heap.current
fastbins = main_arena['fastbinsY']
bins = main_arena['bins']
size_t_size = pwndbg.typeinfo.load('size_t').sizeof
num_fastbins = 7
num_bins = int(bins.type.sizeof / bins.type.target().sizeof)
fd_field_offset = 2 * size_t_size
print(underline(yellow('fastbins')))
size = 2 * size_t_size
for i in range(num_fastbins):
size += 2 * size_t_size
chain = pwndbg.chain.format(int(fastbins[i]), offset=fd_field_offset)
print((bold(size) + ': ').ljust(13) + chain)
# TODO: Print other bins
print(main_heap.mp)
@pwndbg.commands.ParsedCommand
@pwndbg.commands.OnlyWhenRunning
@ -133,9 +110,11 @@ def top_chunk(addr=None):
Prints out the address of the top chunk of the main arena, or of the arena
at the specified address.
"""
main_arena = get_main_arena(addr)
main_heap = pwndbg.heap.current
main_arena = main_heap.get_arena(addr)
if main_arena == None:
heap_start, heap_end = get_heap_bounds()
heap_start, heap_end = main_heap.get_bounds()
if heap_start == None:
print(red('Could not find the heap'))
return
@ -166,23 +145,116 @@ def malloc_chunk(addr):
"""
Prints out the malloc_chunk at the specified address.
"""
main_heap = pwndbg.heap.current
if not isinstance(addr, six.integer_types):
addr = int(addr)
chunk = value_from_type('struct malloc_chunk', addr)
size = int(chunk['size'])
prev_inuse = (size & PREV_INUSE) == 1
is_mmaped = (size & IS_MMAPED) == 1
non_main_arena = (size & NON_MAIN_ARENA) == 1
actual_size = size & ~7
fastbins = main_heap.fastbins()
prev_inuse, is_mmapped, non_main_arena = main_heap.chunk_flags(size)
header = M.get(addr)
if prev_inuse:
header += yellow(' PREV_INUSE')
if is_mmaped:
if actual_size in fastbins:
header += yellow(' FASTBIN')
else:
header += yellow(' PREV_INUSE')
if is_mmapped:
header += yellow(' IS_MMAPED')
if non_main_arena:
header += yellow(' NON_MAIN_ARENA')
print(header)
print(chunk)
print(header, chunk)
return chunk
@pwndbg.commands.ParsedCommand
@pwndbg.commands.OnlyWhenRunning
def bins(addr=None):
"""
Prints out the contents of the fastbins, unsortedbin, smallbins, and largebins from the
main_arena or the specified address.
"""
fastbins(addr)
unsortedbin(addr)
smallbins(addr)
largebins(addr)
@pwndbg.commands.ParsedCommand
@pwndbg.commands.OnlyWhenRunning
def fastbins(addr=None, verbose=True):
"""
Prints out the contents of the fastbins of the main arena or the arena
at the specified address.
"""
main_heap = pwndbg.heap.current
fastbins = main_heap.fastbins(addr)
if fastbins == None:
return
formatted_bins = format_bin(fastbins, verbose)
print(underline(yellow('fastbins')))
for node in formatted_bins:
print(node)
@pwndbg.commands.ParsedCommand
@pwndbg.commands.OnlyWhenRunning
def unsortedbin(addr=None, verbose=True):
"""
Prints out the contents of the unsorted bin of the main arena or the
arena at the specified address.
"""
main_heap = pwndbg.heap.current
unsortedbin = main_heap.unsortedbin(addr)
if unsortedbin == None:
return
formatted_bins = format_bin(unsortedbin, verbose)
print(underline(yellow('unsortedbin')))
for node in formatted_bins:
print(node)
@pwndbg.commands.ParsedCommand
@pwndbg.commands.OnlyWhenRunning
def smallbins(addr=None, verbose=False):
"""
Prints out the contents of the small bin of the main arena or the arena
at the specified address.
"""
main_heap = pwndbg.heap.current
smallbins = main_heap.smallbins(addr)
if smallbins == None:
return
formatted_bins = format_bin(smallbins, verbose)
print(underline(yellow('smallbins')))
for node in formatted_bins:
print(node)
@pwndbg.commands.ParsedCommand
@pwndbg.commands.OnlyWhenRunning
def largebins(addr=None, verbose=False):
"""
Prints out the contents of the large bin of the main arena or the arena
at the specified address.
"""
main_heap = pwndbg.heap.current
largebins = main_heap.largebins(addr)
if largebins == None:
return
formatted_bins = format_bin(largebins, verbose)
print(underline(yellow('largebins')))
for node in formatted_bins:
print(node)

@ -0,0 +1,21 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import platform
import gdb
import pwndbg.arch
# Heap flags
PREV_INUSE = 1
IS_MMAPPED = 2
NON_MAIN_ARENA = 4
SIZE_BITS = ( PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA )
NBINS = 128
NSMALLBINS = 64

@ -5,19 +5,23 @@ from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import pwndbg.heap.dlmalloc
import pwndbg.heap.heap
import pwndbg.heap.ptmalloc
import pwndbg.symbol
current = pwndbg.heap.heap.Heap()
current = None
@pwndbg.events.new_objfile
def update():
import pwndbg.heap.dlmalloc
import pwndbg.heap.ptmalloc
global current
if pwndbg.symbol.get('ptmalloc_init'):
current = pwndbg.heap.ptmalloc.Heap()
if pwndbg.symbol.address('ptmalloc_init'):
current = pwndbg.heap.ptmalloc.Heap()
elif pwndbg.symbol.get('malloc_stats'):
current = pwndbg.heap.dlmalloc.Heap()
else:
# Default to ptmalloc heap for now until
# there are more implementations
current = pwndbg.heap.ptmalloc.Heap()

@ -10,12 +10,5 @@ import gdb
import pwndbg.events
import pwndbg.typeinfo
malloc_chunk = None
malloc_state = None
mallinfo = None
@pwndbg.events.new_objfile
def update():
malloc_chunk = gdb.lookup_type('struct malloc_chunk')
malloc_state = gdb.lookup_type('struct malloc_state')
mallinfo = gdb.lookup_type('struct mallinfo')
class Heap(pwndbg.heap.heap.BaseHeap):
pass

@ -9,7 +9,7 @@ import pwndbg.events
import pwndbg.symbol
class Heap(object):
class BaseHeap(object):
"""Heap abstraction layer."""
def breakpoint(event):

@ -5,17 +5,248 @@ from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from collections import OrderedDict
import gdb
import pwndbg.events
import pwndbg.typeinfo
from pwndbg.color import bold
from pwndbg.color import red
from pwndbg.constants import ptmalloc
class Heap(pwndbg.heap.heap.BaseHeap):
def __init__(self):
# Global ptmalloc objects
self._main_arena = None
self._mp = None
@property
def main_arena(self):
main_arena_addr = pwndbg.symbol.address('main_arena')
if main_arena_addr is not None:
self._main_arena = pwndbg.memory.poi(self.malloc_state, main_arena_addr)
else:
print(bold(red('Symbol \'main arena\' not found. Try installing libc '
'debugging symbols and try again.')))
return self._main_arena
@property
def mp(self):
mp_addr = pwndbg.symbol.address('mp_')
if mp_addr is not None:
self._mp = pwndbg.memory.poi(self.malloc_par, mp_addr)
return self._mp
@property
@pwndbg.memoize.reset_on_objfile
def malloc_chunk(self):
return pwndbg.typeinfo.load('struct malloc_chunk')
@property
@pwndbg.memoize.reset_on_objfile
def malloc_state(self):
return pwndbg.typeinfo.load('struct malloc_state')
@property
@pwndbg.memoize.reset_on_objfile
def mallinfo(self):
return pwndbg.typeinfo.load('struct mallinfo')
@property
@pwndbg.memoize.reset_on_objfile
def malloc_par(self):
return pwndbg.typeinfo.load('struct malloc_par')
@property
@pwndbg.memoize.reset_on_objfile
def malloc_alignment(self):
return pwndbg.arch.ptrsize * 2
@property
@pwndbg.memoize.reset_on_objfile
def min_chunk_size(self):
return pwndbg.arch.ptrsize * 4
def _spaces_table(self):
spaces_table = [ pwndbg.arch.ptrsize * 2 ] * 64 \
+ [ pow(2, 6) ] * 32 \
+ [ pow(2, 9) ] * 16 \
+ [ pow(2, 12) ] * 8 \
+ [ pow(2, 15) ] * 4 \
+ [ pow(2, 18) ] * 2 \
+ [ pow(2, 21) ] * 1
# There is no index 0
spaces_table = [ None ] + spaces_table
# Fix up the slop in bin spacing (part of libc - they made
# the trade off of some slop for speed)
# https://bazaar.launchpad.net/~ubuntu-branches/ubuntu/trusty/eglibc/trusty-security/view/head:/malloc/malloc.c#L1356
if pwndbg.arch.ptrsize == 8:
spaces_table[97] = 64
spaces_table[98] = 448
spaces_table[113] = 1536
spaces_table[121] = 24576
spaces_table[125] = 98304
return spaces_table
def chunk_flags(self, size):
return ( size & ptmalloc.PREV_INUSE ,
size & ptmalloc.IS_MMAPPED,
size & ptmalloc.NON_MAIN_ARENA )
def chunk_key_offset(self, key):
chunk_keys = self.malloc_chunk.keys()
try:
return chunk_keys.index(key) * pwndbg.arch.ptrsize
except:
return None
def get_arena(self, arena_addr=None):
if arena_addr is None:
return self.main_arena
return pwndbg.memory.poi(self.malloc_state, arena_addr)
def get_bounds(self):
"""
Finds the heap bounds by using mp_ structure's sbrk_base property
and falls back to using /proc/self/maps (vmmap) which can be wrong
when .bss is very large
"""
lower, upper = None, None
try:
lower = int(self.mp['sbrk_base'])
except:
lower = None
page = None
for m in pwndbg.vmmap.get():
if m.objfile == '[heap]':
page = m
break
if page is not None:
lower = lower or page.vaddr
return (lower, page.vaddr + page.memsz)
return (None, None)
def fastbins(self, arena_addr=None):
arena = self.get_arena(arena_addr)
if arena is None:
return
fastbinsY = arena['fastbinsY']
fd_offset = self.chunk_key_offset('fd')
num_fastbins = 7
size = pwndbg.arch.ptrsize * 2
result = OrderedDict()
for i in range(num_fastbins):
size += pwndbg.arch.ptrsize * 2
chain = pwndbg.chain.get(int(fastbinsY[i]), offset=fd_offset)
result[size] = chain
return result
def bin_at(self, index, arena_addr=None):
"""
Modeled after glibc's bin_at function - so starts indexing from 1
https://bazaar.launchpad.net/~ubuntu-branches/ubuntu/trusty/eglibc/trusty-security/view/head:/malloc/malloc.c#L1394
bin_at(1) returns the unsorted bin
Bin 1 - Unsorted BiN
Bin 2 to 63 - Smallbins
Bin 64 to 126 - Largebins
"""
index = index - 1
arena = self.get_arena(arena_addr)
if arena is None:
return
normal_bins = arena['bins']
num_bins = normal_bins.type.sizeof // normal_bins.type.target().sizeof
bins_base = int(normal_bins.address) - (pwndbg.arch.ptrsize* 2)
current_base = bins_base + (index * pwndbg.arch.ptrsize * 2)
front, back = normal_bins[index * 2], normal_bins[index * 2 + 1]
fd_offset = self.chunk_key_offset('fd')
chain = pwndbg.chain.get(int(front), offset=fd_offset, hard_stop=current_base)
return chain
def unsortedbin(self, arena_addr=None):
chain = self.bin_at(1, arena_addr=arena_addr)
result = OrderedDict()
if chain is None:
return
result['all'] = chain
return result
def smallbins(self, arena_addr=None):
size = self.min_chunk_size - self.malloc_alignment
spaces_table = self._spaces_table()
result = OrderedDict()
for index in range(2, 64):
size += spaces_table[index]
chain = self.bin_at(index, arena_addr=arena_addr)
if chain is None:
return
result[size] = chain
return result
def largebins(self, arena_addr=None):
size = (ptmalloc.NSMALLBINS * self.malloc_alignment) - self.malloc_alignment
spaces_table = self._spaces_table()
result = OrderedDict()
for index in range(64, 127):
size += spaces_table[index]
chain = self.bin_at(index, arena_addr=arena_addr)
if chain is None:
return
malloc_chunk = None
malloc_state = None
mallinfo = None
result[size] = chain
@pwndbg.events.new_objfile
def update():
malloc_chunk = gdb.lookup_type('struct malloc_chunk')
malloc_state = gdb.lookup_type('struct malloc_state')
mallinfo = gdb.lookup_type('struct mallinfo')
return result

Loading…
Cancel
Save