Skip to content

Ptmalloc

BINMAPSIZE = 4 module-attribute

HEAP_MAX_SIZE: int = None module-attribute

IS_MMAPPED = 2 module-attribute

NBINS = 128 module-attribute

NFASTBINS = 10 module-attribute

NONCONTIGUOUS_BIT = 2 module-attribute

NON_MAIN_ARENA = 4 module-attribute

NSMALLBINS = 64 module-attribute

PREV_INUSE = 1 module-attribute

SIZE_BITS = PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA module-attribute

TCACHE_MAX_BINS = 64 module-attribute

TheType = TypeVar('TheType', pwndbg.dbg_mod.Type, typing.Type[pwndbg.aglib.heap.structs.CStruct2GDB]) module-attribute

TheValue = TypeVar('TheValue', pwndbg.dbg_mod.Value, pwndbg.aglib.heap.structs.CStruct2GDB) module-attribute

Arena

__slots__ = ('_gdbValue', 'address', '_is_main_arena', '_top', '_active_heap', '_heaps', '_mutex', '_flags', '_non_contiguous', '_have_fastchunks', '_fastbinsY', '_bins', '_binmap', '_next', '_next_free', '_system_mem') class-attribute instance-attribute

active_heap: Heap property

address = int(self._gdbValue.address) instance-attribute

binmap: List[int] property

bins: List[int] property

fastbinsY: List[int] property

flags: int | None property

have_fastchunks: int | None property

heaps property

is_main_arena: bool property

mutex: int | None property

next: int | None property

next_free: int | None property

non_contiguous: bool | None property

system_mem: int | None property

top: int | None property

__init__(addr)

__str__()

fastbins()

Bin

bk_chain = bk_chain instance-attribute

count = count instance-attribute

fd_chain = fd_chain instance-attribute

is_corrupted = is_corrupted instance-attribute

__init__(fd_chain, bk_chain=None, count=None, is_corrupted=False)

contains_chunk(chunk)

size_to_display_name(size) staticmethod

BinType

Bases: str, Enum

FAST = 'fastbins' class-attribute instance-attribute

LARGE = 'largebins' class-attribute instance-attribute

NOT_IN_BIN = 'not_in_bin' class-attribute instance-attribute

SMALL = 'smallbins' class-attribute instance-attribute

TCACHE = 'tcachebins' class-attribute instance-attribute

UNSORTED = 'unsortedbin' class-attribute instance-attribute

valid_fields()

Bins

bin_type = bin_type instance-attribute

bins: OrderedDictType[int | str, Bin] = OrderedDict() instance-attribute

__init__(bin_type)

contains_chunk(size, chunk)

Chunk

__slots__ = ('_gdbValue', 'address', '_prev_size', '_size', '_real_size', '_flags', '_non_main_arena', '_is_mmapped', '_prev_inuse', '_fd', '_bk', '_fd_nextsize', '_bk_nextsize', '_heap', '_arena', '_is_top_chunk') class-attribute instance-attribute

address = int(self._gdbValue.address) instance-attribute

arena: Arena | None property

bk property

bk_nextsize property

fd property

fd_nextsize property

flags: Dict[str, bool] | None property

heap: Heap property

is_mmapped: bool | None property

is_top_chunk property

non_main_arena: bool | None property

prev_inuse: bool | None property

prev_size: int | None property

real_size: int | None property

size: int | None property

__contains__(addr)

This allow us to avoid extra constructions like 'if start_addr <= ptr < end_addr', etc.

__init__(addr, heap=None, arena=None)

__match_renamed_field(field)

next_chunk()

ChunkField

Bases: int, Enum

BK = 4 class-attribute instance-attribute

BK_NEXTSIZE = 6 class-attribute instance-attribute

FD = 3 class-attribute instance-attribute

FD_NEXTSIZE = 5 class-attribute instance-attribute

PREV_SIZE = 1 class-attribute instance-attribute

SIZE = 2 class-attribute instance-attribute

DebugSymsHeap

Bases: GlibcMemoryAllocator[Type, Value]

can_be_resolved = GlibcMemoryAllocator.libc_has_debug_syms class-attribute instance-attribute

global_max_fast: int | None property

heap_info: pwndbg.dbg_mod.Type | None property

main_arena: Arena | None property

mallinfo: pwndbg.dbg_mod.Type | None property

malloc_chunk: pwndbg.dbg_mod.Type | None property

malloc_par: pwndbg.dbg_mod.Type | None property

malloc_state: pwndbg.dbg_mod.Type | None property

mp: pwndbg.dbg_mod.Value | None property

tcache_entry: pwndbg.dbg_mod.Type | None property

tcache_perthread_struct: pwndbg.dbg_mod.Type | None property

thread_arena: Arena | None property

thread_cache: pwndbg.dbg_mod.Value | None property

Locate a thread's tcache struct. If it doesn't have one, use the main thread's tcache.

get_heap(addr)

Find & read the heap_info struct belonging to the chunk at 'addr'.

get_sbrk_heap_region()

Return a Page object representing the sbrk heap region. Ensure the region's start address is aligned to SIZE_SZ * 2, which compensates for the presence of GLIBC_TUNABLES.

get_tcache(tcache_addr=None)

has_tcache()

is_initialized()

GlibcMemoryAllocator

Bases: MemoryAllocator, Generic[TheType, TheValue]

arenas: Tuple[Arena, ...] property

Return a tuple of all current arenas.

global_max_fast: int | None property

heap_info: TheType | None property

largebin_reverse_lookup_32 = (512, 576, 640, 704, 768, 832, 896, 960, 1024, 1088, 1152, 1216, 1280, 1344, 1408, 1472, 1536, 1600, 1664, 1728, 1792, 1856, 1920, 1984, 2048, 2112, 2176, 2240, 2304, 2368, 2432, 2496, 2560, 3072, 3584, 4096, 4608, 5120, 5632, 6144, 6656, 7168, 7680, 8192, 8704, 9216, 9728, 10240, 10752, 12288, 16384, 20480, 24576, 28672, 32768, 36864, 40960, 65536, 98304, 131072, 163840, 262144, 524288) class-attribute instance-attribute

largebin_reverse_lookup_32_big = (1008, 1024, 1088, 1152, 1216, 1280, 1344, 1408, 1472, 1536, 1600, 1664, 1728, 1792, 1856, 1920, 1984, 2048, 2112, 2176, 2240, 2304, 2368, 2432, 2496, 2560, 2624, 2688, 2752, 2816, 2880, 2944, 2944, 3072, 3584, 4096, 4608, 5120, 5632, 6144, 6656, 7168, 7680, 8192, 8704, 9216, 9728, 10240, 10752, 12288, 16384, 20480, 24576, 28672, 32768, 36864, 40960, 65536, 98304, 131072, 163840, 262144, 524288) class-attribute instance-attribute

largebin_reverse_lookup_64 = (1024, 1088, 1152, 1216, 1280, 1344, 1408, 1472, 1536, 1600, 1664, 1728, 1792, 1856, 1920, 1984, 2048, 2112, 2176, 2240, 2304, 2368, 2432, 2496, 2560, 2624, 2688, 2752, 2816, 2880, 2944, 3008, 3072, 3136, 3584, 4096, 4608, 5120, 5632, 6144, 6656, 7168, 7680, 8192, 8704, 9216, 9728, 10240, 10752, 12288, 16384, 20480, 24576, 28672, 32768, 36864, 40960, 65536, 98304, 131072, 163840, 262144, 524288) class-attribute instance-attribute

main_arena: Arena | None property

mallinfo: TheType | None property

malloc_align_mask: int property

Corresponds to MALLOC_ALIGN_MASK in glibc malloc.c

malloc_alignment: int property

Corresponds to MALLOC_ALIGNMENT in glibc malloc.c

malloc_chunk: TheType | None property

malloc_par: TheType | None property

malloc_state: TheType | None property

min_chunk_size: int property

Corresponds to MIN_CHUNK_SIZE in glibc malloc.c

minsize: int property

Corresponds to MINSIZE in glibc malloc.c

mp: TheValue | None property

multithreaded: bool property

Is malloc operating within a multithreaded environment.

size_sz: int property

Corresponds to SIZE_SZ in glibc malloc.c

tcache_entry: TheType | None property

tcache_next_offset: int property

tcache_perthread_struct: TheType | None property

thread_arena: Arena | None property

thread_cache: TheValue | None property

__init__()

bin_at(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

Returns: tuple(chain_from_bin_fd, chain_from_bin_bk, is_chain_corrupted) or None

can_be_resolved()

check_chain_corrupted(chain_fd, chain_bk)

Checks if the doubly linked list (of a {unsorted, small, large} bin) defined by chain_fd, chain_bk is corrupted.

Even if the chains do not cover the whole bin, they still are expected to be of the same length.

Returns True if the bin is certainly corrupted, otherwise False.

chunk_flags(size)

chunk_key_offset(key)

Find the index of a field in the malloc_chunk struct.

64bit example

prev_size == 0 size == 8 fd == 16 bk == 24 ...

fastbin_index(size)

fastbins(arena_addr=None)

Returns: chain or None

get_bins(bin_type, addr=None)

get_heap(addr)

get_region(addr)

Find the memory map containing 'addr'.

get_sbrk_heap_region()

get_tcache(tcache_addr=None)

has_tcache()

is_initialized()

is_statically_linked()

largebin_index(sz)

Pick the appropriate largebin_index_ function for this architecture.

largebin_reverse_lookup(index)

Pick the appropriate largebin_reverse_lookup_ function for this architecture.

largebin_size_range_from_index(index)

largebins(arena_addr=None)

libc_has_debug_syms()

The struct malloc_chunk comes from debugging symbols and it will not be there for statically linked binaries

smallbins(arena_addr=None)

tcachebins(tcache_addr=None)

Returns: tuple(chain, count) or None

unsortedbin(arena_addr=None)

Heap

__slots__ = ('_gdbValue', 'arena', '_memory_region', 'start', 'end', '_prev', 'first_chunk') class-attribute instance-attribute

arena = main_arena if arena is None else arena instance-attribute

end: int = self._memory_region.end instance-attribute

first_chunk = Chunk(self.start) instance-attribute

prev property

start: int = self._memory_region.start instance-attribute

__contains__(addr)

__init__(addr, arena=None)

Build a Heap object given an address on that heap. Heap regions are treated differently depending on their arena: 1) main_arena - uses the sbrk heap 2) non-main arena - heap starts after its heap_info struct (and possibly an arena) 3) non-contiguous main_arena - just a memory region 4) no arena - for fake/mmapped chunks

__iter__()

__str__()

HeuristicHeap

Bases: GlibcMemoryAllocator[Type['pwndbg.aglib.heap.structs.CStruct2GDB'], 'pwndbg.aglib.heap.structs.CStruct2GDB']

global_max_fast: int property

heap_info: Type['pwndbg.aglib.heap.structs.HeapInfo'] | None property

main_arena: Arena | None property

mallinfo: Type['pwndbg.aglib.heap.structs.CStruct2GDB'] | None property

malloc_chunk: Type['pwndbg.aglib.heap.structs.MallocChunk'] | None property

malloc_par: Type['pwndbg.aglib.heap.structs.MallocPar'] | None property

malloc_state: Type['pwndbg.aglib.heap.structs.MallocState'] | None property

mp: 'pwndbg.aglib.heap.structs.CStruct2GDB' property

struct_module: types.ModuleType | None property

tcache_entry: Type['pwndbg.aglib.heap.structs.TcacheEntry'] | None property

tcache_perthread_struct: Type['pwndbg.aglib.heap.structs.TcachePerthreadStruct'] | None property

thread_arena: Arena | None property

thread_cache: 'pwndbg.aglib.heap.structs.TcachePerthreadStruct' | None property

Locate a thread's tcache struct. We try to find its address in Thread Local Storage (TLS) first, and if that fails, we guess it's at the first chunk of the heap.

__init__()

brute_force_thread_local_variable_near_tls_base(tls_address, validator)

Brute force the thread-local variable near the TLS base address that can pass the validator.

brute_force_tls_reference_in_got_section(tls_address, validator)

Brute force the TLS-reference in the .got section to that can pass the validator.

can_be_resolved()

get_heap(addr)

Find & read the heap_info struct belonging to the chunk at 'addr'.

get_sbrk_heap_region()

Return a Page object representing the sbrk heap region. Ensure the region's start address is aligned to SIZE_SZ * 2, which compensates for the presence of GLIBC_TUNABLES. This heuristic version requires some sanity checks and may raise SymbolUnresolvableError if malloc's mp_ struct can't be resolved.

get_tcache(tcache_addr=None)

has_tcache()

is_initialized()

prompt_for_brute_force_thread_arena_permission()

Check if the user wants to brute force the thread_arena's value.

prompt_for_brute_force_thread_cache_permission()

Check if the user wants to brute force the tcache's value.

prompt_for_tls_address()

Check if we can determine the TLS address and return it.

SymbolUnresolvableError

Bases: Exception

symbol = symbol instance-attribute

__init__(symbol)

fetch_chunk_metadata(address, include_only_fields=None)

heap_for_ptr(ptr)

Round a pointer to a chunk down to find its corresponding heap_info struct, the pointer must point inside a heap which does not belong to the main arena.