def main(): # Check the command line if len(argv) < 2: print("usage: %s program [arg1 arg2 ...]" % argv[0], file=stderr) print(" or: %s pid" % argv[0], file=stderr) exit(1) # Get the process identifier is_attached = False has_pid = False if len(argv) == 2: try: # User asked to attach a process pid = int(argv[1]) has_pid = True except ValueError: pass if not has_pid: # User asked to create a new program and trace it arguments = argv[1:] pid = traceProgram(arguments) is_attached = True # Create the debugger and attach the process dbg = PtraceDebugger() process = dbg.addProcess(pid, is_attached) # Play with the process and then quit playWithProcess(process) dbg.quit()
def fuzzProg(arguments, program): is_attached = False #create the debugger dbg = PtraceDebugger() #create list to fuzz the argument fuzzed = attack() index = 0 fileProgName = program slashes = program.rfind('/') #if the user has put in the full program path if slashes!=-1: fileProgName = program[slashes+1:] filename = 'ErrorsIn' + fileProgName file = open(filename, 'w') #for fuzzing each argument in turn for arg in arguments: for fuzzedArg in fuzzed: toFuzz = [program, arg, fuzzedArg] pid = procFork(toFuzz) is_attached = True if len(toFuzz)==2: print "Fuzzing %s %s" % (toFuzz[0], toFuzz[1]) elif len(toFuzz)>2: print "Fuzzing %s %s %s" % (toFuzz[0], toFuzz[1], toFuzz[2]) error = fuzz(dbg, pid, is_attached, toFuzz) #if there's an error, print it to file if error != '': print error file.write(error) #make sure to close file after fuzzed file.close() #Quit the debugger after fuzzing everything dbg.quit()
def main(argv=sys.argv): if len(argv) < 2: print(T.red("Error: No command given.")) print("Usage: %s COMMAND [ARGUMENT]..." % argv[0]) return 1 # This is basically "shlex.join" command = " ".join([(("'%s'" % arg) if (" " in arg) else arg) for arg in argv[1:]]) arguments = argv[1:] arguments[0] = locateProgram(arguments[0]) try: pid = createChild(arguments, 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) prepareProcess(process) try: operations = get_operations(debugger) 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: 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(" " + operation) 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(argv[1:]) else: print("%s has not detected any file system operations from %s." % (T.bold("maybe"), T.bold(command)))
def main(): if len(argv) < 2: print(T.red("Error: No command given.")) print("Usage: %s COMMAND [ARGUMENT]..." % argv[0]) exit(1) # This is basically "shlex.join" command = " ".join([(("'%s'" % arg) if (" " in arg) else arg) for arg in argv[1:]]) arguments = argv[1:] arguments[0] = locateProgram(arguments[0]) try: pid = createChild(arguments, False) except Exception as error: print(T.red("Error executing %s: %s." % (T.bold(command) + T.red, error))) exit(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) prepareProcess(process) try: operations = get_operations(debugger) except Exception as error: print(T.red("Error tracing process: %s." % error)) exit(1) except KeyboardInterrupt: print(T.yellow("%s terminated by keyboard interrupt." % (T.bold(command) + T.yellow))) exit(2) finally: # Cut down all processes no matter what happens # to prevent them from doing any damage debugger.quit() if operations: 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(" " + operation) try: choice = input("\nDo you want to rerun %s and permit these operations? [y/N] " % T.bold(command)) except KeyboardInterrupt: choice = "" if choice.lower() == "y": call(argv[1:]) else: print("%s has not detected any file system operations from %s." % (T.bold("maybe"), T.bold(command)))
class TibiaProcess: ips = [ "login01.tibia.com", "login02.tibia.com", "login03.tibia.com", "login04.tibia.com", "login05.tibia.com", "tibia01.cipsoft.com", "tibia02.cipsoft.com", "tibia03.cipsoft.com", "tibia04.cipsoft.com", "tibia05.cipsoft.com", "test.tibia.com", "test.cipsoft.com", "tibia2.cipsoft.com", "tibia1.cipsoft.com", "server.tibia.com", "server2.tibia.com" ] rsas = ( "124710459426827943004376449897985582167801707960697037164044904862948569380850421396904597686953877022394604239428185498284169068581802277612081027966724336319448537811441719076484340922854929273517308661370727105382899118999403808045846444647284499123164879035103627004668521005328367415259939915284902061793", "132127743205872284062295099082293384952776326496165507967876361843343953435544496682053323833394351797728954155097012103928360786959821132214473291575712138800495033169914814069637740318278150290733684032524174782740134357629699062987023311132821016569775488792221429527047321331896351555606801473202394175817" ) ot_rsa = "109120132967399429278860960508995541528237502902798129123468757937266291492576446330739696001110603907230888610072655818825358503429057592827629436413108566029093628212635953836686562675849720620786279431090218017681061521755056710823876476444260558147179707119674283982419152118103759076030616683978566631413" def __init__(self, process): self.maps = [] self.current_ip = None self.tracer = PtraceDebugger() self.process = self.tracer.addProcess(process.pid, False) self.maps.extend(self.process.readMappings()[0:4]) def detach(self): self.process.detach() self.tracer.quit() def changeRsa(self): for rsa in self.rsas: for res in self.maps[0].search(bytes(rsa, 'utf-8')): print("RSA modified.", res) self.process.writeBytes(res, bytes(self.ot_rsa, 'utf-8')) def changeIp(self, newip): for ip in self.ips: for maps in self.maps: for res in maps.search(bytes(ip, 'utf-8')): print("Found: " + ip) self.process.writeBytes(res, bytes(newip, 'utf-8')) if len(ip) > len(newip): for offset in range(len(newip), len(ip)): self.process.writeBytes(res + offset, bytes('\x00', 'utf-8')) print("Writing: " + newip) self.ips.insert(0, newip)
def start(pid): # need to attach to the process with ptrace before you can read it's proc/$pid/maps # or be root I guess # https://unix.stackexchange.com/questions/6301/how-do-i-read-from-proc-pid-mem-under-linux # http://lkml.iu.edu/hypermail/linux/kernel/0505.0/0858.html dbg = PtraceDebugger() try: process = dbg.addProcess(pid, False) except: print("Could not attach to process {}".format(pid)) return dump(process, pid) dbg.quit()
class Sniffer(object): def __init__(self, pid=None): self.sessions = {} self.connections = {} self.dbg = PtraceDebugger() self.processes = {} def __del__(self): for pid in dict(self.processes): self.deleteProcess(pid) self.dbg.quit() def tcp_handler(self, tcp): if tcp.nids_state == nids.NIDS_JUST_EST and tcp.addr[1][1] in PORTS: self.connections[tcp.addr] = Connection(self, tcp) elif tcp.nids_state == nids.NIDS_DATA: if tcp.addr not in self.connections: return connection = self.connections[tcp.addr] try: if tcp.client.count_new: connection.handle_data( connection.server, tcp.client.data[:tcp.client.count_new]) if tcp.server.count_new: connection.handle_data( connection.client, tcp.server.data[:tcp.server.count_new]) except Connection.InvalidDataException: del self.connections[tcp.addr] elif tcp.nids_state in (nids.NIDS_CLOSE, nids.NIDS_TIMEOUT, nids.NIDS_RESET): if tcp.addr in self.connections: del self.connections[tcp.addr] def message_handler(self, message): pass def session_handler(self, session): pass def addProcess(self, pid): self.processes[pid] = self.dbg.addProcess(pid, False) self.processes[pid].cont() def deleteProcess(self, pid): self.dbg.deleteProcess(pid) del self.processes[pid]
def main(): sys.argv = sys.argv[1:] filter_scopes = SYSCALL_FILTERS.keys() syscall_filters = {} for filter_scope in SYSCALL_FILTERS: if filter_scope in filter_scopes: for syscall in SYSCALL_FILTERS[filter_scope]: syscall_filters[syscall] = SYSCALL_FILTERS[filter_scope][ syscall] # Suppress logging output from python-ptrace getLogger().addHandler(NullHandler()) # Prevent python-ptrace from decoding arguments to keep raw numerical values DIRFD_ARGUMENTS.clear() SYSCALL_ARG_DICT.clear() ARGUMENT_CALLBACK.clear() try: sys.argv[0] = locateProgram(sys.argv[0]) pid = createChild(sys.argv, False) except Exception as error: print(f"Error executing {sys.argv}: {error}.") return 1 debugger = PtraceDebugger() debugger.traceFork() debugger.traceExec() process = debugger.addProcess(pid, True) process.syscall() try: operations = get_operations(debugger, syscall_filters, True) except Exception as error: print(f"Error tracing process: {error}.") return 1 finally: # Cut down all processes no matter what happens # to prevent them from doing any damage debugger.quit() if operations: for operation in operations: print(" " + operation) else: print(f"Not detected any file system operations from: {sys.argv}")
class CPtraceDumper: """ Ptrace Dumper """ # Output base file outFile = None # Pid of the process to dump pid = None # PtraceDebugger instance dbg = None # Process name processName = None def __init__(self, pid, outFile): """ Set the pid and the base output filename """ self.pid = pid self.outFile = outFile def dump(self): """ Dump the memory """ self.dbg = PtraceDebugger() #print "Created object" try: self.dbg.addProcess(self.pid, False) #print "Attached to process" except: #print "Error adding process", sys.exc_info()[1] pass try: #print "Resolving process's name" self.processName = getProcessName(self.pid) #print "Resolving segmnents" segments = getMemorySegments(self.pid, True) #print "SEGMENTS:", segments #print "Now dumping memory" dumpProcessMemory(self.pid, self.outFile, segments, True) except: pass #print "Exception in self.dump" #print sys.exc_info()[1] #raise def quit(self): """ Exit from ptrace debugger """ #print "Quiting from ptrace debugger" self.dbg.quit()
class Sniffer(object): def __init__(self, pid=None): self.sessions = {} self.connections = {} self.dbg = PtraceDebugger() self.processes = {} def __del__(self): for pid in dict(self.processes): self.deleteProcess(pid) self.dbg.quit() def tcp_handler(self, tcp): if tcp.nids_state == nids.NIDS_JUST_EST and tcp.addr[1][1] in PORTS: self.connections[tcp.addr] = Connection(self, tcp) elif tcp.nids_state == nids.NIDS_DATA: if tcp.addr not in self.connections: return connection = self.connections[tcp.addr] try: if tcp.client.count_new: connection.handle_data(connection.server, tcp.client.data[:tcp.client.count_new]) if tcp.server.count_new: connection.handle_data(connection.client, tcp.server.data[:tcp.server.count_new]) except Connection.InvalidDataException: del self.connections[tcp.addr] elif tcp.nids_state in (nids.NIDS_CLOSE, nids.NIDS_TIMEOUT, nids.NIDS_RESET): if tcp.addr in self.connections: del self.connections[tcp.addr] def message_handler(self, message): pass def session_handler(self, session): pass def addProcess(self, pid): self.processes[pid] = self.dbg.addProcess(pid, False) self.processes[pid].cont() def deleteProcess(self, pid): self.dbg.deleteProcess(pid) del self.processes[pid]
class Fuzz(object): def try_arg(self, str_argument): print "Trying argument %s" % str_argument process = self.test_process new_str_addr = self.mem + 0x100 arg_addr = process.getreg("esp") + 4 arg = process.readWord(arg_addr) process.writeBytes(new_str_addr, str_argument) process.writeWord(arg_addr, new_str_addr) arg = process.readWord(arg_addr) playWithProcess(process) def fuzz(self, arguments, address): self.arguments = arguments self.address = address self.mem = None self.pid = traceProgram(self.arguments) self.is_attached = True self.dbg = PtraceDebugger() self.process = self.dbg.addProcess(self.pid, self.is_attached) self.process.createBreakpoint(self.address, None) self.process.setoptions(ptrace_bindings.PTRACE_O_TRACEFORK) self.mem = allocateMemory(self.process) playWithProcess(self.process) ip = self.process.getInstrPointer() - 1 breakpoint = self.process.findBreakpoint(ip) if breakpoint: print ("Stopped at %s" % breakpoint) breakpoint.desinstall(set_ip=True) for length in range(0, 20): self.test_process = createCheckpoint(self.process) self.try_arg("a" * length) #self.test_process.terminate(True) self.dbg.quit()
def main(): parser = argparse.ArgumentParser( description='Utility to reset user\'s health to full.') parser.add_argument('pid', nargs='?', type=int, help='Specify the PID of the game') args = parser.parse_args() pid = args.pid if not pid: pid = find_hack_pid() if pid == -1: logging.error('Unable to find hack process to attach to.') return dbg = PtraceDebugger() proc = dbg.addProcess(pid, False) # Read the value for full health, need just the lower 4 bytes full_hp = proc.readWord(0x6425a0) & 0xffffffff # Value is (curr hp, ???) value = proc.readWord(0x642598) curr_hp = value >> 32 logging.info('Changing health from %d -> %d', curr_hp, full_hp) # Calculate the new value to write back value = (full_hp << 32) + (value & 0xffffffff) # Write the new value proc.writeWord(0x642598, value) dbg.quit()
def main(): if len(argv) < 2: usage() # create process env = None arguments = argv[1:] arguments[0] = locateProgram(arguments[0]) pid = createChild(arguments, False, env) # create debugger debugger = PtraceDebugger() debugger.enableSysgood() debugger.traceExec() debugger.traceFork() # attach process debugger.addProcess(pid, True) process = debugger[pid] process.syscall() # start event loop loop(debugger) debugger.quit()
def main(argv=sys.argv[1:]): if PY2: argv = [unicode(arg, sys.getfilesystemencoding()) for arg in argv] # noqa # 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="+", metavar="OPERATION", help="allow the command to perform the specified operation(s). " + "all other operations will be denied. " + "possible values for %(metavar)s are: " + ", ".join(sorted(SYSCALL_FILTERS.keys())) + "; as well as any filter scopes defined by loaded plugins") arg_group.add_argument("-d", "--deny", nargs="+", 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("-p", "--plugin", nargs="+", metavar="FILE", help="load the specified plugin script(s). " + "see the README for details and plugin API documentation") 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.plugin is not None: for plugin_path in args.plugin: try: module_name = splitext(basename(plugin_path))[0] # Note: imp.load_source is *long* deprecated and not even documented # in Python 3 anymore, but it still seems to work and the "alternatives" # (see http://stackoverflow.com/a/67692) are simply too insane to use load_source(module_name, plugin_path) except Exception as error: print(T.red("Error loading %s: %s." % (T.bold(plugin_path) + T.red, error))) return 1 if args.allow is not None: for filter_scope in args.allow: if filter_scope not in SYSCALL_FILTERS: print(T.red("Unknown operation in --allow: %s." % (T.bold(filter_scope) + T.red))) return 1 filter_scopes = set(SYSCALL_FILTERS.keys()) - set(args.allow) elif args.deny is not None: for filter_scope in args.deny: if filter_scope not in SYSCALL_FILTERS: print(T.red("Unknown operation in --deny: %s." % (T.bold(filter_scope) + T.red))) return 1 filter_scopes = args.deny else: filter_scopes = SYSCALL_FILTERS.keys() syscall_filters = {} for filter_scope in SYSCALL_FILTERS: if filter_scope in filter_scopes: for syscall in SYSCALL_FILTERS[filter_scope]: syscall_filters[syscall] = SYSCALL_FILTERS[filter_scope][syscall] # Suppress logging output from python-ptrace getLogger().addHandler(NullHandler()) # Prevent python-ptrace from decoding arguments to keep raw numerical values DIRFD_ARGUMENTS.clear() 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.traceFork() debugger.traceExec() 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: print("\nDo you want to rerun %s and permit these operations? [y/N] " % T.bold(command), end="") try: choice = input() 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)))
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_filters = {} for filter_scope in filter_scopes: for syscall in SYSCALL_FILTERS[filter_scope]: syscall_filters[syscall] = SYSCALL_FILTERS[filter_scope][syscall] # Prevent python-ptrace from decoding arguments to keep raw numerical values DIRFD_ARGUMENTS.clear() 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.traceFork() debugger.traceExec() 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)))
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_filters = {} for filter_scope in filter_scopes: for syscall_filter in SYSCALL_FILTERS[filter_scope]: syscall_filters[syscall_filter.syscall] = syscall_filter # Prevent python-ptrace from decoding arguments to keep raw numerical values DIRFD_ARGUMENTS.clear() 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)))
def main(argv=sys.argv[1:]): # 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_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) # 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, args) 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)))
class DebugServerManager(ServerManager): def __init__(self, config, queue_sync, queue_out, targetPort): ServerManager.__init__(self, config, queue_sync, queue_out, targetPort) self.dbg = None self.crashEvent = None self.proc = None self.p = None def _startServer(self): # create child via ptrace debugger # API: createChild(arguments[], no_stdout, env=None) logging.debug("START: " + str( serverutils.getInvokeTargetArgs(self.config, self.targetPort + 1000))) self.pid = createChild( serverutils.getInvokeTargetArgs(self.config, self.targetPort), False, # no_stdout None, ) # Attach to the process with ptrace and let it run self.dbg = PtraceDebugger() self.proc = self.dbg.addProcess(self.pid, True) self.proc.cont() time.sleep(1) # i dont think this works here... # FIXME event = self.dbg.waitProcessEvent(blocking=False) if event is not None and type(event) == ProcessExit: logging.error("Started server, but it already exited: " + str(event)) return False return True def _stopServer(self): try: self.dbg.quit() os.kill(self.pid, signal.SIGTERM) except: # is already dead... pass def _waitForCrash(self): #subprocess.call("echo AAA1; ls -l /proc/" + str(self.pid), shell=True) while True: logging.info("DebugServer: Waiting for process event") event = self.dbg.waitProcessEvent() logging.info("DebugServer: Got event: " + str(event)) # If this is a process exit we need to check if it was abnormal if type(event) == ProcessExit: if event.signum is None or event.exitcode == 0: # Clear the event since this was a normal exit event = None # If this is a signal we need to check if we're ignoring it elif type(event) == ProcessSignal: if event.signum == signal.SIGCHLD: # Ignore these signals and continue waiting continue elif event.signum == signal.SIGTERM: # server cannot be started, return event = None self.queue_sync.put(("err", event.signum)) break if event is not None and event.signum != 15: logging.info("DebugServer: Event Result: Crash") #subprocess.call("echo AAA2; ls -l /proc/" + str(self.pid), shell=True) self.crashEvent = event return True else: logging.info("DebugServer: Event Result: No crash") self.crashEvent = None return False def _getCrashDetails(self): event = self.crashEvent # Get the address where the crash occurred faultAddress = 0 try: faultAddress = event.process.getInstrPointer() except Exception as e: # process already dead, hmm print(("GetCrashDetails exception: " + str(e))) # Find the module that contains this address # Now we need to turn the address into an offset. This way when the process # is loaded again if the module is loaded at another address, due to ASLR, # the offset will be the same and we can correctly detect those as the same # crash module = None faultOffset = 0 try: for mapping in event.process.readMappings(): if faultAddress >= mapping.start and faultAddress < mapping.end: module = mapping.pathname faultOffset = faultAddress - mapping.start break except Exception as error: print("getCrashDetails Exception: " + str(error)) # it always has a an exception... pass # Apparently the address didn't fall within a mapping if module is None: module = "Unknown" faultOffset = faultAddress # Get the signal sig = event.signum # Get the details of the crash details = None details = "" stackAddr = 0 stackPtr = 0 backtraceFrames = None pRegisters = None try: if event._analyze() is not None: details = event._analyze().text # more data stackAddr = self.proc.findStack() stackPtr = self.proc.getStackPointer() # convert backtrace backtrace = self.proc.getBacktrace() backtraceFrames = [] for frame in backtrace.frames: backtraceFrames.append(str(frame)) # convert registers from ctype to python registers = self.proc.getregs() pRegisters = {} for field_name, field_type in registers._fields_: regName = str(field_name) regValue = str(getattr(registers, field_name)) pRegisters[regName] = regValue except Exception as e: # process already dead, hmm print(("GetCrashDetails exception: " + str(e))) vCrashData = verifycrashdata.VerifyCrashData( faultAddress=faultAddress, faultOffset=faultOffset, module=module, sig=sig, details=details, stackPointer=stackPtr, stackAddr=str(stackAddr), backtrace=backtraceFrames, registers=pRegisters, ) asanOutput = serverutils.getAsanOutput(self.config, self.pid) vCrashData.setTemp(asanOutput) return vCrashData