def processExit(self, event): # Get the Fusil process agent fusil_process = self.getFusilProcess(event.process) # Create exit status if event.signum is not None: status = -event.signum elif event.exitcode is not None: status = event.exitcode else: status = ABNORMAL_STATUS # Display the exit status? if fusil_process.show_exit: message = str(event) if event.signum or (event.exitcode is None): # killed by a signal or abnormal exit log = self.error elif event.exitcode: log = self.warning else: log = self.info log(message) if event.exitcode: fusil_process.send('session_rename', 'exitcode%s' % event.exitcode) elif event.signum: name = signalName(event.signum) name = name.lower() fusil_process.send('session_rename', name) fusil_process.send("process_exit", fusil_process, status) return status
def syscall(self, signum=0): message = "Break process %s at next syscall" % self.pid if signum: message += ": continue with %s" % signalName(signum) info(message) ptrace_syscall(self.pid, signum) self.is_stopped = False
def createText(self): value = self.value argtype = self.type name = self.name if not argtype or not name: return formatWordHex(self.value) syscall = self.function.name # Special cases try: return SYSCALL_ARG_DICT[syscall][name][value] except KeyError: pass try: callback = ARGUMENT_CALLBACK[syscall][name] except KeyError: callback = None if callback: return callback(value) if syscall == "execve": if name in ("argv", "envp"): return self.readCStringArray(value) if syscall == "socketcall": if name == "call": try: return SOCKETCALL[value][0] except KeyError: return str(value) if name == "args": func_call = FunctionCall("socketcall", self.options) setupSocketCall(func_call, self.function.process, self.function[0], self.value) text = "<%s>" % func_call.format() return self.formatPointer(text, self.value) if syscall == "write" and name == "buf": fd = self.function[0].value if fd < 3: length = self.function[2].value return self.readString(value, length) if name == "signum": return signalName(value) if name in FILENAME_ARGUMENTS: return self.readCString(value) # Remove "const " prefix if argtype.startswith("const "): argtype = argtype[6:] # Format depending on the type if argtype.endswith("*"): try: text = self.formatValuePointer(argtype[:-1]) if text: return text except PTRACE_ERRORS, err: writeError(getLogger(), err, "Format argument value error") value = None return formatAddress(self.value)
def cont(self, signum=0): if signum: info("Continue process %s (send signal %s)" % ( self.pid, signalName(signum))) else: info("Continue process %s" % self.pid) ptrace_cont(self.pid, signum) self.is_stopped = False
def cont(self, signum=0): if signum: info("Continue process %s (send signal %s)" % (self.pid, signalName(signum))) else: info("Continue process %s" % self.pid) ptrace_cont(self.pid, signum) self.is_stopped = False
def formatProcessStatus(status, title="Process"): if WIFSTOPPED(status): signum = WSTOPSIG(status) return "%s stopped by signal %s" % (title, signalName(signum)) if WIFSIGNALED(status): signum = WTERMSIG(status) return "%s killed by signal %s" % (title, signalName(signum)) if not WIFEXITED(status): raise ValueError("Invalid status: %r" % status) exitcode = WEXITSTATUS(status) if exitcode: return "%s exited with code %s" % (title, exitcode) else: return "%s exited normally" % title
def displayProcessStatus(logger, status, prefix="Process"): if status == 0: logger.info("%s exited normally" % prefix) elif status < 0: signum = -status logger.error("%s killed by signal %s" % (prefix, signalName(signum))) else: logger.warning("%s exited with error code: %s" % (prefix, status))
def formatCloneFlags(flags): bits = readBits(flags, CLONE_FLAGS_BITMASK) signum = flags & 0xFF if signum: bits.insert(0, signalName(signum)) if bits: bits = "%s" % ("|".join(bits)) return "<%s> (%s)" % (bits, str(flags)) else: return str(flags)
def formatCloneFlags(argument): flags = argument.value bits = readBits(flags, CLONE_FLAGS_BITMASK) signum = flags & 0xFF if signum: bits.insert(0, signalName(signum)) if bits: bits = "%s" % ("|".join(bits)) return "<%s> (%s)" % (bits, str(flags)) else: return str(flags)
def renameSession(self, status): if status < 0: signum = -status name = signalName(signum) name = name.lower() elif 0 < status: name = "exitcode%s" % status else: # nul exitcode: don't rename the session return self.send('session_rename', name)
def _continueProcess(self, process, signum=None): if not signum and process in self.last_signal: signum = self.last_signal[process] if signum: error("Send %s to %s" % (signalName(signum), process)) process.cont(signum) try: del self.last_signal[process] except KeyError: pass else: process.cont()
def formatProcessStatus(status, title="Process"): """ Format a process status (integer) as a string. """ if WIFSTOPPED(status): signum = WSTOPSIG(status) text = "%s stopped by signal %s" % (title, signalName(signum)) elif WIFSIGNALED(status): signum = WTERMSIG(status) text = "%s killed by signal %s" % (title, signalName(signum)) else: if not WIFEXITED(status): raise ValueError("Invalid status: %r" % status) exitcode = WEXITSTATUS(status) if exitcode: text = "%s exited with code %s" % (title, exitcode) else: text = "%s exited normally" % title if WCOREDUMP(status): text += " (core dumped)" return text
def runCommand(logger, command, stdin=False, stdout=True, options=None): """ Run specified command: - logger is an object with a info() method - command: a string or a list of strings (eg. 'uname' or ['gcc', 'printf.c']) - stdin: if value is False, use null device as stdin (default: no stdin) - stdout: if value is False, use null device as stdout and stderr (default: keep stdout) Raise RuntimeError on failure: if the exit code is not nul or if the process is killed by a signal. """ if isinstance(command, (str, unicode)): command_str = repr(command) else: command_str = ' '.join(command) command_str = repr(command_str) logger.info("Run the command: %s" % command_str) if not options: options = {} if not stdin: stdin_file = open(devnull, 'r') options['stdin'] = stdin_file else: stdin_file = None stdout_file = None if not stdout: stdout_file = open(devnull, 'wb') options['stdout'] = stdout_file options['stderr'] = STDOUT elif stdout is not True: options['stdout'] = stdout options['stderr'] = STDOUT if ('close_fds' not in options) \ and (not RUNNING_WINDOWS): options['close_fds'] = True process = Popen(command, **options) status = process.wait() if stdin_file: stdin_file.close() if stdout_file: stdout_file.close() if not status: return if status < 0: errmsg = 'process killed by signal %s' % signalName(-status) else: errmsg = 'exit code %s' % status raise RuntimeError("Unable to run the command %s: %s" % ( command_str, errmsg))
def formatProcessStatus(status, title="Process"): """ Format a process status (integer) as a string. """ if RUNNING_WINDOWS: raise NotImplementedError() if WIFSTOPPED(status): signum = WSTOPSIG(status) text = "%s stopped by signal %s" % (title, signalName(signum)) elif WIFSIGNALED(status): signum = WTERMSIG(status) text = "%s killed by signal %s" % (title, signalName(signum)) else: if not WIFEXITED(status): raise ValueError("Invalid status: %r" % status) exitcode = WEXITSTATUS(status) if exitcode: text = "%s exited with code %s" % (title, exitcode) else: text = "%s exited normally" % title if WCOREDUMP(status): text += " (core dumped)" return text
def __init__(self, process, signum=None, exitcode=None): pid = process.pid if signum: message = "Process %s killed by signal %s" % ( pid, signalName(signum)) elif exitcode is not None: if not exitcode: message = "Process %s exited normally" % pid else: message = "Process %s exited with code %s" % (pid, exitcode) else: message = "Process %s terminated abnormally" % pid ProcessEvent.__init__(self, process, message) self.signum = signum self.exitcode = exitcode
def waitSignals(self, *signals, **kw): """ No signal means "any signal" """ pid = kw.get('pid', None) message = "Wait " if pid: message += "process %s for " % pid if signals: message += "signals (%s)" % ", ".join( signalName(signum) for signum in signals) else: message += "any signal" info(message) while True: event = self._wait(pid) if event.__class__ != ProcessSignal: raise event signum = event.signum if signum in signals or not signals: return event raise event
def processSignal(self, signum): debug("%s received signal %s" % (self, signalName(signum))) self.is_stopped = True return ProcessSignal(signum, self)
def kill(self, signum): debug("Send %s to %s" % (signalName(signum), self)) kill(self.pid, signum)
def processKilled(self, signum): debug("%s killed by signal %s" % (self, signalName(signum))) self._notRunning() return ProcessExit(self, signum=signum)
def createText(self): value = self.value argtype = self.type name = self.name if not argtype or not name: return formatWordHex(self.value) syscall = self.function.name # Special cases try: return SYSCALL_ARG_DICT[syscall][name][value] except KeyError: pass try: callback = ARGUMENT_CALLBACK[syscall][name] except KeyError: callback = None if callback: return callback(self) if syscall == "execve": if name in ("argv", "envp"): return self.readCStringArray(value) if syscall == "socketcall": if name == "call": try: return SOCKETCALL[value] except KeyError: return str(value) if name == "args": func_call = FunctionCall("socketcall", self.options) setupSocketCall(func_call, self.function.process, self.function[0], self.value) text = "<%s>" % func_call.format() return self.formatPointer(text, self.value) if syscall == "write" and name == "buf": fd = self.function[0].value if fd < 3: length = self.function[2].value return self.readString(value, length) if name == "signum": return signalName(value) if name in DIRFD_ARGUMENTS and argtype == "int": return formatDirFd(uint2int(value)) # Remove "const " prefix if argtype.startswith("const "): argtype = argtype[6:] if name in FILENAME_ARGUMENTS and argtype == "char *": return self.readCString(value) # Format depending on the type if argtype.endswith("*"): try: text = self.formatValuePointer(argtype[:-1]) if text: return text except PTRACE_ERRORS as err: writeError(getLogger(), err, "Warning: Format %r value error" % self, log_level=INFO) return formatAddress(self.value) # Array like "int[2]" match = re.match("(.*)\[([0-9])+\]", argtype) if match: basetype = match.group(1) count = int(match.group(2)) if basetype == "int": return self.readArray(self.value, c_int, count) # Simple types if argtype in ("unsigned int", "unsigned long", "u32"): return str(self.value) if argtype in INTEGER_TYPES: return str(uint2int(self.value)) # Default formatter: hexadecimal return formatWordHex(self.value)
def formatter(key): key += 1 return signalName(key)