def memoize(): pwndbg.memoize.memoize.caching = not pwndbg.memoize.memoize.caching status = message.off( 'OFF (pwndbg will work slower, use only for debugging pwndbg)') if pwndbg.memoize.memoize.caching: status = message.on('ON') print("Caching is now %s" % status)
def aslr(state=None): if state: gdb.execute('set disable-randomization %s' % options[state], from_tty=False, to_string=True) if pwndbg.proc.alive: print("Change will take effect when the process restarts") aslr, method = pwndbg.vmmap.check_aslr() if aslr is True: status = message.on('ON') elif aslr is False: status = message.off('OFF') else: status = message.off('???') print("ASLR is %s (%s)" % (status, method))
def top_chunk(addr=None): """Print relevant information about an arena's top chunk, default to the current thread's arena. """ allocator = pwndbg.heap.current arena = allocator.get_arena(addr) address = arena['top'] size = pwndbg.memory.u(int(address) + allocator.chunk_key_offset('size')) out = message.off("Top chunk\n") + "Addr: {}\nSize: 0x{:02x}".format(M.get(address), size) print(out)
def aslr(state=None): """ Check the current ASLR status, or turn it on/off. Does not take effect until the program is restarted. """ if state: gdb.execute('set disable-randomization %s' % options[state], from_tty=False, to_string=True) if pwndbg.proc.alive: print("Change will take effect when the process restarts") aslr = pwndbg.vmmap.check_aslr() status = message.off('OFF') if aslr: status = message.on('ON') print("ASLR is %s" % status)
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)
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