Ejemplo n.º 1
0
    def is_64bit(self):
        """Determines if a process is 64bit.
        @return: True if 64bit, False if not
        """
        if self.h_process == 0:
            self.open()

        try:
            val = c_int(0)
            ret = KERNEL32.IsWow64Process(self.h_process, byref(val))
            if ret and not val.value and is_os_64bit():
                return True
        except:
            pass

        return False
Ejemplo n.º 2
0
def foreach_child(hwnd, lparam):
    # List of buttons labels to click.
    buttons = [
        "yes",
        "ok",
        "accept",
        "next",
        "install",
        "run",
        "agree",
        "enable",
        "don't send",
        "continue",
        "unzip",
        "open",
    ]

    # List of buttons labels to not click.
    dontclick = [
        "don't run",
    ]

    classname = create_unicode_buffer(50)
    USER32.GetClassNameW(hwnd, classname, 50)

    # Check if the class of the child is button.
    if classname.value == "Button":
        # Get the text of the button.
        length = USER32.SendMessageW(hwnd, WM_GETTEXTLENGTH, 0, 0)
        text = create_unicode_buffer(length + 1)
        USER32.SendMessageW(hwnd, WM_GETTEXT, length + 1, text)

        # Check if the button is set as "clickable" and click it.
        textval = text.value.replace("&", "").lower()
        for button in buttons:
            if button in textval:
                for btn in dontclick:
                    if btn in textval:
                        break
                else:
                    log.info("Found button \"%s\", clicking it" % text.value)
                    USER32.SetForegroundWindow(hwnd)
                    KERNEL32.Sleep(1000)
                    USER32.SendMessageW(hwnd, BM_CLICK, 0, 0)

    # Recursively search for childs (USER32.EnumChildWindows).
    return True
Ejemplo n.º 3
0
    def __init__(self, result_ip, result_port, logserver_path):
        h_pipe = KERNEL32.CreateNamedPipeA(logserver_path,
                                            PIPE_ACCESS_INBOUND,
                                            PIPE_TYPE_MESSAGE |
                                            PIPE_READMODE_MESSAGE |
                                            PIPE_WAIT,
                                            PIPE_UNLIMITED_INSTANCES,
                                            BUFSIZE,
                                            LOGBUFSIZE,
                                            0,
                                            None)

        if h_pipe == INVALID_HANDLE_VALUE:
            log.warning("Unable to create log server pipe.")
            return False

        logserver = LogServerThread(h_pipe, result_ip, result_port)
        logserver.daemon = True
        logserver.start()
Ejemplo n.º 4
0
def foreach_child(hwnd, lparam):
    # List of buttons labels to click.
    buttons = [
        "yes", "oui", "ok", "i accept", "next", "suivant", "new", "nouveau",
        "install", "installer", "file", "fichier", "run", "start", "marrer",
        "cuter", "i agree", "accepte", "enable", "activer", "accord",
        "valider", "don't send", "ne pas envoyer", "don't save", "continue",
        "continuer", "personal", "personnel", "scan", "scanner", "unzip",
        "dezip", "open", "ouvrir", "close the program", "execute", "executer",
        "launch", "lancer", "save", "sauvegarder", "download", "load",
        "charger", "end", "fin", "terminer"
        "later", "finish", "end", "allow access", "remind me later", "save",
        "sauvegarder"
    ]

    # List of buttons labels to not click.
    dontclick = ["don't run", "i do not accept"]

    classname = create_unicode_buffer(50)
    USER32.GetClassNameW(hwnd, classname, 50)

    # Check if the class of the child is button.
    if "button" in classname.value.lower():
        # Get the text of the button.
        length = USER32.SendMessageW(hwnd, WM_GETTEXTLENGTH, 0, 0)
        text = create_unicode_buffer(length + 1)
        USER32.SendMessageW(hwnd, WM_GETTEXT, length + 1, text)

        # Check if the button is set as "clickable" and click it.
        textval = text.value.replace("&", "").lower()
        for button in buttons:
            if button in textval:
                for btn in dontclick:
                    if btn in textval:
                        break
                else:
                    log.info("Found button \"%s\", clicking it" % text.value)
                    USER32.SetForegroundWindow(hwnd)
                    KERNEL32.Sleep(1000)
                    USER32.SendMessageW(hwnd, BM_CLICK, 0, 0)

    # Recursively search for childs (USER32.EnumChildWindows).
    return True
Ejemplo n.º 5
0
def foreach_child(hwnd, lparam):
    buttons = ["&yes", "&ok", "&accept", "&next", "&install", "&run", "&agree"]

    classname = create_unicode_buffer(50)
    USER32.GetClassNameW(hwnd, classname, 50)

    # Check if the class of the child is button.
    if classname.value == "Button":
        # Get the text of the button.
        length = USER32.SendMessageW(hwnd, WM_GETTEXTLENGTH, 0, 0)
        text = create_unicode_buffer(length + 1)
        USER32.SendMessageW(hwnd, WM_GETTEXT, length + 1, text)

        # Check if the button is "positive".
        for button in buttons:
            if text.value.lower().startswith(button):
                log.info("Found button \"%s\", clicking it" % text.value)
                KERNEL32.Sleep(1000)
                USER32.SendMessageW(hwnd, BM_CLICK, 0, 0)
Ejemplo n.º 6
0
    def get_parent_pid(self):
        """Get the Parent Process ID."""
        process_handle = self.open_process()

        NT_SUCCESS = lambda val: val >= 0

        pbi = (c_int * 6)()
        size = c_int()

        # Set return value to signed 32bit integer.
        NTDLL.NtQueryInformationProcess.restype = c_int

        ret = NTDLL.NtQueryInformationProcess(process_handle, 0, byref(pbi),
                                              sizeof(pbi), byref(size))

        KERNEL32.CloseHandle(process_handle)

        if NT_SUCCESS(ret) and size.value == sizeof(pbi):
            return pbi[5]

        return None
Ejemplo n.º 7
0
def dump_file(file_path):
    """Create a copy of the give file path."""
    try:
        if os.path.exists(file_path):
            sha256 = hash_file(hashlib.sha256, file_path)
            if sha256 in DUMPED_LIST:
                # The file was already dumped, just skip.
                return
        else:
            log.warning("File at path \"%s\" does not exist, skip.",
                        file_path)
            return
    except IOError as e:
        log.warning("Unable to access file at path \"%s\": %s", file_path, e)
        return

    # 32k is the maximum length for a filename
    path = create_unicode_buffer(32 * 1024)
    name = c_wchar_p()
    KERNEL32.GetFullPathNameW(unicode(file_path), 32 * 1024, path, byref(name))
    file_path = path.value

    # Check if the path has a valid file name, otherwise it's a directory
    # and we should abort the dump.
    if name.value:
        # Should be able to extract Alternate Data Streams names too.
        file_name = name.value[name.value.find(":")+1:]
    else:
        return

    upload_path = os.path.join("files",
                               str(random.randint(100000000, 9999999999)),
                               file_name)
    try:
        upload_to_host(file_path, upload_path)
        DUMPED_LIST.append(sha256)
    except (IOError, socket.error) as e:
        log.error("Unable to upload dropped file at path \"%s\": %s",
                  file_path, e)
Ejemplo n.º 8
0
    def __init__(self, result_ip, result_port, logserver_path):
        # Create the Named Pipe.
        sd = SECURITY_DESCRIPTOR()
        sa = SECURITY_ATTRIBUTES()
        ADVAPI32.InitializeSecurityDescriptor(byref(sd), 1)
        ADVAPI32.SetSecurityDescriptorDacl(byref(sd), True, None, False)
        sa.nLength = sizeof(SECURITY_ATTRIBUTES)
        sa.bInheritHandle = False
        sa.lpSecurityDescriptor = addressof(sd)

        h_pipe = KERNEL32.CreateNamedPipeA(
            logserver_path, PIPE_ACCESS_INBOUND,
            PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
            PIPE_UNLIMITED_INSTANCES, BUFSIZE, LOGBUFSIZE, 0, byref(sa))

        if h_pipe == INVALID_HANDLE_VALUE:
            log.warning("Unable to create log server pipe.")
            return False

        logserver = LogServerThread(h_pipe, result_ip, result_port)
        logserver.daemon = True
        logserver.start()
Ejemplo n.º 9
0
    def run(self):
        seconds = 0

        # Global disable flag.
        if "human" in self.options:
            self.do_move_mouse = int(self.options["human"])
            self.do_click_mouse = int(self.options["human"])
            self.do_click_buttons = int(self.options["human"])
        else:
            self.do_move_mouse = True
            self.do_click_mouse = True
            self.do_click_buttons = True

        # Per-feature enable or disable flag.
        if "human.move_mouse" in self.options:
            self.do_move_mouse = int(self.options["human.move_mouse"])

        if "human.click_mouse" in self.options:
            self.do_click_mouse = int(self.options["human.click_mouse"])

        if "human.click_buttons" in self.options:
            self.do_click_buttons = int(self.options["human.click_buttons"])

        while self.do_run:
            if seconds and not seconds % 60:
                USER32.EnumWindows(EnumWindowsProc(get_office_window), 0)

            if self.do_click_mouse:
                click_mouse()

            if self.do_move_mouse:
                move_mouse()

            if self.do_click_buttons:
                USER32.EnumWindows(EnumWindowsProc(foreach_window), 0)

            KERNEL32.Sleep(1000)
            seconds += 1
Ejemplo n.º 10
0
def foreach_child(hwnd, lparam):
    # List of buttons labels to click.
    buttons = [
        "yes", "ok", "accept", "next", "install", "run", "agree", "enable",
        "don't send", "don't save", "continue", "unzip", "open",
        "close the program", "save"
    ]

    # List of buttons labels to not click.
    dontclick = [
        "don't run",
    ]

    classname = create_unicode_buffer(50)
    USER32.GetClassNameW(hwnd, classname, 50)

    # Check if the class of the child is button.
    if classname.value == "Button":
        # Get the text of the button.
        length = USER32.SendMessageW(hwnd, WM_GETTEXTLENGTH, 0, 0)
        text = create_unicode_buffer(length + 1)
        USER32.SendMessageW(hwnd, WM_GETTEXT, length + 1, text)
        textval = text.value.replace('&', '')
        # Check if the button is set as "clickable" and click it.
        for button in buttons:
            if button in textval.lower():
                dontclickb = False
                for btn in dontclick:
                    if btn in textval.lower():
                        dontclickb = True
                if not dontclickb:
                    log.info("Found button \"%s\", clicking it" % text.value)
                    USER32.SetForegroundWindow(hwnd)
                    KERNEL32.Sleep(1000)
                    USER32.SendMessageW(hwnd, BM_CLICK, 0, 0)
                    # only stop searching when we click a button
                    return False
    return True
Ejemplo n.º 11
0
def foreach_child(hwnd, lparam):
    buttons = [
        "yes",
        "ok",
        "accept",
        "next",
        "install",
        "run",
        "agree",
        "enable",
        "don't send",
        "continue",
        "unzip",
    ]

    classname = create_unicode_buffer(50)
    USER32.GetClassNameW(hwnd, classname, 50)

    # Check if the class of the child is button.
    if classname.value == "Button":
        # Get the text of the button.
        length = USER32.SendMessageW(hwnd, WM_GETTEXTLENGTH, 0, 0)
        text = create_unicode_buffer(length + 1)
        USER32.SendMessageW(hwnd, WM_GETTEXT, length + 1, text)

        # Check if the button is "positive".
        for button in buttons:
            if button in text.value.lower():
                log.info("Found button \"%s\", clicking it" % text.value)
                USER32.SetForegroundWindow(hwnd)
                KERNEL32.Sleep(1000)
                USER32.SendMessageW(hwnd, BM_CLICK, 0, 0)
        # Don't search for childs (USER32.EnumChildWindows).
        return False
    else:
        # Recursively search for childs (USER32.EnumChildWindows).
        return True
Ejemplo n.º 12
0
    def execute(self,
                path,
                args=None,
                dll=None,
                free=False,
                curdir=None,
                source=None,
                mode=None,
                maximize=False,
                env=None,
                trigger=None):
        """Execute sample process.
        @param path: sample path.
        @param args: process args.
        @param dll: dll path.
        @param free: do not inject our monitor.
        @param curdir: current working directory.
        @param source: process identifier or process name which will
                       become the parent process for the new process.
        @param mode: monitor mode - which functions to instrument.
        @param maximize: whether the GUI should be maximized.
        @param env: environment variables.
        @param trigger: trigger to indicate analysis start
        @return: operation status.
        """
        if not os.access(path, os.X_OK):
            log.error("Unable to access file at path %r, execution aborted!",
                      path)
            return False

        is32bit = self.is32bit(path=path)

        if not dll:
            if is32bit:
                dll = "monitor-x86.dll"
            else:
                dll = "monitor-x64.dll"

        dllpath = os.path.abspath(os.path.join("bin", dll))

        if not os.path.exists(dllpath):
            log.warning("No valid DLL specified to be injected, "
                        "injection aborted.")
            return False

        if source:
            if isinstance(source, (int, long)) or source.isdigit():
                inject_is32bit = self.is32bit(pid=int(source))
            else:
                inject_is32bit = self.is32bit(process_name=source)
        else:
            inject_is32bit = is32bit

        if inject_is32bit:
            inject_exe = os.path.join("bin", "inject-x86.exe")
        else:
            inject_exe = os.path.join("bin", "inject-x64.exe")

        argv = [
            inject_exe,
            "--app",
            self.shortpath(path),
            "--only-start",
        ]

        if args:
            argv += ["--args", self._encode_args(args)]

        if curdir:
            argv += ["--curdir", self.shortpath(curdir)]

        if source:
            if isinstance(source, (int, long)) or source.isdigit():
                argv += ["--from", "%s" % source]
            else:
                argv += ["--from-process", source]

        if maximize:
            argv += ["--maximize"]

        try:
            output = subprocess_checkoutput(argv, env)
            self.pid, self.tid = map(int, output.split())
        except Exception:
            log.error(
                "Failed to execute process from path %r with "
                "arguments %r (Error: %s)", path, argv,
                get_error_string(KERNEL32.GetLastError()))
            return False

        # Report this PID to the kernel driver (if present).
        zer0m0n.addpid(self.pid)

        if is32bit:
            inject_exe = os.path.join("bin", "inject-x86.exe")
        else:
            inject_exe = os.path.join("bin", "inject-x64.exe")

        argv = [
            inject_exe,
            "--resume-thread",
            "--pid",
            "%s" % self.pid,
            "--tid",
            "%s" % self.tid,
        ]

        if free:
            argv.append("--free")
        else:
            argv += [
                "--apc",
                "--dll",
                dllpath,
                "--config",
                self.drop_config(mode=mode, trigger=trigger),
            ]

        try:
            subprocess_checkoutput(argv, env)
        except Exception:
            log.error(
                "Failed to execute process from path %r with "
                "arguments %r (Error: %s)", path, argv,
                get_error_string(KERNEL32.GetLastError()))
            return False

        log.info(
            "Successfully executed process from path %r with "
            "arguments %r and pid %d", path, args or "", self.pid)
        return True
Ejemplo n.º 13
0
 def run(self):
     while self.do_run:
         click_mouse()
         move_mouse()
         USER32.EnumWindows(EnumWindowsProc(foreach_window), 0)
         KERNEL32.Sleep(1000)
Ejemplo n.º 14
0
    def old_inject(self, dll, apc):
        arg = KERNEL32.VirtualAllocEx(self.h_process, None,
                                      len(dll) + 1, MEM_RESERVE | MEM_COMMIT,
                                      PAGE_READWRITE)

        if not arg:
            log.error(
                "VirtualAllocEx failed when injecting process with "
                "pid %d, injection aborted (Error: %s)", self.pid,
                get_error_string(KERNEL32.GetLastError()))
            return False

        bytes_written = c_int(0)
        if not KERNEL32.WriteProcessMemory(self.h_process, arg, dll + "\x00",
                                           len(dll) + 1, byref(bytes_written)):
            log.error(
                "WriteProcessMemory failed when injecting process with "
                "pid %d, injection aborted (Error: %s)", self.pid,
                get_error_string(KERNEL32.GetLastError()))
            return False

        kernel32_handle = KERNEL32.GetModuleHandleA("kernel32.dll")
        load_library = KERNEL32.GetProcAddress(kernel32_handle, "LoadLibraryA")

        if apc or self.suspended:
            if not self.h_thread:
                log.info(
                    "No valid thread handle specified for injecting "
                    "process with pid %d, injection aborted.", self.pid)
                return False

            if not KERNEL32.QueueUserAPC(load_library, self.h_thread, arg):
                log.error(
                    "QueueUserAPC failed when injecting process with "
                    "pid %d (Error: %s)", self.pid,
                    get_error_string(KERNEL32.GetLastError()))
                return False
        else:
            new_thread_id = c_ulong(0)
            thread_handle = KERNEL32.CreateRemoteThread(
                self.h_process, None, 0, load_library, arg, 0,
                byref(new_thread_id))
            if not thread_handle:
                log.error(
                    "CreateRemoteThread failed when injecting process "
                    "with pid %d (Error: %s)", self.pid,
                    get_error_string(KERNEL32.GetLastError()))
                return False
            else:
                KERNEL32.CloseHandle(thread_handle)

        return True
Ejemplo n.º 15
0
    def run(self):
        """Run analysis.
        @return: operation status.
        """
        self.prepare()

        log.info("Starting analyzer from: %s", os.getcwd())
        log.info("Storing results at: %s", PATHS["root"])
        log.info("Pipe server name: %s", PIPE)

        # If no analysis package was specified at submission, we try to select
        # one automatically.
        if not self.config.package:
            log.info("No analysis package specified, trying to detect "
                     "it automagically")
            # If the analysis target is a file, we choose the package according
            # to the file format.
            if self.config.category == "file":
                package = choose_package(self.config.file_type,
                                         self.config.file_name)
            # If it's an URL, we'll just use the default Internet Explorer
            # package.
            else:
                package = "ie"

            # If we weren't able to automatically determine the proper package,
            # we need to abort the analysis.
            if not package:
                raise CuckooError("No valid package available for file "
                                  "type: {0}".format(self.config.file_type))

            log.info("Automatically selected analysis package \"%s\"", package)
        # Otherwise just select the specified package.
        else:
            package = self.config.package

        # Generate the package path.
        package_name = "modules.packages.%s" % package

        # Try to import the analysis package.
        try:
            __import__(package_name, globals(), locals(), ["dummy"], -1)
        # If it fails, we need to abort the analysis.
        except ImportError:
            raise CuckooError("Unable to import package \"{0}\", does "
                              "not exist.".format(package_name))

        # Initialize the package parent abstract.
        Package()

        # Enumerate the abstract's subclasses.
        try:
            package_class = Package.__subclasses__()[0]
        except IndexError as e:
            raise CuckooError("Unable to select package class "
                              "(package={0}): {1}".format(package_name, e))

        # Initialize the analysis package.
        pack = package_class(self.get_options())

        # Initialize Auxiliary modules
        Auxiliary()
        prefix = auxiliary.__name__ + "."
        for loader, name, ispkg in pkgutil.iter_modules(
                auxiliary.__path__, prefix):
            if ispkg:
                continue

            # Import the auxiliary module.
            try:
                __import__(name, globals(), locals(), ["dummy"], -1)
            except ImportError as e:
                log.warning(
                    "Unable to import the auxiliary module "
                    "\"%s\": %s", name, e)

        # Walk through the available auxiliary modules.
        aux_enabled = []
        for module in Auxiliary.__subclasses__():
            # Try to start the auxiliary module.
            try:
                aux = module()
                aux.start()
            except (NotImplementedError, AttributeError):
                log.warning("Auxiliary module %s was not implemented",
                            aux.__class__.__name__)
                continue
            except Exception as e:
                log.warning("Cannot execute auxiliary module %s: %s",
                            aux.__class__.__name__, e)
                continue
            finally:
                log.info("Started auxiliary module %s", aux.__class__.__name__)
                aux_enabled.append(aux)

        # Start analysis package. If for any reason, the execution of the
        # analysis package fails, we have to abort the analysis.
        try:
            pids = pack.start(self.target)
        except NotImplementedError:
            raise CuckooError("The package \"{0}\" doesn't contain a run "
                              "function.".format(package_name))
        except CuckooPackageError as e:
            raise CuckooError("The package \"{0}\" start function raised an "
                              "error: {1}".format(package_name, e))
        except Exception as e:
            raise CuckooError("The package \"{0}\" start function encountered "
                              "an unhandled exception: "
                              "{1}".format(package_name, e))

        # If the analysis package returned a list of process IDs, we add them
        # to the list of monitored processes and enable the process monitor.
        if pids:
            add_pids(pids)
            pid_check = True
        # If the package didn't return any process ID (for example in the case
        # where the package isn't enabling any behavioral analysis), we don't
        # enable the process monitor.
        else:
            log.info("No process IDs returned by the package, running "
                     "for the full timeout")
            pid_check = False

        # Check in the options if the user toggled the timeout enforce. If so,
        # we need to override pid_check and disable process monitor.
        if self.config.enforce_timeout:
            log.info("Enabled timeout enforce, running for the full timeout")
            pid_check = False

        time_counter = 0

        while True:
            time_counter += 1
            if time_counter == int(self.config.timeout):
                log.info("Analysis timeout hit, terminating analysis")
                break

            # If the process lock is locked, it means that something is
            # operating on the list of monitored processes. Therefore we cannot
            # proceed with the checks until the lock is released.
            if PROCESS_LOCK.locked():
                KERNEL32.Sleep(1000)
                continue

            try:
                # If the process monitor is enabled we start checking whether
                # the monitored processes are still alive.
                if pid_check:
                    for pid in PROCESS_LIST:
                        if not Process(pid=pid).is_alive():
                            log.info("Process with pid %s has terminated", pid)
                            PROCESS_LIST.remove(pid)

                    # If none of the monitored processes are still alive, we
                    # can terminate the analysis.
                    if len(PROCESS_LIST) == 0:
                        log.info("Process list is empty, "
                                 "terminating analysis...")
                        break

                    # Update the list of monitored processes available to the
                    # analysis package. It could be used for internal
                    # operations within the module.
                    pack.set_pids(PROCESS_LIST)

                try:
                    # The analysis packages are provided with a function that
                    # is executed at every loop's iteration. If such function
                    # returns False, it means that it requested the analysis
                    # to be terminate.
                    if not pack.check():
                        log.info("The analysis package requested the "
                                 "termination of the analysis...")
                        break

                # If the check() function of the package raised some exception
                # we don't care, we can still proceed with the analysis but we
                # throw a warning.
                except Exception as e:
                    log.warning(
                        "The package \"%s\" check function raised "
                        "an exception: %s", package_name, e)
            finally:
                # Zzz.
                KERNEL32.Sleep(1000)

        try:
            # Before shutting down the analysis, the package can perform some
            # final operations through the finish() function.
            pack.finish()
        except Exception as e:
            log.warning(
                "The package \"%s\" finish function raised an "
                "exception: %s", package_name, e)

        # Terminate the Auxiliary modules.
        for aux in aux_enabled:
            try:
                aux.stop()
            except (NotImplementedError, AttributeError):
                continue
            except Exception as e:
                log.warning("Cannot terminate auxiliary module %s: %s",
                            aux.__class__.__name__, e)

        # Try to terminate remaining active processes. We do this to make sure
        # that we clean up remaining open handles (sockets, files, etc.).
        log.info("Terminating remaining processes before shutdown...")

        for pid in PROCESS_LIST:
            proc = Process(pid=pid)
            if proc.is_alive():
                try:
                    proc.terminate()
                except:
                    continue

        # Let's invoke the completion procedure.
        self.complete()

        return True
Ejemplo n.º 16
0
    def run(self):
        """Run handler.
        @return: operation status.
        """
        data = ""
        response = "OK"
        wait = False
        proc = None

        # Read the data submitted to the Pipe Server.
        while True:
            bytes_read = c_int(0)

            buf = create_string_buffer(BUFSIZE)
            success = KERNEL32.ReadFile(self.h_pipe, buf, sizeof(buf),
                                        byref(bytes_read), None)

            data += buf.value

            if not success and KERNEL32.GetLastError() == ERROR_MORE_DATA:
                continue
            #elif not success or bytes_read.value == 0:
            #    if KERNEL32.GetLastError() == ERROR_BROKEN_PIPE:
            #        pass

            break

        if data:
            command = data.strip()

            # Parse the prefix for the received notification.
            # In case of GETPIDS we're gonna return the current process ID
            # and the process ID of our parent process (agent.py).
            if command == "GETPIDS":
                response = struct.pack("II", PID, PPID)

            # When analyzing we don't want to hook all functions, as we're
            # having some stability issues with regards to webbrowsers.
            elif command == "HOOKDLLS":
                is_url = Config(cfg="analysis.conf").category != "file"

                url_dlls = "ntdll", "kernel32"

                def hookdll_encode(names):
                    # We have to encode each dll name as unicode string
                    # with length 16.
                    names = [
                        name + "\x00" * (16 - len(name)) for name in names
                    ]
                    f = lambda s: "".join(ch + "\x00" for ch in s)
                    return "".join(f(name) for name in names)

                # If this sample is not a URL, then we don't want to limit
                # any API hooks (at least for now), so we write a null-byte
                # which indicates that all DLLs should be hooked.
                if not is_url:
                    response = "\x00"
                else:
                    response = hookdll_encode(url_dlls)

            # In case of PID, the client is trying to notify the creation of
            # a new process to be injected and monitored.
            elif command.startswith("PROCESS:"):
                # We acquire the process lock in order to prevent the analyzer
                # to terminate the analysis while we are operating on the new
                # process.
                PROCESS_LOCK.acquire()

                # Set the current DLL to the default one provided
                # at submission.
                dll = DEFAULT_DLL

                # We parse the process ID.
                data = command[8:]
                process_id = thread_id = None
                if not "," in data:
                    if data.isdigit():
                        process_id = int(data)
                elif len(data.split(",")) == 2:
                    process_id, param = data.split(",")
                    thread_id = None
                    if process_id.isdigit():
                        process_id = int(process_id)
                    else:
                        process_id = None

                    if param.isdigit():
                        thread_id = int(param)
                    else:
                        # XXX: Expect a new DLL as a message parameter?
                        if isinstance(param, str):
                            dll = param

                if process_id:
                    if process_id not in (PID, PPID):
                        # We inject the process only if it's not being
                        # monitored already, otherwise we would generated
                        # polluted logs.
                        if process_id not in PROCESS_LIST:
                            # Open the process and inject the DLL.
                            # Hope it enjoys it.
                            proc = Process(pid=process_id, thread_id=thread_id)

                            filepath = proc.get_filepath()
                            filename = os.path.basename(filepath)

                            log.info("Announced process name: %s", filename)

                            if not protected_filename(filename):
                                # Add the new process ID to the list of
                                # monitored processes.
                                add_pids(process_id)

                                # If we have both pid and tid, then we can use
                                # apc to inject
                                if process_id and thread_id:
                                    proc.inject(dll, apc=True)
                                else:
                                    # we inject using CreateRemoteThread, this
                                    # needs the waiting in order to make sure
                                    # no race conditions occur
                                    proc.inject(dll)
                                    wait = True

                                log.info(
                                    "Successfully injected process with "
                                    "pid %s", proc.pid)
                    else:
                        log.warning("Received request to inject Cuckoo "
                                    "processes, skip")

                # Once we're done operating on the processes list, we release
                # the lock.
                PROCESS_LOCK.release()
            # In case of FILE_NEW, the client is trying to notify the creation
            # of a new file.
            elif command.startswith("FILE_NEW:"):
                # We extract the file path.
                file_path = command[9:].decode("utf-8")
                # We add the file to the list.
                add_file(file_path)
            # In case of FILE_DEL, the client is trying to notify an ongoing
            # deletion of an existing file, therefore we need to dump it
            # straight away.
            elif command.startswith("FILE_DEL:"):
                # Extract the file path.
                file_path = command[9:].decode("utf-8")
                # Dump the file straight away.
                del_file(file_path)
            elif command.startswith("FILE_MOVE:"):
                # syntax = FILE_MOVE:old_file_path::new_file_path
                if "::" in command[10:]:
                    old_fname, new_fname = command[10:].split("::", 1)
                    move_file(old_fname.decode("utf-8"),
                              new_fname.decode("utf-8"))

        KERNEL32.WriteFile(self.h_pipe, create_string_buffer(response),
                           len(response), byref(bytes_read), None)

        KERNEL32.CloseHandle(self.h_pipe)

        # We wait until cuckoomon reports back.
        if wait:
            proc.wait()

        if proc:
            proc.close()

        return True
Ejemplo n.º 17
0
    def run(self):
        """Run analysis.
        @return: operation status.
        """
        self.prepare()
        self.path = os.getcwd()

        log.debug("Starting analyzer from: %s", self.path)
        log.debug("Pipe server name: %s", self.config.pipe)
        log.debug("Log pipe server name: %s", self.config.logpipe)

        # If no analysis package was specified at submission, we try to select
        # one automatically.
        if not self.config.package:
            log.debug("No analysis package specified, trying to detect "
                      "it automagically.")

            # If the analysis target is a file, we choose the package according
            # to the file format.
            if self.config.category == "file":
                package = choose_package(self.config.file_type,
                                         self.config.file_name,
                                         self.config.pe_exports.split(","))
            # If it's an URL, we'll just use the default Internet Explorer
            # package.
            else:
                package = "ie"

            # If we weren't able to automatically determine the proper package,
            # we need to abort the analysis.
            if not package:
                raise CuckooError("No valid package available for file "
                                  "type: {0}".format(self.config.file_type))

            log.info("Automatically selected analysis package \"%s\"", package)
        # Otherwise just select the specified package.
        else:
            package = self.config.package

        # Generate the package path.
        package_name = "modules.packages.%s" % package

        # Try to import the analysis package.
        try:
            __import__(package_name, globals(), locals(), ["dummy"], -1)
        # If it fails, we need to abort the analysis.
        except ImportError:
            raise CuckooError("Unable to import package \"{0}\", does "
                              "not exist.".format(package_name))

        # Initialize the package parent abstract.
        Package()

        # Enumerate the abstract subclasses.
        try:
            package_class = Package.__subclasses__()[0]
        except IndexError as e:
            raise CuckooError("Unable to select package class "
                              "(package={0}): {1}".format(package_name, e))

        # Initialize the analysis package.
        self.package = package_class(self.config.options, analyzer=self)

        # Move the sample to the current working directory as provided by the
        # task - one is able to override the starting path of the sample.
        # E.g., for some samples it might be useful to run from %APPDATA%
        # instead of %TEMP%.
        if self.config.category == "file":
            self.target = self.package.move_curdir(self.target)

        # Initialize Auxiliary modules
        Auxiliary()
        prefix = auxiliary.__name__ + "."
        for loader, name, ispkg in pkgutil.iter_modules(
                auxiliary.__path__, prefix):
            if ispkg:
                continue

            # Import the auxiliary module.
            try:
                __import__(name, globals(), locals(), ["dummy"], -1)
            except ImportError as e:
                log.warning(
                    "Unable to import the auxiliary module "
                    "\"%s\": %s", name, e)

        # Walk through the available auxiliary modules.
        aux_enabled, aux_avail = [], []
        for module in Auxiliary.__subclasses__():
            # Try to start the auxiliary module.
            try:
                aux = module(options=self.config.options, analyzer=self)
                aux_avail.append(aux)
                aux.start()
            except (NotImplementedError, AttributeError):
                log.warning("Auxiliary module %s was not implemented",
                            module.__name__)
            except CuckooDisableModule:
                continue
            except Exception as e:
                log.warning("Cannot execute auxiliary module %s: %s",
                            module.__name__, e)
            else:
                log.debug("Started auxiliary module %s", module.__name__)
                aux_enabled.append(aux)

###########################################################
#new code 2017_05_19
        shutil.copyfile(self.target, 'C:\\dbg\\sample.exe')
        with open('log.txt', 'w') as creation:
            creation.write('log start')

        with open(self.target, 'rb') as sample:
            s = sample.read(2)
            if s != 'MZ':
                is32bit = False
            else:
                sample.seek(60)
                s = sample.read(4)
                header_offset = struct.unpack("<L", s)[0]
                sample.seek(header_offset + 4)
                s = sample.read(2)
                machine = struct.unpack('<H', s)[0]

                is32bit = (machine == 332)

        if is32bit:
            self.target = 'C:\\dbg\\Helper32.exe'
        else:
            self.target = 'C:\\dbg\\Helper64.exe'

        try:
            proc = Popen(self.target)
            pids = proc.pid
        except Exception as e:
            log.error('custom : fail to open process %s : %s', self.target, e)

###########################################################
#origin
# Start analysis package. If for any reason, the execution of the
# analysis package fails, we have to abort the analysis.
# try:
#     pids = self.package.start(self.target)
# except NotImplementedError:
#     raise CuckooError(
#         "The package \"%s\" doesn't contain a run function." %
#         package_name
#     )
# except CuckooPackageError as e:
#     raise CuckooError(
#         "The package \"%s\" start function raised an error: %s" %
#         (package_name, e)
#     )
# except Exception as e:
#     raise CuckooError(
#         "The package \"%s\" start function encountered an unhandled "
#         "exception: %s" % (package_name, e)
#     )
###########################################################

# If the analysis package returned a list of process identifiers, we
# add them to the list of monitored processes and enable the process monitor.
        if pids:
            self.process_list.add_pids(pids)
            pid_check = True

        # If the package didn't return any process ID (for example in the case
        # where the package isn't enabling any behavioral analysis), we don't
        # enable the process monitor.
        else:
            log.info("No process IDs returned by the package, running "
                     "for the full timeout.")
            pid_check = False

        # Check in the options if the user toggled the timeout enforce. If so,
        # we need to override pid_check and disable process monitor.
        if self.config.enforce_timeout:
            log.info("Enabled timeout enforce, running for the full timeout.")
            pid_check = False

        while self.do_run:
            self.time_counter += 1
            if self.time_counter == int(self.config.timeout):
                log.info("Analysis timeout hit, terminating analysis.")
                break

            # If the process lock is locked, it means that something is
            # operating on the list of monitored processes. Therefore we
            # cannot proceed with the checks until the lock is released.
            if self.process_lock.locked():
                KERNEL32.Sleep(1000)
                continue

            try:
                # If the process monitor is enabled we start checking whether
                # the monitored processes are still alive.
                if pid_check:
                    for pid in self.process_list.pids:
                        if not Process(pid=pid).is_alive():
                            log.info("Process with pid %s has terminated", pid)
                            self.process_list.remove_pid(pid)

                    # If none of the monitored processes are still alive, we
                    # can terminate the analysis.
                    if not self.process_list.pids:
                        log.info("Process list is empty, "
                                 "terminating analysis.")
                        break

                    # Update the list of monitored processes available to the
                    # analysis package. It could be used for internal
                    # operations within the module.
                    self.package.set_pids(self.process_list.pids)

                try:
                    # The analysis packages are provided with a function that
                    # is executed at every loop's iteration. If such function
                    # returns False, it means that it requested the analysis
                    # to be terminate.
                    if not self.package.check():
                        log.info("The analysis package requested the "
                                 "termination of the analysis.")
                        break

                # If the check() function of the package raised some exception
                # we don't care, we can still proceed with the analysis but we
                # throw a warning.
                except Exception as e:
                    log.warning(
                        "The package \"%s\" check function raised "
                        "an exception: %s", package_name, e)
            finally:
                # Zzz.
                KERNEL32.Sleep(1000)

        if not self.do_run:
            log.debug("The analyzer has been stopped on request by an "
                      "auxiliary module.")

        # Create the shutdown mutex.
        KERNEL32.CreateMutexA(None, False, SHUTDOWN_MUTEX)

        try:
            # Before shutting down the analysis, the package can perform some
            # final operations through the finish() function.
            self.package.finish()
        except Exception as e:
            log.warning(
                "The package \"%s\" finish function raised an "
                "exception: %s", package_name, e)

        try:
            # Upload files the package created to package_files in the
            # results folder.
            for path, name in self.package.package_files() or []:
                upload_to_host(path, os.path.join("package_files", name))
        except Exception as e:
            log.warning(
                "The package \"%s\" package_files function raised an "
                "exception: %s", package_name, e)

        # Terminate the Auxiliary modules.
        for aux in aux_enabled:
            try:
                aux.stop()
            except (NotImplementedError, AttributeError):
                continue
            except Exception as e:
                log.warning("Cannot terminate auxiliary module %s: %s",
                            aux.__class__.__name__, e)

        if self.config.terminate_processes:
            # Try to terminate remaining active processes.
            log.info("Terminating remaining processes before shutdown.")

            for pid in self.process_list.pids:
                proc = Process(pid=pid)
                if proc.is_alive():
                    try:
                        proc.terminate()
                    except:
                        continue

###############################################################################
#new code
        time.sleep(3)
        with open('C:\\dbg\\log.txt') as f_log:
            raw = f_log.read()
            data = ''.join(raw.split('\x00'))
            log.debug('logged : \n%s', data)


###############################################################################

# Run the finish callback of every available Auxiliary module.
        for aux in aux_avail:
            try:
                aux.finish()
            except (NotImplementedError, AttributeError):
                continue
            except Exception as e:
                log.warning(
                    "Exception running finish callback of auxiliary "
                    "module %s: %s", aux.__class__.__name__, e)

        # Let's invoke the completion procedure.
        self.complete()

        return True
Ejemplo n.º 18
0
 def open_thread(self):
     """Open a thread handle."""
     return KERNEL32.OpenThread(THREAD_ALL_ACCESS, False, self.tid)
Ejemplo n.º 19
0
    def inject(self, dll=None, apc=False):
        """Cuckoo DLL injection.
        @param dll: Cuckoo DLL path.
        @param apc: APC use.
        """
        if not self.pid:
            log.warning("No valid pid specified, injection aborted")
            return False

        if not self.is_alive():
            log.warning(
                "The process with pid %s is not alive, "
                "injection aborted", self.pid)
            return False

        if not dll:
            dll = "cuckoomon.dll"

        dll = randomize_dll(os.path.join("dll", dll))

        if not dll or not os.path.exists(dll):
            log.warning(
                "No valid DLL specified to be injected in process "
                "with pid %d, injection aborted.", self.pid)
            return False

        arg = KERNEL32.VirtualAllocEx(self.h_process, None,
                                      len(dll) + 1, MEM_RESERVE | MEM_COMMIT,
                                      PAGE_READWRITE)

        if not arg:
            log.error(
                "VirtualAllocEx failed when injecting process with "
                "pid %d, injection aborted (Error: %s)", self.pid,
                get_error_string(KERNEL32.GetLastError()))
            return False

        bytes_written = c_int(0)
        if not KERNEL32.WriteProcessMemory(self.h_process, arg, dll + "\x00",
                                           len(dll) + 1, byref(bytes_written)):
            log.error(
                "WriteProcessMemory failed when injecting process with "
                "pid %d, injection aborted (Error: %s)", self.pid,
                get_error_string(KERNEL32.GetLastError()))
            return False

        kernel32_handle = KERNEL32.GetModuleHandleA("kernel32.dll")
        load_library = KERNEL32.GetProcAddress(kernel32_handle, "LoadLibraryA")

        config_path = os.path.join(os.getenv("TEMP"), "%s.ini" % self.pid)
        with open(config_path, "w") as config:
            cfg = Config("analysis.conf")
            cfgoptions = cfg.get_options()

            # The first time we come up with a random startup-time.
            if Process.first_process:
                # This adds 1 up to 30 times of 20 minutes to the startup
                # time of the process, therefore bypassing anti-vm checks
                # which check whether the VM has only been up for <10 minutes.
                Process.startup_time = random.randint(1, 30) * 20 * 60 * 1000

            config.write("host-ip={0}\n".format(cfg.ip))
            config.write("host-port={0}\n".format(cfg.port))
            config.write("pipe={0}\n".format(PIPE))
            config.write("results={0}\n".format(PATHS["root"]))
            config.write("analyzer={0}\n".format(os.getcwd()))
            config.write("first-process={0}\n".format(
                "1" if Process.first_process else "0"))
            config.write("startup-time={0}\n".format(Process.startup_time))
            config.write("shutdown-mutex={0}\n".format(SHUTDOWN_MUTEX))
            config.write("force-sleepskip={0}\n".format(
                cfgoptions.get("force-sleepskip", "0")))

            Process.first_process = False

        if apc or self.suspended:
            log.info("Using QueueUserAPC injection.")
            if not self.h_thread:
                log.info(
                    "No valid thread handle specified for injecting "
                    "process with pid %d, injection aborted.", self.pid)
                return False

            if not KERNEL32.QueueUserAPC(load_library, self.h_thread, arg):
                log.error(
                    "QueueUserAPC failed when injecting process with "
                    "pid %d (Error: %s)", self.pid,
                    get_error_string(KERNEL32.GetLastError()))
                return False
            log.info("Successfully injected process with pid %d." % self.pid)
        else:
            event_name = "CuckooEvent%d" % self.pid
            self.event_handle = KERNEL32.CreateEventA(None, False, False,
                                                      event_name)
            if not self.event_handle:
                log.warning("Unable to create notify event..")
                return False

            log.info("Using CreateRemoteThread injection.")
            new_thread_id = c_ulong(0)
            thread_handle = KERNEL32.CreateRemoteThread(
                self.h_process, None, 0, load_library, arg, 0,
                byref(new_thread_id))
            if not thread_handle:
                log.error(
                    "CreateRemoteThread failed when injecting process "
                    "with pid %d (Error: %s)", self.pid,
                    get_error_string(KERNEL32.GetLastError()))
                KERNEL32.CloseHandle(self.event_handle)
                self.event_handle = None
                return False
            else:
                KERNEL32.CloseHandle(thread_handle)

        return True
Ejemplo n.º 20
0
def click(hwnd):
    USER32.SetForegroundWindow(hwnd)
    KERNEL32.Sleep(1000)
    USER32.SendMessageW(hwnd, BM_CLICK, 0, 0)
Ejemplo n.º 21
0
    def run(self):
        try:
            seconds = 0
            randoff = random.randint(0, 10)

            # add some random data to the clipboard
            randchars = list(
                "   aaaabcddeeeeeefghhhiiillmnnnooooprrrsssttttuwy")
            cliplen = random.randint(10, 1000)
            clipval = []
            for i in range(cliplen):
                clipval.append(randchars[random.randint(0,
                                                        len(randchars) - 1)])
            clipstr = "".join(clipval)
            cliprawstr = create_string_buffer(clipstr)
            USER32.OpenClipboard(None)
            USER32.EmptyClipboard()

            buf = KERNEL32.GlobalAlloc(GMEM_MOVEABLE, sizeof(cliprawstr))
            lockbuf = KERNEL32.GlobalLock(buf)
            memmove(lockbuf, cliprawstr, sizeof(cliprawstr))
            KERNEL32.GlobalUnlock(buf)
            USER32.SetClipboardData(CF_TEXT, buf)
            USER32.CloseClipboard()

            nohuman = self.options.get("nohuman")
            if nohuman:
                return True

            officedoc = False
            if hasattr(self.config, "file_type"):
                file_type = self.config.file_type
                file_name = self.config.file_name
                if "Rich Text Format" in file_type or "Microsoft Word" in file_type or \
                    "Microsoft Office Word" in file_type or "MIME entity" in file_type or \
                    file_name.endswith((".doc", ".docx", ".rtf", ".mht", ".mso")):
                    officedoc = True
                elif "Microsoft Office Excel" in file_type or "Microsoft Excel" in file_type or \
                    file_name.endswith((".xls", ".xlsx", ".xlsm", ".xlsb")):
                    officedoc = True
                elif "Microsoft PowerPoint" in file_type or \
                    file_name.endswith((".ppt", ".pptx", ".pps", ".ppsx", ".pptm", ".potm", ".potx", ".ppsm")):
                    officedoc = True

            USER32.EnumWindows(EnumWindowsProc(getwindowlist), 0)

            while self.do_run:
                if officedoc and (seconds % 30) == 0 and not CLOSED_OFFICE:
                    USER32.EnumWindows(EnumWindowsProc(get_office_window), 0)

                # only move the mouse 50% of the time, as malware can choose to act on an "idle" system just as it can on an "active" system
                if random.randint(0, 3) > 1:
                    click_mouse()
                    move_mouse()

                if (seconds % (15 + randoff)) == 0:
                    curwind = USER32.GetForegroundWindow()
                    other_hwnds = INITIAL_HWNDS[:]
                    try:
                        other_hwnds.remove(USER32.GetForegroundWindow())
                    except:
                        pass
                    if len(other_hwnds):
                        USER32.SetForegroundWindow(other_hwnds[random.randint(
                            0,
                            len(other_hwnds) - 1)])

                USER32.EnumWindows(EnumWindowsProc(foreach_window), 0)
                KERNEL32.Sleep(1000)
                seconds += 1
        except Exception as e:
            error_exc = traceback.format_exc()
            log.exception(error_exc)
Ejemplo n.º 22
0
    def kernel_analyze(self):
        """zer0m0n kernel analysis
        """
        log.info("Starting kernel analysis")
        log.info("Installing driver")
        if is_os_64bit():
            sys_file = os.path.join(os.getcwd(), "dll", "zer0m0n_x64.sys")
        else:
            sys_file = os.path.join(os.getcwd(), "dll", "zer0m0n.sys")
        exe_file = os.path.join(os.getcwd(), "dll", "logs_dispatcher.exe")
        if not sys_file or not exe_file or not os.path.exists(
                sys_file) or not os.path.exists(exe_file):
            log.warning(
                "No valid zer0m0n files to be used for process with pid %d, injection aborted",
                self.pid)
            return False

        exe_name = service_name = driver_name = random_string(6)

        inf_data = ('[Version]\r\n'
                        'Signature = "$Windows NT$"\r\n'
                        'Class = "ActivityMonitor"\r\n'
                        'ClassGuid = {{b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2}}\r\n'
                        'Provider = %Prov%\r\n'
                        'DriverVer = 22/01/2014,1.0.0.0\r\n'
                        'CatalogFile = %DriverName%.cat\r\n'
                    '[DestinationDirs]\r\n'
                        'DefaultDestDir = 12\r\n'
                        'MiniFilter.DriverFiles = 12\r\n'
                    '[DefaultInstall]\r\n'
                        'OptionDesc = %ServiceDescription%\r\n'
                        'CopyFiles = MiniFilter.DriverFiles\r\n'
                    '[DefaultInstall.Services]\r\n' \
                        'AddService = %ServiceName%,,MiniFilter.Service\r\n'
                    '[DefaultUninstall]\r\n'
                        'DelFiles = MiniFilter.DriverFiles\r\n'
                    '[DefaultUninstall.Services]\r\n'
                        'DelService = %ServiceName%,0x200\r\n'
                    '[MiniFilter.Service]\r\n'
                        'DisplayName = %ServiceName%\r\n'
                        'Description = %ServiceDescription%\r\n'
                        'ServiceBinary = %12%\\%DriverName%.sys\r\n'
                        'Dependencies = "FltMgr"\r\n'
                        'ServiceType = 2\r\n'
                        'StartType = 3\r\n'
                        'ErrorControl = 1\r\n'
                        'LoadOrderGroup = "FSFilter Activity Monitor"\r\n'
                        'AddReg = MiniFilter.AddRegistry\r\n'
                    '[MiniFilter.AddRegistry]\r\n'
                        'HKR,,"DebugFlags",0x00010001 ,0x0\r\n'
                        'HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance%\r\n'
                        'HKR,"Instances\\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude%\r\n'
                        'HKR,"Instances\\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags%\r\n'
                    '[MiniFilter.DriverFiles]\r\n'
                        '%DriverName%.sys\r\n'
                    '[SourceDisksFiles]\r\n'
                        '{driver_name}.sys = 1,,\r\n'
                    '[SourceDisksNames]\r\n'
                        '1 = %DiskId1%,,,\r\n'
                    '[Strings]\r\n'
                        'Prov = "{random_string8}"\r\n'
                        'ServiceDescription = "{random_string12}"\r\n'
                        'ServiceName = "{service_name}"\r\n'
                        'DriverName = "{driver_name}"\r\n'
                        'DiskId1 = "{service_name} Device Installation Disk"\r\n'
                        'DefaultInstance = "{service_name} Instance"\r\n'
                        'Instance1.Name = "{service_name} Instance"\r\n'
                        'Instance1.Altitude = "370050"\r\n'
                        'Instance1.Flags = 0x0'
                    ).format(
                        service_name=service_name, driver_name=driver_name, random_string8=random_string(8), random_string12=random_string(12)
                    )

        new_inf = os.path.join(os.getcwd(), "dll",
                               "{0}.inf".format(service_name))
        new_sys = os.path.join(os.getcwd(), "dll",
                               "{0}.sys".format(driver_name))
        copy(sys_file, new_sys)
        new_exe = os.path.join(os.getcwd(), "dll", "{0}.exe".format(exe_name))
        copy(exe_file, new_exe)
        log.info("[-] Driver name : " + new_sys)
        log.info("[-] Inf name : " + new_inf)
        log.info("[-] Application name : " + new_exe)
        log.info("[-] Service : " + service_name)

        fh = open(new_inf, "w")
        fh.write(inf_data)
        fh.close()

        os_is_64bit = is_os_64bit()
        if os_is_64bit:
            wow64 = c_ulong(0)
            KERNEL32.Wow64DisableWow64FsRedirection(byref(wow64))

        os.system(
            'cmd /c "rundll32 setupapi.dll, InstallHinfSection DefaultInstall 132 '
            + new_inf + '"')
        os.system("net start " + service_name)

        si = STARTUPINFO()
        si.cb = sizeof(si)
        pi = PROCESS_INFORMATION()
        cr = CREATE_NEW_CONSOLE

        ldp = KERNEL32.CreateProcessW(new_exe, None, None, None, None, cr,
                                      None, os.getenv("TEMP"), byref(si),
                                      byref(pi))
        if not ldp:
            if os_is_64bit:
                KERNEL32.Wow64RevertWow64FsRedirection(wow64)
            log.error("Failed starting " + exe_name + ".exe.")
            return False

        config_path = os.path.join(os.getenv("TEMP"), "%s.ini" % self.pid)
        with open(config_path, "w") as config:
            cfg = Config("analysis.conf")

            config.write("host-ip={0}\n".format(cfg.ip))
            config.write("host-port={0}\n".format(cfg.port))
            config.write("pipe={0}\n".format(PIPE))

        log.info("Sending startup information")
        hFile = KERNEL32.CreateFileW(PATH_KERNEL_DRIVER,
                                     GENERIC_READ | GENERIC_WRITE, 0, None,
                                     OPEN_EXISTING, 0, None)
        if os_is_64bit:
            KERNEL32.Wow64RevertWow64FsRedirection(wow64)
        if hFile:
            p = Process(pid=os.getpid())
            ppid = p.get_parent_pid()
            pid_vboxservice = 0
            pid_vboxtray = 0

            # get pid of VBoxService.exe and VBoxTray.exe
            proc_info = PROCESSENTRY32()
            proc_info.dwSize = sizeof(PROCESSENTRY32)

            snapshot = KERNEL32.CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
            flag = KERNEL32.Process32First(snapshot, byref(proc_info))
            while flag:
                if proc_info.sz_exeFile == "VBoxService.exe":
                    log.info("VBoxService.exe found !")
                    pid_vboxservice = proc_info.th32ProcessID
                    flag = 0
                elif proc_info.sz_exeFile == "VBoxTray.exe":
                    pid_vboxtray = proc_info.th32ProcessID
                    log.info("VBoxTray.exe found !")
                    flag = 0
                flag = KERNEL32.Process32Next(snapshot, byref(proc_info))
            bytes_returned = c_ulong(0)
            msg = str(self.pid) + "_" + str(ppid) + "_" + str(
                os.getpid()) + "_" + str(pi.dwProcessId) + "_" + str(
                    pid_vboxservice) + "_" + str(pid_vboxtray) + '\0'
            KERNEL32.DeviceIoControl(hFile, IOCTL_PID, msg, len(msg), None, 0,
                                     byref(bytes_returned), None)
            msg = os.getcwd() + '\0'
            KERNEL32.DeviceIoControl(hFile,
                                     IOCTL_CUCKOO_PATH, str(msg, 'utf-8'),
                                     len(str(msg, 'utf-8')), None, 0,
                                     byref(bytes_returned), None)
        else:
            log.warning("Failed to access kernel driver")

        return True
Ejemplo n.º 23
0
 def get_system_info(self):
     """Get system information."""
     KERNEL32.GetSystemInfo(byref(self.system_info))
Ejemplo n.º 24
0
    def execute(self,
                path,
                args=None,
                dll=None,
                free=False,
                curdir=None,
                source=None,
                mode=None,
                maximize=False):
        """Execute sample process.
        @param path: sample path.
        @param args: process args.
        @param dll: dll path.
        @param free: do not inject our monitor.
        @param curdir: current working directory.
        @param source: process identifier or process name which will
                       become the parent process for the new process.
        @param mode: monitor mode - which functions to instrument.
        @param maximize: whether the GUI should be maximized.
        @return: operation status.
        """
        if not os.access(path, os.X_OK):
            log.error(
                "Unable to access file at path \"%s\", "
                "execution aborted", path)
            return False

        is32bit = self.is32bit(path=path)

        if not dll:
            if is32bit:
                dll = "monitor-x86.dll"
            else:
                dll = "monitor-x64.dll"

        dllpath = os.path.abspath(os.path.join("bin", dll))

        if not os.path.exists(dllpath):
            log.warning("No valid DLL specified to be injected, "
                        "injection aborted.")
            return False

        if is32bit:
            inject_exe = os.path.join("bin", "inject-x86.exe")
        else:
            inject_exe = os.path.join("bin", "inject-x64.exe")

        argv = [inject_exe, "--app", self.shortpath(path)]

        if args:
            argv += ["--args", self._encode_args(args)]

        if free:
            argv += ["--free"]
        else:
            argv += [
                "--apc", "--dll", dllpath, "--config",
                self.drop_config(mode=mode)
            ]

        if curdir:
            argv += ["--curdir", self.shortpath(curdir)]

        if source:
            if isinstance(source, (int, long)) or source.isdigit():
                argv += ["--from", "%s" % source]
            else:
                argv += ["--from-process", source]

        if maximize:
            argv += ["--maximize"]

        try:
            self.pid = int(subprocess.check_output(argv))
        except Exception:
            log.error(
                "Failed to execute process from path %r with "
                "arguments %r (Error: %s)", path, argv,
                get_error_string(KERNEL32.GetLastError()))
            return False

        log.info(
            "Successfully executed process from path %r with "
            "arguments %r and pid %d", path, args or "", self.pid)
        return True
Ejemplo n.º 25
0
 def get_system_info(self):
     """Get system information."""
     self.system_info = SYSTEM_INFO()
     KERNEL32.GetSystemInfo(byref(self.system_info))
Ejemplo n.º 26
0
 def wait(self):
     if self.event_handle:
         KERNEL32.WaitForSingleObject(self.event_handle, 10000)
         KERNEL32.CloseHandle(self.event_handle)
         self.event_handle = None
     return True
Ejemplo n.º 27
0
 def open_process(self):
     """Open a process handle."""
     return KERNEL32.OpenProcess(PROCESS_ALL_ACCESS, False, self.pid)
Ejemplo n.º 28
0
 def __del__(self):
     """Close open handles."""
     if self.h_process and self.h_process != KERNEL32.GetCurrentProcess():
         KERNEL32.CloseHandle(self.h_process)
     if self.h_thread:
         KERNEL32.CloseHandle(self.h_thread)
Ejemplo n.º 29
0
    def run(self):
        """Run handler.
        @return: operation status.
        """
        global MONITORED_SERVICES
        global LASTINJECT_TIME
        data = ""
        response = "OK"

        # Read the data submitted to the Pipe Server.
        while True:
            bytes_read = c_int(0)

            buf = create_string_buffer(BUFSIZE)
            success = KERNEL32.ReadFile(self.h_pipe,
                                        buf,
                                        sizeof(buf),
                                        byref(bytes_read),
                                        None)

            data += buf.value

            if not success and KERNEL32.GetLastError() == ERROR_MORE_DATA:
                continue
            # elif not success or bytes_read.value == 0:
            #    if KERNEL32.GetLastError() == ERROR_BROKEN_PIPE:
            #        pass

            break

        if data:
            command = data.strip()

            # Debug, Regular, Warning, or Critical information from CuckooMon.
            if command.startswith("DEBUG:"):
                log.debug(command[6:])
            elif command.startswith("INFO:"):
                log.info(command[5:])
            elif command.startswith("WARNING:"):
                log.warning(command[8:])
            elif command.startswith("CRITICAL:"):
                log.critical(command[9:])

            # Parse the prefix for the received notification.
            # In case of GETPIDS we're gonna return the current process ID
            # and the process ID of our parent process (agent.py).
            elif command == "GETPIDS":
                response = struct.pack("II", PID, PPID)

            # When analyzing we don't want to hook all functions, as we're
            # having some stability issues with regards to webbrowsers.
            elif command == "HOOKDLLS":
                is_url = Config(cfg="analysis.conf").category != "file"

                url_dlls = "ntdll", "kernel32"

                def hookdll_encode(names):
                    # We have to encode each dll name as unicode string
                    # with length 16.
                    names = [name + "\x00" * (16-len(name)) for name in names]
                    f = lambda s: "".join(ch + "\x00" for ch in s)
                    return "".join(f(name) for name in names)

                # If this sample is not a URL, then we don't want to limit
                # any API hooks (at least for now), so we write a null-byte
                # which indicates that all DLLs should be hooked.
                if not is_url:
                    response = "\x00"
                else:
                    response = hookdll_encode(url_dlls)

            # remove pid from process list because we received a notification
            # from kernel land
            elif command.startswith("KTERMINATE:"):
                data = command[11:]
                process_id = int(data)
                if process_id:
                    if process_id in PROCESS_LIST:
                        remove_pid(process_id) 

            # same than below but we don't want to inject any DLLs because
            # it's a kernel analysis
            elif command.startswith("KPROCESS:"):
                PROCESS_LOCK.acquire()
                data = command[9:]
                process_id = int(data)
                thread_id = None
                if process_id:
                    if process_id not in (PID, PPID):
                        if process_id not in PROCESS_LIST:
                            proc = Process(pid=process_id,thread_id=thread_id)
                            filepath = proc.get_filepath()
                            filename = os.path.basename(filepath)

                            if not protected_filename(filename):
                                add_pid(process_id)
                                log.info("Announce process name : %s", filename)
                PROCESS_LOCK.release()                
            
            elif command.startswith("KERROR:"):
                error_msg = command[7:]
                log.error("Error : %s", str(error_msg))
           
            # if a new driver has been loaded, we stop the analysis
            elif command == "KSUBVERT":
                for pid in PROCESS_LIST:
                    log.info("Process with pid %s has terminated", pid)
                    PROCESS_LIST.remove(pid)

            # Handle case of a service being started by a monitored process
            # Switch the service type to own process behind its back so we
            # can monitor the service more easily with less noise
            elif command.startswith("SERVICE:"):
                servname = command[8:]
                si = subprocess.STARTUPINFO()
                si.dwFlags = subprocess.STARTF_USESHOWWINDOW
                si.wShowWindow = subprocess.SW_HIDE
                subprocess.call("sc config " + servname + " type= own", startupinfo=si)
                log.info("Announced starting service \"%s\"", servname)

                if not MONITORED_SERVICES:
                    # Inject into services.exe so we can monitor service creation
                    # if tasklist previously failed to get the services.exe PID we'll be
                    # unable to inject
                    if SERVICES_PID:
                        servproc = Process(pid=SERVICES_PID,suspended=False)
                        filepath = servproc.get_filepath()
                        servproc.inject(dll=DEFAULT_DLL, interest=filepath, nosleepskip=True)
                        LASTINJECT_TIME = datetime.now()
                        servproc.close()
                        KERNEL32.Sleep(1000)
                        MONITORED_SERVICES = True
                    else:
                        log.error('Unable to monitor service %s' % (servname))

            # For now all we care about is bumping up our LASTINJECT_TIME to account for long delays between
            # injection and actual resume time where the DLL would have a chance to load in the new process
            # and report back to have its pid added to the list of monitored processes
            elif command.startswith("RESUME:"):
                LASTINJECT_TIME = datetime.now()

            # Handle case of malware terminating a process -- notify the target
            # ahead of time so that it can flush its log buffer
            elif command.startswith("KILL:"):
                PROCESS_LOCK.acquire()

                process_id = int(command[5:])
                if process_id not in (PID, PPID) and process_id in PROCESS_LIST:
                    # only notify processes we've hooked
                    event_name = TERMINATE_EVENT + str(process_id)
                    event_handle = KERNEL32.OpenEventA(EVENT_MODIFY_STATE, False, event_name)
                    if not event_handle:
                        log.warning("Unable to open termination event for pid %u.", process_id)
                    else:
                        # make sure process is aware of the termination
                        KERNEL32.SetEvent(event_handle)
                        KERNEL32.CloseHandle(event_handle)

                PROCESS_LOCK.release()
            # Handle notification of cuckoomon loading in a process
            elif command.startswith("LOADED:"):
                PROCESS_LOCK.acquire()
                process_id = int(command[7:])
                if process_id not in PROCESS_LIST:
                    add_pids(process_id)
                PROCESS_LOCK.release()
                log.info("Cuckoomon successfully loaded in process with pid %u.", process_id)

            # In case of PID, the client is trying to notify the creation of
            # a new process to be injected and monitored.
            elif command.startswith("PROCESS:"):
                # We acquire the process lock in order to prevent the analyzer
                # to terminate the analysis while we are operating on the new
                # process.
                PROCESS_LOCK.acquire()

                # Set the current DLL to the default one provided
                # at submission.
                dll = DEFAULT_DLL
                suspended = False
                # We parse the process ID.
                data = command[8:]
                if len(data) > 2 and data[1] == ':':
                    if data[0] == '1':
                        suspended = True
                    data = command[10:]

                process_id = thread_id = None
                if "," not in data:
                    if data.isdigit():
                        process_id = int(data)
                elif data.count(",") == 1:
                    process_id, param = data.split(",")
                    thread_id = None
                    if process_id.isdigit():
                        process_id = int(process_id)
                    else:
                        process_id = None

                    if param.isdigit():
                        thread_id = int(param)

                if process_id:
                    if process_id not in (PID, PPID):
                        # We inject the process only if it's not being
                        # monitored already, otherwise we would generate
                        # polluted logs.
                        if process_id not in PROCESS_LIST:
                            # Open the process and inject the DLL.
                            # Hope it enjoys it.
                            proc = Process(pid=process_id,
                                           thread_id=thread_id,
                                           suspended=suspended)

                            filepath = proc.get_filepath()
                            is_64bit = proc.is_64bit()
                            filename = os.path.basename(filepath)

                            log.info("Announced %s process name: %s pid: %d", "64-bit" if is_64bit else "32-bit", filename, process_id)

                            if not in_protected_path(filename):
                                res = proc.inject(dll, filepath)
                                LASTINJECT_TIME = datetime.now()
                            proc.close()
                    else:
                        log.warning("Received request to inject Cuckoo "
                                    "process with pid %d, skip", process_id)

                # Once we're done operating on the processes list, we release
                # the lock.
                PROCESS_LOCK.release()
            # In case of FILE_NEW, the client is trying to notify the creation
            # of a new file.
            elif command.startswith("FILE_NEW:"):
                # We extract the file path.
                file_path = unicode(command[9:].decode("utf-8"))
                # We add the file to the list.
                add_file(file_path)
            # In case of FILE_DEL, the client is trying to notify an ongoing
            # deletion of an existing file, therefore we need to dump it
            # straight away.
            elif command.startswith("FILE_DEL:"):
                # Extract the file path.
                file_path = unicode(command[9:].decode("utf-8"))
                # Dump the file straight away.
                del_file(file_path)
            elif command.startswith("FILE_MOVE:"):
                # Syntax = "FILE_MOVE:old_file_path::new_file_path".
                if "::" in command[10:]:
                    old_fname, new_fname = command[10:].split("::", 1)
                    move_file(unicode(old_fname.decode("utf-8")),
                              unicode(new_fname.decode("utf-8")))
            else:
                log.warning("Received unknown command from cuckoomon: %s", command)

        KERNEL32.WriteFile(self.h_pipe,
                           create_string_buffer(response),
                           len(response),
                           byref(bytes_read),
                           None)

        KERNEL32.CloseHandle(self.h_pipe)

        return True
Ejemplo n.º 30
0
def foreach_child(hwnd, lparam):
    # List of buttons labels to click.
    buttons = [
        # english
        "yes",
        "ok",
        "accept",
        "next",
        "install",
        "run",
        "agree",
        "enable",
        "don't send",
        "don't save",
        "continue",
        "unzip",
        "open",
        "close the program",
        "save",
        "later",
        "finish",
        "end",
        "allow access",
        "remind me later",
        # german
        "ja",
        "weiter",
        "akzeptieren",
        "ende",
        "starten",
        "jetzt starten",
        "neustarten",
        "neu starten",
        "jetzt neu starten",
        "beenden",
        "oeffnen",
        "schliessen",
        "installation weiterfuhren",
        "fertig",
        "beenden",
        "fortsetzen",
        "fortfahren",
        "stimme zu",
        "zustimmen",
        "senden",
        "nicht senden",
        "speichern",
        "nicht speichern",
        "ausfuehren",
        "spaeter",
        "einverstanden"
    ]

    # List of buttons labels to not click.
    dontclick = [
        # english
        "check online for a solution",
        "don't run",
        "do not ask again until the next update is available",
        "cancel",
        "do not accept the agreement",
        "i would like to help make reader even better",
        # german
        "abbrechen",
        "online nach losung suchen",
        "abbruch",
        "nicht ausfuehren",
        "hilfe",
        "stimme nicht zu"
    ]

    classname = create_unicode_buffer(128)
    USER32.GetClassNameW(hwnd, classname, 128)

    # Check if the class of the child is button.
    if "button" in classname.value.lower(
    ) or classname.value == "NUIDialog" or classname.value == "bosa_sdm_msword":
        # Get the text of the button.
        length = USER32.SendMessageW(hwnd, WM_GETTEXTLENGTH, 0, 0)
        if not length:
            return True
        text = create_unicode_buffer(length + 1)
        USER32.SendMessageW(hwnd, WM_GETTEXT, length + 1, text)
        textval = text.value.replace('&', '')
        if "Microsoft" in textval and (classname.value == "NUIDialog" or
                                       classname.value == "bosa_sdm_msword"):
            log.info("Issuing keypress on Office dialog")
            USER32.SetForegroundWindow(hwnd)
            # enter key down/up
            USER32.keybd_event(0x0d, 0x1c, 0, 0)
            USER32.keybd_event(0x0d, 0x1c, 2, 0)
            return False

        # we don't want to bother clicking any non-visible child elements, as they
        # generally won't respond and will cause us to fixate on them for the
        # rest of the analysis, preventing progress with visible elements

        if not USER32.IsWindowVisible(hwnd):
            return True

        # Check if the button is set as "clickable" and click it.
        for button in buttons:
            if button in textval.lower():
                dontclickb = False
                for btn in dontclick:
                    if btn in textval.lower():
                        dontclickb = True
                if not dontclickb:
                    log.info("Found button \"%s\", clicking it" % text.value)
                    USER32.SetForegroundWindow(hwnd)
                    KERNEL32.Sleep(1000)
                    USER32.SendMessageW(hwnd, BM_CLICK, 0, 0)
                    # only stop searching when we click a button
                    return False
    return True