def trace_syscall(pid, flags, tricklist): scno = ptrace.peekuser(pid, ORIG_EAX) assert (0 <= scno < len(syscallmap.table) or scno == _badcall or flags.has_key('sigreturn')), \ "unknown system call (=%s, pid=%s, flags=%s)" % (scno, pid, flags) if scno == _badcall: call = 'badcall' elif flags.has_key('sigreturn'): call = flags['sigreturn'] # !beforecall else: sysent = syscallmap.table[scno] call = sysent[syscallmap.CALL] eax = ptrace.peekuser(pid, EAX) beforecall = not flags.has_key('insyscall') if eax != -errno.ENOSYS and beforecall: # XXX: is this test right? if call == 'execve' and debug(): print 'debug: ignoring additional execve stop' return if 0 <= eax <= len(syscallmap.table): eaxcall = syscallmap.table[eax][3] else: eaxcall = "" # FIX: this probably fires for SIG_DFL stop calls (except SIGSTOP) print 'warning: received SIGTRAP or stray syscall exit: eax = %d (%s)' % ( eax, eaxcall) # FIX: is this right? what's really going on here? # no, don't do this, because of the SIG_DFL issue above!!!!! --mkc #raise 'warning: received SIGTRAP or stray syscall exit:' \ # 'pid = %d, eax = %d (%s)' % (pid, eax, eaxcall) #return # FIX: is this the right thing to do? flags['insyscall'] = 1 flags['state'] = {} flags['call_changes'] = {} global _skipcallafter if beforecall: if not _call_weedout_mask[scno]: _skipcallafter[pid] = 1 flags['insyscall'] = 1 ptrace.syscall(pid, 0) return return trace_syscall_before(pid, flags, tricklist, call, scno, sysent) if _skipcallafter.has_key(pid): del _skipcallafter[pid] del flags['insyscall'] ptrace.syscall(pid, 0) return return trace_syscall_after(pid, flags, tricklist, call, eax)
def trace_syscall(pid, flags, tricklist): scno = ptrace.peekuser(pid, ORIG_EAX) assert ( 0 <= scno < len(syscallmap.table) or scno == _badcall or flags.has_key("sigreturn") ), "unknown system call (=%s, pid=%s, flags=%s)" % (scno, pid, flags) if scno == _badcall: call = "badcall" elif flags.has_key("sigreturn"): call = flags["sigreturn"] # !beforecall else: sysent = syscallmap.table[scno] call = sysent[syscallmap.CALL] eax = ptrace.peekuser(pid, EAX) beforecall = not flags.has_key("insyscall") if eax != -errno.ENOSYS and beforecall: # XXX: is this test right? if call == "execve" and debug(): print "debug: ignoring additional execve stop" return if 0 <= eax <= len(syscallmap.table): eaxcall = syscallmap.table[eax][3] else: eaxcall = "" # FIX: this probably fires for SIG_DFL stop calls (except SIGSTOP) print "warning: received SIGTRAP or stray syscall exit: eax = %d (%s)" % (eax, eaxcall) # FIX: is this right? what's really going on here? # no, don't do this, because of the SIG_DFL issue above!!!!! --mkc # raise 'warning: received SIGTRAP or stray syscall exit:' \ # 'pid = %d, eax = %d (%s)' % (pid, eax, eaxcall) # return # FIX: is this the right thing to do? flags["insyscall"] = 1 flags["state"] = {} flags["call_changes"] = {} global _skipcallafter if beforecall: if not _call_weedout_mask[scno]: _skipcallafter[pid] = 1 flags["insyscall"] = 1 ptrace.syscall(pid, 0) return return trace_syscall_before(pid, flags, tricklist, call, scno, sysent) if _skipcallafter.has_key(pid): del _skipcallafter[pid] del flags["insyscall"] ptrace.syscall(pid, 0) return return trace_syscall_after(pid, flags, tricklist, call, eax)
def _alter_arg(number, arg, saved_arg, pid=pid): if not saved_arg: saved_arg = ptrace.peekuser(pid, 4 * number) if arg != saved_arg: if debug(): print "ptrace.pokeuser(%s, %s, %s)" % (pid, 4 * number, arg) ptrace.pokeuser(pid, 4 * number, arg) return (number, saved_arg)
def force_syscall(pid, scno, p1=0, p2=0, p3=0, p4=0, p5=0, p6=0): registers = peek_args(pid, 6) eip = ptrace.peekuser(pid, EIP) eax = ptrace.peekuser(pid, EAX) ptrace.pokeuser(pid, EIP, eip - 2) ptrace.pokeuser(pid, EAX, scno) # Select new scno and point eip to syscall poke_args(pid, 6, [p1, p2, p3, p4, p5, p6]) ptrace.syscall(pid, 0) # We make it return to userland and do syscal wpid, status = os.waitpid(pid, wait_flags) assert pid == wpid ptrace.syscall(pid, 0) # Kernel stops us before syscall is done wpid, status = os.waitpid(pid, wait_flags) if wpid != pid: print "problem: other syscall stopped woke up /2: ", pid, " != ", wpid assert pid == wpid # Kernel stops us when syscall is done res = ptrace.peekuser(pid, EAX) # We get the syscall result ptrace.pokeuser(pid, EAX, eax) # and then mimic like nothing happened poke_args(pid, 6, registers) return res
else: sys.exit("%s wait error [%s]" % (sys.argv[0], e)) except KeyboardInterrupt: assert 0, "this can't happen--we're ignoring SIGINT" if fastmainloop and not beforecall: allflags[lastpid]['insyscall'] = 1 set_skipcallafter(lastpid) lastpid = wpid if not allflags.has_key(wpid): # new child # FIX: what happens if parent or child already dead? # FIX: what happens if parent waits before child reports? try: ppid = ptrace.peekuser(wpid, EDI) except OSError, e: if e.errno == errno.ESRCH: # If a trick has started a process and it dies, we find # out here. Might get here for other reasons, though. print 'non-traced process exited (?)' continue else: raise if debug(): print "[%s] new child, parent is %s" % (wpid, ppid) # ppid could be 1 here if the parent died very quickly and init # inherited the child. With the new tagging scheme, though, we'll # still have the old ppid here, even though the process is gone. # So, for example, depending on the order of events,
def peek_args(pid, nargs): args = [] assert nargs <= 6, "kernel doesn't support 7+ args?" for i in range(nargs): args.append(ptrace.peekuser(pid, 4 * i)) return args
def callafter(self, pid, call, result, state): v = ptrace.peekuser(pid, self.options['offset']) print '[%s] found %s at offset %s' % (pid, v, self.options['offset']) print '[%s] restoring %s at offset %s' % (pid, state, self.options['offset']) ptrace.pokeuser(pid, self.options['offset'], state)
def callbefore(self, pid, call, args): v = ptrace.peekuser(pid, self.options['offset']) print '[%s] poking %s at offset %s' % (pid, self.options['data'], self.options['offset']) ptrace.pokeuser(pid, self.options['offset'], self.options['data']) return (v, None, None, None)