(addr=None, count=None, naive=None)
Visualize chunks on a heap, default to the current arena's active heap.
Visualize chunks on a heap, default to the current arena's active heap.
Visualize chunks on a heap default to the current arena s active heap .
def vis_heap_chunks(addr=None, count=None, naive=None):
    """Visualize chunks on a heap, default to the current arena's active heap."""
    allocator = pwndbg.heap.current
    heap_region = allocator.get_heap_boundaries(addr)
    arena = allocator.get_arena_for_chunk(addr) if addr else allocator.get_arena()

    top_chunk = arena['top']
    ptr_size = allocator.size_sz

    # Build a list of addresses that delimit each chunk.
    chunk_delims = []
    if addr:
        cursor = int(addr)
    elif arena == allocator.main_arena:
        cursor = heap_region.start
    else:
        cursor = heap_region.start + allocator.heap_info.sizeof
        if pwndbg.vmmap.find(allocator.get_heap(heap_region.start)['ar_ptr']) == heap_region:
            # Round up to a 2-machine-word alignment after an arena to
            # compensate for the presence of the have_fastchunks variable
            # in GLIBC versions >= 2.27.
            cursor += (allocator.malloc_state.sizeof + ptr_size) & ~allocator.malloc_align_mask

    # Check if there is an alignment at the start of the heap, adjust if necessary.
    if not addr:
        first_chunk_size = pwndbg.arch.unpack(pwndbg.memory.read(cursor + ptr_size, ptr_size))
        if first_chunk_size == 0:
            cursor += ptr_size * 2

    cursor_backup = cursor

    for _ in range(count + 1):
        # Don't read beyond the heap mapping if --naive or corrupted heap.
        if cursor not in heap_region:
            chunk_delims.append(heap_region.end)
            break

        size_field = pwndbg.memory.u(cursor + ptr_size)
        real_size = size_field & ~allocator.malloc_align_mask
        prev_inuse = allocator.chunk_flags(size_field)[0]

        # Don't repeatedly operate on the same address (e.g. chunk size of 0).
        if cursor in chunk_delims or cursor + ptr_size in chunk_delims:
            break

        if prev_inuse:
            chunk_delims.append(cursor + ptr_size)
        else:
            chunk_delims.append(cursor)

        if (cursor == top_chunk and not naive) or (cursor == heap_region.end - ptr_size*2):
            chunk_delims.append(cursor + ptr_size*2)
            break

        cursor += real_size

    # Build the output buffer, changing color at each chunk delimiter.
    # TODO: maybe print free chunks in bold or underlined
    color_funcs = [
        generateColorFunction("yellow"),
        generateColorFunction("cyan"),
        generateColorFunction("purple"),
        generateColorFunction("green"),
        generateColorFunction("blue"),
    ]

    bin_collections = [
        allocator.fastbins(arena.address),
        allocator.unsortedbin(arena.address),
        allocator.smallbins(arena.address),
        allocator.largebins(arena.address),
        ]
    if allocator.has_tcache():
        # Only check for tcache entries belonging to the current thread,
        # it's difficult (impossible?) to find all the thread caches for a
        # specific heap.
        bin_collections.insert(0, allocator.tcachebins(None))

    printed = 0
    out = ''
    asc = ''
    labels = []

    cursor = cursor_backup

    for c, stop in enumerate(chunk_delims):
        color_func = color_funcs[c % len(color_funcs)]

        while cursor != stop:
            if printed % 2 == 0:
                out += "\n0x%x" % cursor

            cell = pwndbg.arch.unpack(pwndbg.memory.read(cursor, ptr_size))
            cell_hex = '\t0x{:0{n}x}'.format(cell, n=ptr_size*2)

            out += color_func(cell_hex)
            printed += 1

            labels.extend(bin_labels(cursor, bin_collections))
            if cursor == top_chunk:
                labels.append('Top chunk')

            asc += bin_ascii(pwndbg.memory.read(cursor, ptr_size))
            if printed % 2 == 0:
                out += '\t' + color_func(asc) + ('\t <-- ' + ', '.join(labels) if len(labels) else '')
                asc = ''
                labels = []

            cursor += ptr_size

    print(out)
def vis_heap_chunks ( addr = None , count = None , naive = None ) : allocator = pwndbg . heap . current heap_region = allocator . get_heap_boundaries ( addr ) arena = allocator . get_arena_for_chunk ( addr ) if addr else allocator . get_arena ( ) top_chunk = arena [ 'top' ] ptr_size = allocator . size_sz # Build a list of addresses that delimit each chunk. chunk_delims = [ ] if addr : cursor = int ( addr ) elif arena == allocator . main_arena : cursor = heap_region . start else : cursor = heap_region . start + allocator . heap_info . sizeof if pwndbg . vmmap . find ( allocator . get_heap ( heap_region . start ) [ 'ar_ptr' ] ) == heap_region : # Round up to a 2-machine-word alignment after an arena to # compensate for the presence of the have_fastchunks variable # in GLIBC versions >= 2.27. cursor += ( allocator . malloc_state . sizeof + ptr_size ) & ~ allocator . malloc_align_mask # Check if there is an alignment at the start of the heap, adjust if necessary. if not addr : first_chunk_size = pwndbg . arch . unpack ( pwndbg . memory . read ( cursor + ptr_size , ptr_size ) ) if first_chunk_size == 0 : cursor += ptr_size * 2 cursor_backup = cursor for _ in range ( count + 1 ) : # Don't read beyond the heap mapping if --naive or corrupted heap. if cursor not in heap_region : chunk_delims . append ( heap_region . end ) break size_field = pwndbg . memory . u ( cursor + ptr_size ) real_size = size_field & ~ allocator . malloc_align_mask prev_inuse = allocator . chunk_flags ( size_field ) [ 0 ] # Don't repeatedly operate on the same address (e.g. chunk size of 0). if cursor in chunk_delims or cursor + ptr_size in chunk_delims : break if prev_inuse : chunk_delims . append ( cursor + ptr_size ) else : chunk_delims . append ( cursor ) if ( cursor == top_chunk and not naive ) or ( cursor == heap_region . end - ptr_size * 2 ) : chunk_delims . append ( cursor + ptr_size * 2 ) break cursor += real_size # Build the output buffer, changing color at each chunk delimiter. # TODO: maybe print free chunks in bold or underlined color_funcs = [ generateColorFunction ( "yellow" ) , generateColorFunction ( "cyan" ) , generateColorFunction ( "purple" ) , generateColorFunction ( "green" ) , generateColorFunction ( "blue" ) , ] bin_collections = [ allocator . fastbins ( arena . address ) , allocator . unsortedbin ( arena . address ) , allocator . smallbins ( arena . address ) , allocator . largebins ( arena . address ) , ] if allocator . has_tcache ( ) : # Only check for tcache entries belonging to the current thread, # it's difficult (impossible?) to find all the thread caches for a # specific heap. bin_collections . insert ( 0 , allocator . tcachebins ( None ) ) printed = 0 out = '' asc = '' labels = [ ] cursor = cursor_backup for c , stop in enumerate ( chunk_delims ) : color_func = color_funcs [ c % len ( color_funcs ) ] while cursor != stop : if printed % 2 == 0 : out += "\n0x%x" % cursor cell = pwndbg . arch . unpack ( pwndbg . memory . read ( cursor , ptr_size ) ) cell_hex = '\t0x{:0{n}x}' . format ( cell , n = ptr_size * 2 ) out += color_func ( cell_hex ) printed += 1 labels . extend ( bin_labels ( cursor , bin_collections ) ) if cursor == top_chunk : labels . append ( 'Top chunk' ) asc += bin_ascii ( pwndbg . memory . read ( cursor , ptr_size ) ) if printed % 2 == 0 : out += '\t' + color_func ( asc ) + ( '\t <-- ' + ', ' . join ( labels ) if len ( labels ) else '' ) asc = '' labels = [ ] cursor += ptr_size print ( out )