def thread_cache(self): tcache_addr = pwndbg.symbol.address('tcache') # The symbol.address returns ptr to ptr to tcache struct, as in: # pwndbg> p &tcache # $1 = (tcache_perthread_struct **) 0x7ffff7fd76f0 # so we need to dereference it if tcache_addr is not None: tcache_addr = pwndbg.memory.pvoid(tcache_addr) if tcache_addr is None: tcache_addr = self._fetch_tcache_addr() if tcache_addr is not None: try: self._thread_cache = pwndbg.memory.poi(self.tcache_perthread_struct, tcache_addr) _ = self._thread_cache['entries'].fetch_lazy() except Exception as e: print(message.error('Error fetching tcache. GDB cannot access ' 'thread-local variables unless you compile with -lpthread.')) return None else: if not self.has_tcache(): print(message.warn('Your libc does not use thread cache')) return None print(message.error('Symbol \'tcache\' not found. Try installing libc ' 'debugging symbols and try again.')) return self._thread_cache
def validate_context_sections(): valid_values = [context.__name__.replace('context_', '') for context in context_sections.values()] # If someone tries to set an empty string, we let to do that informing about possible values # (so that it is possible to have no context at all) if not config_context_sections.value or config_context_sections.value.lower() in ('none', 'empty'): config_context_sections.value = '' print(message.warn("Sections set to be empty. FYI valid values are: %s" % ', '.join(valid_values))) return for section in config_context_sections.split(): if section not in valid_values: print(message.warn("Invalid section: %s, valid values: %s" % (section, ', '.join(valid_values)))) print(message.warn("(setting none of them like '' will make sections not appear)")) config_context_sections.revert_default() return
def update(): global abi global linux # Detect current ABI of client side by 'show osabi' # # Examples of strings returned by `show osabi`: # 'The current OS ABI is "auto" (currently "GNU/Linux").\nThe default OS ABI is "GNU/Linux".\n' # 'The current OS ABI is "GNU/Linux".\nThe default OS ABI is "GNU/Linux".\n' # 'El actual SO ABI es «auto» (actualmente «GNU/Linux»).\nEl SO ABI predeterminado es «GNU/Linux».\n' # 'The current OS ABI is "auto" (currently "none")' # # As you can see, there might be GDBs with different language versions # and so we have to support it there too. # Lets assume and hope that `current osabi` is returned in first line in all languages... current_osabi = gdb.execute('show osabi', to_string=True).split('\n')[0] # Currently we support those osabis: # 'GNU/Linux': linux # 'none': bare metal linux = 'GNU/Linux' in current_osabi if not linux: msg = M.warn( "The bare metal debugging is enabled since the gdb's osabi is '%s' which is not 'GNU/Linux'.\n" "Ex. the page resolving and memory de-referencing ONLY works on known pages.\n" "This option is based ib gdb client compile arguments (by default) and will be corrected if you load an ELF which has the '.note.ABI-tag' section.\n" "If you are debuging a program that runs on Linux ABI, please select the correct gdb client." % abi) print(msg)
def check_title_position(): valid_values = ['center', 'left', 'right'] if title_position not in valid_values: print( message.warn('Invalid title position: %s, must be one of: %s' % (title_position, ', '.join(valid_values)))) title_position.revert_default()
def thread_cache(self): """Locate a thread's tcache struct. If it doesn't have one, use the main thread's tcache. """ if self.has_tcache(): tcache = self.mp['sbrk_base'] + 0x10 if self.multithreaded: tcache_addr = pwndbg.memory.pvoid( pwndbg.symbol.address('tcache')) if tcache_addr != 0: tcache = tcache_addr try: self._thread_cache = pwndbg.memory.poi( self.tcache_perthread_struct, tcache) _ = self._thread_cache['entries'].fetch_lazy() except Exception as e: print( message.error( 'Error fetching tcache. GDB cannot access ' 'thread-local variables unless you compile with -lpthread.' )) return None return self._thread_cache else: print( message.warn( 'This version of GLIBC was not compiled with tcache support.' )) return None
def update(): global abi global linux # Detect current ABI of client side by 'show osabi' osabi_string = gdb.execute('show osabi', to_string=True) # The return string will be: # The current OS ABI is "auto" (currently "GNU/Linux"). match = re.search('currently "([^"]+)"', osabi_string) if match: # 'GNU/Linux': linux # 'none': bare metal abi = match.group(1) linux = 'Linux' in abi if not linux: msg = M.warn( "The bare metal debugging is enabled since the gdb's osabi is '%s' which is not 'GNU/Linux'.\n" "Ex. the page resolving and memory de-referencing ONLY works on known pages.\n" "This option is based ib gdb client compile arguments (by default) and will be corrected if you load an ELF which has the '.note.ABI-tag' section.\n" "If you are debuging a program that runs on Linux ABI, please select the correct gdb client." % abi) print(msg)
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 get_regs(*regs): result = [] if not regs and pwndbg.config.show_retaddr_reg: regs = pwndbg.regs.gpr + (pwndbg.regs.frame, pwndbg.regs.current.stack) + pwndbg.regs.retaddr + (pwndbg.regs.current.pc,) elif not regs: regs = pwndbg.regs.gpr + (pwndbg.regs.frame, pwndbg.regs.current.stack, pwndbg.regs.current.pc) if pwndbg.config.show_flags: regs += tuple(pwndbg.regs.flags) changed = pwndbg.regs.changed for reg in regs: if reg is None: continue if reg not in pwndbg.regs: print(message.warn("Unknown register: %r" % reg)) continue value = pwndbg.regs[reg] # Make the register stand out regname = C.register(reg.ljust(4).upper()) # Show a dot next to the register if it changed change_marker = "%s" % C.config_register_changed_marker m = ' ' * len(change_marker) if reg not in changed else C.register_changed(change_marker) if reg not in pwndbg.regs.flags: desc = pwndbg.chain.format(value) else: names = [] desc = C.flag_value('%#x' % value) last = pwndbg.regs.last.get(reg, 0) or 0 flags = pwndbg.regs.flags[reg] for name, bit in sorted(flags.items()): bit = 1<<bit if value & bit: name = name.upper() name = C.flag_set(name) else: name = name.lower() name = C.flag_unset(name) if value & bit != last & bit: name = C.flag_changed(name) names.append(name) if names: desc = '%s %s %s %s' % (desc, C.flag_bracket('['), ' '.join(names), C.flag_bracket(']')) result.append("%s%s %s" % (m, regname, desc)) return result
def get_regs(*regs): result = [] if not regs and pwndbg.config.show_retaddr_reg: regs = pwndbg.regs.gpr + (pwndbg.regs.frame, pwndbg.regs.current.stack ) + pwndbg.regs.retaddr + ( pwndbg.regs.current.pc, ) elif not regs: regs = pwndbg.regs.gpr + (pwndbg.regs.frame, pwndbg.regs.current.stack, pwndbg.regs.current.pc) if pwndbg.config.show_flags: regs += tuple(pwndbg.regs.flags) changed = pwndbg.regs.changed for reg in regs: if reg is None: continue if reg not in pwndbg.regs: message.warn("Unknown register: %r" % reg) continue value = pwndbg.regs[reg] # Make the register stand out regname = C.register(reg.ljust(4).upper()) # Show a dot next to the register if it changed change_marker = "%s" % C.config_register_changed_marker m = ' ' * len( change_marker) if reg not in changed else C.register_changed( change_marker) if reg in pwndbg.regs.flags: desc = C.format_flags(value, pwndbg.regs.flags[reg], pwndbg.regs.last.get(reg, 0)) else: desc = pwndbg.chain.format(value) result.append("%s%s %s" % (m, regname, desc)) return result
def arm_print_psr(): if pwndbg.arch.current not in ('arm', 'armcm'): print(message.warn("This is only available on ARM")) return reg = 'cpsr' if pwndbg.arch.current == 'arm' else 'xpsr' print('%s %s' % (reg, context.format_flags(getattr(pwndbg.regs, reg), pwndbg.regs.flags[reg])))
def check_style(): global formatter try: formatter = pygments.formatters.Terminal256Formatter( style=str(style) ) except pygments.util.ClassNotFound: print(message.warn("The pygment formatter style '%s' is not found, restore to default" % style)) style.revert_default()
def check_style(): try: formatter = pygments.formatters.Terminal256Formatter( style=str(style) ) except pygments.util.ClassNotFound: msg = message.warn("The pygment formatter style '%s' is not found, restore to default" % style) print(msg) style.value = style.default
def arm_print_psr(): if pwndbg.arch.current not in ("arm", "armcm"): print(message.warn("This is only available on ARM")) return reg = "cpsr" if pwndbg.arch.current == "arm" else "xpsr" print("%s %s" % (reg, context.format_flags(getattr(pwndbg.regs, reg), pwndbg.regs.flags[reg])))
def check_title_position(): valid_values = ["center", "left", "right"] if title_position not in valid_values: print( message.warn( "Invalid title position: %s, must be one of: %s" % (title_position, ", ".join(valid_values)) ) ) title_position.revert_default()
def resolve_heap(is_first_run=False): import pwndbg.heap.ptmalloc global current if resolve_heap_via_heuristic: current = pwndbg.heap.ptmalloc.HeuristicHeap() if not is_first_run and pwndbg.proc.alive and current.libc_has_debug_syms( ): print( message.warn( "You are going to resolve the heap via heuristic even though you have libc debug symbols." " This is not recommended!")) else: print( message.warn( "You are going to resolve the heap via heuristic. This might not work in all cases." )) else: current = pwndbg.heap.ptmalloc.DebugSymsHeap()
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 find_module(addr): mod_filter = lambda page: page.vaddr <= addr < page.vaddr + page.memsz pages = list(filter(mod_filter, pwndbg.vmmap.get())) if not pages: return None if len(pages) > 1: print(message.warn('Warning: There is more than one page containing address 0x%x (wtf?)', addr)) return pages[0]
def check_style(): global formatter try: formatter = pygments.formatters.Terminal256Formatter( style=str(style) ) # Reset the highlighted source cache from pwndbg.commands.context import get_highlight_source get_highlight_source._reset() except pygments.util.ClassNotFound: print(message.warn("The pygment formatter style '%s' is not found, restore to default" % style)) style.revert_default()
def check_style(): global formatter try: formatter = pygments.formatters.Terminal256Formatter(style=str(style)) # Reset the highlighted source cache from pwndbg.commands.context import get_highlight_source get_highlight_source._reset() except pygments.util.ClassNotFound: print( message.warn( "The pygment formatter style '%s' is not found, restore to default" % style)) style.revert_default()
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
def validate_context_sections(): # if trying to set an empty string, we assume default is wanted if not config_context_sections.value: config_context_sections.value = config_context_sections.default valid_values = [ context.__name__.replace('context_', '') for context in context_sections.values() ] for section in config_context_sections.split(): if section not in valid_values: print( message.warn("Invalid section: %s, valid values: %s" % (section, ', '.join(valid_values)))) config_context_sections.value = config_context_sections.default return
def attachp(target): try: resolved_target = int(target) except ValueError: # GDB supposedly supports device files, so let's try it here...: # <disconnect3d> hey, does anyone know what does `attach <device-file>` do? # <disconnect3d> is this an alias for `target extended /dev/ttyACM0` or similar? # <disconnect3d> I mean, `help attach` suggests that the `attach` command supports a device file target... # <simark> I had no idea # <simark> what you pass to attach is passed directly to target_ops::attach # <simark> so it must be very target-specific # <disconnect3d> how can it be target specific if it should attach you to a target? # <disconnect3d> or do you mean osabi/arch etc? # <simark> in "attach foo", foo is interpreted by the target you are connected to # <simark> But all targets I can find interpret foo as a PID # <simark> So it might be that old targets had some other working mode if _is_device(target): resolved_target = target else: try: pids = check_output(["pidof", target]).decode().rstrip("\n").split(" ") except FileNotFoundError: print(message.error("Error: did not find `pidof` command")) return except CalledProcessError: pids = [] if not pids: print(message.error("Process %s not found" % target)) return if len(pids) > 1: print( message.warn("Found pids: %s (use `attach <pid>`)" % ", ".join(pids))) return resolved_target = int(pids[0]) print(message.on("Attaching to %s" % resolved_target)) try: gdb.execute("attach %s" % resolved_target) except gdb.error as e: print(message.error("Error: %s" % e))
def get_file(path): """ Downloads the specified file from the system where the current process is being debugged. If the `path` is prefixed with "target:" the prefix is stripped (to support remote target paths properly). Returns: The local path to the file """ assert path.startswith("/") or path.startswith( "target:"), "get_file called with incorrect path" if path.startswith("target:"): path = path[7:] # len('target:') == 7 local_path = path qemu_root = pwndbg.qemu.root() if qemu_root: return os.path.join(qemu_root, path) elif pwndbg.remote.is_remote(): if not pwndbg.qemu.is_qemu(): local_path = tempfile.mktemp(dir=pwndbg.symbol.remote_files_dir) error = None try: error = gdb.execute('remote get "%s" "%s"' % (path, local_path), to_string=True) except gdb.error as e: error = e if error: raise OSError("Could not download remote file %r:\n" "Error: %s" % (path, error)) else: print( message.warn( "pwndbg.file.get(%s) returns local path as we can't download file from QEMU" % path)) return local_path
def thread_cache(self): tcache_addr = pwndbg.symbol.address('tcache') if tcache_addr is not None: try: self._thread_cache = pwndbg.memory.poi(self.tcache_perthread_struct, tcache_addr) _ = self._thread_cache['entries'].fetch_lazy() except Exception as e: print(message.error('Error fetching tcache. GDB cannot access ' 'thread-local variables unless you compile with -lpthread.')) else: if not self.has_tcache(): print(message.warn('Your libc does not use thread cache')) return None print(message.error('Symbol \'tcache\' not found. Try installing libc ' 'debugging symbols and try again.')) return self._thread_cache
def canary(): global_canary, at_random = canary_value() if global_canary is None or at_random is None: print(message.error("Couldn't find AT_RANDOM - can't display canary.")) return print(message.notice("AT_RANDOM = %#x # points to (not masked) global canary value" % at_random)) print(message.notice("Canary = 0x%x" % global_canary)) stack_canaries = list( pwndbg.search.search(pwndbg.arch.pack(global_canary), mappings=pwndbg.stack.stacks.values()) ) if not stack_canaries: print(message.warn('No valid canaries found on the stacks.')) return print(message.success('Found valid canaries on the stacks:')) for stack_canary in stack_canaries: pwndbg.commands.telescope.telescope(address=stack_canary, count=1)
def canary(): global_canary, at_random = canary_value() if global_canary is None or at_random is None: print(message.error("Couldn't find AT_RANDOM - can't display canary.")) return print(message.notice("AT_RANDOM = %#x # points to (not masked) global canary value" % at_random)) print(message.notice("Canary = 0x%x (may be incorrect on != glibc)" % global_canary)) stack_canaries = list( pwndbg.search.search(pwndbg.arch.pack(global_canary), mappings=pwndbg.stack.stacks.values()) ) if not stack_canaries: print(message.warn('No valid canaries found on the stacks.')) return print(message.success('Found valid canaries on the stacks:')) for stack_canary in stack_canaries: pwndbg.commands.telescope.telescope(address=stack_canary, count=1)
def find_fake_fast(addr, size=None): """Find candidate fake fast chunks overlapping the specified address.""" psize = pwndbg.arch.ptrsize allocator = pwndbg.heap.current align = allocator.malloc_alignment min_fast = allocator.min_chunk_size max_fast = allocator.global_max_fast max_fastbin = allocator.fastbin_index(max_fast) start = int(addr) - max_fast + psize if start < 0: print( message.warn( 'addr - global_max_fast is negative, if the max_fast is not corrupted, you gave wrong address' )) start = 0 # TODO, maybe some better way to handle case when global_max_fast is overwritten with something large mem = pwndbg.memory.read(start, max_fast - psize, partial=True) fmt = { 'little': '<', 'big': '>' }[pwndbg.arch.endian] + { 4: 'I', 8: 'Q' }[psize] if size is None: sizes = range(min_fast, max_fast + 1, align) else: sizes = [int(size)] print(C.banner("FAKE CHUNKS")) for size in sizes: fastbin = allocator.fastbin_index(size) for offset in range((max_fastbin - fastbin) * align, max_fast - align + 1): candidate = mem[offset:offset + psize] if len(candidate) == psize: value = struct.unpack(fmt, candidate)[0] if allocator.fastbin_index(value) == fastbin: malloc_chunk(start + offset - psize, fake=True)
def cpsr(): if pwndbg.arch.current != 'arm': print(message.warn("This is only available on ARM")) return cpsr = pwndbg.regs.cpsr N = cpsr & (1 << 31) Z = cpsr & (1 << 30) C = cpsr & (1 << 29) V = cpsr & (1 << 28) T = cpsr & (1 << 5) result = [ context.flag_set('N') if N else context.flag_unset('n'), context.flag_set('Z') if Z else context.flag_unset('z'), context.flag_set('C') if C else context.flag_unset('c'), context.flag_set('V') if V else context.flag_unset('v'), context.flag_set('T') if T else context.flag_unset('t') ] print('CPSR %s %s %s %s' % (context.flag_value('%#x' % cpsr), context.flag_bracket('['), ' '.join(result), context.flag_bracket(']')))
def thread_cache(self): tcache_addr = pwndbg.symbol.address('tcache') # The symbol.address returns ptr to ptr to tcache struct, as in: # pwndbg> p &tcache # $1 = (tcache_perthread_struct **) 0x7ffff7fd76f0 # so we need to dereference it if tcache_addr is not None: tcache_addr = pwndbg.memory.pvoid(tcache_addr) if tcache_addr is None: tcache_addr = self._fetch_tcache_addr() if tcache_addr is not None: try: self._thread_cache = pwndbg.memory.poi( self.tcache_perthread_struct, tcache_addr) _ = self._thread_cache['entries'].fetch_lazy() except Exception as e: print( message.error( 'Error fetching tcache. GDB cannot access ' 'thread-local variables unless you compile with -lpthread.' )) return None else: if not self.has_tcache(): print(message.warn('Your libc does not use thread cache')) return None print( message.error( 'Symbol \'tcache\' not found. Try installing libc ' 'debugging symbols and try again.')) return self._thread_cache
def check_title_position(): valid_values = ['center', 'left', 'right'] if title_position not in valid_values: print(message.warn('Invalid title position: %s, must be one of: %s' % (title_position, ', '.join(valid_values)))) title_position.revert_default()
def leakfind(address=None, page_name=None, max_offset=0x40, max_depth=0x4, step=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( message.warn("leakfind may take a while to run on larger depths.")) stride = int(step) 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: cur_addr &= pwndbg.arch.ptrmask 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 get_regs(*regs): result = [] if not regs and pwndbg.config.show_retaddr_reg: regs = pwndbg.regs.gpr + (pwndbg.regs.frame, pwndbg.regs.current.stack ) + pwndbg.regs.retaddr + ( pwndbg.regs.current.pc, ) elif not regs: regs = pwndbg.regs.gpr + (pwndbg.regs.frame, pwndbg.regs.current.stack, pwndbg.regs.current.pc) if pwndbg.config.show_flags: regs += tuple(pwndbg.regs.flags) changed = pwndbg.regs.changed for reg in regs: if reg is None: continue if reg not in pwndbg.regs: message.warn("Unknown register: %r" % reg) continue value = pwndbg.regs[reg] # Make the register stand out regname = C.register(reg.ljust(4).upper()) # Show a dot next to the register if it changed change_marker = "%s" % C.config_register_changed_marker m = ' ' * len( change_marker) if reg not in changed else C.register_changed( change_marker) if reg not in pwndbg.regs.flags: desc = pwndbg.chain.format(value) else: names = [] desc = C.flag_value('%#x' % value) last = pwndbg.regs.last.get(reg, 0) or 0 flags = pwndbg.regs.flags[reg] for name, bit in sorted(flags.items()): bit = 1 << bit if value & bit: name = name.upper() name = C.flag_set(name) else: name = name.lower() name = C.flag_unset(name) if value & bit != last & bit: name = C.flag_changed(name) names.append(name) if names: desc = '%s %s %s %s' % (desc, C.flag_bracket('['), ' '.join(names), C.flag_bracket(']')) result.append("%s%s %s" % (m, regname, desc)) return result