def xinfo(address=None): address = address.cast(pwndbg.typeinfo.pvoid ) # Fixes issues with function ptrs (xinfo malloc) addr = int(address) addr &= pwndbg.arch.ptrmask page = pwndbg.vmmap.find(addr) if page is None: print("\n Virtual address {:#x} is not mapped.".format(addr)) return print("Extended information for virtual address {}:".format(M.get(addr))) print("\n Containing mapping:") print(M.get(address, text=str(page))) print("\n Offset information:") if page.is_stack: xinfo_stack(page, addr) else: xinfo_default(page, addr) if page.is_memory_mapped_file: xinfo_mmap_file(page, addr)
def heap(addr=None): """ Prints out all chunks in the main_arena, or the arena specified by `addr`. """ main_arena = get_main_arena(addr) if main_arena == None: return heap_base = get_heap_bounds()[0] if heap_base == None: print(red('Could not find the heap')) return top = main_arena['top'] last_remainder = main_arena['last_remainder'] print(bold('Top Chunk: ') + M.get(top)) print(bold('Last Remainder: ') + M.get(last_remainder)) print() # Print out all chunks on the heap # TODO: Add an option to print out only free or allocated chunks addr = heap_base while addr <= top: chunk = malloc_chunk(addr) size = int(chunk['size']) # Clear the bottom 3 bits size &= ~7 addr += size
def vmmap(gdbval_or_str=None, writable=False, executable=False): pages = pwndbg.vmmap.get() if gdbval_or_str: pages = list(filter(pages_filter(gdbval_or_str), pages)) if not pages: print("There are no mappings for specified address or module.") return print(M.legend()) if len(pages) == 1 and isinstance(gdbval_or_str, integer_types): page = pages[0] print( M.get(page.vaddr, text=str(page) + " +0x%x" % (int(gdbval_or_str) - page.vaddr))) else: for page in pages: if (executable and not page.execute) or (writable and not page.write): continue print(M.get(page.vaddr, text=str(page))) if pwndbg.qemu.is_qemu(): print( "\n[QEMU target detected - vmmap result might not be accurate; see `help vmmap`]" )
def heap(addr=None): """ Prints out all chunks in the main_arena, or the arena specified by `addr`. """ main_arena = get_main_arena(addr) if main_arena == None: return heap_base = get_heap_bounds()[0] if heap_base == None: print(red('Could not find the heap')) return top = main_arena['top'] last_remainder = main_arena['last_remainder'] print(bold('Top Chunk: ') + M.get(top)) print(bold('Last Remainder: ') + M.get(last_remainder)) print() # Print out all chunks on the heap # TODO: Add an option to print out only free or allocated chunks addr = heap_base while addr <= top: chunk = malloc_chunk(addr) size = int(chunk['size']) # Clear the bottom 3 bits size &= ~7 if size == 0: break addr += size
def instruction(ins): asm = '%-06s %s' % (ins.mnemonic, ins.op_str) if pwndbg.config.syntax_highlight: asm = syntax_highlight(asm) is_branch = set(ins.groups) & capstone_branch_groups # Highlight the current line if enabled if pwndbg.config.highlight_pc and ins.address == pwndbg.regs.pc: asm = C.highlight(asm) # tl;dr is a branch? if ins.target not in (None, ins.address + ins.size): sym = pwndbg.symbol.get(ins.target) or None target = M.get(ins.target) const = ins.target_const hextarget = hex(ins.target) hexlen = len(hextarget) # If it's a constant expression, color it directly in the asm. if const: asm = '%s <%s>' % (ljust_colored(asm, 36), target) asm = asm.replace(hex(ins.target), sym or target) # It's not a constant expression, but we've calculated the target # address by emulation or other means (for example showing ret instruction target) elif sym: asm = '%s <%s; %s>' % (ljust_colored(asm, 36), target, sym) # We were able to calculate the target, but there is no symbol # name for it. else: asm += '<%s>' % (target) # not a branch elif ins.symbol: if is_branch and not ins.target: asm = '%s <%s>' % (asm, ins.symbol) # XXX: not sure when this ever happens asm += '<-- file a pwndbg bug for this' else: inlined_sym = asm.replace(hex(ins.symbol_addr), ins.symbol) # display symbol as mem text if no inline replacement was made mem_text = ins.symbol if inlined_sym == asm else None asm = '%s <%s>' % (ljust_colored(inlined_sym, 36), M.get(ins.symbol_addr, mem_text)) # Style the instruction mnemonic if it's a branch instruction. if is_branch: asm = asm.replace(ins.mnemonic, branch(ins.mnemonic), 1) # If we know the conditional is taken, mark it as taken. if ins.condition is None: asm = ' ' + asm elif ins.condition: asm = on('✔ ') + asm else: asm = ' ' + asm return asm
def instruction(ins): asm = '%-06s %s' % (ins.mnemonic, ins.op_str) # TODO: detect 'arm', 'x86,64' is only for Intel x86/64 _highlighted, asm = pwndbg.color.syntax_highlight(asm, language='ARM') is_branch = set(ins.groups) & capstone_branch_groups # Highlight the current line if enabled if pwndbg.config.highlight_pc and ins.address == pwndbg.regs.pc: asm = C.highlight(asm) # tl;dr is a branch? if ins.target not in (None, ins.address + ins.size): sym = pwndbg.symbol.get(ins.target) or None target = M.get(ins.target) const = ins.target_const hextarget = hex(ins.target) hexlen = len(hextarget) # If it's a constant expression, color it directly in the asm. if const: asm = asm.replace(hex(ins.target), sym or target) if sym: asm = '%s <%s>' % (ljust_colored(asm, 36), target) # It's not a constant expression, but we've calculated the target # address by emulation. elif sym: asm = '%s <%s; %s>' % (ljust_colored(asm, 36), target, sym) # We were able to calculate the target, but there is no symbol # name for it. else: asm += '<%s>' % (target) # not a branch elif ins.symbol: if is_branch and not ins.target: asm = '%s <%s>' % (asm, ins.symbol) # XXX: not sure when this ever happens asm += '<-- file a pwndbg bug for this' else: asm = asm.replace(hex(ins.symbol_addr), ins.symbol) asm = '%s <%s>' % (ljust_colored(asm, 36), M.get(ins.symbol_addr)) # Style the instruction mnemonic if it's a branch instruction. if is_branch: asm = asm.replace(ins.mnemonic, branch(ins.mnemonic), 1) # If we know the conditional is taken, mark it as green. if ins.condition is None: asm = ' ' + asm elif ins.condition: asm = green('✔ ') + asm else: asm = ' ' + asm return asm
def probeleak(address=None, count=0x40, max_distance=0x0): address = int(address) address &= pwndbg.arch.ptrmask ptrsize = pwndbg.arch.ptrsize count = max(int(count), ptrsize) off_zeros = int(math.ceil(math.log(count, 2) / 4)) if count > address > 0x10000: # in case someone puts in an end address and not a count (smh) print( message.warn( "Warning: you gave an end address, not a count. Substracting 0x%x from the count." % (address))) count -= address try: data = pwndbg.memory.read(address, count, partial=True) except gdb.error as e: print(message.error(str(e))) return if not data: print( message.error( "Couldn't read memory at 0x%x. See 'probeleak -h' for the usage." % (address, ))) return found = False for i in range(0, len(data) - ptrsize + 1): p = pwndbg.arch.unpack(data[i:i + ptrsize]) page = find_module(p, max_distance) if page: if not found: print(M.legend()) found = True mod_name = page.objfile if not mod_name: mod_name = '[anon]' if p >= page.end: right_text = '(%s) %s + 0x%x + 0x%x (outside of the page)' % ( page.permstr, mod_name, page.memsz, p - page.end) elif p < page.start: right_text = '(%s) %s - 0x%x (outside of the page)' % ( page.permstr, mod_name, page.start - p) else: right_text = '(%s) %s + 0x%x' % (page.permstr, mod_name, p - page.start) offset_text = '0x%0*x' % (off_zeros, i) p_text = '0x%0*x' % (int(ptrsize * 2), p) print('%s: %s = %s' % (offset_text, M.get( p, text=p_text), M.get(p, text=right_text))) if not found: print( message.hint('No leaks found at 0x%x-0x%x :(' % (address, address + count)))
def print_line(name, addr, first, second, op, width=20): print("{} {} = {} {} {:#x}".format( name.rjust(width), M.get(addr), M.get(first) if not isinstance(first, str) else first.ljust( len(hex(addr).rstrip('L'))), op, second, ))
def probeleak(address=None, count=0x40, max_distance=0x0): address = int(address) address &= pwndbg.arch.ptrmask ptrsize = pwndbg.arch.ptrsize count = max(int(count), ptrsize) off_zeros = int(math.ceil(math.log(count,2)/4)) if count > address > 0x10000: # in case someone puts in an end address and not a count (smh) print(message.warn("Warning: you gave an end address, not a count. Substracting 0x%x from the count." % (address))) count -= address try: data = pwndbg.memory.read(address, count, partial=True) except gdb.error as e: print(message.error(str(e))) return if not data: print(message.error("Couldn't read memory at 0x%x. See 'probeleak -h' for the usage." % (address,))) return found = False for i in range(0, len(data) - ptrsize + 1): p = pwndbg.arch.unpack(data[i:i+ptrsize]) page = find_module(p, max_distance) if page: if not found: print(M.legend()) found = True mod_name = page.objfile if not mod_name: mod_name = '[anon]' if p >= page.end: right_text = '(%s) %s + 0x%x + 0x%x (outside of the page)' % (page.permstr, mod_name, page.memsz, p - page.end) elif p < page.start: right_text = '(%s) %s - 0x%x (outside of the page)' % (page.permstr, mod_name, page.start - p) else: right_text = '(%s) %s + 0x%x' % (page.permstr, mod_name, p - page.start) offset_text = '0x%0*x' % (off_zeros, i) p_text = '0x%0*x' % (int(ptrsize*2), p) text = '%s: %s = %s' % (offset_text, M.get(p, text=p_text), M.get(p, text=right_text)) symbol = pwndbg.symbol.get(p) if symbol: text += ' (%s)' % symbol print(text) if not found: print(message.hint('No leaks found at 0x%x-0x%x :(' % (address, address+count)))
def malloc_chunk(addr): """ Prints out the malloc_chunk at the specified address. """ main_heap = pwndbg.heap.current if not isinstance(addr, six.integer_types): addr = int(addr) chunk = value_from_type('struct malloc_chunk', addr) size = int(chunk['size']) actual_size = size & ~7 prev_inuse, is_mmapped, non_main_arena = main_heap.chunk_flags(size) arena = None if non_main_arena: arena = main_heap.get_heap(addr)['ar_ptr'] fastbins = main_heap.fastbins(arena) header = M.get(addr) if prev_inuse: if actual_size in fastbins: header += yellow(' FASTBIN') else: header += yellow(' PREV_INUSE') if is_mmapped: header += yellow(' IS_MMAPED') if non_main_arena: header += yellow(' NON_MAIN_ARENA') print(header, chunk) return chunk
def malloc_chunk(addr, fake=False): """ Prints out the malloc_chunk at the specified address. """ main_heap = pwndbg.heap.current if not isinstance(addr, six.integer_types): addr = int(addr) chunk = read_chunk(addr) size = int(chunk['size']) actual_size = size & ~7 prev_inuse, is_mmapped, non_main_arena = main_heap.chunk_flags(size) arena = None if not fake and non_main_arena: arena = main_heap.get_heap(addr)['ar_ptr'] fastbins = [] if fake else main_heap.fastbins(arena) header = M.get(addr) if fake: header += message.prompt(' FAKE') if prev_inuse: if actual_size in fastbins: header += message.hint(' FASTBIN') else: header += message.hint(' PREV_INUSE') if is_mmapped: header += message.hint(' IS_MMAPED') if non_main_arena: header += message.hint(' NON_MAIN_ARENA') print(header, chunk["value"]) return chunk
def format(value, limit=LIMIT, code=True, offset=0, hard_stop=None, hard_end=0): """ Recursively dereferences an address into string representation, or convert the list representation of address dereferences into string representation. Arguments: value(int|list): Either the starting address to be sent to get, or the result of get (a list) limit(int): Number of valid pointers code(bool): Hint that indicates the value may be an instruction offset(int): Offset into the address to get the next pointer hard_stop(int): Value to stop on hard_end: Value to append when hard_stop is reached: null, value of hard stop, a string. Returns: A string representing pointers of each address and reference Strings format: 0x0804a10 —▸ 0x08061000 ◂— 0x41414141 """ limit = int(limit) + 1 # Allow results from get function to be passed to format if type(value) == list: chain = value else: chain = get(value, limit, offset, hard_stop, hard_end) arrow_left = C.arrow(' %s ' % config_arrow_left) arrow_right = C.arrow(' %s ' % config_arrow_right) # Enhance the last entry # If there are no pointers (e.g. eax = 0x41414141), then enhance # the only element there is. if len(chain) == 1: enhanced = pwndbg.enhance.enhance(chain[-1], code=code) # Otherwise, the last element in the chain is the non-pointer value. # We want to enhance the last pointer value. If an offset was used # chain failed at that offset, so display that offset. elif len(chain) < limit: enhanced = pwndbg.enhance.enhance(chain[-2] + offset, code=code) else: enhanced = C.contiguous('%s' % config_contiguous) # Colorize the rest rest = [] for link in chain[:-1]: symbol = pwndbg.symbol.get(link) or None if symbol: symbol = '%#x (%s)' % (link, symbol) rest.append(M.get(link, symbol)) if len(chain) == 1: return enhanced return arrow_right.join(rest) + arrow_left + enhanced
def format(value, limit=LIMIT, code=True, offset=0): chain = get(value, limit, offset) arrow_left = C.arrow(' %s ' % config_arrow_left) arrow_right = C.arrow(' %s ' % config_arrow_right) # Enhance the last entry # If there are no pointers (e.g. eax = 0x41414141), then enhance # the only element there is. if len(chain) == 1: enhanced = pwndbg.enhance.enhance(chain[-1], code=code) # Otherwise, the last element in the chain is the non-pointer value. # We want to enhance the last pointer value. elif len(chain) < limit: enhanced = pwndbg.enhance.enhance(chain[-2], code=code) else: enhanced = C.contiguous('%s' % config_contiguous) # Colorize the rest rest = [] for link in chain[:-1]: symbol = pwndbg.symbol.get(link) or None if symbol: symbol = '%#x (%s)' % (link, symbol) rest.append(M.get(link, symbol)) if len(chain) == 1: return enhanced return arrow_right.join(rest) + arrow_left + enhanced
def xinfo_mmap_file(page, addr): # If it's an address pointing into a memory mapped file, print offsets # to beginning of file in memory and on disk file_name = page.objfile objpages = filter(lambda p: p.objfile == file_name, pwndbg.vmmap.get()) first = sorted(objpages, key = lambda p: p.vaddr)[0] # print offset from ELF base load address rva = addr - first.vaddr print_line("File (Base)", addr, first.vaddr, rva, "+") # find possible LOAD segments that designate memory and file backings containing_loads = [seg for seg in pwndbg.elf.get_containing_segments(file_name, first.vaddr, addr) if seg['p_type'] == 'PT_LOAD'] for segment in containing_loads: if segment['p_type'] == 'PT_LOAD' and addr < segment['x_vaddr_mem_end']: offset = addr - segment['p_vaddr'] print_line('File (Segment)', addr, segment['p_vaddr'], offset, '+') break for segment in containing_loads: if segment['p_type'] == 'PT_LOAD' and addr < segment['x_vaddr_file_end']: file_offset = segment['p_offset'] + (addr - segment['p_vaddr']) print_line("File (Disk)", addr, file_name, file_offset, "+") break else: print('{} {} = [not file backed]'.format('File (Disk)'.rjust(20), M.get(addr))) containing_sections = pwndbg.elf.get_containing_sections(file_name, first.vaddr, addr) if len(containing_sections) > 0: print('\n Containing ELF sections:') for sec in containing_sections: print_line(sec['x_name'], addr, sec['sh_addr'], addr - sec['sh_addr'], '+')
def malloc_chunk(addr,fake=False): """ Prints out the malloc_chunk at the specified address. """ main_heap = pwndbg.heap.current if not isinstance(addr, six.integer_types): addr = int(addr) chunk = read_chunk(addr) size = int(chunk['size']) actual_size = size & ~7 prev_inuse, is_mmapped, non_main_arena = main_heap.chunk_flags(size) arena = None if not fake and non_main_arena: arena = main_heap.get_heap(addr)['ar_ptr'] fastbins = [] if fake else main_heap.fastbins(arena) header = M.get(addr) if fake: header += message.prompt(' FAKE') if prev_inuse: if actual_size in fastbins: header += message.hint(' FASTBIN') else: header += message.hint(' PREV_INUSE') if is_mmapped: header += message.hint(' IS_MMAPED') if non_main_arena: header += message.hint(' NON_MAIN_ARENA') print(header, chunk["value"]) return chunk
def minfo(address=None): pages = pwndbg.vmmap.get() prev_end = 0 print(M.legend()) for page in pages: if prev_end < page.vaddr: print(short_page_str(pwndbg.memory.Page(prev_end, page.vaddr - prev_end, 0, 0, '(empty)'))) prev_end = page.vaddr + page.memsz print(M.get(page.vaddr, text=short_page_str(page)))
def vmmap(pages_filter=None): pages = list(filter(pages_filter, pwndbg.vmmap.get())) if not pages: print('There are no mappings for specified address or module.') return print(M.legend()) for page in pages: print(M.get(page.vaddr, text=str(page)))
def print_chunk_detail(addr, size): def get_type(size): return { 4: pwndbg.typeinfo.uint32, 8: pwndbg.typeinfo.uint64, }[size] def enhex(size, value, fill=True): value = value & pwndbg.arch.ptrmask x = "%x" % abs(value) if fill: x = x.rjust(size * 2, '0') return x values = [] address = addr & pwndbg.arch.ptrmask type = get_type(pwndbg.arch.ptrsize) count = int((size & ~7) / pwndbg.arch.ptrsize) for i in range(count): try: gval = pwndbg.memory.poi(type, address + i * pwndbg.arch.ptrsize) values.append(int(gval)) except gdb.MemoryError: break n_rows = int(math.ceil(count * pwndbg.arch.ptrsize / float(16))) row_sz = 2 rows = [values[i * row_sz:(i + 1) * row_sz] for i in range(n_rows)] lines = [] repeat_row = None appended = False for i, row in enumerate(rows): if not row: continue if row == repeat_row and size > 0x100 and i != len(rows) - 1: if not appended: lines.append('.' * 6) appended = True continue if i == 0: line = [M.get(address), ' '] else: line = [ '0x' + enhex(pwndbg.arch.ptrsize, address + (i * 16), fill=False), ' ' ] for value in row: line.append(enhex(pwndbg.arch.ptrsize, value)) lines.append(' '.join(line)) repeat_row = row appended = False print('\n'.join(lines))
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 print_search_hit(address): """Prints out a single search hit. Arguments: address(int): Address to print """ if not address: return vmmap = pwndbg.vmmap.find(address) if vmmap: region = os.path.basename(vmmap.objfile) else: region = '[mapped]' region = region.ljust(15) region = M.get(address, region) addr = M.get(address) display = pwndbg.enhance.enhance(address) print(region, addr, display)
def print_search_hit(address): """Prints out a single search hit. Arguments: address(int): Address to print """ if not address: return vmmap = pwndbg.vmmap.find(address) if vmmap: region = os.path.basename(vmmap.objfile) else: region = '[mapped]' region = region.ljust(15) region = M.get(address, region) addr = M.get(address) display = pwndbg.enhance.enhance(address) print(region,addr,display)
def vmmap(pages_filter=None): pages = list(filter(pages_filter, pwndbg.vmmap.get())) if not pages: print('There are no mappings for specified address or module.') return print(M.legend()) for page in pages: print(M.get(page.vaddr, text=str(page))) if pwndbg.qemu.is_qemu(): print("\n[QEMU target detected - vmmap result might not be accurate; see `help vmmap`]")
def xinfo(address=None): addr = int(address) addr &= pwndbg.arch.ptrmask page = pwndbg.vmmap.find(addr) if page is None: print("\n Virtual address {:#x} is not mapped.".format(addr)) return print("Extended information for virtual address {}:".format(M.get(addr))) print("\n Containing mapping:") print(M.get(address, text=str(page))) print("\n Offset information:") if page.is_stack: xinfo_stack(page, addr) else: xinfo_default(page, addr) if page.is_memory_mapped_file: xinfo_mmap_file(page, addr)
def heap(addr=None): """ Prints out all chunks in the main_arena, or the arena specified by `addr`. """ main_heap = pwndbg.heap.current main_arena = main_heap.get_arena(addr) if main_arena is None: return heap_region = main_heap.get_region(addr) if heap_region is None: print(red('Could not find the heap')) return top = main_arena['top'] last_remainder = main_arena['last_remainder'] print(bold('Top Chunk: ') + M.get(top)) print(bold('Last Remainder: ') + M.get(last_remainder)) print() # Print out all chunks on the heap # TODO: Add an option to print out only free or allocated chunks addr = heap_region.vaddr while addr <= top: chunk = malloc_chunk(addr) size = int(chunk['size']) # Clear the bottom 3 bits size &= ~7 if size == 0: break addr += size
def print_search(value, *a, **kw): hits = set() for address in pwndbg.search.search(value, *a, **kw): if not address: continue if address in hits: continue hits.add(address) vmmap = pwndbg.vmmap.find(address) if vmmap: region = os.path.basename(vmmap.objfile) else: region = '[mapped]' region = region.ljust(15) region = M.get(address, region) addr = M.get(address) display = pwndbg.enhance.enhance(address) print(region,addr,display)
def locate(address=0x0): if address == 0x0: print("Invalid argument provided. Please give a valid address such as 0x") return pages = list(filter(None, pwndbg.vmmap.get())) print(M.legend()) for page in pages: if address >= page.vaddr and address <= page.vaddr + page.memsz: texta = str(page) + " + " + hex(int(address-page.vaddr)) print(M.get(page.vaddr, text=texta)) if pwndbg.qemu.is_qemu(): print("\n[QEMU target detected - locate result might not be accurate; see `help vmmap`]")
def malloc_chunk(addr, fake=False): """ Prints out the malloc_chunk at the specified address. """ main_heap = pwndbg.heap.current if not isinstance(addr, six.integer_types): addr = int(addr) chunk = read_chunk(addr) size = int(chunk['size']) actual_size = size & ~7 prev_inuse, is_mmapped, non_main_arena = main_heap.chunk_flags(size) arena = None if not fake and non_main_arena: arena = main_heap.get_heap(addr)['ar_ptr'] fastbins = [] if fake else main_heap.fastbins(arena) header = M.get(addr) if fake: header += message.prompt(' FAKE') if prev_inuse: if actual_size in fastbins: header += message.hint(' FASTBIN') else: header += message.hint(' PREV_INUSE') if is_mmapped: header += message.hint(' IS_MMAPED') if non_main_arena: header += message.hint(' NON_MAIN_ARENA') ## edit start chunk_str = '{\n' for key in chunk["value"].type.keys(): if (str(key) == "prev_size" or str(key) == "size"): chunk_str += ' %s = %s [%s]\n' % (str(key), hex(int(chunk["value"][key])), int(chunk["value"][key])) else: chunk_str += ' %s = %s\n' % (str(key), hex(int(chunk["value"][key]))) chunk_str += '}' print(header, chunk_str) ## edit end return chunk
def probeleak(address=None, count=0x40): address = int(address) address &= pwndbg.arch.ptrmask count = max(int(count), 0) ptrsize = pwndbg.arch.ptrsize off_zeros = int(math.ceil(math.log(count,2)/4)) if count > address > 0x10000: # in case someone puts in an end address and not a count (smh) count -= address if count % ptrsize > 0: newcount = count - (count % ptrsize) print(message.warning("Warning: count 0x%x is not a multiple of 0x%x; truncating to 0x%x." % (count, ptrsize, newcount))) count = newcount try: data = pwndbg.memory.read(address, count, partial=True) except gdb.error as e: print(message.error(str(e))) return if not data: print(message.error("Couldn't read memory at 0x%x" % (address,))) return found = False for i in range(0, count, ptrsize): p = pwndbg.arch.unpack(data[i:i+ptrsize]) page = find_module(p) if page: if not found: print(M.legend()) found = True mod_name = page.objfile if not mod_name: mod_name = '[anon]' fmt = '+0x{offset:0{n1}x}: 0x{ptr:0{n2}x} = {page}' right_text = ('(%s) %s + 0x%x') % (page.permstr, mod_name, p - page.vaddr + page.offset) print(fmt.format(n1=off_zeros, n2=ptrsize*2, offset=i, ptr=p, page=M.get(p, text=right_text))) if not found: print(message.hint('No leaks found at 0x{:x}-0x{:x} :('.format(address, address+count)))
def get_rec_addr_string(addr,visited_map): page = pwndbg.vmmap.find(addr) arrow_right = C.arrow(' %s ' % config_arrow_right) if not page is None: if not addr in visited_map: return "" parent_info = visited_map[addr] parent = parent_info[0] parent_base_addr = parent_info[1] if parent - parent_base_addr < 0: curText = hex(parent_base_addr) + hex(parent - parent_base_addr) else: curText = hex(parent_base_addr) + "+" + hex(parent - parent_base_addr) if parent_base_addr == addr: return "" return get_rec_addr_string(parent_base_addr,visited_map) + M.get(parent_base_addr,text=curText) + arrow_right else: return ""
def vmmap(map=None): """ Print the virtal memory map, or the specific mapping for the provided address / module name. """ int_map = None str_map = None if isinstance(map, six.string_types): str_map = map elif isinstance(map, six.integer_types + (gdb.Value, )): int_map = int(map) print(M.legend()) for page in pwndbg.vmmap.get(): if str_map and str_map not in page.objfile: continue if int_map and int_map not in page: continue print(M.get(page.vaddr, text=str(page)))
def vmmap(map=None): """ Print the virtal memory map, or the specific mapping for the provided address / module name. """ int_map = None str_map = None if isinstance(map, six.string_types): str_map = map elif isinstance(map, six.integer_types + (gdb.Value,)): int_map = int(map) print(M.legend()) for page in pwndbg.vmmap.get(): if str_map and str_map not in page.objfile: continue if int_map and int_map not in page: continue print(M.get(page.vaddr, text=str(page)))
def malloc_chunk(addr): """ Prints out the malloc_chunk at the specified address. """ if not isinstance(addr, six.integer_types): addr = int(addr) chunk = value_from_type('struct malloc_chunk', addr) size = int(chunk['size']) prev_inuse = (size & PREV_INUSE) == 1 is_mmaped = (size & IS_MMAPED) == 1 non_main_arena = (size & NON_MAIN_ARENA) == 1 header = M.get(addr) if prev_inuse: header += yellow(' PREV_INUSE') if is_mmaped: header += yellow(' IS_MMAPED') if non_main_arena: header += yellow(' NON_MAIN_ARENA') print(header) print(chunk) return chunk
def leakfind(address=None, page_name=None, max_offset=0x40, max_depth=0x4, stride=0x1, negative_offset=0x0): if address is None: raise argparse.ArgumentTypeError('No starting address provided.') foundPages = pwndbg.vmmap.find(address) if not foundPages: raise argparse.ArgumentTypeError('Starting address is not mapped.') if not pwndbg.memory.peek(address): raise argparse.ArgumentTypeError( 'Unable to read from starting address.') max_depth = int(max_depth) # Just warn the user that a large depth might be slow. # Probably worth checking offset^depth < threshold. Do this when more benchmarking is established. if max_depth > 8: print("leakfind may take a while to run on larger depths.") stride = int(stride) address = int(address) max_offset = int(max_offset) negative_offset = int(negative_offset) # The below map stores a map of child address->(parent_address,parent_start_address) # In the above tuple, parent_address is the exact address with a pointer to the child adddress. # parent_start_address is an address that a previous address pointed to. # We need to store both so that we can nicely create our leak chain. visited_map = {} visited_set = {int(address)} address_queue = Queue() address_queue.put(int(address)) depth = 0 time_to_depth_increase = 0 # Run a bfs # TODO look into performance gain from checking if an address is mapped before calling pwndbg.memory.pvoid() # TODO also check using pwndbg.memory.read for possible performance boosts. while address_queue.qsize() > 0 and depth < max_depth: if time_to_depth_increase == 0: depth = depth + 1 time_to_depth_increase = address_queue.qsize() cur_start_addr = address_queue.get() time_to_depth_increase -= 1 for cur_addr in range(cur_start_addr - negative_offset, cur_start_addr + max_offset, stride): try: result = int(pwndbg.memory.pvoid(cur_addr)) if result in visited_map or result in visited_set: continue visited_map[result] = ( cur_addr, cur_start_addr ) # map is of form child->(parent,parent_start) address_queue.put(result) visited_set.add(result) except gdb.error: # That means the memory was unmapped. Just skip it if we can't read it. break # A map of length->list of lines. Used to let us print in a somewhat nice manner. output_map = {} arrow_right = C.arrow(' %s ' % config_arrow_right) for child in visited_map: child_page = pwndbg.vmmap.find(child) if child_page is not None: if page_name is not None and page_name not in child_page.objfile: continue line = get_rec_addr_string( child, visited_map) + M.get(child) + " " + M.get( child, text=child_page.objfile) chain_length = line.count(arrow_right) if chain_length in output_map: output_map[chain_length].append(line) else: output_map[chain_length] = [line] # Output sorted by length of chain for chain_length in output_map: for line in output_map[chain_length]: print(line) if pwndbg.qemu.is_qemu(): print( "\n[QEMU target detected - leakfind result might not be accurate; see `help vmmap`]" )
def ds(address, max): address = int(address) address &= pwndbg.arch.ptrmask print(M.get(address), repr(pwndbg.strings.get(address, max, whatever=True)))
def format(value, limit=LIMIT, code=True, offset=0, hard_stop=None, hard_end=0): """ Recursively dereferences an address into string representation, or convert the list representation of address dereferences into string representation. Arguments: value(int|list): Either the starting address to be sent to get, or the result of get (a list) limit(int): Number of valid pointers code(bool): Hint that indicates the value may be an instruction offset(int): Offset into the address to get the next pointer hard_stop(int): Value to stop on hard_end: Value to append when hard_stop is reached: null, value of hard stop, a string. Returns: A string representing pointers of each address and reference Strings format: 0x0804a10 —▸ 0x08061000 ◂— 0x41414141 """ limit = int(limit) # Allow results from get function to be passed to format if isinstance(value, list): chain = value else: chain = get(value, limit, offset, hard_stop, hard_end) arrow_left = C.arrow(' %s ' % config_arrow_left) arrow_right = C.arrow(' %s ' % config_arrow_right) # Colorize the chain rest = [] for link in chain: symbol = pwndbg.symbol.get(link) or None if symbol: symbol = '%#x (%s)' % (link, symbol) rest.append(M.get(link, symbol)) # If the dereference limit is zero, skip any enhancements. if limit == 0: return rest[0] # Otherwise replace last element with the enhanced information. rest = rest[:-1] # Enhance the last entry # If there are no pointers (e.g. eax = 0x41414141), then enhance # the only element there is. if len(chain) == 1: enhanced = pwndbg.enhance.enhance(chain[-1], code=code) # Otherwise, the last element in the chain is the non-pointer value. # We want to enhance the last pointer value. If an offset was used # chain failed at that offset, so display that offset. elif len(chain) < limit + 1: enhanced = pwndbg.enhance.enhance(chain[-2] + offset, code=code) else: enhanced = C.contiguous('%s' % config_contiguous) if len(chain) == 1: return enhanced return arrow_right.join(rest) + arrow_left + enhanced
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 print_line(name, addr, first, second, op, width = 20): print("{} {} = {} {} {:#x}".format(name.rjust(width), M.get(addr), M.get(first) if type(first) is not str else first.ljust(len(hex(addr))), op, second,))
def instruction(ins): asm = '%-06s %s' % (ins.mnemonic, ins.op_str) if pwndbg.config.syntax_highlight: asm = syntax_highlight(asm) is_branch = set(ins.groups) & capstone_branch_groups # Highlight the current line if enabled if pwndbg.config.highlight_pc and ins.address == pwndbg.regs.pc: asm = C.highlight(asm) # tl;dr is a branch? if ins.target not in (None, ins.address + ins.size): sym = pwndbg.symbol.get(ins.target) or None target = M.get(ins.target) const = ins.target_const # Use format string instead of hex() to avoid suffix 'l' or 'L' hextarget = '0x%x' % ins.target hexlen = len(hextarget) # If it's a constant expression, color it directly in the asm. if const: # TODO: Also colorize the address starts with '$' and '#' asm = asm.replace(hextarget, sym or target) if sym: asm = '%s <%s>' % (ljust_colored(asm, 36), target) # It's not a constant expression, but we've calculated the target # address by emulation. elif sym: asm = '%s <%s; %s>' % (ljust_colored(asm, 36), target, sym) # We were able to calculate the target, but there is no symbol # name for it. else: asm += '<%s>' % (target) # not a branch elif ins.symbol: if is_branch and not ins.target: asm = '%s <%s>' % (asm, ins.symbol) # XXX: not sure when this ever happens asm += '<-- file a pwndbg bug for this' else: asm = asm.replace(hex(ins.symbol_addr), ins.symbol) asm = '%s <%s>' % (ljust_colored(asm, 36), M.get(ins.symbol_addr)) # Style the instruction mnemonic if it's a branch instruction. if is_branch: asm = asm.replace(ins.mnemonic, branch(ins.mnemonic), 1) # If we know the conditional is taken, mark it as taken. if ins.condition is None: asm = ' ' + asm elif ins.condition: asm = on('✔ ') + asm else: asm = ' ' + asm return asm
def print_line(name, addr, first, second, op, width = 20): print("{} {} = {} {} {:#x}".format(name.rjust(width), M.get(addr), M.get(first) if not isinstance(first, str) else first.ljust(len(hex(addr).rstrip('L'))), op, second,))