def current_line(self): if self.is_optimized_out(): return '(frame information optimized out)' filename = self.filename() inferior_cwd = '/proc/%d/cwd' % gdb.selected_inferior().pid if filename.startswith('/dev/fd/'): filename.replace('/dev/fd/', '/proc/%d/fd/' % gdb.selected_inferior().pid, 1) else: filename = os.path.join(inferior_cwd, filename) try: sourcefile = self.OpenFile(filename) except IOError: # couldn't find the file, let's try extracting the path from the frame filename = self.extract_filename() if filename.endswith('.pyc'): filename = filename[:-1] try: sourcefile = self.OpenFile(filename) except IOError: return '<file not available>' for _ in xrange(self.current_line_num()): line = sourcefile.readline() sourcefile.close() return line if line else '<file not available>'
def _next_instn_jump_len(gdb_frame): '-> None means don\'t jump' try: arch_name = gdb_frame.architecture().name() except AttributeError: arch_name = None if arch_name.startswith('powerpc:'): # 'powerpc:common64' on ppc64 big endian i = bytes(gdb.selected_inferior().read_memory(gdb.parse_and_eval('$pc'), 4)) if (i == b'\x7d\x82\x10\x08') or (i == b'\x08\x10\x82\x7d'): return 4 else: # not stopped on a breakpoint instruction return None triplet = _target_triplet() if re.match(r'^arm-', triplet): i = bytes(gdb.selected_inferior().read_memory(gdb.parse_and_eval('$pc'), 4)) if i == b'\xf0\x01\xf0\xe7': return 4 elif i.startswith(b'\x01\xde'): return 2 elif i == b'\xf0\xf7\x00\xa0 ': # 'arm_linux_thumb2_le_breakpoint' from arm-linux-tdep.c in GDB return 4 else: # not stopped on a breakpoint instruction return None return None
def invoke(self, *args): self.process.set_inferior(gdb.selected_inferior(), gdb.selected_thread()) selected_thread = gdb.selected_thread() if selected_thread is None: raise Exception("You are calling the 'NotifySyscall' function with no thread running. This should never happen because only the catchpoint-syscall should call this function and by definition those require an running thread.") my_id = (gdb.selected_inferior().num, selected_thread.num) syscall_tracer = self._syscall_tracer_by_invokation_id.get(my_id) am_at_syscall_enter = syscall_tracer is None # if None no tracing was started so we must be at the enter of a syscall if am_at_syscall_enter: syscall_tracer = PtraceSyscallPublisher(self.gdb_module, self.process, Opts()) self._syscall_tracer_by_invokation_id[my_id] = syscall_tracer # save this tracer to be called at the syscall's exit syscall_tracer.enter() else: syscall_tracer = self._syscall_tracer_by_invokation_id[my_id] # created at the syscall's enter syscall_tracer.exit() del self._syscall_tracer_by_invokation_id[my_id] # clean up # publish the data from the syscall's enter/exit syscall_tracer.publish_syscall() return False
def setup_tasks(self): init_task = gdb.lookup_global_symbol('init_task') task_list = init_task.value()['tasks'] self.pid_to_task_struct = {} for task in list_for_each_entry(task_list, init_task.type, 'tasks'): thread = gdb.selected_inferior().new_thread((1, task['pid'], 0), task) thread.name = task['comm'].string() gdb.selected_inferior().executing = False
def dereference(self, pointer, target_id=0): """ Recursively dereference a pointer for display """ if isinstance(pointer, six.integer_types): fmt = ('<' if self.get_byte_order() == 'little' else '>') + {2: 'H', 4: 'L', 8: 'Q'}[self.get_addr_size()] addr = pointer chain = [] # recursively dereference while True: try: mem = gdb.selected_inferior().read_memory(addr, self.get_addr_size()) (ptr,) = struct.unpack(fmt, mem) if ptr in [x[1] for x in chain]: break chain.append(('pointer', addr)) addr = ptr except gdb.MemoryError: log.exception("Dereferencing pointer 0x{:X}".format(addr)) break except OverflowError: log.exception("Dereferencing pointer 0x{:X}".format(addr)) break # get some info for the last pointer # first try to resolve a symbol context for the address if len(chain): p, addr = chain[-1] output = gdb.execute('info symbol 0x{:x}'.format(addr), to_string=True) log.debug('output = {}'.format(output)) if 'No symbol matches' not in output: chain.append(('symbol', output.strip())) log.debug("symbol context: {}".format(str(chain[-1]))) else: log.debug("no symbol context, trying as a string") mem = gdb.selected_inferior().read_memory(addr, 2) if ord(mem[0]) <= 127 and ord(mem[0]) != 0: a = [] for i in range(0, self.max_string): mem = gdb.selected_inferior().read_memory(addr + i, 1) if ord(mem[0]) == 0 or ord(mem[0]) > 127: break if isinstance(mem, memoryview): a.append(mem.tobytes().decode('latin1')) else: a.append(str(mem)) chain.append(('string', ''.join(a))) log.debug("chain: {}".format(chain)) else: chain = [] return chain
def setup_tasks(self): init_task = gdb.lookup_global_symbol('init_task') task_list = init_task.value()['tasks'] runqueues = gdb.lookup_global_symbol('runqueues') rqs = get_percpu_var(runqueues) rqscurrs = {long(x["curr"]) : k for (k, x) in rqs.items()} self.pid_to_task_struct = {} print("Loading tasks...", end='') sys.stdout.flush() task_count = 0 tasks = [] for taskg in list_for_each_entry(task_list, init_task.type, 'tasks'): tasks.append(taskg) for task in list_for_each_entry(taskg['thread_group'], init_task.type, 'thread_group'): tasks.append(task) for task in tasks: cpu = None regs = None active = long(task.address) in rqscurrs if active: cpu = rqscurrs[long(task.address)] regs = self.kdump.attr.cpu[cpu].reg ltask = LinuxTask(task, active, cpu, regs) ptid = (LINUX_KERNEL_PID, task['pid'], 0) try: thread = gdb.selected_inferior().new_thread(ptid, ltask) except gdb.error as e: print("Failed to setup task @{:#x}".format(long(task.address))) continue thread.name = task['comm'].string() self.arch.setup_thread_info(thread) ltask.attach_thread(thread) ltask.set_get_stack_pointer(self.arch.get_stack_pointer) crash.cache.tasks.cache_task(ltask) task_count += 1 if task_count % 100 == 0: print(".", end='') sys.stdout.flush() print(" done. ({} tasks total)".format(task_count)) gdb.selected_inferior().executing = False
def stop(self): buf_size = gdb.parse_and_eval(self.size) mod_count = int(math.floor(buf_size * 8 * gdb.parse_and_eval(self.factor))) if mod_count < 1: mod_count = 1 for i in range(0, mod_count): offset = random.randint(0,buf_size-1) rand_byte = gdb.parse_and_eval('%s + %d' % (self.target, offset)) buf = gdb.selected_inferior().read_memory(rand_byte, 1) orig = struct.unpack('B', buf[0])[0] rand_bit = random.randint(0,7) update = (orig ^ (1 << rand_bit)) gdb.selected_inferior().write_memory(rand_byte, chr(update), 1) return False
def invoke(self, arg='', from_tty=False, sig=0): gdb.newest_frame().select() regs = [0] * 19 # parse_and_eval seems to be the only way to access target registers for i in range(16): regs[i] = int(gdb.parse_and_eval("(unsigned long)$r%d" % i)) regs[16] = int(gdb.parse_and_eval("(unsigned long)$xpsr")) # Don't know how to include other registers in core dump prstatus = ARM_prstatus() prstatus.pr_cursig = sig # Is it possible to include a target register description? notes = note_desc("CORE", 1, prstatus.dumps() + struct.pack("<19L", *regs)) inf = gdb.selected_inferior() # How do we query the memory map from GDB? # TODO: Use 'info mem' ram = inf.read_memory(0x20000000, 128*1024) ccmram = inf.read_memory(0x10000000, 64*1024) scs = inf.read_memory(0xE000ED00, 0x40) core = CoreFile() core.set_type(Elf32_Ehdr.ET_CORE) core.set_machine(0x28) #ARM core.add_program(Elf32_Phdr.PT_NOTE, 0, notes) core.add_program(Elf32_Phdr.PT_LOAD, 0x10000000, ccmram) core.add_program(Elf32_Phdr.PT_LOAD, 0x20000000, ram) core.add_program(Elf32_Phdr.PT_LOAD, 0xE000ED00, scs) fn = arg if arg else gcore_file_name.value fn += "-" + time.strftime("%y%m%d-%H%M%S") core.dump(open(fn, "w")) print "(core dumped to %r)" % fn
def invoke(self, arg, from_tty): args = gdb.string_to_argv(arg) # generally, we type "plot someimage" in the GDB commandline # where "someimage" is an instance of cv::Mat v = gdb.parse_and_eval(args[0]) # the value v is a gdb.Value object of C++ # code's cv::Mat, we need to translate to # a python object under cv2.cv image_size = (v['cols'],v['rows']) # print v # these two below lines do not work. I don't know why # channel = gdb.execute("call "+ args[0] + ".channels()", False, True) # channel = v.channels(); CV_8U =0 CV_8S =1 CV_16U=2 CV_16S=3 CV_32S=4 CV_32F=5 CV_64F=6 CV_USRTYPE1=7 CV_CN_MAX = 512 CV_CN_SHIFT = 3 CV_MAT_CN_MASK = (CV_CN_MAX - 1) << CV_CN_SHIFT flags = v['flags'] channel = (((flags) & CV_MAT_CN_MASK) >> CV_CN_SHIFT) + 1 CV_DEPTH_MAX = (1 << CV_CN_SHIFT) CV_MAT_DEPTH_MASK = CV_DEPTH_MAX - 1 depth = (flags) & CV_MAT_DEPTH_MASK IPL_DEPTH_SIGN = 0x80000000 cv_elem_size = (((4<<28)|0x8442211) >> depth*4) & 15 if (depth == CV_8S or depth == CV_16S or depth == CV_32S): mask = IPL_DEPTH_SIGN else: mask = 0 ipl_depth = cv_elem_size*8 | mask img = cv.CreateImageHeader(image_size, ipl_depth, channel) # conver the v['data'] type to "char*" type char_type = gdb.lookup_type("char") char_pointer_type =char_type.pointer() buffer = v['data'].cast(char_pointer_type) # read bytes from inferior's memory, because # we run the opencv-python module in GDB's own process # otherwise, we use memory corss processes buf = v['step']['buf'] bytes = buf[0] * v['rows'] # buf[0] is the step? Not quite sure. inferior = gdb.selected_inferior() mem = inferior.read_memory(buffer, bytes) # set the img's raw data cv.SetData(img, mem) mat = np.asarray(img[:,:]) print ("Type: {}".format(mat.dtype)) print (mat)
def invoke(self, arg, from_tty): args = gdb.string_to_argv(arg) v = gdb.parse_and_eval(args[0]) strType = gdb.execute("print "+ args[0] + ".type()", False, True) # strType contains gdb answers as a string of the form "$2 = 42" img = cv.CreateMat(v['rows'], v['cols'], int(strType.split(" ")[2])) # convert v['data'] to char* char_type = gdb.lookup_type("char") char_pointer_type = char_type.pointer() buffer = v['data'].cast(char_pointer_type) # read bytes from inferior's process memory buf = v['step']['buf'] bytes = buf[0] * v['rows'] inferior = gdb.selected_inferior() mem = inferior.read_memory(buffer, bytes) # set the matrix raw data cv.SetData(img, mem) # save matrix as an xml file and open it with matrix viewer cv.Save("/tmp/dump.xml", img, "matrix") call(["matrix-viewer", "/tmp/dump.xml"])
def get_current_inferior(inferior_repository, inferior_factory): inferior_num = gdb.selected_inferior().num if not inferior_repository.has_inferior(inferior_num): inferior = inferior_factory.create_inferior(inferior_num) inferior_repository.add_inferior(inferior) return inferior_repository.get_inferior(inferior_num)
def search(searchfor): value = searchfor size = None i = gdb.selected_inferior() maps = pwndbg.vmmap.get() hits = [] for vmmap in maps: start = vmmap.vaddr end = start + vmmap.memsz while True: # No point in searching if we can't read the memory if not pwndbg.memory.peek(start): break start = i.search_memory(start, end - start, searchfor) if start is None: break # For some reason, search_memory will return a positive hit # when it's unable to read memory. if not pwndbg.memory.peek(start): break yield start start += len(searchfor)
def print_ucontext(arg, from_tty): """print-context: print a ucontext_t* from a signal handler""" # Assumes linux/amd64. ctxt = gdb.parse_and_eval(arg) size_t = "Q" stack_t = "Pi" + size_t stack_fields = ("ss_sp", "ss_flags", "ss_size") mcontext_t = "qqqqqqqqqqqqqqqqqqhhhhqqqq" + "P" + 8 * "Q" mcontext_fields = ("R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15", "RDI", "RSI", "RBP", "RBX", "RDX", "RAX", "RCX", "RSP", "RIP", "EFLAGS", "CS", "GS", "FS", "__pad0", "ERR", "TRAPNO", "OLDMASK", "CR2", "fpregs*") + 8 * ("__reserved1",) ucontext_t = "LP" + stack_t + mcontext_t # + uc_sigmask + fpregs ucontext_fields = ("uc_flags", "uc_link") + stack_fields + mcontext_fields nbytes = struct.calcsize(ucontext_t) data = gdb.selected_inferior().read_memory(ctxt, nbytes) fields = struct.unpack(ucontext_t, data) for field, value in zip(ucontext_fields, fields): if field.startswith("__"): continue print("%-10s %#18x %20d" % (field, value, value))
def _update(self): # Update name in case it changed self.name = self.tp.dereference()['p_name'].string() self.regs = list(reg_cache) # Make a copy of the list if self.tp == currp: self.active = True self._update_frame() return self.active = False r13 = self.tp.dereference()['p_ctx']['r13'] longtype = gdb.lookup_type('unsigned long') self.regs[13] = int((r13+1).cast(longtype)) self.regs[15] = int(r13['lr'].cast(longtype)) for i in range(4, 12): self.regs[i] = int(r13['r%d'%i].cast(longtype)) self._update_frame() # Attempt the nasty unwind out of _port_switch_from_isr # get function for pc if str(self.block.function) == "_port_switch_from_isr": #if here pop exception frame from stack... ex_struct = "<8L" stack = gdb.selected_inferior().read_memory(self.regs[13], struct.calcsize(ex_struct)) stack = struct.unpack(ex_struct, stack) self.regs[:4] = stack[:4] self.regs[12] = stack[4] self.regs[14] = stack[5] self.regs[15] = stack[6] self.regs[16] = stack[7] # TODO check for extended/standard frame self.regs[13] += 0x68 # size of extended frame self._update_frame()
def invoke(self, arg, from_tty): argv = gdb.string_to_argv(arg) if len(argv) != 2: raise gdb.GdbError('hex-dump takes exactly 2 arguments.') addr = gdb.parse_and_eval(argv[0]).cast( gdb.lookup_type('void').pointer()) try: bytes = int(gdb.parse_and_eval(argv[1])) except ValueError: raise gdb.GdbError('Byte count numst be an integer value.') inferior = gdb.selected_inferior() align = gdb.parameter('hex-dump-align') width = gdb.parameter('hex-dump-width') if width == 0: width = 16 mem = inferior.read_memory(addr, bytes) pr_addr = int(str(addr), 16) pr_offset = width if align: pr_offset = width - (pr_addr % width) pr_addr -= pr_addr % width for group in groups_of(mem, width, pr_offset): print '0x%x: ' % (pr_addr,) + ' '*(width - pr_offset), print ' '.join(['%02X' % (ord(g),) for g in group]) + \ ' ' * (width - len(group) if pr_offset == width else 0) + ' ', print ' '*(width - pr_offset) + ''.join( [g if isgraph(g) or g == ' ' else '.' for g in group]) pr_addr += width pr_offset = width
def saveCurrentState(state): curr_thread = gdb.selected_thread() for thread in gdb.selected_inferior().threads(): if not thread.num in state: thread.switch() state[thread.num] = HermitTaskState() curr_thread.switch()
def read(addr, count, partial=False): result = b'' try: result = gdb.selected_inferior().read_memory(addr, count) except gdb.error as e: if not partial: raise if not hasattr(e, 'message'): e.message=str(e) stop_addr = int(e.message.split()[-1], 0) if stop_addr != addr: return read(addr, stop_addr-addr) # QEMU will return the start address as the failed # read address. Try moving back a few pages at a time. stop_addr = addr + count # Move the stop address down to the previous page boundary stop_addr &= PAGE_MASK while stop_addr > addr: result = read(addr, stop_addr-addr) if result: return result # Move down by another page stop_addr -= PAGE_SIZE # if pwndbg.compat.python3: # result = bytes(result) return bytearray(result)
def stop(self): """Method called by GDB when the breakpoint is triggered.""" # Read the i-th argument of an x86_64 function call args = ["$rdi", "$rsi", "$rdx", "$rcx"] fmt_addr = int(gdb.parse_and_eval(args[self.fmt_idx])) # Parse /proc/<pid>/maps for this process proc_map = [] with open("/proc/%d/maps" % gdb.selected_inferior().pid) as fp: proc_map = self._parse_map(fp.read()) # Find the memory range which contains our format address for mapping in proc_map: if mapping["start"] <= fmt_addr < mapping["end"]: break else: print "%016x belongs to an unknown memory range" % fmt_addr return True # Check the memory permissions if "w" in mapping["perms"]: print "Format string in writable memory!" return True return False
def step_bps(self, selector): inf = gdb.selected_inferior() bps = [] for (name, bloc) in self.dbg_map['blocs'].items(): buf = str(inf.read_memory(bloc['addr'], bloc['size']) ) il = Decode(bloc['addr'], buf, Decode32Bits) if len(il) != len(bloc['insns']): raise Exception( "decoded instruction list of different size than instruction map: %d != %d" % ( len(il), len(bloc['insns']) ) ) for i in range(0, len(il)): ins = il[i] if bloc['insns'][i]['dummy']: continue if not selector(bloc['insns'][i]['src_lineno'], bloc['insns'][i]['sir_lineno']): continue bps.append(MultiBP( bps, "*0x%08x" % (ins[0]) )) return bps
def get_fragment_instances(): # Walk the threadlist to find fragment instance ids. Assumes IMPALA-6416, so # this will not work with releases older than Impala 2.12. It may be possible # to search for FragmentInstanceState::Exec() in the older releases to get # to the FInstIDs. Returns a dictionary of FInstID->[gdb thread ID]. # Note that multiple threads might be doing tasks for the same FInstID. fragment_instances = defaultdict(list) for thread in gdb.selected_inferior().threads(): thread.switch() f = gdb.newest_frame() while f: # Skip unresolved frame if gdb.Frame.name(f) is None: f = gdb.Frame.older(f) continue if 'impala::Thread::SuperviseThread' in gdb.Frame.name(f): gdb.Frame.select(f) block = gdb.Frame.block(f) gdb.lookup_symbol('thread_debug_info', block) tdi = f.read_var('thread_debug_info') # No valid thread_debug_info if not tdi: break hi = long(tdi['instance_id_']['hi']) lo = long(tdi['instance_id_']['lo']) fi = "%lx:%lx" % (hi, lo) if fi != "0:0": fragment_instances[fi.strip('"')].append(thread.num) break f = gdb.Frame.older(f) return fragment_instances
def update(): """ For each running thread, updates the known address range for its stack. """ curr_thread = gdb.selected_thread() try: for thread in gdb.selected_inferior().threads(): thread.switch() sp = pwndbg.regs.sp # If we don't already know about this thread, create # a new Page mapping for it. page = stacks.get(thread.ptid, None) if page is None: start = pwndbg.memory.find_lower_boundary(sp) stop = find_upper_stack_boundary(sp) page = pwndbg.memory.Page(start, stop-start, 6 if not is_executable() else 7, 0, '[stack]') stacks[thread.ptid] = page continue elif page.objfile is None: page.objfile = '[stack]' # If we *DO* already know about this thread, just # update the lower boundary. low = pwndbg.memory.find_lower_boundary(page.vaddr) if low != page.vaddr: page.memsz += (page.vaddr - low) page.vaddr = low finally: curr_thread.switch()
def invoke(self, arg, from_tty): args = gdb.string_to_argv(arg) v = gdb.parse_and_eval(args[0]) cols, rows = int(v['cols']), int(v['rows']) channel, depth = self.decode_flag(v['flags']) if depth != CV_8U: print("support CV_8U only") return # conver the v['data'] type to "char*" type char_type = gdb.lookup_type("char") char_pointer_type = char_type.pointer() buffer_ptr = v['data'].cast(char_pointer_type) # read bytes from inferior's memory, because # we run the opencv-python module in GDB's own process # otherwise, we use memory corss processes buf = v['step']['buf'] bytes_cnt = buf[0] * v['rows'] inferior = gdb.selected_inferior() mem = inferior.read_memory(buffer_ptr, bytes_cnt) img = np.frombuffer(mem, dtype='uint8', count=int(bytes_cnt)) img = img.reshape(rows, cols, channel) # cv2.startWindowThread() cv2.namedWindow('viewer') cv2.imshow('viewer', img) cv2.waitKey(0) cv2.destroyWindow('viewer')
def stop(self): args = ["$ebp+8"] fmt_addr = gdb.parse_and_eval(args[self.fmt_idx]) proc_map = [] with open("/proc/%d/maps" % gdb.selected_inferior().pid) as fp: proc_map = self._parse_map(fp.read()) for mapping in proc_map: if mapping["start"] <= fmt_addr < mapping["end"]: break else: print("%08x belongs to an unknown memory range" % fmt_addr) return True if "w" in mapping["perms"]: print("Format string in writable memory!") #gdb.execute('set logging off') #gdb.execute('set logging on') #gdb.execute('set logging overwrite on') gdb.execute('bt -1') return True #gdb.execute('quit') return False
def all_traces(): constants = TraceConstants() inf = gdb.selected_inferior() trace_log = gdb.lookup_global_symbol('trace_log').value() max_trace = ulong(gdb.parse_and_eval('max_trace')) trace_log = inf.read_memory(trace_log.address, max_trace) trace_page_size = ulong(gdb.parse_and_eval('trace_page_size')) last = ulong(gdb.lookup_global_symbol('trace_record_last').value()['_M_i']) last %= max_trace pivot = align_up(last, trace_page_size) trace_log = trace_log[pivot:] + trace_log[:pivot] last += max_trace - pivot backtrace_len = constants.backtrace_len i = 0 while i < last: tp_key, thread, time, cpu, flags = struct.unpack('QQQII', trace_log[i:i+32]) if tp_key == 0: i = align_up(i + 8, trace_page_size) continue tp = gdb.Value(tp_key).cast(gdb.lookup_type('tracepoint_base').pointer()) sig = sig_to_string(ulong(tp['sig'])) # FIXME: cache i += 32 backtrace = None if flags & 1: backtrace = struct.unpack('Q' * backtrace_len, trace_log[i:i+8*backtrace_len]) i += 8 * backtrace_len size = struct.calcsize(sig) data = struct.unpack(sig, trace_log[i:i+size]) i += size i = align_up(i, 8) yield Trace(tp, thread, time, cpu, data, backtrace=backtrace)
def invoke(self, arg="", from_tty=False, sig=SIGINT): # Iterate over each ChibiOS thread and add a PRSTATUS note descriptor # for the general-purposes registers. notes = b"" for t in chibios.thread_cache: prstatus = corefile.ARM_prstatus() if t.active: # Only set signal for the running thread prstatus.pr_cursig = sig prstatus.pr_pid = t.lwp # Is it possible to include a target register description? notes += corefile.note_desc("CORE", 1, prstatus.dumps() + struct.pack("<19L", *t.regs)) inf = gdb.selected_inferior() # How do we query the memory map from GDB? # TODO: Use 'info mem' ram = inf.read_memory(0x20000000, 128 * 1024) ccmram = inf.read_memory(0x10000000, 64 * 1024) scs = inf.read_memory(0xE000ED00, 0x40) core = corefile.CoreFile() core.set_type(corefile.ET_CORE) core.set_machine(0x28) # ARM core.add_program(corefile.PT_NOTE, 0, notes) core.add_program(corefile.PT_LOAD, 0x10000000, ccmram) core.add_program(corefile.PT_LOAD, 0x20000000, ram) core.add_program(corefile.PT_LOAD, 0xE000ED00, scs) fn = arg if arg else gcore_file_name.value fn += "-" + time.strftime("%y%m%d-%H%M%S") core.dump(open(fn, "wb")) print("(core dumped to %r)" % fn)
def handle_basetype(self, tp_name, p): basetype_dict = {"type": tp_name} if tp_name in "bool int long": obj = gdb.parse_and_eval("*(PyIntObject *)(%s)" % p) basetype_dict["value"] = str(obj["ob_ival"]) elif tp_name == "float": obj = gdb.parse_and_eval("*(PyFloatObject *)(%s)" % p) basetype_dict["value"] = str(obj["ob_fval"]) elif tp_name == "str": obj = gdb.parse_and_eval("*(PyStringObject *)(%s)" % p) inferior = gdb.selected_inferior() ob_size = int(str(obj["ob_size"])) value = inferior.read_memory(int(str(obj["ob_sval"].address), 16), ob_size) basetype_dict["value"] = str(value) basetype_dict["length"] = ob_size elif tp_name == "bytearray": obj = gdb.parse_and_eval("*(PyByteArrayObject *)(%s)" % p) basetype.update({ "value": obj["ob_bytes"].string(), "ob_alloc": obj["ob_alloc"].string(), "ob_exports": obj["ob_exports"].string() }) elif tp_name == "complex": obj = gdb.parse_and_eval("*(PyComplexObject *)(%s)" % p) basetype_dict["real"] = str(obj["cval"]["real"]) basetype_dict["image"] = str(obj["cval"]["imag"]) return basetype_dict
def reactor_threads(): orig = gdb.selected_thread() for t in gdb.selected_inferior().threads(): t.switch() if has_reactor(): yield t orig.switch()
def _GetGdbThreadMapping(self, position): """Gets a mapping from python tid to gdb thread num. There's no way to get the thread ident from a gdb thread. We only get the "ID of the thread, as assigned by GDB", which is completely useless for everything except talking to gdb. So in order to translate between these two, we have to execute 'info threads' and parse its output. Note that this may only work on linux, and only when python was compiled to use pthreads. It may work elsewhere, but we won't guarantee it. Args: position: array of pid, tid, framedepth specifying the requested position. Returns: A dictionary of the form {python_tid: gdb_threadnum}. """ if len(gdb.selected_inferior().threads()) == 1: # gdb's output for info threads changes and only displays PID. We cheat. return {position[1]: 1} # example: # 8 Thread 0x7f0a637fe700 (LWP 11894) "test.py" 0x00007f0a69563e63 in # select () from /usr/lib64/libc.so.6 thread_line_regexp = r'\s*\**\s*([0-9]+)\s+[a-zA-Z]+\s+([x0-9a-fA-F]+)\s.*' output = gdb.execute('info threads', to_string=True) matches = [re.match(thread_line_regexp, line) for line in output.split('\n')[1:]] return {int(match.group(2), 16): int(match.group(1)) for match in matches if match}
def invoke(self, arg, from_tty): frame = gdb.selected_frame() val = frame.read_var(arg) if str(val.type.strip_typedefs()) == 'SkBitmap': pixels = val['fPixels'] row_bytes = val['fRowBytes'] info = val['fInfo'] width = info['fWidth'] height = info['fHeight'] color_type = info['fColorType'] alpha_type = info['fAlphaType'] process = gdb.selected_inferior() memory_data = process.read_memory(pixels, row_bytes * height) size = (width, height) image = None # See Unpack.c for the values understood after the "raw" parameter. if color_type == ColorType.bgra_8888.value: if alpha_type == AlphaType.unpremul.value: image = Image.frombytes("RGBA", size, memory_data.tobytes(), "raw", "BGRA", row_bytes, 1) elif alpha_type == AlphaType.premul.value: # RGBA instead of RGBa, because Image.show() doesn't work with RGBa. image = Image.frombytes("RGBA", size, memory_data.tobytes(), "raw", "BGRa", row_bytes, 1) if image: # Fails on premultiplied alpha, it cannot convert to RGB. image.show() else: print ("Need to add support for %s %s." % ( str(ColorType(int(color_type))), str(AlphaType(int(alpha_type))) ))
def parse_proc_maps(): # gdb doesn't have a direct way to tell us if a given address is # claimed by some shared library or the executable. See # https://sourceware.org/bugzilla/show_bug.cgi?id=19288 # In the interest of not requiring a patched gdb, instead we read # /proc/.../maps. This only works locally, but maybe could work # remotely using "remote get". FIXME. mapfile = '/proc/' + str(gdb.selected_inferior().pid) + '/maps' # Note we only examine executable mappings here. matcher = re.compile("^([a-fA-F0-9]+)-([a-fA-F0-9]+)\s+..x.\s+\S+\s+\S+\s+\S*(.*)$") mappings = [] with open(mapfile, "r") as inp: for line in inp: match = matcher.match(line) if not match: # Header lines and such. continue start = match.group(1) end = match.group(2) name = match.group(3).strip() if name is '' or (name.startswith('[') and name is not '[vdso]'): # Skip entries not corresponding to a file. continue mappings.append((long(start, 16), long(end, 16))) return mappings
def hexinput(arg, padding=0): procs = gdb.selected_inferior() arg = shell_unhexlify(arg.replace("0x", "").replace("\n", ""), padding) + "\\x0a" pid = procs.pid with open('/dev/stdout') as fd: tty_path = os.ttyname(fd.fileno()) emit_str = baseIOCTL.format(tty_path, "c") + \ " + " + baseIOCTL.format(tty_path, "\\n") for i in range(0, len(arg), 4): emit_str += " + " + baseIOCTL.format(tty_path, arg[i:i + 4]) emit_str = "call (void)(" + emit_str + ")" SILENT = True gdb.execute(emit_str)
def handle(self, event): regs = grabParameterRegs() pid = gdb.selected_inferior().pid if self.beginOfCall: syscall = int(regs[0]) self.currentSyscall = syscall syscallEnter(syscall, regs, pid) else: syscallExit(self.currentSyscall, regs, pid) self.currentSyscall = -1 self.beginOfCall = not self.beginOfCall
def test_coredump_command_no_target(gdb_base_fixture, capsys, settings_coredump_allowed): """Test coredump command shows error when no target is connected""" import gdb from memfault_gdb import MemfaultCoredump inferior = gdb.selected_inferior() inferior.threads.return_value = [] cmd = MemfaultCoredump() cmd.invoke("--project-key {}".format(TEST_PROJECT_KEY), True) stdout = capsys.readouterr().out assert "No target" in stdout
def __init__(self): self.inferior = gdb.selected_inferior() self.pid = 0 self.base_addr = None self.efl_map = {} self.efl_map['CF'] = 1 << 0 self.efl_map['PF'] = 1 << 2 self.efl_map['AF'] = 1 << 4 self.efl_map['ZF'] = 1 << 6 self.efl_map['SF'] = 1 << 7 self.efl_map['TF'] = 1 << 8 self.efl_map['IF'] = 1 << 9 self.efl_map['DF'] = 1 << 10 self.efl_map['OF'] = 1 << 11
def invoke(self, arg, unused_from_tty): addr = gdb.parse_and_eval(arg) pid = int(gdb.selected_inferior().pid) map_name = "/proc/%d/maps" % pid with open(map_name, 'r') as map: for line in map: line = line.rstrip() if not line: continue match = re.match(r'^(\w+)-(\w+)', line) if match: start = int(match.group(1), 16) end = int(match.group(2), 16) if addr >= start and addr < end: print(line)
def fake_fast(addr, size): get_heap_info() result = [] idx = fastbin_idx(size) chunk_size = size & 0xfffffffffffffff8 start = addr - chunk_size chunk_data = gdb.selected_inferior().read_memory(start, chunk_size) for offset in range(chunk_size - 4): fake_size = u32(chunk_data[offset:offset + 4]) if fastbin_idx(fake_size) == idx: if ((fake_size & 2 == 2) and (fake_size & 4 == 4)) or (fake_size & 4 == 0): padding = addr - (start + offset - capsize) - capsize * 2 result.append((start + offset - capsize, padding)) return result
def read(addr, count, partial=False): """read(addr, count, partial=False) -> bytearray Read memory from the program being debugged. Arguments: addr(int): Address to read count(int): Number of bytes to read partial(bool): Whether less than ``count`` bytes can be returned Returns: :class:`bytearray`: The memory at the specified address, or ``None``. """ result = b'' count = max(int(count), 0) try: result = gdb.selected_inferior().read_memory(addr, count) except gdb.error as e: if not partial: raise if not hasattr(e, 'message'): e.message=str(e) stop_addr = int(e.message.split()[-1], 0) if stop_addr != addr: return read(addr, stop_addr-addr) # QEMU will return the start address as the failed # read address. Try moving back a few pages at a time. stop_addr = addr + count # Move the stop address down to the previous page boundary stop_addr &= PAGE_MASK while stop_addr > addr: result = read(addr, stop_addr-addr) if result: return result # Move down by another page stop_addr -= PAGE_SIZE # if pwndbg.compat.python3: # result = bytes(result) return bytearray(result)
def javascript_stack(): """GDB in-process python supplement.""" for thread in gdb.selected_inferior().threads(): try: if not thread.is_valid(): print("Ignoring invalid thread %d in javascript_stack" % thread.num) continue thread.switch() # Switch frames so gdb actually knows about the mongo::mozjs namespace. It doesn't # actually matter which frame so long as it isn't the top of the stack. This also # enables gdb to know about the mongo::mozjs::kCurrentScope thread-local variable # when using gdb.parse_and_eval(). gdb.selected_frame().older().select() except gdb.error as err: print("Ignoring GDB error '%s' in javascript_stack" % str(err)) continue try: # The following block is roughly equivalent to this: # namespace mongo::mozjs { # std::atomic<MozJSImplScope*> kCurrentScope = ...; # } # if (!scope || scope->_inOp == 0) { continue; } # print(scope->buildStackString()->c_str()); atomic_scope = gdb.parse_and_eval( "mongo::mozjs::kCurrentScope") ptr = MongoDBJavaScriptStack.atomic_get_ptr(atomic_scope) if not ptr: continue scope = ptr.dereference() if scope['_inOp'] == 0: continue gdb.execute('thread', from_tty=False, to_string=False) # gdb continues to not support calling methods through Python, # so work around it by casting the raw ptr back to its type, # and calling the method through execute darkness gdb.execute( f'printf "%s\\n", ((mongo::mozjs::MozJSImplScope*)({ptr}))->buildStackString().c_str()', from_tty=False, to_string=False) except gdb.error as err: print("Ignoring GDB error '%s' in javascript_stack" % str(err)) continue
def invoke(self, arg, from_tty): args = gdb.string_to_argv(arg) var_name = str(args[0]) picked_obj = gdb.parse_and_eval(args[0]) buffer, width, height, channels, type, step = gdbiwtype.get_buffer_info( picked_obj) bytes = get_buffer_size(width, height, channels, type, step) inferior = gdb.selected_inferior() mem = inferior.read_memory(buffer, bytes) lib.plot_binary(mem, var_name, width, height, channels, type, step) pass
def sigrt_handler(self): inf = gdb.selected_inferior() self.notifier.cond.acquire() if inf.pid != 0: res = self.edit(self.modifications) if res: # Empty the modifications while len(self.modifications) > 0: f = self.modifications.pop() self.notifier.cond.notify() self.notifier.cond.release() return res
def patch_addr(self, addr, code): inf = gdb.selected_inferior() if inf.pid == 0: print("Target is not running, there is no memory") return context = self.context if context == None: context = get_current_arch() cmd = ['pwn', 'asm', '-f', 'raw', '-c', context, code] opcodes = subprocess.check_output(cmd) inf.write_memory(addr, opcodes)
def shape_object(addr): inferior = gdb.selected_inferior() # Reeading Shape shape_contents = inferior.read_memory(addr, 0x20) shape = {} shape['base_'] = u64(memview_substr(shape_contents, 0, 8)) shape['propid_'] = u64(memview_substr(shape_contents, 8, 16)) shape['slotInfo'] = u32(memview_substr(shape_contents, 16, 20)) shape['attr'] = u8(memview_substr(shape_contents, 20, 21)) shape['flags'] = u8(memview_substr(shape_contents, 21, 22)) shape['parent'] = u64(memview_substr(shape_contents, 24, 32)) return shape
def stop(self, bp): fr = gdb.selected_frame() block = fr.block() while block.function is None: block = block.superblock file, ptr, len_ = (arg.value(fr) for arg in block if arg.is_argument) try: buf = gdb.selected_inferior().read_memory(ptr, len_) gdb.execute("set var res = {}".format(self.os_.write(file, buf.tobytes()))) gdb.execute("set var err = 0") except OSError as e: gdb.execute("set var err = {}".format(map_errno(int(str(e)), errno, Flags)))
def is_inferior_running(): inf = gdb.selected_inferior() """ if m_debug.Debug: m_debug.dbg_print("inf.is_valid()=", inf.is_valid()) m_debug.dbg_print("inf.num=", inf.num) m_debug.dbg_print("inf.pid=", inf.pid) m_debug.dbg_print("inf.was_attached=", inf.was_attached) m_debug.dbg_print("inf.threads()=", inf.threads(), "len(theads()=", len(inf.threads())) """ if inf.pid != 0 and len(inf.threads()) > 0: return True else: return False
def read_dwords(addr, size): proc = gdb.selected_inferior() dword_size = get_dword_size() ndwords = size * dword_size bytearr = read_bytes(addr, ndwords) dwords = [] for i in range(0, ndwords, dword_size): dword = bytearr[i] for j in range(1, dword_size): dword += bytearr[i + j] << j * 8 dwords.append(dword) return dwords
def invoke(self, arg, from_tty): argv = gdb.string_to_argv(arg) addr = gdb.parse_and_eval(argv[0]).cast( gdb.lookup_type('void').pointer()) if len(argv) == 2: try: bytes = int(gdb.parse_and_eval(argv[1])) except ValueError: raise gdb.GdbError('Byte count numst be an integer value.') else: bytes = 512 inferior = gdb.selected_inferior() align = gdb.parameter('hex-dump-align') width = gdb.parameter('hex-dump-width') if width == 0: width = 16 mem = inferior.read_memory(addr, bytes) pr_addr = int(str(addr), 16) pr_offset = width if align: pr_offset = width - (pr_addr % width) pr_addr -= pr_addr % width start = (pr_addr) & 0xff print(' ', end="") print(' '.join( ['%01X' % (i & 0x0f, ) for i in range(start, start + width)]), end="") print(' ', end="") print(''.join( ['%01X' % (i & 0x0f, ) for i in range(start, start + width)])) for group in groups_of(mem, width, pr_offset): print('0x%x: ' % (pr_addr, ) + ' ' * (width - pr_offset), end="") print (' '.join(['%02X' % (ord(g),) for g in group]) + \ ' ' * (width - len(group) if pr_offset == width else 0) + ' ', end="") print(' ' * (width - pr_offset) + ''.join([ chr(int.from_bytes(g, byteorder='big')) if isgraph( int.from_bytes(g, byteorder='big')) or g == ' ' else '.' for g in group ])) pr_addr += width pr_offset = width
def find_goroutine(goid): """ find_goroutine attempts to find the goroutine identified by goid. It returns a tuple of gdb.Value's representing the stack pointer and program counter pointer for the goroutine. @param int goid @return tuple (gdb.Value, gdb.Value) """ vp = gdb.lookup_type('void').pointer() for ptr in SliceValue(gdb.parse_and_eval("'runtime.allgs'")): if ptr['atomicstatus'] == G_DEAD: continue if ptr['goid'] == goid: break else: return None, None # Get the goroutine's saved state. pc, sp = ptr['sched']['pc'], ptr['sched']['sp'] status = ptr['atomicstatus'] & ~G_SCAN # Goroutine is not running nor in syscall, so use the info in goroutine if status != G_RUNNING and status != G_SYSCALL: return pc.cast(vp), sp.cast(vp) # If the goroutine is in a syscall, use syscallpc/sp. pc, sp = ptr['syscallpc'], ptr['syscallsp'] if sp != 0: return pc.cast(vp), sp.cast(vp) # Otherwise, the goroutine is running, so it doesn't have # saved scheduler state. Find G's OS thread. m = ptr['m'] if m == 0: return None, None for thr in gdb.selected_inferior().threads(): if thr.ptid[1] == m['procid']: break else: return None, None # Get scheduler state from the G's OS thread state. curthr = gdb.selected_thread() try: thr.switch() pc = gdb.parse_and_eval('$pc') sp = gdb.parse_and_eval('$sp') finally: curthr.switch() return pc.cast(vp), sp.cast(vp)
def nice_str(v, length=None): if v == 0: return "NULL" else: inferior = gdb.selected_inferior() try: readable = inferior.read_memory(v, 1) except gdb.MemoryError: return "(invalid)" try: if length is not None: return '"%s"' % v.string(length=length) else: return '"%s"' % v.string() except UnicodeDecodeError: return "(garbage)"
def _state(self): """ Get the state of a given target. Internal use. """ target = gdb.selected_inferior() if target.is_valid(): try: output = gdb.execute('info program', to_string=True) if "not being run" in output: state = "invalid" elif "stopped" in output: state = "stopped" except gdb.error, e: if 'Selected thread is running.' == str(e): state = "running"
def invoke(self, arg, _): argv = gdb.string_to_argv(arg) (exe, test_input, result_path) = argv gdb.execute("file {}".format(exe)) gdb.Breakpoint("exit") gdb.execute("r {} 2>&1 >/dev/null".format(test_input)) tables = get_tables() for (module, (addr, length)) in tables.items(): mem = gdb.selected_inferior().read_memory(addr, length) with open(os.path.join(result_path, module + ".cov"), "wb") as handle: handle.write(mem)
def select_thread(self, global_num): if self.selected_global_num == global_num: return for thread in gdb.selected_inferior().threads(): if thread.global_num == global_num: thread.switch() pkgs = [ self.pkg_unselect_thread(self.selected_global_num), self.pkg_select_thread(global_num), ] self.selected_global_num = global_num return self.pkg_transaction(pkgs) self.send_error("can't find selected thread") #It seems that in gui window show old thread data, because user select unexisted thread. #Update this. return self.need_update()
def stop(self): gdb.execute('set logging overwrite on') gdb.execute('set logging on') for i in range(0, 10): gdb.execute('x/s *((char **)environ+%d)' % i) gdb.execute('set logging off') gdb.execute('set logging overwrite off') gdb.execute('set logging on') gdb.execute('set logging off') proc_map = [] with open("/proc/%d/maps" % gdb.selected_inferior().pid) as fp: f = open('procMap.txt', 'w') f.write(fp.read())
def _GetTag(self): """Helper for .children() and .to_string(). We don't know the order in which they'll be called. """ if self.asdl_tag is None: # Get address of value and look at first 16 bits obj = self.val.dereference() # location of part_value_t # Read the uint16_t tag tag_mem = gdb.selected_inferior().read_memory(obj.address, 2) # Unpack 2 bytes into an integer (self.asdl_tag,) = struct.unpack('H', tag_mem) return self.asdl_tag
def __init__(self): self.inferior = gdb.selected_inferior() # Pagination is not helpful here gdb.execute("set pagination off") # Connect to our target gdb.execute("att 1") # Load everything into gdb and run gdb.execute("load") gdb.execute("b main") gdb.execute("run") # Stopped at the top of main. Go to tc_main via tc_prelude gdb.execute("del 1") gdb.execute("b tc_main") gdb.execute("set $pc=tc_prelude") gdb.execute("c")
def get_thread_list_str(): start_pos = 0 thread_desc = "" conn = gdb.selected_inferior().connection if not isinstance(conn, gdb.RemoteTargetConnection): raise gdb.GdbError("connection is the wrong type") while True: str = conn.send_packet("qXfer:threads:read::%d,200" % start_pos).decode("ascii") start_pos += 200 c = str[0] str = str[1:] thread_desc += str if c == "l": break return thread_desc
def invoke(self, arg, _from_tty): stacks = {} if not arg: arg = 'bt' # default to 'bt' current_thread = gdb.selected_thread() try: for thread in gdb.selected_inferior().threads(): if not thread.is_valid(): continue thread.switch() self._process_thread_stack(arg, stacks, thread) self._dump_unique_stacks(stacks) finally: if current_thread and current_thread.is_valid(): current_thread.switch()
def invoke(self, arg, from_tty): # print('Invoke %s %s' %(arg, from_tty)) argv = gdb.string_to_argv(arg) if len(argv) != 2: raise gdb.GdbError('hex-dump takes exactly 2 arguments.') addr = gdb.parse_and_eval(argv[0]).cast( gdb.lookup_type('void').pointer()) try: bytes = int(gdb.parse_and_eval(argv[1])) except ValueError: raise gdb.GdbError('Byte count numst be an integer value.') inferior = gdb.selected_inferior() align = False # gdb.parameter('hex-dump-align') width = 16 # gdb.parameter('hex-dump-width') if width == 0: width = 16 mem = inferior.read_memory(addr, bytes) pr_addr = int(str(addr), 16) pr_offset = width if align: pr_offset = width - (pr_addr % width) pr_addr -= pr_addr % width for group in groups_of(mem, width, pr_offset): print '0x%x: ' % (pr_addr, ) + ' ' * (width - pr_offset), x = 0 if pr_offset == width: x = width - len(group) print ' '.join(['%02X' % (ord(g),) for g in group]) + \ ' ' * x + ' ', # ' ' * (width - len(group) if pr_offset == width else 0) + ' ', for g in group: if isgraph(g) or g == ' ': l.apppend(g) else: l.append('.') print ' ' * (width - pr_offset) + ''.join(l) # [g if isgraph(g) or g == ' ' else '.' for g in group]) pr_addr += width pr_offset = width
def search(searchfor, mapping=None, start=None, end=None, executable=False, writable=False): value = searchfor size = None i = gdb.selected_inferior() maps = pwndbg.vmmap.get() hits = [] if end and start: maps = [m for m in maps if start <= m < end] if executable: maps = [m for m in maps if m.execute] if writable: maps = [m for m in maps if m.write] for vmmap in maps: start = vmmap.vaddr end = start + vmmap.memsz if mapping and mapping not in vmmap.objfile: continue while True: # No point in searching if we can't read the memory if not pwndbg.memory.peek(start): break start = i.search_memory(start, end - start, searchfor) if start is None: break # For some reason, search_memory will return a positive hit # when it's unable to read memory. if not pwndbg.memory.peek(start): break yield start start += len(searchfor)
def update(): """ For each running thread, updates the known address range for its stack. """ curr_thread = gdb.selected_thread() try: for thread in gdb.selected_inferior().threads(): thread.switch() sp = pwndbg.regs.sp # Skip if sp is None or 0 # (it might be 0 if we debug a qemu kernel) if not sp: continue sp_low = sp & ~(0xFFF) sp_low -= 0x1000 # If we don't already know about this thread, create # a new Page mapping for it. page = stacks.get(thread.ptid, None) if page is None: start = sp_low stop = find_upper_stack_boundary(sp) page = pwndbg.memory.Page(start, stop - start, 6 if not is_executable() else 7, 0, "[stack]") stacks[thread.ptid] = page continue elif page.objfile is None: pid, tid, _ = thread.ptid if pid == tid: page.objfile = "[stack]" else: page.objfile = "[stack:%i]" % tid # If we *DO* already know about this thread, just # update the lower boundary if it got any lower. low = min(page.vaddr, sp_low) if low != page.vaddr: page.memsz += page.vaddr - low page.vaddr = low finally: if curr_thread: curr_thread.switch()
def __init__(self, addr, pc=None, inferior=None): self.addr = addr self.pc = pc self.inferior = inferior if inferior is None: self.inferior = gdb.selected_inferior() items = [None] * self.ITEMS_SIZE for frame_item in self.items_info: items[frame_item.id_number] = self.read_item(frame_item) self.items = items self.is_valid_frame = self.validate() self.is_valid_js_frame = self.is_valid_frame and self.js_validate() if self.is_valid_js_frame: self.codeBlock = CodeBlock(self.items[self.CODEBLOCK])