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
def trace_syscall_after(pid, flags, tricklist, call, eax): result = eax state = flags["state"] call_changes = flags["call_changes"] # del flags['call_changes'] # ? # FIX: copy/reverse slow? tricklist = tricklist[:] # do callafters for tricks we did callbefores on, in reverse order, # minus the annulled trick (if any) if flags.has_key("annul"): result, annultrick = flags["annul"] assert isinstance(result, types.IntType), "oops: waitsuspend not cleared?" del flags["annul"] call = call_changes[annultrick] while tricklist and tricklist.pop()[0] != annultrick: pass elif flags.has_key("sigreturn"): # we have to do this because ORIG_EAX somehow gets stomped by the # sigreturn calls. maybe this is a kernel bug? call = flags["sigreturn"] del flags["sigreturn"] tricklist.reverse() memory = getMemory(pid) for trick, callmask, signalmask in tricklist: call = call_changes.get(trick, call) if (not callmask or callmask.has_key(call)) and trick.is_enabled(pid): r = trick.callafter(pid, call, result, state.get(trick)) if r != None: result = r memory.pop(trick) assert memory.empty() # all momentary changes got popped if result != eax: if debug(): print "ptrace.pokeuser(%s, %s, %s)" % (pid, EAX, result) ptrace.pokeuser(pid, EAX, result) # undo any changes to child's args made on entry # XXX: does this go here? for n, arg in flags.get("args_delta", []): if debug(): print "ptrace.pokeuser(%s, %s, %s)" % (pid, 4 * n, arg) ptrace.pokeuser(pid, 4 * n, arg) call_save = flags.get("call_delta", -1) if call_save >= 0: if debug(): print "ptrace.pokeuser(%s, %s, %s)" % (pid, ORIG_EAX, call_save) ptrace.pokeuser(pid, ORIG_EAX, call_save) # could continue child hereabouts ptrace.syscall(pid, 0) if flags.has_key("call_delta"): del flags["call_delta"] if flags.has_key("args_delta"): del flags["args_delta"] del flags["insyscall"]
def trace_syscall_before(pid, flags, tricklist, call, scno, sysent): nargs = sysent[syscallmap.NARGS] args = peek_args(pid, nargs) call_save = call args_save = args[:] call_changes = {} state = {} for trick, callmask, signalmask in tricklist: if (not callmask or callmask.has_key(call)) and trick.is_enabled(pid): r = trick.callbefore(pid, call, args) # r is None or (state, result, call, args) if r: assert len(r) == 4, "callbefore must return None or a 4-tuple" if r[1] != None: # annul the call call_changes[trick] = call call = _badcall args = [] flags["annul"] = (r[1], trick) break if r[0] != None: state[trick] = r[0] if r[2] != None: call_changes[trick] = call call = r[2] assert isinstance(call, types.StringType) if r[3] != None: args = r[3] assert len(args) <= 6, "kernel doesn't support 7+ args?" # STATE at this point ? # call_changes, args, args_save, scno, call, call_save, state, ??? flags["call_changes"] = call_changes # XXX: maybe faster to just brute force args rather than this careful # delta stuff? # make any necessary changes to child's args, saving undo info # (XXX: hmm, is this actually better than the iterative version?) 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) n = len(args) args_delta = filter(None, map(_alter_arg, range(n), args, args_save[:n])) if args_delta: flags["args_delta"] = args_delta if call == "sigreturn" or call == "rt_sigreturn": flags["sigreturn"] = call if call != call_save: if isinstance(call, types.StringType): callno = syscallmap.lookup_number(call) else: callno = call try: if debug(): print "ptrace.pokeuser(%s, %s, %s)" % (pid, ORIG_EAX, callno) ptrace.pokeuser(pid, ORIG_EAX, callno) flags["call_delta"] = scno except OSError, e: # FIX: do something better here sys.exit("panic: call alter failed in trick %s (%s)" % (trick, e))
def poke_args(pid, nargs, args): assert nargs <= 6, "kernel doesn't support 7+ args?" for i in range(nargs): ptrace.pokeuser(pid, 4 * i, args[i])
_skipcallafter = {} def set_skipcallafter(pid): global _skipcallafter _skipcallafter[pid] = 1 def hard_kill(pid): # FIX: isn't this a race? don't we need to wait on the child? try: poke_args(pid, 6, [0, 0, 0, 0, 0, 0]) except OSError, e: print "warning: attempt to annul last syscall by zapping args failed" " (pid=%s, error=%s)" % (pid, e) try: ptrace.pokeuser(pid, ORIG_EAX, _badcall) except OSError, e: print "warning: attempt to annul last syscall" " (pid=%s, error=%s)" % (pid, e) try: ptrace.kill(pid) except OSError, e: print "warning: attempt to ptrace.kill process failed" " (pid=%s, error=%s)" % (pid, e) except: # XXX: should we really catch this? we shouldn't be absorbing # arbitrary exceptions; this will cause trouble print "warning: attempt to ptrace.kill process failed strangely" " (pid=%s, error=%s)" % (pid, e) raise try: # SIGKILL isn't delivered until completion of the current syscall; this # function tries to abort the current syscall before killing the child.
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)
def trace_syscall_after(pid, flags, tricklist, call, eax): result = eax state = flags['state'] call_changes = flags['call_changes'] #del flags['call_changes'] # ? # FIX: copy/reverse slow? tricklist = tricklist[:] # do callafters for tricks we did callbefores on, in reverse order, # minus the annulled trick (if any) if flags.has_key('annul'): result, annultrick = flags['annul'] assert isinstance(result, types.IntType), "oops: waitsuspend not cleared?" del flags['annul'] call = call_changes[annultrick] while tricklist and tricklist.pop()[0] != annultrick: pass elif flags.has_key('sigreturn'): # we have to do this because ORIG_EAX somehow gets stomped by the # sigreturn calls. maybe this is a kernel bug? call = flags['sigreturn'] del flags['sigreturn'] tricklist.reverse() memory = getMemory(pid) for trick, callmask, signalmask in tricklist: call = call_changes.get(trick, call) if (not callmask or callmask.has_key(call)) \ and trick.is_enabled(pid): r = trick.callafter(pid, call, result, state.get(trick)) if r != None: result = r memory.pop(trick) assert memory.empty() # all momentary changes got popped if result != eax: if debug(): print "ptrace.pokeuser(%s, %s, %s)" % (pid, EAX, result) ptrace.pokeuser(pid, EAX, result) # undo any changes to child's args made on entry # XXX: does this go here? for n, arg in flags.get('args_delta', []): if debug(): print "ptrace.pokeuser(%s, %s, %s)" % (pid, 4 * n, arg) ptrace.pokeuser(pid, 4 * n, arg) call_save = flags.get('call_delta', -1) if call_save >= 0: if debug(): print "ptrace.pokeuser(%s, %s, %s)" % (pid, ORIG_EAX, call_save) ptrace.pokeuser(pid, ORIG_EAX, call_save) # could continue child hereabouts ptrace.syscall(pid, 0) if flags.has_key('call_delta'): del flags['call_delta'] if flags.has_key('args_delta'): del flags['args_delta'] del flags['insyscall']
def trace_syscall_before(pid, flags, tricklist, call, scno, sysent): nargs = sysent[syscallmap.NARGS] args = peek_args(pid, nargs) call_save = call args_save = args[:] call_changes = {} state = {} for trick, callmask, signalmask in tricklist: if (not callmask or callmask.has_key(call)) \ and trick.is_enabled(pid): r = trick.callbefore(pid, call, args) # r is None or (state, result, call, args) if r: assert len(r) == 4, "callbefore must return None or a 4-tuple" if r[1] != None: # annul the call call_changes[trick] = call call = _badcall args = [] flags['annul'] = (r[1], trick) break if r[0] != None: state[trick] = r[0] if r[2] != None: call_changes[trick] = call call = r[2] assert isinstance(call, types.StringType) if r[3] != None: args = r[3] assert len(args) <= 6, "kernel doesn't support 7+ args?" # STATE at this point ? # call_changes, args, args_save, scno, call, call_save, state, ??? flags['call_changes'] = call_changes # XXX: maybe faster to just brute force args rather than this careful # delta stuff? # make any necessary changes to child's args, saving undo info # (XXX: hmm, is this actually better than the iterative version?) 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) n = len(args) args_delta = filter(None, map(_alter_arg, range(n), args, args_save[:n])) if args_delta: flags['args_delta'] = args_delta if call == 'sigreturn' or call == 'rt_sigreturn': flags['sigreturn'] = call if call != call_save: if isinstance(call, types.StringType): callno = syscallmap.lookup_number(call) else: callno = call try: if debug(): print "ptrace.pokeuser(%s, %s, %s)" % (pid, ORIG_EAX, callno) ptrace.pokeuser(pid, ORIG_EAX, callno) flags['call_delta'] = scno except OSError, e: # FIX: do something better here sys.exit('panic: call alter failed in trick %s (%s)' % (trick, e))
def set_skipcallafter(pid): global _skipcallafter _skipcallafter[pid] = 1 def hard_kill(pid): # FIX: isn't this a race? don't we need to wait on the child? try: poke_args(pid, 6, [0, 0, 0, 0, 0, 0]) except OSError, e: print "warning: attempt to annul last syscall by zapping args failed" \ " (pid=%s, error=%s)" % (pid, e) try: ptrace.pokeuser(pid, ORIG_EAX, _badcall) except OSError, e: print "warning: attempt to annul last syscall" \ " (pid=%s, error=%s)" % (pid, e) try: ptrace.kill(pid) except OSError, e: print "warning: attempt to ptrace.kill process failed" \ " (pid=%s, error=%s)" % (pid, e) except: # XXX: should we really catch this? we shouldn't be absorbing # arbitrary exceptions; this will cause trouble print "warning: attempt to ptrace.kill process failed strangely" \ " (pid=%s, error=%s)" % (pid, e) raise