From 7335fe40dabb28ef5c4c95a0ebbcfda34ddd0ea7 Mon Sep 17 00:00:00 2001 From: 7AV <7AV@users.noreply.github.com> Date: Sun, 16 Apr 2017 15:34:18 -0700 Subject: [PATCH] Decrypted WINDOWS warez the ORIGINAL files for Equation Group's DANDERSPRITZ, FUZZBUNCH and NSA 0day exploits found in Shadow Broker's [Lost In Translation] archive. --- windows/fuzzbunch/__init__.py | 6 + windows/fuzzbunch/coli.py | 264 ++ windows/fuzzbunch/command.py | 555 +++++ windows/fuzzbunch/context.py | 75 + windows/fuzzbunch/daveplugin.py | 528 ++++ windows/fuzzbunch/deployablemanager.py | 157 ++ windows/fuzzbunch/edfexecution.py | 268 ++ windows/fuzzbunch/edfmeta.py | 276 +++ windows/fuzzbunch/edfplugin.py | 360 +++ windows/fuzzbunch/env.py | 59 + windows/fuzzbunch/exception.py | 82 + windows/fuzzbunch/exma.py | 74 + windows/fuzzbunch/figlet.py | 250 ++ windows/fuzzbunch/fuzzbunch.py | 995 ++++++++ windows/fuzzbunch/iohandler.py | 860 +++++++ windows/fuzzbunch/log.py | 369 +++ windows/fuzzbunch/plugin.py | 450 ++++ windows/fuzzbunch/pluginfinder.py | 93 + windows/fuzzbunch/pluginmanager.py | 743 ++++++ windows/fuzzbunch/pyreadline/__init__.py | 31 + .../pyreadline/clipboard/__init__.py | 73 + .../clipboard/ironpython_clipboard.py | 28 + .../pyreadline/clipboard/win32_clipboard.py | 105 + .../configuration/pyreadlineconfig.ini | 85 + .../pyreadline/configuration/startup.py | 16 + .../fuzzbunch/pyreadline/console/__init__.py | 21 + windows/fuzzbunch/pyreadline/console/ansi.py | 181 ++ .../fuzzbunch/pyreadline/console/console.py | 758 ++++++ .../pyreadline/console/console_attributes.py | 16 + .../pyreadline/console/consolebase.py | 52 + windows/fuzzbunch/pyreadline/console/event.py | 25 + .../pyreadline/console/ironpython_console.py | 428 ++++ windows/fuzzbunch/pyreadline/error.py | 14 + windows/fuzzbunch/pyreadline/get_doc.py | 18 + .../fuzzbunch/pyreadline/keysyms/__init__.py | 20 + .../fuzzbunch/pyreadline/keysyms/common.py | 118 + .../pyreadline/keysyms/ironpython_keysyms.py | 206 ++ .../fuzzbunch/pyreadline/keysyms/keysyms.py | 130 + .../pyreadline/keysyms/winconstants.py | 171 ++ .../pyreadline/lineeditor/__init__.py | 0 .../pyreadline/lineeditor/history.py | 245 ++ .../pyreadline/lineeditor/lineobj.py | 790 ++++++ .../pyreadline/lineeditor/wordmatcher.py | 103 + windows/fuzzbunch/pyreadline/logger.py | 45 + windows/fuzzbunch/pyreadline/logserver.py | 60 + .../fuzzbunch/pyreadline/modes/__init__.py | 5 + .../fuzzbunch/pyreadline/modes/basemode.py | 457 ++++ windows/fuzzbunch/pyreadline/modes/emacs.py | 626 +++++ .../fuzzbunch/pyreadline/modes/fuzzbunch.py | 54 + .../fuzzbunch/pyreadline/modes/notemacs.py | 601 +++++ windows/fuzzbunch/pyreadline/modes/vi.py | 1219 ++++++++++ windows/fuzzbunch/pyreadline/release.py | 74 + windows/fuzzbunch/pyreadline/rlmain.py | 460 ++++ windows/fuzzbunch/pyreadline/test/common.py | 78 + .../fuzzbunch/pyreadline/test/emacs_test.py | 396 +++ .../pyreadline/test/lineeditor_test.py | 390 +++ windows/fuzzbunch/pyreadline/test/vi_test.py | 2146 +++++++++++++++++ windows/fuzzbunch/redirection.py | 510 ++++ windows/fuzzbunch/session.py | 316 +++ windows/fuzzbunch/truantchild.py | 962 ++++++++ windows/fuzzbunch/util.py | 285 +++ 61 files changed, 18782 insertions(+) create mode 100644 windows/fuzzbunch/__init__.py create mode 100644 windows/fuzzbunch/coli.py create mode 100644 windows/fuzzbunch/command.py create mode 100644 windows/fuzzbunch/context.py create mode 100644 windows/fuzzbunch/daveplugin.py create mode 100644 windows/fuzzbunch/deployablemanager.py create mode 100644 windows/fuzzbunch/edfexecution.py create mode 100644 windows/fuzzbunch/edfmeta.py create mode 100644 windows/fuzzbunch/edfplugin.py create mode 100644 windows/fuzzbunch/env.py create mode 100644 windows/fuzzbunch/exception.py create mode 100644 windows/fuzzbunch/exma.py create mode 100644 windows/fuzzbunch/figlet.py create mode 100644 windows/fuzzbunch/fuzzbunch.py create mode 100644 windows/fuzzbunch/iohandler.py create mode 100644 windows/fuzzbunch/log.py create mode 100644 windows/fuzzbunch/plugin.py create mode 100644 windows/fuzzbunch/pluginfinder.py create mode 100644 windows/fuzzbunch/pluginmanager.py create mode 100644 windows/fuzzbunch/pyreadline/__init__.py create mode 100644 windows/fuzzbunch/pyreadline/clipboard/__init__.py create mode 100644 windows/fuzzbunch/pyreadline/clipboard/ironpython_clipboard.py create mode 100644 windows/fuzzbunch/pyreadline/clipboard/win32_clipboard.py create mode 100644 windows/fuzzbunch/pyreadline/configuration/pyreadlineconfig.ini create mode 100644 windows/fuzzbunch/pyreadline/configuration/startup.py create mode 100644 windows/fuzzbunch/pyreadline/console/__init__.py create mode 100644 windows/fuzzbunch/pyreadline/console/ansi.py create mode 100644 windows/fuzzbunch/pyreadline/console/console.py create mode 100644 windows/fuzzbunch/pyreadline/console/console_attributes.py create mode 100644 windows/fuzzbunch/pyreadline/console/consolebase.py create mode 100644 windows/fuzzbunch/pyreadline/console/event.py create mode 100644 windows/fuzzbunch/pyreadline/console/ironpython_console.py create mode 100644 windows/fuzzbunch/pyreadline/error.py create mode 100644 windows/fuzzbunch/pyreadline/get_doc.py create mode 100644 windows/fuzzbunch/pyreadline/keysyms/__init__.py create mode 100644 windows/fuzzbunch/pyreadline/keysyms/common.py create mode 100644 windows/fuzzbunch/pyreadline/keysyms/ironpython_keysyms.py create mode 100644 windows/fuzzbunch/pyreadline/keysyms/keysyms.py create mode 100644 windows/fuzzbunch/pyreadline/keysyms/winconstants.py create mode 100644 windows/fuzzbunch/pyreadline/lineeditor/__init__.py create mode 100644 windows/fuzzbunch/pyreadline/lineeditor/history.py create mode 100644 windows/fuzzbunch/pyreadline/lineeditor/lineobj.py create mode 100644 windows/fuzzbunch/pyreadline/lineeditor/wordmatcher.py create mode 100644 windows/fuzzbunch/pyreadline/logger.py create mode 100644 windows/fuzzbunch/pyreadline/logserver.py create mode 100644 windows/fuzzbunch/pyreadline/modes/__init__.py create mode 100644 windows/fuzzbunch/pyreadline/modes/basemode.py create mode 100644 windows/fuzzbunch/pyreadline/modes/emacs.py create mode 100644 windows/fuzzbunch/pyreadline/modes/fuzzbunch.py create mode 100644 windows/fuzzbunch/pyreadline/modes/notemacs.py create mode 100644 windows/fuzzbunch/pyreadline/modes/vi.py create mode 100644 windows/fuzzbunch/pyreadline/release.py create mode 100644 windows/fuzzbunch/pyreadline/rlmain.py create mode 100644 windows/fuzzbunch/pyreadline/test/common.py create mode 100644 windows/fuzzbunch/pyreadline/test/emacs_test.py create mode 100644 windows/fuzzbunch/pyreadline/test/lineeditor_test.py create mode 100644 windows/fuzzbunch/pyreadline/test/vi_test.py create mode 100644 windows/fuzzbunch/redirection.py create mode 100644 windows/fuzzbunch/session.py create mode 100644 windows/fuzzbunch/truantchild.py create mode 100644 windows/fuzzbunch/util.py diff --git a/windows/fuzzbunch/__init__.py b/windows/fuzzbunch/__init__.py new file mode 100644 index 0000000..308ba92 --- /dev/null +++ b/windows/fuzzbunch/__init__.py @@ -0,0 +1,6 @@ + +__all__ = ['command' 'edeconfig', 'edeplugin', 'edfexecution', + 'edfmeta', 'edfplugin', 'exception', 'exma', 'figlet', + 'fuzzbunch', 'iohandler', 'plugin', 'pluginmanager', + 'redirection', 'session', 'trch', 'truantchild', 'util', + 'env'] diff --git a/windows/fuzzbunch/coli.py b/windows/fuzzbunch/coli.py new file mode 100644 index 0000000..84fde5a --- /dev/null +++ b/windows/fuzzbunch/coli.py @@ -0,0 +1,264 @@ +import sys +import truantchild +import exma +from optparse import OptionParser +import logging +import ctypes + +EDF_CLEANUP_WAIT = 0 + +# +# @todo coli needs a logger that will duplicate output to stdout and the file +# + +# ID - sha1 of Name-Maj.Min.Rev of the version number. See EDF/noarch/EDF-CMake/generatePluginID.py +class ExploitConfigError(ValueError): + pass + +def get_logger(logfile): + # configure the root logger + logger = logging.getLogger() + logger.setLevel(logging.DEBUG) + + if logfile is not None: + fh = logging.FileHandler(logfile, mode="w") + fh.setLevel(logging.DEBUG) + fhFormatter = logging.Formatter('[%(levelname)-8s] %(filename)-18s (line %(lineno)-4s) -- %(message)s') + fh.setFormatter(fhFormatter) + logger.addHandler(fh) + + ch = logging.StreamHandler() + ch.setLevel(logging.INFO) + chFormatter = logging.Formatter("%(message)s") + ch.setFormatter(chFormatter) + logger.addHandler(ch) + return logger + + +class CommandlineWrapper(object): + def __init__(self): + # This takes care of addWrapperInputs + self.__coli_parser = OptionParser() + self.__coli_parser.add_option("--InConfig", dest="InConfig", help="The Input XML file" ) + self.__coli_parser.add_option("--OutConfig", dest="OutConfig", default=sys.stdout, + help="Output XML file") + self.__coli_parser.add_option("--LogFile", dest="LogFile", default=None, + help="Truantchild log file") + self.__coli_parser.add_option("--ValidateOnly", dest="ValidateOnly", default=False, action="store_true", + help="Valid params") + + def __hack_params_parseCommandLine(self, params, args, doHelp): + params = ctypes.pointer(params) + cArgs = len(args) + args = ctypes.pointer(args) + doHelp = ctypes.c_uint() + pass + + def __call__(self, argv): + """Effectively "main" from Commandlinewrapper""" + logConfig = None + context = {} + rendezvous = None + try: + (opts, args) = self.__coli_parser.parse_args(argv) + if opts.InConfig is None: + raise ExploitConfigError("You must pass a valid --InConfig option") + + # Read the input config and create a truanchild Config object + self.config = truantchild.Config([opts.InConfig]) + + # make sure the id from the binary matches the config + if self.getID() != self.config.id: + print "Mismatching configurations!!" + return 1 + + # XXX Add the bit about help, line 215 + + inputs = self.config._inputParams + outputs= self.config._outputParams + constants = None # Fuzzbunch doesn't support these yet + + #pytrch.Params_parseCommandLine( inputs.parameters, len(sys.argv), sys.argv, doHelp) + + # Convert the options from Truanchild to easy-to-handle input for the plugin + iOptions = self.tc2List( inputs ) + oOptions = self.tc2Dict( outputs ) + + # add the params from the wrapper + valid = self.validateParams(iOptions) + # XXX Print the invalid options + if opts.ValidateOnly is True: + return 0 + + (fhNo, logConfig) = self.processWrapperParams( opts ) + + # Setup all of the existing sockets + self.doRendezvousClient(inputs) + retval = self.processParams(iOptions, constants, oOptions, context, logConfig) + + try: + self.options2Tc( oOptions, outputs ) + except Exception as e: + # If this fails, the plugin was not successful + print str(oOptions) + print "Failed: {0}".format(e) + return 1 + + # Add the output parameter for the rendezvous + (rendezvous, sock) = self.addWrapperOutputParams( outputs, self.config.namespaceUri, self.config.schemaVersion ) + exma.writeParamsToEM( fhNo, self.config.getMarshalledInConfig() ) + + # This sends us into a send/recv loop + self.doRendezvousServer( rendezvous, sock ) + self.cleanup( EDF_CLEANUP_WAIT, context, logConfig ) + + except Exception as e: + print "Failed: {0}".format(e) + raise + + def __putConfig(self, config, outfile): + self.config.putMarshalledConfig( opts.OutConfig ) + + def processWrapperParams(self, options): + """Setup so that we can do logging""" + fh = None + if options.LogFile is not None: + print "logging to file" + fh = exma.openEMForWriting( options.OutConfig ) + logger = get_logger(options.LogFile) + #logging.basicConfig(filename=options.LogFile, filemode="w", format="%(message)s", level=logging.INFO) + else: + print "logging to stdout" + fh = exma.openEMForWriting( None ) # Will cause stdout to be used + logger = get_logger( None ) + #logging.basicConfig(level=logging.INFO, stream=sys.stdout) + return (fh, logger) + + def tc2Dict(self, params): + """Convert Truantchild parameters into a dictionary for easy processing""" + d = {} + for k,v in params.getParameterList(): + d[k] = v + return d + + def tc2List(self, inputs): + """Convert inputs to optparse style options for ease of processing in Python""" + args = [] + for k,v in inputs.getParameterList(): + args += ["--{0}".format(k), str(v)] + return args + + def iterParams(self, params): + """A parameter iterator""" + for k,v in params.getParameterList(): + yield k + + def options2Tc( self, options, outputs ): + """Convert from optparse options back to Truantchild parameters after execution""" + # Need to match between the names of the outputs and the types in the config + for name, val in outputs.getParameterList(): + if name in options.keys(): + # set the value + outputs.set(name, options[name]) + + def __needRendezvous(self, params, checkForContract): + """Basically stolen from plugin::createsRendezvous""" + if checkForContract: + for name,val in params.getParameterList(): + if "Socket" == params.findOption(name).getType(): + return True + return False + + def __exma_bindRendezvous(self, outputs, namespaceUri, schemaVersion): + """bindRendezvous taken from exma.dll""" + rendezvous = ctypes.c_ushort() + sock = ctypes.c_uint() + ret = exma.bindRendezvous(ctypes.pointer(rendezvous), ctypes.pointer(sock)) + return (rendezvous.value, sock.value) + + def addWrapperOutputParams(self, outputs, namespaceUri, schemaVersion): + """Add output parameters after the script runs to do rendezvous""" + rendezvous = None + sock = None + if self.__needRendezvous(outputs, True): + (rendezvous, sock) = self.__exma_bindRendezvous( outputs, namespaceUri, schemaVersion) + outputs.addRendezvousParam( str(rendezvous) ) # addRendezvous needs a string + return (rendezvous, sock) + + def __transformSocket(self, rendezvous, remoteSocket, localSocket, cache): + """Perform the rendezvous socket transfer between plugins""" + ls = ctypes.c_uint() + if remoteSocket is None: + localSocket = None + return + + # Look in the cache + for (l,r) in cache: + if remoteSocket == r: + localSocket = l + return + + # Didn't find it in the cache, so add it + exma.recvSocket( ctypes.c_uint(rendezvous), ctypes.c_uint(remoteSocket), ctypes.pointer(ls) ) + localSocket = ls.value + entry = (ls.value, remoteSocket ) + cache.append(entry) + + def doRendezvousClient(self, inputs): + """Connect all sockets to rendezvous server sockets""" + cache = [] + rendezvousLocation = None + sock = ctypes.c_uint() + local = None + sockparams = [] + for name,val in inputs.getParameterList(): + if name == "Rendezvous" and inputs.hasValidValue("Rendezvous"): + rendezvousLocation = inputs.get("Rendezvous") + elif "Socket" == inputs.findOption(name).getType(): + sockets.append(inputs.findOption(name)) + + if rendezvousLocation is not None and sockparam is not None: + exma.connectRendezvous( rendezvousLocation, ctypes.pointer(sock) ) + + for param in sockparams: + if param.getFormat() == "Scalar": + remote = param.getValue() + self.__transformSocket( sock.value, remote, local, cache) + param.setValue(local) + else: + socks = params.getvalue() # this is a list + for i in xrange(remotes): + self.__transformSocket( sock, socks[i], local, cache ) + socks[i] = local + param.setValue(socks) + exma.disconnectRendezvous( sock ) + # Now get rid of the rendezvous parameter + + def doRendezvousServer(self, rendezvous, sock): + """Setup the rendezvous server so the next plugin can talk 'through' us""" + if sock is not None: + r = ctypes.c_uint(sock) + if -1 == exma.sendSockets(r): + return -1 + exma.closeRendezvous( ctypes.c_ushort(rendezvous), r) + sock = None + return 0 + + # + # These need to be implemented by the exploit + # + def processParams(self, inputs, constants, outputs, context, logConfig): + """Process the input parameters and achieve the intended purpose""" + raise NotImplementedError("processParams must be implemented") + + def getID(self): + """Return the plugin ID""" + raise NotImplementedError("getID must be implemented") + + def cleanup(self, flags, context, logConfig): + """Cleanup any errant connections or data after the rendezvous is done""" + raise NotImplementedError("cleanup must be implemented") + + def validateParams(self, inputs): + """Validate parameters to verify sane values""" + raise NotImplementedError("validateParams must be implemented") diff --git a/windows/fuzzbunch/command.py b/windows/fuzzbunch/command.py new file mode 100644 index 0000000..3135e58 --- /dev/null +++ b/windows/fuzzbunch/command.py @@ -0,0 +1,555 @@ +""" +Derived command line processing handler class from the Python standard +module 'cmd'. Many methods overridden to support more FB like behavior. + +""" +import string +import subprocess +import time + +from context import CmdCtx +import exception +import iohandler +import cmd + +__all__ = ["FbCmd"] + +PROMPT_PRE = 'fb' +PROMPT_POST = '> ' +PROMPT_FMTSTR = " %s (%s) " + +IDENTCHARS = string.ascii_letters + string.digits + '_' + +INTERACTIVE = 1 +NONINTERACTIVE = 2 + +class FbCmd(cmd.Cmd): + """Reimplementation of the Python Cmd class to fit more inline with Fuzzbunch + needs and operation. It effectively provides a base set of capabilities and + commands. The commands are: + * changeprompt + * help + * history + * sleep + * echo + * shell + * quit + * python + * script + + """ + + use_rawinput = 1 + identchars = IDENTCHARS + + # Create a CmdCtx for this class + defaultcontext = CmdCtx("Fuzzbunch", "Fuzzbunch") + shortcutKeys = {"?" : "help", + "!" : "shell"} + + helpKeys = {"?" : "help"} + + def __init__(self, stdin=None, stdout=None, stderr=None, enablecolor=True): + # Set our I/O handlers + self.init_io(stdin=stdin, stdout=stdout, stderr=stdout, enablecolor=enablecolor) + self.runmode_interactive() # default to interactive mode + + self.promptpre = PROMPT_PRE + self.completekey = 'tab' + self.cmdqueue = [] # Holds a list of commands yet to be executed + self.cmdhistory = [] # Holds a list of commands already executed + + self.setcontext(None) # Set us to the default context + self.setprompt() + + """ + I/O Handling + + Changed so that we can handle raw I/O, which python cmd.py cannot. + """ + def init_io(self, stdin=None, stdout=None, stderr=None, logfile=None, enablecolor=True): + self.io = iohandler.IOhandler(stdin, stdout, logfile, enablecolor=enablecolor) + + def set_raw(self, mode=1): + if mode in (1,0): + self.io.raw_input = mode + + def set_ionoprompt(self, mode=False): + if mode in (True,False): + self.io.noprompt = mode + + """ + Run Mode Handling + + Added to enable scriptability + """ + def runmode_interactive(self): + self.runmode = INTERACTIVE + self.set_raw() + self.scripting(False) + + def runmode_noninteractive(self): + self.runmode = NONINTERACTIVE + self.set_raw(0) + self.scripting(True) + + def scripting(self, mode=False): + if mode in (False,True): + self.scriptmode = mode + self.set_ionoprompt(mode) + + def is_interactive(self): + if self.runmode == INTERACTIVE: + return True + else: + return False + + def is_scripted(self): + return self.scriptmode + + """ + Context handling + + Added to enable us to change the prompt easily among different plug-in or + base contexts. + """ + def setprompt(self, prompt=None): + """Set the prompt for the current context. Append the name of + the current plugin to the prompt + """ + if prompt is None: + if self.getcontext().get_name() == self.defaultcontext.get_name(): + context = " " + else: + context = PROMPT_FMTSTR % (self.getcontext().get_type(), + self.getcontext().get_name()) + prompt = self.promptpre + context + PROMPT_POST + self.prompt = prompt + + def setcontext(self, new_context): + """Change contexts""" + if new_context is None: + new_context = self.defaultcontext + self.ctx = new_context + + def getcontext(self): + """Retrieve the current plugin context""" + return self.ctx + + """ + Change prompt look + + """ + def help_changeprompt(self): + usage = ["changeprompt [new prompt]", + "Change the command prompt string. Run with no", + "args for default prompt."] + self.io.print_usage(usage) + + def do_changeprompt(self, input): + """Change the command prompt""" + newprompt = input.strip() + if newprompt: + self.promptpre = newprompt + else: + self.promptpre = PROMPT_PRE + self.setprompt() + + """ + Command parsing and handling + + """ + def cmdloop(self): + """Repeatedly issue a prompt, accept input, parse an initial prefix + off the received input, and dispatch to action methods, passing them + the remainder of the line as argument. + """ + self.preloop() + self.io.pre_input(self.complete) + + try: + stop = None + while not stop: + if self.cmdqueue: + # First, clear out anything we have in the command queue + line = self.cmdqueue.pop(0) + else: + # Then, accept input + line = self.io.get_input(self.prompt) + stop = self.runcmd(line) + self.postloop() + finally: + self.io.post_input() + + + def runcmdlist(self, cmdlist): + stop = None + while cmdlist and not stop: + stop = self.runcmd(cmdlist.pop(0)) + + def runcmdlist_noex(self, cmdlist): + stop = None + while cmdlist and not stop: + stop = self.runcmd_noex(cmdlist.pop(0)) + + def runcmd_noex(self, line): + line = self.precmd(line) + stop = self.onecmd(line) + return self.postcmd(stop, line) + + def runcmd(self, line): + try: + stop = self.runcmd_noex(line) + except exception.CmdErr, err: + self.io.print_error(err.getErr()) + stop = None + return stop + + + def register_shortcut(self, shortcutChar, expansion): + """Register a new shortcut key expansion. If a shortcut key is reused + the old command will be deleted. + + """ + if shortcutChar in self.shortcutKeys: + del self.shortcutKeys[shortcutChar] + self.shortcutKeys[shortcutChar] = expansion + + def precmd(self, line): + """Executed before each command. Append the line to history and then log + the line to the output. + + """ + if len(line.strip()): + self.cmdhistory.append(line) + self.io.log(self.prompt + line) + return line + + #def postcmd(self, stop, line): + # """Executed after each command.""" + # return stop + + #def preloop(self): + # pass + + #def postloop(self): + # pass + + def parseline(self, line): + """Parse the line into a command name and a string containing the + arguments. Returns a tuple containing (command, args, line). + 'command' and 'args' may be None if line couldn't be parsed. Check for + registered special handlers. + """ + line = line.strip() + if not line: + return None, None, line + + if line[-1:] in self.helpKeys: + line = self.helpKeys[line[-1:]] + " " + line[:-1] + + if line[0] in self.shortcutKeys: + line = self.shortcutKeys[line[0]] + " " + line[1:] + + i, n = 0, len(line) + while i < n and line[i] in self.identchars: + i = i+1 + cmd, arg = line[:i], line[i:].strip() + return cmd, arg, line + + def onecmd(self, line): + """Run a single command. Exceptions should be caught by the caller""" + cmd, arg, line = self.parseline(line) + if not line: + return self.emptyline() + if cmd is None: + return self.default(line) + self.lastcmd = line + if cmd == '': + return self.default(line) + else: + try: + # retrieve the command execution function, which will be + # self.do_ + func = getattr(self, 'do_' + cmd.lower()) + except AttributeError: + return self.default(line) + return func(arg) + + def emptyline(self): + """Called when an empty line is encountered""" + pass + + def default(self, line): + """Called when command prefix is not recognized.""" + cmd, arg, line = self.parseline(line) + + # Check if the current context handles the cmd instead + try: + func = self.ctx.lookup_function(cmd) + except AttributeError: + self.io.print_error("Unknown syntax: %s" % line) + else: + func(arg) + + + #def completedefault(self, *ignored): + # return [] + + def completenames(self, text, *ignored): + """Return a list of command names for command completion.""" + dotext = 'do_' + text + return [ a[3:] for a in self.ctx.get_names() if a.startswith(dotext) ] +\ + [ a[3:] for a in self.get_names() if a.startswith(dotext) ] + + def get_compstate(self, text, arglist): + if text == "": + return len(arglist) + else: + return max(len(arglist) - 1, 0) + + def complete(self, text, state): + """Return the next possible completion for 'text'.""" + if state == 0: + try: + import readline + except ImportError: + import pyreadline as readline + origline = readline.get_line_buffer() + begidx = readline.get_begidx() + endidx = readline.get_endidx() + if begidx > 0: + cmd, args, foo = self.parseline(origline) + if cmd == '': + compfunc = self.completedefault + else: + try: + compfunc = getattr(self, 'complete_' + cmd.lower()) + except AttributeError: + try: + compfunc = self.ctx.lookup_compfunction(cmd) + except AttributeError: + compfunc = self.completedefault + else: + compfunc = self.completenames + arglist = [item.strip() for item in origline.strip().split()] + comp_state = self.get_compstate(text, arglist) + self.completion_matches = compfunc(text, origline, arglist, comp_state, begidx, endidx) + + try: + return self.completion_matches[state] + except IndexError: + return None + + #def get_names(self): + # names = [] + # classes = [self.__class__] + # while classes: + # aclass = classes.pop(0) + # if aclass.__bases__: + # classes = classes + list(aclass.__bases__) + # names = names + dir(aclass) + # return names + + def complete_help(self, *args): + return self.completenames(*args) + + + """ + Cmd: help + + """ + def get_help_lists(self, names, ctx): + do_cmds = list(set([name for name in names if name.startswith('do_')])) + do_cmds.sort() + return [(name[3:], str(getattr(ctx, name).__doc__)) for name in do_cmds] + + def get_shortcut_help(self): + """Shortcut help""" + return [(key, "Shortcut for %s" % val) for key,val in self.shortcutKeys.items()] + + def do_help(self, input): + """Print out help""" + args = input.strip().split() + if len(args) > 0: + arg = args[0] + try: + func = self.ctx.lookup_helpfunction(arg) + func() + except AttributeError: + pass + try: + func = getattr(self, 'help_' + arg.lower()) + func() + except AttributeError: + pass + else: + cmds = self.get_shortcut_help() + self.get_help_lists(self.get_names(), self) + cmdlist = {'title' : "Core Commands", + 'commands' : cmds} + self.io.print_cmd_list(cmdlist) + + if self.ctx.get_name() != self.defaultcontext.get_name(): + cmds = self.get_help_lists(self.ctx.get_names(), self.ctx) + cmdlist = {'title' : "%s Commands" %self.ctx.get_type(), + 'commands' : cmds} + self.io.print_cmd_list(cmdlist) + + + """ + Cmd: history + + """ + def help_history(self): + usage = ["history [index]", + "Rerun a previous command. Omit index to print history"] + self.io.print_usage(usage) + + def do_history(self, arg): + """Run a previous command.""" + # keep the history cmds out of the history to reduce noise + self.cmdhistory.pop() + if len(arg) == 0: + history = {'items' : enumerate(self.cmdhistory)} + self.io.print_history(history) + else: + try: + index = int(arg) + except ValueError: + self.io.print_error("Bad history index") + return + + try: + self.cmdqueue.append(self.cmdhistory[index]) + except IndexError: + maxIndex = len(self.cmdhistory) - 1 + self.io.print_error("History index out of range [0 : %d]" % maxIndex) + + + """ + Cmd: sleep + + """ + def help_sleep(self): + usage = ["sleep [N seconds]", + "Sleep for N seconds"] + self.io.print_usage(usage) + + def do_sleep(self, count): + """Sleep for n seconds""" + try: + count = int(count) + except ValueError: + self.io.print_error("Invalid delay") + return + self.io.print_msg("Sleeping for %d seconds" % count) + try: + time.sleep(count) + except KeyboardInterrupt: + self.io.print_error("User Interrupt") + + """ + Cmd: echo + + """ + def help_echo(self): + usage = ["echo [msg]", + "echo the given message"] + self.io.print_usage(usage) + + def do_echo(self, msg): + """Echo a message""" + self.io.print_msg(msg.strip()) + + """ + Cmd: shell + + """ + def help_shell(self): + usage = ["shell [command [args]]", + "Runs command with args in OS shell"] + self.io.print_usage(usage) + + def do_shell(self, arg): + """Execute a shell command""" + try: + retcode = subprocess.call(arg, shell=True) + del retcode + except OSError, e: + self.io.print_error("Execution failed: " + e.message) + except KeyboardInterrupt: + self.io.print_warning("Execution aborted by user: Ctrl-c") + + + """ + Cmd: EOF, quit + + """ + def help_eof(self): + usage = ["eof", + "Quits program (CTRL-D)"] + self.io.print_usage(usage) + + def do_eof(self, arg): + """Quit program (CTRL-D)""" + return self.do_quit(arg) + + def help_quit(self): + usage = ["quit", + "Quits program (CTRL-D)"] + self.io.print_usage(usage) + def do_quit(self, arg): + """Quit program""" + return True + + """ + Cmd: Python + + """ + def help_python(self): + usage = ["python", + "Enters the interactive python interpreter. Exit the", + "interpreter to return back to Fuzzbunch."] + self.io.print_usage(usage) + + def do_python(self, arg): + """Drop to an interactive Python interpreter""" + raise exception.Interpreter + + """ + Scripting Support + + """ + def help_script(self): + usage = ["script [scriptfile]", + "Run the given scriptfile"] + self.io.print_usage(usage) + + def do_script(self, input): + """Run a script""" + inputList = input.strip().split() + + if len(inputList) == 0: + self.help_script() + else: + try: + self.scripting(True) + try: + script = [ line.strip() + for line in open(inputList[0]).readlines() + if not line.startswith('#') ] + except IOError: + raise exception.CmdErr, "Couldn't read script file" + self.runcmdlist_noex(script) + except exception.CmdErr, err: + self.io.print_error(err.getErr()) + self.io.print_error("Aborting script") + finally: + self.scripting(False) + + +if __name__ == "__main__": + fb = FbCmd() + fb.cmdloop() + + diff --git a/windows/fuzzbunch/context.py b/windows/fuzzbunch/context.py new file mode 100644 index 0000000..65069c8 --- /dev/null +++ b/windows/fuzzbunch/context.py @@ -0,0 +1,75 @@ +""" +Base command context class used to extend the cmd class. A context provides +it's own set of do_cmds but is largely managed by the cmd class. + +The CmdCtx class by itself is largely useless. It should be used as base class +to derive any desired context from. + +Contexts can be standalone or can be plugin managers. +""" + +__all__ = ["CmdCtx"] + +class CmdCtx: + """Cmd context class that can be used to dynamically extend the Cmd + class to handle additional commands. This defines the interface + for derived classes. + """ + def __init__(self, name, type): + # Metadata abou the Command Context, including name and type + self.set_name(name) + self.set_type(type) + + def get_name(self): + """Get the name of the context""" + return self.name + + def get_type(self): + """Get the type of the context""" + return self.type + + def set_name(self, name): + """Set the name of the context""" + self.name = name + + def set_type(self, type): + """Set the type of the context""" + self.type = type + + def print_info(self): + """Print context info""" + return + + def lookup_function(self, name): + return getattr(self, 'do_' + name.lower()) + + def lookup_compfunction(self, name): + return getattr(self, 'complete_' + name.lower()) + + def lookup_helpfunction(self, name): + return getattr(self, 'help_' + name.lower()) + + def get_names(self): + names = [] + classes = [self.__class__] + while classes: + aclass = classes.pop(0) + if aclass.__bases__: + classes = classes + list(aclass.__bases__) + names = names + dir(aclass) + return names + + def set_active_plugin(self, unused): + """Set the active plugin""" + pass + + def get_active_name(self): + """Get the name of the active plugin""" + return self.get_name() + + def get_plugins(self): + """Return a list of all plugins""" + return [] + + + diff --git a/windows/fuzzbunch/daveplugin.py b/windows/fuzzbunch/daveplugin.py new file mode 100644 index 0000000..efb6b4b --- /dev/null +++ b/windows/fuzzbunch/daveplugin.py @@ -0,0 +1,528 @@ +import hashlib, os, threading +import xml.parsers.expat as expat + +import exception, util, truantchild +import edfmeta, edfexecution +#from plugin import Plugin +from edfplugin import EDFPlugin +from pytrch import TrchError + +from redirection import LocalRedirection, RemoteRedirection + +__all__ = ["DAVEPlugin"] + +class DAVEPlugin(EDFPlugin): + def __init__(self, files, io): + # DAVE plugins are *currently* supported only on Win32 (that's a + # restriction based on delivery mechanisms, not the DAVE spec itself) + import sys + if sys.platform != "win32": + raise EnvironmentError("DAVEPlugins are supported only on Windows for this version of Fuzzbunch!") + + try: + EDFPlugin.__init__(self, files, io) + self.metaconfig = files[2] + self.initTouches() + self.initConsoleMode() + self.initRedirection() + except TrchError: + # There was an error parsing the plug-in XML + raise + except IndexError: + # We didn't get the right number of files + raise + self.procs = [] + self.package_arches = edfmeta.parse_forward(self.metaconfig) + if not self.package_arches: + raise EnvironmentError("A DAVEPlugin is missing required 'package' information in its .fb file!") + + def getMetaHash(self): + return "%s %s %s" % (hashlib.sha1(open(self.metaconfig, 'rb').read()).hexdigest(), + os.lstat(self.metaconfig).st_size, + os.path.basename(self.metaconfig)) + + + def canDeploy(self): + return True + + def killPlugin(self): + """Helper function for forceful termination of the plugin executable, + primarily used for testing + """ + for p in self.procs: + p.kill() + self.procs = [] + + + def write_interpreted_xml_file(self, inConfFile, globalvars={}): + """Rewrite the inconfig, substituting variables""" + tmpFile = open(inConfFile, "w"); + + # Note: Truantchild has been used to this point to store parameters, so + # the inconfig here represents all of the prompted data + configdata = self.getMarshalledInConfig() + configlines = configdata.split("\n") + newlines = [] + for line in configlines: + newlines.append(util.variable_replace(line, globalvars)) + newconfig = "\n".join(newlines) + tmpFile.write(newconfig) + tmpFile.close() + return inConfFile + + """ + Plugin validation routine + + """ + def validate(self, dirs, globalvars={}): + baseDir, logDir = dirs + timestamp = util.formattime() + exeBaseName = os.path.basename(self.executable) + + logName = "%s-%s.log" % (exeBaseName, timestamp) + logFile = os.path.join(logDir, logName) + try: + os.remove(logFile) + except: + pass + + inConfName = "%s-%s-InConfig.validate.xml" % (exeBaseName, timestamp) + inConfFile = os.path.join(logDir, inConfName) + self.write_interpreted_xml_file(inConfFile, globalvars=globalvars) + + if edfexecution.validate_plugin(self.executable, inConfFile, self.io) == 0: + return True + else: + return False + + def marshal_params(self, logDir, archOs, output_filename=None, globalvars={}): + import sys, subprocess, platform + + # Find/compute various paths and filename components we'll need later + storageDir = globalvars['FbStorage'] + timestamp = util.formattime() + exeBaseName = os.path.basename(self.executable) + + # Get our own packaging options (for reference) + arch_map = self.package_arches + + # Figure out which piece to use for marshaling + host_archOs = "%s-%s" % (platform.machine(), platform.system()) + proxy = arch_map[host_archOs][0] + core = arch_map[archOs][1] + + # Non supported! + if proxy is None: + return (None, None) + + # Get files/paths set up for marshaling + if output_filename is None: + output_filename = os.path.join(logDir, "%s-%s-Marshal.bin" % (exeBaseName, timestamp)) + output_path = os.path.dirname(output_filename) + try: + os.makedirs(output_path) + except os.error: + assert os.path.isdir(output_path), "Output path '%s' could not be found/created!" % output_path + xml_config_name = os.path.join(logDir, "%s-%s-InConfig.marshal.xml" % (exeBaseName, timestamp)) + self.write_interpreted_xml_file(xml_config_name, globalvars=globalvars) + + # Fire off the DANE config utility to actually create the package. Note that this is strictly + # Win32[/64] for now... + # (This stuff should be abtracted away form cross-platformness and to avoid hard-coded paths.) + proxy_dll = os.path.join(os.path.dirname(self.executable), proxy) + assert os.path.isfile(proxy_dll), "Required file '%s' doesn't exist!" % proxy_dll + self.io.print_msg("\tUsing '%s' to handle parameter marshaling" % proxy_dll) + self.io.print_msg("\tMarshaling the contents of '%s'" % xml_config_name) + + config_exe = os.path.join(storageDir, 'dvmarshal.exe') + assert os.path.isfile(config_exe), "Required program '%s' doesn't exist!" % config_exe + subprocess.check_call([config_exe, proxy_dll, xml_config_name, output_filename]) + + core_dll = os.path.join(os.path.dirname(self.executable), core) + return (core_dll, output_filename) + + def build_package(self, logDir, archOs, listenPort=None, output_filename=None, globalvars={}): + import sys, subprocess, platform + + # Find/compute various paths and filename components we'll need later + storageDir = globalvars['FbStorage'] + timestamp = util.formattime() + exeBaseName = os.path.basename(self.executable) + + # Get our own packaging options (for reference) + arch_map = self.package_arches + + # Figure out which architecture/OS to use for each piece + host_archOs = "%s-%s" % (platform.machine(), platform.system()) + proxy = arch_map[host_archOs][0] + core = arch_map[archOs][1] + + if (proxy is None) or (core is None): + # Not supported! + return None + + if output_filename is None: + output_filename = os.path.join(logDir, "%s-%s-Package.dll" % (exeBaseName, timestamp)) + + output_path = os.path.dirname(output_filename) + try: + os.makedirs(output_path) + except os.error: + assert os.path.isdir(output_path), "Output path '%s' could not be found/created!" % output_path + + xml_config_name = os.path.join(logDir, "%s-%s-InConfig.package.xml" % (exeBaseName, timestamp)) + self.write_interpreted_xml_file(xml_config_name, globalvars=globalvars) + + # Fire off the DANE config utility to actually create the package. Note that this is strictly + # Win32[/64] for now... + # (This stuff should be abtracted away for cross-platformness and to avoid hard-coded paths.) + baseArch = archOs.split('-')[0] + + dane_dll = os.path.join(storageDir, 'dane_%s.dll' % baseArch) + assert os.path.isfile(dane_dll), "Required file '%s' doesn't exist!" % dane_dll + self.io.print_msg("\tUsing '%s' as the output template" % dane_dll) + + proxy_dll = os.path.join(os.path.dirname(self.executable), proxy) + assert os.path.isfile(proxy_dll), "Required file '%s' doesn't exist!" % proxy_dll + self.io.print_msg("\tUsing '%s' to handle parameter marshaling" % proxy_dll) + + core_dll = os.path.join(os.path.dirname(self.executable), core) + assert os.path.isfile(core_dll), "Required file '%s' doesn't exist!" % core_dll + self.io.print_msg("\tUsing '%s' as the input payload" % core_dll) + + config_exe = os.path.join(storageDir, 'danecfg.exe') + assert os.path.isfile(config_exe), "Required program '%s' doesn't exist!" % config_exe + subprocess.check_call([config_exe, dane_dll, proxy_dll, core_dll, xml_config_name, output_filename]) + + if listenPort: + # Pack in the listen-port as a particular binary resource + import ctypes, struct + RT_RCDATA = 10 + ID_PORTNUM = 101 + LANG_ID = 0x0000 + + BeginUpdateResource = ctypes.windll.kernel32.BeginUpdateResourceA + UpdateResource = ctypes.windll.kernel32.UpdateResourceA + EndUpdateResource = ctypes.windll.kernel32.EndUpdateResourceA + + rblob = struct.pack("