def parseOptions(self): parser = OptionParser(usage="%prog [options] -- program [arg1 arg2 ...]") self.createCommonOptions(parser) parser.add_option("--enter", help="Show system call enter and exit", action="store_true", default=False) parser.add_option("--profiler", help="Use profiler", action="store_true", default=False) parser.add_option("--type", help="Display arguments type and result type (default: no)", action="store_true", default=False) parser.add_option("--name", help="Display argument name (default: no)", action="store_true", default=False) parser.add_option("--string-length", "-s", help="String max length (default: 300)", type="int", default=300) parser.add_option("--array-count", help="Maximum number of array items (default: 20)", type="int", default=20) parser.add_option("--raw-socketcall", help="Raw socketcall form", action="store_true", default=False) parser.add_option("--output", "-o", help="Write output to specified log file", type="str") parser.add_option("--ignore-regex", help="Regex used to filter syscall names (eg. --ignore='^(gettimeofday|futex|f?stat)')", type="str") parser.add_option("--address", help="Display structure addressl", action="store_true", default=False) parser.add_option("--syscalls", '-e', help="Comma separated list of shown system calls (other will be skipped)", type="str", default=None) parser.add_option("--socket", help="Show only socket functions", action="store_true", default=False) parser.add_option("--filename", help="Show only syscall using filename", action="store_true", default=False) parser.add_option("--show-pid", help="Prefix line with process identifier", action="store_true", default=False) parser.add_option("--list-syscalls", help="Display system calls and exit", action="store_true", default=False) parser.add_option("-i", "--show-ip", help="print instruction pointer at time of syscall", action="store_true", default=False) self.createLogOptions(parser) self.options, self.program = parser.parse_args() if self.options.list_syscalls: syscalls = list(SYSCALL_NAMES.items()) syscalls.sort(key=lambda data: data[0]) for num, name in syscalls: print("% 3s: %s" % (num, name)) exit(0) if self.options.pid is None and not self.program: parser.print_help() exit(1) # Create "only" filter only = set() if self.options.syscalls: # split by "," and remove spaces for item in self.options.syscalls.split(","): item = item.strip() if not item or item in only: continue ok = True valid_names = list(SYSCALL_NAMES.values()) for name in only: if name not in valid_names: print("ERROR: unknow syscall %r" % name, file=stderr) ok = False if not ok: print(file=stderr) print("Use --list-syscalls options to get system calls list", file=stderr) exit(1) # remove duplicates only.add(item) if self.options.filename: for syscall, format in SYSCALL_PROTOTYPES.items(): restype, arguments = format if any(argname in FILENAME_ARGUMENTS for argtype, argname in arguments): only.add(syscall) if self.options.socket: only |= SOCKET_SYSCALL_NAMES self.only = only if self.options.ignore_regex: try: self.ignore_regex = re.compile(self.options.ignore_regex) except Exception as err: print("Invalid regular expression! %s" % err) print("(regex: %r)" % self.options.ignore_regex) exit(1) else: self.ignore_regex = None if self.options.fork: self.options.show_pid = True self.processOptions()
def parseOptions(self): parser = OptionParser( usage="%prog [options] -- program [arg1 arg2 ...]") self.createCommonOptions(parser) parser.add_option("--enter", help="Show system call enter and exit", action="store_true", default=False) parser.add_option("--profiler", help="Use profiler", action="store_true", default=False) parser.add_option( "--type", help="Display arguments type and result type (default: no)", action="store_true", default=False) parser.add_option("--name", help="Display argument name (default: no)", action="store_true", default=False) parser.add_option("--string-length", "-s", help="String max length (default: 300)", type="int", default=300) parser.add_option("--array-count", help="Maximum number of array items (default: 20)", type="int", default=20) parser.add_option("--raw-socketcall", help="Raw socketcall form", action="store_true", default=False) parser.add_option("--output", "-o", help="Write output to specified log file", type="str") parser.add_option( "--ignore-regex", help= "Regex used to filter syscall names (e.g. --ignore='^(gettimeofday|futex|f?stat)')", type="str") parser.add_option("--address", help="Display structure address", action="store_true", default=False) parser.add_option( "--syscalls", '-e', help= "Comma separated list of shown system calls (other will be skipped)", type="str", default=None) parser.add_option("--socket", help="Show only socket functions", action="store_true", default=False) parser.add_option("--filename", help="Show only syscall using filename", action="store_true", default=False) parser.add_option("--show-pid", help="Prefix line with process identifier", action="store_true", default=False) parser.add_option("--list-syscalls", help="Display system calls and exit", action="store_true", default=False) parser.add_option("-i", "--show-ip", help="print instruction pointer at time of syscall", action="store_true", default=False) self.createLogOptions(parser) self.options, self.program = parser.parse_args() if self.options.list_syscalls: syscalls = list(SYSCALL_NAMES.items()) syscalls.sort(key=lambda data: data[0]) for num, name in syscalls: print("% 3s: %s" % (num, name)) exit(0) if self.options.pid is None and not self.program: parser.print_help() exit(1) # Create "only" filter only = set() if self.options.syscalls: # split by "," and remove spaces for item in self.options.syscalls.split(","): item = item.strip() if not item or item in only: continue ok = True valid_names = list(SYSCALL_NAMES.values()) for name in only: if name not in valid_names: print("ERROR: unknown syscall %r" % name, file=stderr) ok = False if not ok: print(file=stderr) print( "Use --list-syscalls options to get system calls list", file=stderr) exit(1) # remove duplicates only.add(item) if self.options.filename: for syscall, format in SYSCALL_PROTOTYPES.items(): restype, arguments = format if any(argname in FILENAME_ARGUMENTS for argtype, argname in arguments): only.add(syscall) if self.options.socket: only |= SOCKET_SYSCALL_NAMES self.only = only if self.options.ignore_regex: try: self.ignore_regex = re.compile(self.options.ignore_regex) except Exception as err: print("Invalid regular expression! %s" % err) print("(regex: %r)" % self.options.ignore_regex) exit(1) else: self.ignore_regex = None if self.options.fork: self.options.show_pid = True self.processOptions()
def __init__(self, application, settings): self.application = application self.ui = GtkBuilderLoader(FILE_UI_MAIN) self.settings = settings self.loadUI() # Restore the intercepted syscalls list from settings saved_syscalls = settings.get_intercepted_syscalls() # Restore the options from settings self.ui.menuitemAutoClear.set_active(self.settings.get_boolean( SECTION_APPLICATION, 'autoclear', self.ui.menuitemAutoClear.get_active())) # Update the Show only called syscalls in counts status self.ui.menuitemCountsOnlyCalled.set_active(self.settings.get_boolean( SECTION_COUNTS, 'only called', self.ui.menuitemCountsOnlyCalled.get_active())) self.on_menuitemCountsOnlyCalled_toggled(None) # Update the Show only existing files status self.ui.menuitemFilesShowOnlyExisting.set_active(self.settings.get_boolean( SECTION_FILES, 'only existing', self.ui.menuitemFilesShowOnlyExisting.get_active())) self.on_menuitemFilesShowOnlyExisting_toggled(None) self.ui.infobarInformation.set_visible(False) # Load all the available syscall names for syscall in sorted(SYSCALL_NAMES.values()): prototype = SYSCALL_PROTOTYPES.get(syscall, ('', ( ))) self.modelInterceptedSyscalls.add(items=( # If the configuration file has a list of intercepted syscalls then # set each syscall status accordingly saved_syscalls is None and True or syscall in saved_syscalls, # Add syscall name syscall, # Add return type prototype[0], # Add prototype arguments ', '.join(['%s %s' % m for m in prototype[1]]), # Does this syscall use any filename/pathname argument? any(argname in FILENAME_ARGUMENTS for argtype, argname in prototype[1]), # Is this syscall used by sockets? syscall in SOCKET_SYSCALL_NAMES, )) self.modelCounts.add(items=(syscall, 0, False)) self.update_InterceptedSyscalls_count() # Restore the saved size and position if self.settings.get_value('width', 0) and self.settings.get_value('height', 0): self.ui.winMain.set_default_size( self.settings.get_value('width', -1), self.settings.get_value('height', -1)) if self.settings.get_value('left', 0) and self.settings.get_value('top', 0): self.ui.winMain.move( self.settings.get_value('left', 0), self.settings.get_value('top', 0)) # Restore visible columns for current_section in self.column_headers.get_sections(): self.column_headers.load_visible_columns(current_section) # Set ModelFilter self.filtered_items = [] self.ui.filterActivities.set_visible_func(self.check_for_filtered_syscall, self.filtered_items) # Set counts filter self.ui.filterCounts.set_visible_column(self.modelCounts.COL_VISIBILITY) self.ui.filterCounts.refilter() # Set counts filter self.ui.filterFiles.set_visible_column(self.modelFiles.COL_EXISTING) self.ui.filterFiles.refilter() # Load the others dialogs self.about = AboutWindow(self.ui.winMain, False) self.thread_loader = None self.debugger = None
from ptrace.syscall.syscall_argument import ARGUMENT_CALLBACK from .syscall_filters import SYSCALL_FILTERS from .utilities import T, SYSCALL_REGISTER, RETURN_VALUE_REGISTER # Python 2/3 compatibility hack # Source: http://stackoverflow.com/a/7321970 try: input = raw_input except NameError: pass # Register filtered syscalls with python-ptrace so they are parsed correctly SYSCALL_PROTOTYPES.clear() FILENAME_ARGUMENTS.clear() for syscall_filter in SYSCALL_FILTERS: SYSCALL_PROTOTYPES[syscall_filter.name] = syscall_filter.signature for argument in syscall_filter.signature[1]: if argument[0] == "const char *": FILENAME_ARGUMENTS.add(argument[1]) # Turn list into dictionary indexed by syscall name for fast filter retrieval SYSCALL_FILTERS = {syscall_filter.name: syscall_filter for syscall_filter in SYSCALL_FILTERS} # Prevent python-ptrace from decoding arguments to keep raw numerical values SYSCALL_ARG_DICT.clear() ARGUMENT_CALLBACK.clear()
def main(argv=sys.argv[1:]): filter_scopes = sorted(SYSCALL_FILTERS.keys()) # Insert positional argument separator, if not already present if "--" not in argv: for i, argument in enumerate(argv): if not argument.startswith("-"): argv.insert(i, "--") break arg_parser = ArgumentParser( prog="maybe", usage="%(prog)s [options] command [argument ...]", description="Run a command without the ability to make changes to your system " + "and list the changes it would have made.", epilog="For more information, to report issues or to contribute, " + "visit https://github.com/p-e-w/maybe.", ) arg_parser.add_argument("command", nargs="+", help="the command to run under maybe's control") arg_group = arg_parser.add_mutually_exclusive_group() arg_group.add_argument("-a", "--allow", nargs="+", choices=filter_scopes, metavar="OPERATION", help="allow the command to perform the specified operation(s). " + "all other operations will be denied. " + "possible values for %(metavar)s are: %(choices)s") arg_group.add_argument("-d", "--deny", nargs="+", choices=filter_scopes, metavar="OPERATION", help="deny the command the specified operation(s). " + "all other operations will be allowed. " + "see --allow for a list of possible values for %(metavar)s. " + "--allow and --deny cannot be combined") arg_parser.add_argument("-l", "--list-only", action="store_true", help="list operations without header, indentation and rerun prompt") arg_parser.add_argument("--style-output", choices=["yes", "no", "auto"], default="auto", help="colorize output using ANSI escape sequences (yes/no) " + "or automatically decide based on whether stdout is a terminal (auto, default)") arg_parser.add_argument("-v", "--verbose", action="count", help="if specified once, print every filtered syscall. " + "if specified twice, print every syscall, highlighting filtered syscalls") arg_parser.add_argument("--version", action="version", version="%(prog)s 0.4.0") args = arg_parser.parse_args(argv) initialize_terminal(args.style_output) if args.allow is not None: filter_scopes = set(filter_scopes) - set(args.allow) elif args.deny is not None: filter_scopes = args.deny SYSCALL_PROTOTYPES.clear() FILENAME_ARGUMENTS.clear() syscall_filters = {} for filter_scope in filter_scopes: for syscall_filter in SYSCALL_FILTERS[filter_scope]: syscall_filters[syscall_filter.name] = syscall_filter # Register filtered syscalls with python-ptrace so they are parsed correctly SYSCALL_PROTOTYPES[syscall_filter.name] = syscall_filter.signature for argument in syscall_filter.signature[1]: if argument[0] == "const char *": FILENAME_ARGUMENTS.add(argument[1]) # Prevent python-ptrace from decoding arguments to keep raw numerical values SYSCALL_ARG_DICT.clear() ARGUMENT_CALLBACK.clear() # This is basically "shlex.join" command = " ".join([(("'%s'" % arg) if (" " in arg) else arg) for arg in args.command]) try: args.command[0] = locateProgram(args.command[0]) pid = createChild(args.command, False) except Exception as error: print(T.red("Error executing %s: %s." % (T.bold(command) + T.red, error))) return 1 debugger = PtraceDebugger() debugger.traceExec() try: debugger.traceFork() except DebuggerError: print(T.yellow("Warning: Running without traceFork support. " + "Syscalls from subprocesses can not be intercepted.")) process = debugger.addProcess(pid, True) process.syscall() try: operations = get_operations(debugger, syscall_filters, args.verbose) except Exception as error: print(T.red("Error tracing process: %s." % error)) return 1 except KeyboardInterrupt: print(T.yellow("%s terminated by keyboard interrupt." % (T.bold(command) + T.yellow))) return 2 finally: # Cut down all processes no matter what happens # to prevent them from doing any damage debugger.quit() if operations: if not args.list_only: print("%s has prevented %s from performing %d file system operations:\n" % (T.bold("maybe"), T.bold(command), len(operations))) for operation in operations: print(("" if args.list_only else " ") + operation) if not args.list_only: try: choice = input("\nDo you want to rerun %s and permit these operations? [y/N] " % T.bold(command)) except KeyboardInterrupt: choice = "" # Ctrl+C does not print a newline automatically print("") if choice.lower() == "y": subprocess.call(args.command) else: print("%s has not detected any file system operations from %s." % (T.bold("maybe"), T.bold(command)))