Example #1
0
    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()
Example #3
0
 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
Example #4
0
File: maybe.py Project: wflk/maybe
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()

Example #5
0
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)))