Skip to content

Commit

Permalink
Mimic glibc's MALLOC_ALIGNMENT for heap chunks
Browse files Browse the repository at this point in the history
  • Loading branch information
theguy147 committed Aug 3, 2021
1 parent c21fe01 commit 11bd6a7
Showing 1 changed file with 44 additions and 25 deletions.
69 changes: 44 additions & 25 deletions gef.py
Expand Up @@ -795,22 +795,41 @@ def __str__(self):


class GlibcChunk:
"""Glibc chunk class.
"""Glibc chunk class. The default behavior (from_base=False) is to interpret the data starting at the memory
address pointed to as the chunk data. Setting from_base to True instead treats that data as the chunk header.
Ref: https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/."""

def __init__(self, addr, from_base=False):
self.ptrsize = current_arch.ptrsize
if from_base:
self.chunk_base_address = addr
self.address = addr + 2 * self.ptrsize
self.data_address = addr + 2 * self.ptrsize
else:
self.chunk_base_address = int(addr - 2 * self.ptrsize)
self.address = addr
self.data_address = addr
self.align_data_address()
self.base_address = addr - 2 * self.ptrsize

self.size_addr = int(self.address - self.ptrsize)
self.prev_size_addr = self.chunk_base_address
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."""
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 = 0x10
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)"))
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 All @@ -833,17 +852,17 @@ def get_prev_chunk_size(self):
return read_int_from_memory(self.prev_size_addr)

def get_next_chunk(self):
addr = self.address + self.get_chunk_size()
addr = self.data_address + self.get_chunk_size()
return GlibcChunk(addr)

# if free-ed functions
def get_fwd_ptr(self, sll):
# Not a single-linked-list (sll) or no Safe-Linking support yet
if not sll or get_libc_version() < (2, 32):
return read_int_from_memory(self.address)
return read_int_from_memory(self.data_address)
# Unmask ("reveal") the Safe-Linking pointer
else:
return read_int_from_memory(self.address) ^ (self.address >> 12)
return read_int_from_memory(self.data_address) ^ (self.data_address >> 12)

@property
def fwd(self):
Expand All @@ -852,7 +871,7 @@ def fwd(self):
fd = fwd # for compat

def get_bkw_ptr(self):
return read_int_from_memory(self.address + self.ptrsize)
return read_int_from_memory(self.data_address + self.ptrsize)

@property
def bck(self):
Expand Down Expand Up @@ -902,16 +921,16 @@ def _str_sizes(self):
msg.append("Previous chunk size: {0:d} ({0:#x})".format(self.get_prev_chunk_size()))
failed = True
except gdb.MemoryError:
msg.append("Previous chunk size: Cannot read at {:#x} (corrupted?)".format(self.chunk_base_address))
msg.append("Previous chunk size: Cannot read at {:#x} (corrupted?)".format(self.base_address))

if failed:
msg.append(self.str_chunk_size_flag())

return "\n".join(msg)

def _str_pointers(self):
fwd = self.address
bkw = self.address + self.ptrsize
fwd = self.data_address
bkw = self.data_address + self.ptrsize

msg = []
try:
Expand Down Expand Up @@ -944,7 +963,7 @@ def flags_as_string(self):

def __str__(self):
msg = "{:s}(addr={:#x}, size={:#x}, flags={:s})".format(Color.colorify("Chunk", "yellow bold underline"),
int(self.address),
int(self.base_address),
self.get_chunk_size(),
self.flags_as_string())
return msg
Expand Down Expand Up @@ -6637,11 +6656,11 @@ def do_invoke(self, argv):
nb = self.get_setting("peek_nb_byte")
current_chunk = GlibcChunk(heap_section, from_base=True)
while True:
if current_chunk.chunk_base_address == arena.top:
if current_chunk.base_address == arena.top:
gef_print("{} {} {}".format(str(current_chunk), LEFT_ARROW, Color.greenify("top chunk")))
break

if current_chunk.chunk_base_address > arena.top:
if current_chunk.base_address > arena.top:
break

if current_chunk.size == 0:
Expand All @@ -6650,14 +6669,14 @@ def do_invoke(self, argv):

line = str(current_chunk)
if nb:
line += "\n [" + hexdump(read_memory(current_chunk.address, nb), nb, base=current_chunk.address) + "]"
line += "\n [" + hexdump(read_memory(current_chunk.data_address, nb), nb, base=current_chunk.data_address) + "]"
gef_print(line)

next_chunk = current_chunk.get_next_chunk()
if next_chunk is None:
break

next_chunk_addr = Address(value=next_chunk.address)
next_chunk_addr = Address(value=next_chunk.data_address)
if not next_chunk_addr.valid:
# corrupted
break
Expand Down Expand Up @@ -6783,19 +6802,19 @@ def do_invoke(self, argv):

try:
msg.append("{:s} {:s} ".format(LEFT_ARROW, str(chunk)))
if chunk.address in chunks:
if chunk.data_address in chunks:
msg.append("{:s} [loop detected]".format(RIGHT_ARROW))
break

chunks.add(chunk.address)
chunks.add(chunk.data_address)

next_chunk = chunk.get_fwd_ptr(True)
if next_chunk == 0:
break

chunk = GlibcChunk(next_chunk)
except gdb.MemoryError:
msg.append("{:s} [Corrupted chunk at {:#x}]".format(LEFT_ARROW, chunk.address))
msg.append("{:s} [Corrupted chunk at {:#x}]".format(LEFT_ARROW, chunk.data_address))
break

if msg:
Expand Down Expand Up @@ -6917,22 +6936,22 @@ def fastbin_index(sz):

try:
gef_print("{:s} {:s} ".format(LEFT_ARROW, str(chunk)), end="")
if chunk.address in chunks:
if chunk.data_address in chunks:
gef_print("{:s} [loop detected]".format(RIGHT_ARROW), end="")
break

if fastbin_index(chunk.get_chunk_size()) != i:
gef_print("[incorrect fastbin_index] ", end="")

chunks.add(chunk.address)
chunks.add(chunk.data_address)

next_chunk = chunk.get_fwd_ptr(True)
if next_chunk == 0:
break

chunk = GlibcChunk(next_chunk, from_base=True)
except gdb.MemoryError:
gef_print("{:s} [Corrupted chunk at {:#x}]".format(LEFT_ARROW, chunk.address), end="")
gef_print("{:s} [Corrupted chunk at {:#x}]".format(LEFT_ARROW, chunk.data_address), end="")
break
gef_print()
return
Expand Down

0 comments on commit 11bd6a7

Please sign in to comment.