def op_syscall(process): state = process.syscall_state syscall = state.event(FunctionCallOptions()) words = get_words() if syscall.name == 'write': co = re.sub(r'(\\x.*?m)', '', syscall.format()) matched = re.match(r".*\,(.*)\,.*", co) if matched: cleanup = re.sub(r'(\\r|\'|\\n)+', '', matched.group(1)) if cleanup: splitted = cleanup.split(" ") for word in words: for captured in splitted: if captured.startswith(word): v = virtkey.virtkey() for key in word: key_value = gtk.gdk.keyval_from_name(key) v.press_unicode(key_value) v.release_unicode(key_value) v.press_keysym(enter_key) v.release_keysym(enter_key) process.syscall()
def __init__(self, manager, pid, record, old_debugger=None, is_thread=False): """ Create a new tracing thread :param manager: overall tracing management instance :param pid: Process to trace :param record: Action recording instance :param old_debugger: Old debugger the process might be attached to at the moment :param is_thread: True if the pid is a thread else False """ super().__init__() self.manager = manager # Save tracing record self.record = record self.record.runtime_log(RuntimeActionRecord.TYPE_STARTED) self._debugger_options = FunctionCallOptions(replace_socketcall=False) self.is_thread = is_thread self.process = psutil.Process(pid) self.debugger = None self.traced_item = None self.stopped = False # Check if the process is attached to the debugger if old_debugger is not None and old_debugger.dict.get( self.process.pid): old_debugger.dict.get(self.process.pid).detach() posix.kill(self.process.pid, signal.SIGSTOP)
def __init__(self, args: LaunchArguments, pollobj: PaulaPoll): self.pollobj = pollobj # PollObj used by the input monitor, needed to register new processes self.syscalls_to_trace = [] self.processList = [] self.debugger = self.startDebugger(args) self.currentProcess = self.processList[0] self.syscall_options = FunctionCallOptions( write_types=True, write_argname=True, write_address=True, ) self.named_processes = BiDict()
def runDebugger(self): # Create debugger and traced process self.setupDebugger() process = self.createProcess() if not process: return self.syscall_options = FunctionCallOptions( write_types=True, write_argname=True, replace_socketcall=False, string_max_length=300, write_address=False, max_array_count=20, ) self.syscall_options.instr_pointer = self.options.show_ip self.syscallTrace(process)
def runDebugger(self): # Create debugger and traced process self.setupDebugger() process = self.createProcess() if not process: return self.syscall_options = FunctionCallOptions( write_types=self.options.type, write_argname=self.options.name, string_max_length=self.options.string_length, replace_socketcall=not self.options.raw_socketcall, write_address=self.options.address, max_array_count=self.options.array_count, ) self.syscall_options.instr_pointer = self.options.show_ip return self.syscallTrace(process)
def __init__(self): Application.__init__(self) # Parse self.options self.parseOptions() # Setup output (log) self.setupLog() self.last_signal = {} # We assume user wants all possible information self.syscall_options = FunctionCallOptions( write_types=True, write_argname=True, write_address=True, ) # FIXME: Remove self.breaks! self.breaks = dict() self.followterms = []
def watch_syscalls(dbg): syscall_options = FunctionCallOptions( write_types=False, write_argname=True, string_max_length=300, replace_socketcall=True, write_address=False, max_array_count=20, ) syscall_options.instr_pointer = False # tell all processes to break at the next syscall for process in dbg: prepare_process(process) process.syscall() while True: if not dbg: break # wait for next syscall event try: event = dbg.waitSyscall() except ProcessExit as event: handle_exit(event) continue except ProcessSignal as event: handle_signal(event) continue except NewProcessEvent as event: handle_new_process(event) continue except ProcessExecution as event: handle_exec(event) continue # process syscall enter or exit handle_syscall(event, syscall_options)
def parse_argument(argument): argument = argument.createText() if argument.startswith(("'", '"')): # Remove quotes from string argument return argument[1:-1] elif argument.startswith(("b'", 'b"')): # Python 3 bytes literal return argument[2:-1] else: # Note that "int" with base 0 infers the base from the prefix return int(argument, 0) format_options = FunctionCallOptions( replace_socketcall=False, string_max_length=4096, ) def get_operations(debugger, syscall_filters, verbose): operations = [] while True: if not debugger: # All processes have exited break # This logic is mostly based on python-ptrace's "strace" example try: syscall_event = debugger.waitSyscall() except ProcessSignal as event:
def debug_process(debugger, syscall_filters): format_options = FunctionCallOptions( replace_socketcall=False, string_max_length=4096, ) processes = {} operations = [] exit_code = 0 snapshots = Snapshots() while True: if not debugger: # All processes have exited break # This logic is mostly based on python-ptrace's "strace" example try: syscall_event = debugger.waitSyscall() except ProcessSignal as event: event.process.syscall(event.signum) continue except NewProcessEvent as event: event.process.syscall() event.process.parent.syscall() continue except ProcessExecution as event: event.process.syscall() continue except ProcessExit as event: exit_code = event.exitcode continue process = syscall_event.process syscall_state = process.syscall_state syscall = syscall_state.event(format_options) if syscall and syscall_state.next_event == "exit": # Syscall is about to be executed (just switched from "enter" to "exit") # print(syscall.format()) if syscall.name in syscall_filters: filter_function = syscall_filters[syscall.name] if process.pid not in processes: processes[process.pid] = Process(process) arguments = [ parse_argument(argument) for argument in syscall.arguments ] name, paths, return_value = filter_function( processes[process.pid], arguments) if name is not None: operations.append(name) for path in paths: snapshots.snapshot_path(path) process.syscall() return snapshots, exit_code
def start(self): # First query to break at next syscall self.root.syscall() while not self.stop_requested and self.debugger: try: try: # FIXME: better mechanism to stop debugger # debugger._waitPid(..., blocking=False) may help # actually debugger receives SIGTERM, terminates all remaining process # then this method is unblocked and fails with KeyError event = self.debugger.waitSyscall() except: if self.stop_requested: return raise state = event.process.syscall_state syscall = state.event(FunctionCallOptions()) self.syscalls[event.process.pid] = syscall yield SyscallEvent(event.process.pid, syscall.name) # FIXME: # when exit_group is called, it seems that thread is still monitored # in next event we area accessing pid that is not running anymore # so when exit_group occurs, remove all processes with same Tgid and detach from them if syscall.name == 'exit_group': # result is None, never return! def read_group(pid): with open('/proc/%d/status' % pid) as f: return int({ i: j.strip() for i, j in [ i.split(':', 1) for i in f.read().splitlines() ] }['Tgid']) me = read_group(syscall.process.pid) for process in self.debugger: if read_group(process.pid) == me: process.detach() self.debugger.deleteProcess(pid=process.pid) yield ProcessExited(process.pid, syscall.arguments[0].value) else: event.process.syscall() except ProcessExit as event: # Display syscall which has not exited state = event.process.syscall_state if (state.next_event == "exit") and state.syscall: # self.syscall(state.process) TODO: pass yield ProcessExited(event.process.pid, event.exitcode) except ProcessSignal as event: # event.display() event.process.syscall(event.signum) except NewProcessEvent as event: process = event.process logging.info("*** New process %s ***", process.pid) yield ProcessCreated(pid=process.pid, parent_pid=process.parent.pid, is_thread=process.is_thread) process.syscall() process.parent.syscall() except ProcessExecution as event: logging.info("*** Process %s execution ***", event.process.pid) event.process.syscall() except DebuggerError: if self.stop_requested: return raise except PtraceError as e: if e.errno == 3: # FIXME: same problem as exit_group above ? logging.warning("Process dead?") else: raise
def __init__(self, args=None, debugger=None, redirect=False, parent=None, ptraceprocess=None): self.syscall_options = FunctionCallOptions( write_types=True, write_argname=True, write_address=True, ) self.stdinRequested = False self.remember_insert_bp = False self.atBreakpoint = False self.inserted_function_data = False self.parent = None self.children = [] self.is_terminated = False self.syscalls_to_trace = parent.syscalls_to_trace if parent else None # first will be set by ProcessManager self.own_segment = None if args: assert debugger is not None assert not parent self.disable_randomization = not args.random # create three pseudo terminals self.in_pipe = Pipe() self.out_pipe = Pipe() self.err_pipe = Pipe() self.stdin_buf = b"" self.stdout_buf = b"" self.stderr_buf = b"" # if we want to redirect, tell the subprocess to write to our pipe, else it will print to normal stdout if redirect: stdout_arg = self.out_pipe.writeobj stderr_arg = self.err_pipe.writeobj else: stdout_arg = None stderr_arg = None args = [path_launcher] + args.argvlist self.popen_obj = Popen(args, stdin=self.in_pipe.readobj, stdout=stdout_arg, stderr=stderr_arg) self.debugger = debugger self.ptraceProcess = self.setupPtraceProcess( ) # launches actual program, halts immediately self.heap = None self.programinfo = ProgramInfo(args[1], self.getPid(), self) self.get_own_segment() # setup own mmap page # this is used when a process is forked by user else: assert isinstance(parent, ProcessWrapper) and isinstance( ptraceprocess, PtraceProcess) self.parent = parent self.in_pipe = parent.in_pipe # TODO self.out_pipe = parent.out_pipe self.err_pipe = parent.err_pipe self.stdin_buf = parent.stdin_buf self.stdout_buf = parent.stdout_buf self.stderr_buf = parent.stderr_buf self.debugger = parent.debugger self.ptraceProcess = ptraceprocess self._copyBreakpoints() self.remember_insert_bp = parent.remember_insert_bp self.heap = None self.own_segment = parent.own_segment # if the process spawns new children for other purposes, it might load another library. # the loaded path could be determined TODO if LOAD_PROGRAMINFO: # can be disabled in Constants to improve performance try: self.programinfo = ProgramInfo( parent.programinfo.path_to_hack, self.ptraceProcess.pid, self) except ValueError as e: # if another library is loaded instead of our initial executable self.programinfo = ProgramInfo(None, self.ptraceProcess.pid, self)
def get_operations(debugger, syscall_filters, verbose): format_options = FunctionCallOptions( replace_socketcall=False, string_max_length=4096, ) processes = {} operations = [] while True: if not debugger: # All processes have exited break # This logic is mostly based on python-ptrace's "strace" example try: syscall_event = debugger.waitSyscall() except ProcessSignal as event: event.process.syscall(event.signum) continue except NewProcessEvent as event: event.process.syscall() event.process.parent.syscall() continue except ProcessExecution as event: event.process.syscall() continue except ProcessExit as event: continue process = syscall_event.process syscall_state = process.syscall_state syscall = syscall_state.event(format_options) if syscall and syscall_state.next_event == "exit": # Syscall is about to be executed (just switched from "enter" to "exit") if syscall.name in syscall_filters: if verbose == 1: print(syscall.format()) elif verbose == 2: print(T.bold(syscall.format())) filter_function = syscall_filters[syscall.name] if process.pid not in processes: processes[process.pid] = Process(process) arguments = [parse_argument(argument) for argument in syscall.arguments] operation, return_value = filter_function(processes[process.pid], arguments) if operation is not None: operations.append(operation) if return_value is not None: # Set invalid syscall number to prevent call execution process.setreg(SYSCALL_REGISTER, -1) # Substitute return value to make syscall appear to have succeeded process.setreg(RETURN_VALUE_REGISTER, return_value) elif verbose == 2: print(syscall.format()) process.syscall() return operations