def initial_hook(*a): if show_tip and not pwndbg.decorators.first_prompt: colored_tip = re.sub( "`(.*?)`", lambda s: message.warn(s.group()[1:-1]), get_tip_of_the_day() ) print( message.prompt("------- tip of the day") + message.system(" (disable with %s)" % message.notice("set show-tips off")) + message.prompt(" -------") ) print((colored_tip)) pwndbg.decorators.first_prompt = True prompt_hook(*a) gdb.prompt_hook = prompt_hook
import pwndbg.decorators import pwndbg.events import pwndbg.gdbutils import pwndbg.memoize from pwndbg.color import disable_colors from pwndbg.color import message funcs_list_str = ', '.join(message.notice('$' + f.name) for f in pwndbg.gdbutils.functions.functions) hint_lines = ( 'loaded %i commands. Type %s for a list.' % (len(pwndbg.commands.commands), message.notice('pwndbg [filter]')), 'created %s gdb functions (can be used with print/break)' % funcs_list_str ) for line in hint_lines: print(message.prompt('pwndbg: ') + message.system(line)) cur = (gdb.selected_inferior(), gdb.selected_thread()) def prompt_hook(*a): global cur pwndbg.decorators.first_prompt = True new = (gdb.selected_inferior(), gdb.selected_thread()) if cur != new: pwndbg.events.after_reload(start=False) cur = new if pwndbg.proc.alive and pwndbg.proc.thread_is_stopped:
def malloc_chunk(addr, fake=False, verbose=False, simple=False): """Print a malloc_chunk struct's contents.""" # points to the real start of the chunk cursor = int(addr) allocator = pwndbg.heap.current ptr_size = allocator.size_sz size_field = pwndbg.memory.u(cursor + allocator.chunk_key_offset('size')) real_size = size_field & ~allocator.malloc_align_mask headers_to_print = [] # both state (free/allocated) and flags fields_to_print = set() # in addition to addr and size out_fields = "Addr: {}\n".format(M.get(cursor)) if fake: headers_to_print.append(message.on("Fake chunk")) verbose = True # print all fields for fake chunks if simple: chunk = read_chunk(cursor) if not headers_to_print: headers_to_print.append(message.hint(M.get(cursor))) prev_inuse, is_mmapped, non_main_arena = allocator.chunk_flags( int(chunk['size'])) if prev_inuse: headers_to_print.append(message.hint('PREV_INUSE')) if is_mmapped: headers_to_print.append(message.hint('IS_MMAPED')) if non_main_arena: headers_to_print.append(message.hint('NON_MAIN_ARENA')) print(' | '.join(headers_to_print)) for key, val in chunk.items(): print(message.system(key) + ": 0x{:02x}".format(int(val))) print('') return arena = allocator.get_arena_for_chunk(cursor) arena_address = None is_top = False if not fake and arena: arena_address = arena.address top_chunk = arena['top'] if cursor == top_chunk: headers_to_print.append(message.off("Top chunk")) is_top = True if not is_top: fastbins = allocator.fastbins(arena_address) or {} smallbins = allocator.smallbins(arena_address) or {} largebins = allocator.largebins(arena_address) or {} unsortedbin = allocator.unsortedbin(arena_address) or {} if allocator.has_tcache(): tcachebins = allocator.tcachebins(None) if real_size in fastbins.keys() and cursor in fastbins[real_size]: headers_to_print.append(message.on("Free chunk (fastbins)")) if not verbose: fields_to_print.add('fd') elif real_size in smallbins.keys() and cursor in bin_addrs( smallbins[real_size], "smallbins"): headers_to_print.append(message.on("Free chunk (smallbins)")) if not verbose: fields_to_print.update(['fd', 'bk']) elif real_size >= list( largebins.items())[0][0] and cursor in bin_addrs( largebins[(list( largebins.items())[allocator.largebin_index(real_size) - 64][0])], "largebins"): headers_to_print.append(message.on("Free chunk (largebins)")) if not verbose: fields_to_print.update( ['fd', 'bk', 'fd_nextsize', 'bk_nextsize']) elif cursor in bin_addrs(unsortedbin['all'], "unsortedbin"): headers_to_print.append(message.on("Free chunk (unsortedbin)")) if not verbose: fields_to_print.update(['fd', 'bk']) elif allocator.has_tcache() and real_size in tcachebins.keys( ) and cursor + ptr_size * 2 in bin_addrs(tcachebins[real_size], "tcachebins"): headers_to_print.append(message.on("Free chunk (tcache)")) if not verbose: fields_to_print.add('fd') else: headers_to_print.append(message.hint("Allocated chunk")) if verbose: fields_to_print.update( ['prev_size', 'size', 'fd', 'bk', 'fd_nextsize', 'bk_nextsize']) else: out_fields += "Size: 0x{:02x}\n".format(size_field) prev_inuse, is_mmapped, non_main_arena = allocator.chunk_flags(size_field) if prev_inuse: headers_to_print.append(message.hint('PREV_INUSE')) if is_mmapped: headers_to_print.append(message.hint('IS_MMAPED')) if non_main_arena: headers_to_print.append(message.hint('NON_MAIN_ARENA')) fields_ordered = [ 'prev_size', 'size', 'fd', 'bk', 'fd_nextsize', 'bk_nextsize' ] for field_to_print in fields_ordered: if field_to_print in fields_to_print: out_fields += message.system( field_to_print) + ": 0x{:02x}\n".format( pwndbg.memory.u( cursor + allocator.chunk_key_offset(field_to_print))) print(' | '.join(headers_to_print) + "\n" + out_fields)
from pwndbg.color import disable_colors from pwndbg.color import message from pwndbg.tips import get_tip_of_the_day funcs_list_str = ", ".join( message.notice("$" + f.name) for f in pwndbg.gdbutils.functions.functions ) hint_lines = ( "loaded %i commands. Type %s for a list." % (len(pwndbg.commands.commands), message.notice("pwndbg [filter]")), "created %s gdb functions (can be used with print/break)" % funcs_list_str, ) for line in hint_lines: print(message.prompt("pwndbg: ") + message.system(line)) # noinspection PyPackageRequirements show_tip = pwndbg.config.Parameter( "show-tips", True, "whether to display the tip of the day on startup" ) cur = None def initial_hook(*a): if show_tip and not pwndbg.decorators.first_prompt: colored_tip = re.sub( "`(.*?)`", lambda s: message.warn(s.group()[1:-1]), get_tip_of_the_day() ) print(
def new_malloc_chunk(addr, fake=False, verbose=False, simple=False): idx, start, end, rest = (lambda idx, start, end, **rest: (idx, start, end, rest))(**scope) scope['idx'] += 1 if start is not None and end is not None and idx not in range( start, end + 1): return ## original begin cursor = int(addr) allocator = pwndbg.heap.current ptr_size = allocator.size_sz size_field = pwndbg.memory.u(cursor + allocator.chunk_key_offset('size')) real_size = size_field & ~allocator.malloc_align_mask headers_to_print = [] # both state (free/allocated) and flags fields_to_print = set() # in addition to addr and size out_fields = "Addr: {}\n".format(M.get(cursor)) if fake: headers_to_print.append(message.on("Fake chunk")) verbose = True # print all fields for fake chunks if simple: chunk = read_chunk(cursor) if not headers_to_print: headers_to_print.append(message.hint(M.get(cursor))) prev_inuse, is_mmapped, non_main_arena = allocator.chunk_flags( int(chunk['size'])) if prev_inuse: headers_to_print.append(message.hint('PREV_INUSE')) if is_mmapped: headers_to_print.append(message.hint('IS_MMAPED')) if non_main_arena: headers_to_print.append(message.hint('NON_MAIN_ARENA')) print(' | '.join(headers_to_print)) for key, val in chunk.items(): print(message.system(key) + ": 0x{:02x}".format(int(val))) print('') return arena = allocator.get_arena_for_chunk(cursor) arena_address = None is_top = False if not fake and arena: arena_address = arena.address top_chunk = arena['top'] if cursor == top_chunk: headers_to_print.append(message.off("Top".center(9, " "))) is_top = True if not is_top: fastbins = allocator.fastbins(arena_address) or {} smallbins = allocator.smallbins(arena_address) or {} largebins = allocator.largebins(arena_address) or {} unsortedbin = allocator.unsortedbin(arena_address) or {} if allocator.has_tcache(): tcachebins = allocator.tcachebins(None) if real_size in fastbins.keys() and cursor in fastbins[real_size]: headers_to_print.append(message.on("F(fast)".center(9, " "))) if not verbose: fields_to_print.add('fd') elif real_size in smallbins.keys() and cursor in bin_addrs( smallbins[real_size], "smallbins"): headers_to_print.append(message.on("F(small)".center(9, " "))) if not verbose: fields_to_print.update(['fd', 'bk']) elif real_size >= list( largebins.items())[0][0] and cursor in bin_addrs( largebins[(list(largebins.items())[ allocator.largebin_index(real_size) - 64][0])], "largebins"): headers_to_print.append(message.on("F(large)".center(9, " "))) if not verbose: fields_to_print.update( ['fd', 'bk', 'fd_nextsize', 'bk_nextsize']) elif cursor in bin_addrs(unsortedbin['all'], "unsortedbin"): headers_to_print.append(message.on("F(unsort)".center(9, " "))) if not verbose: fields_to_print.update(['fd', 'bk']) elif allocator.has_tcache() and real_size in tcachebins.keys( ) and cursor + ptr_size * 2 in bin_addrs(tcachebins[real_size], "tcachebins"): headers_to_print.append(message.on("F(tcache)".center(9, " "))) if not verbose: fields_to_print.add('fd') else: headers_to_print.append( message.hint("Allocated".center(9, " "))) if verbose: fields_to_print.update([ 'prev_size', 'size', 'fd', 'bk', 'fd_nextsize', 'bk_nextsize' ]) else: out_fields += "Size: 0x{:02x}\n".format(size_field) prev_inuse, is_mmapped, non_main_arena = allocator.chunk_flags( size_field) if prev_inuse: headers_to_print.append(message.hint('PREV_INUSE')) if is_mmapped: headers_to_print.append(message.hint('IS_MMAPED')) if non_main_arena: headers_to_print.append(message.hint('NON_MAIN_ARENA')) ## original end if not verbose: def ascii_char(byte): if byte >= 0x20 and byte < 0x7e: return chr(byte) # Ensure we return a str else: return "." content = "" content += "[{:03d}] ".format(idx) content += M.get(cursor) + f" SIZE=0x{size_field:08x}" data = addr + pwndbg.arch.ptrsize * 2 content += " DATA[" + hex(data) + "]" if size_field >= 0x20: bytes_read = pwndbg.memory.read(data, 0x20, partial=True) else: bytes_read = pwndbg.memory.read(data, size, partial=True) content += " |" + "".join([ascii_char(c) for c in bytes_read]) + "| " print(content + " | ".join(headers_to_print)) else: print_chunk_detail(addr, size_field)
def heap(addr=None, verbose=False): """Iteratively print chunks on a heap, default to the current thread'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 # Calculate where to start printing; if an address was supplied, use that, # if this heap belongs to the main arena, start at the beginning of the # heap's mapping, otherwise, compensate for the presence of a heap_info # struct and possibly an arena. 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 # i686 alignment heuristic first_chunk_size = pwndbg.arch.unpack( pwndbg.memory.read(cursor + ptr_size, ptr_size)) if first_chunk_size == 0: cursor += ptr_size * 2 while cursor in heap_region: old_cursor = cursor size_field = pwndbg.memory.u(cursor + allocator.chunk_key_offset('size')) real_size = size_field & ~allocator.malloc_align_mask if cursor == top_chunk: out = message.off("Top chunk\n") out += "Addr: {}\nSize: 0x{:02x}".format(M.get(cursor), size_field) print(out) break fastbins = allocator.fastbins(arena.address) smallbins = allocator.smallbins(arena.address) largebins = allocator.largebins(arena.address) unsortedbin = allocator.unsortedbin(arena.address) if allocator.has_tcache(): tcachebins = allocator.tcachebins(None) out = "Addr: {}\nSize: 0x{:02x}\n".format(M.get(cursor), size_field) if real_size in fastbins.keys() and cursor in fastbins[real_size]: out = message.on("Free chunk (fastbins)\n") + out if not verbose: out += message.system("fd: ") + "0x{:02x}\n".format( pwndbg.memory.u(cursor + allocator.chunk_key_offset('fd'))) elif real_size in smallbins.keys() and cursor in bin_addrs( smallbins[real_size], "smallbins"): out = message.on("Free chunk (smallbins)\n") + out if not verbose: out += message.system("fd: ") + "0x{:02x}\n".format( pwndbg.memory.u(cursor + allocator.chunk_key_offset('fd'))) out += message.system("bk: ") + "0x{:02x}\n".format( pwndbg.memory.u(cursor + allocator.chunk_key_offset('bk'))) elif real_size >= list( largebins.items())[0][0] and cursor in bin_addrs( largebins[(list( largebins.items())[allocator.largebin_index(real_size) - 64][0])], "largebins"): out = message.on("Free chunk (largebins)\n") + out if not verbose: out += message.system("fd: ") + "0x{:02x}\n".format( pwndbg.memory.u(cursor + allocator.chunk_key_offset('fd'))) out += message.system("bk: ") + "0x{:02x}\n".format( pwndbg.memory.u(cursor + allocator.chunk_key_offset('bk'))) out += message.system("fd_nextsize: ") + "0x{:02x}\n".format( pwndbg.memory.u(cursor + allocator.chunk_key_offset('fd_nextsize'))) out += message.system("bk_nextsize: ") + "0x{:02x}\n".format( pwndbg.memory.u(cursor + allocator.chunk_key_offset('bk_nextsize'))) elif cursor in bin_addrs(unsortedbin['all'], "unsortedbin"): out = message.on("Free chunk (unsortedbin)\n") + out if not verbose: out += message.system("fd: ") + "0x{:02x}\n".format( pwndbg.memory.u(cursor + allocator.chunk_key_offset('fd'))) out += message.system("bk: ") + "0x{:02x}\n".format( pwndbg.memory.u(cursor + allocator.chunk_key_offset('bk'))) elif allocator.has_tcache() and real_size in tcachebins.keys( ) and cursor + ptr_size * 2 in bin_addrs(tcachebins[real_size], "tcachebins"): out = message.on("Free chunk (tcache)\n") + out if not verbose: out += message.system("fd: ") + "0x{:02x}\n".format( pwndbg.memory.u(cursor + allocator.chunk_key_offset('fd'))) else: out = message.hint("Allocated chunk\n") + out if verbose: out += message.system("fd: ") + "0x{:02x}\n".format( pwndbg.memory.u(cursor + allocator.chunk_key_offset('fd'))) out += message.system("bk: ") + "0x{:02x}\n".format( pwndbg.memory.u(cursor + allocator.chunk_key_offset('bk'))) out += message.system("fd_nextsize: ") + "0x{:02x}\n".format( pwndbg.memory.u(cursor + allocator.chunk_key_offset('fd_nextsize'))) out += message.system("bk_nextsize: ") + "0x{:02x}\n".format( pwndbg.memory.u(cursor + allocator.chunk_key_offset('bk_nextsize'))) print(out) cursor += real_size # Avoid an infinite loop when a chunk's size is 0. if cursor == old_cursor: break