예제 #1
0
    def run(self):
        while self.do_run:
            flags = FILE_FLAG_WRITE_THROUGH
            if self.message:
                pipe_handle = KERNEL32.CreateNamedPipeA(
                    self.pipe_name, PIPE_ACCESS_DUPLEX | flags,
                    PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
                    PIPE_UNLIMITED_INSTANCES, BUFSIZE, BUFSIZE, 0, None)
            else:
                pipe_handle = KERNEL32.CreateNamedPipeA(
                    self.pipe_name, PIPE_ACCESS_INBOUND | flags,
                    PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
                    PIPE_UNLIMITED_INSTANCES, 0, BUFSIZE, 0, None)

            if pipe_handle == INVALID_HANDLE_VALUE:
                log.warning("Error opening logging pipe server.")
                continue

            if KERNEL32.ConnectNamedPipe(pipe_handle, None) or \
                    KERNEL32.GetLastError() == ERROR_PIPE_CONNECTED:
                handler = self.pipe_handler(pipe_handle, **self.kwargs)
                handler.daemon = True
                handler.start()
                self.handlers.add(handler)
            else:
                KERNEL32.CloseHandle(pipe_handle)
예제 #2
0
def spCreateProcessW(application_name, command_line, process_attributes,
                     thread_attributes, inherit_handles, creation_flags,
                     environment, current_directory, startup_info):
    class STARTUPINFO(Structure):
        _fields_ = [
            ("cb", c_uint),
            ("reserved1", c_void_p),
            ("desktop", c_void_p),
            ("title", c_void_p),
            ("unused1", c_uint * 7),
            ("flags", c_uint),
            ("show_window", c_uint16),
            ("reserved2", c_uint16),
            ("reserved3", c_void_p),
            ("std_input", c_void_p),
            ("std_output", c_void_p),
            ("std_error", c_void_p),
        ]

    class PROCESS_INFORMATION(Structure):
        _fields_ = [
            ("process_handle", c_void_p),
            ("thread_handle", c_void_p),
            ("process_identifier", c_uint),
            ("thread_identifier", c_uint),
        ]

    class Handle(int):
        def Close(self):
            KERNEL32.CloseHandle(self)

    if environment:
        environment = "\x00".join("%s=%s" % (k, v)
                                  for k, v in environment.items()) + "\x00\x00"

    si = STARTUPINFO()
    si.cb = sizeof(STARTUPINFO)

    if startup_info:
        si.flags = startup_info.dwFlags
        si.show_window = startup_info.wShowWindow

    if si.flags & STARTF_USESTDHANDLES:
        si.std_input = cast(int(startup_info.hStdInput), c_void_p)
        si.std_output = cast(int(startup_info.hStdOutput), c_void_p)
        si.std_error = cast(int(startup_info.hStdError), c_void_p)

    pi = PROCESS_INFORMATION()

    result = KERNEL32.CreateProcessW(application_name, command_line, None,
                                     None, inherit_handles, creation_flags,
                                     environment, current_directory, byref(si),
                                     byref(pi))
    if not result:
        # TODO We'll just assume this is correct for now.
        raise WindowsError(KERNEL32.GetLastError())

    return (Handle(pi.process_handle), Handle(pi.thread_handle),
            pi.process_identifier, pi.thread_identifier)
예제 #3
0
    def exit_code(self):
        """Get process exit code.
        @return: exit code value.
        """
        process_handle = self.open_process()

        exit_code = c_ulong(0)
        KERNEL32.GetExitCodeProcess(process_handle, byref(exit_code))
        KERNEL32.CloseHandle(process_handle)

        return exit_code.value
예제 #4
0
    def terminate(self):
        """Terminate process.
        @return: operation status.
        """
        process_handle = self.open_process()

        ret = KERNEL32.TerminateProcess(process_handle, 1)
        KERNEL32.CloseHandle(process_handle)

        if ret:
            log.info("Successfully terminated process with pid %d.", self.pid)
            return True
        else:
            log.error("Failed to terminate process with pid %d.", self.pid)
            return False
예제 #5
0
    def _read_message(self, buf):
        """Reads a message."""
        bytes_read = c_uint()
        ret = ""

        while True:
            success = KERNEL32.ReadFile(self.pipe_handle, byref(buf),
                                        sizeof(buf), byref(bytes_read), None)

            if KERNEL32.GetLastError() == ERROR_MORE_DATA:
                ret += buf.raw[:bytes_read.value]
            elif success:
                return ret + buf.raw[:bytes_read.value]
            else:
                return
예제 #6
0
    def get_parent_pid(self):
        """Get the Parent Process ID."""
        class PROCESS_BASIC_INFORMATION(Structure):
            _fields_ = [
                ("ExitStatus", c_void_p),
                ("PebBaseAddress", c_void_p),
                ("AffinityMask", c_void_p),
                ("BasePriority", c_void_p),
                ("UniqueProcessId", c_void_p),
                ("InheritedFromUniqueProcessId", c_void_p),
            ]

        NT_SUCCESS = lambda val: val >= 0

        pbi = PROCESS_BASIC_INFORMATION()
        size = c_int()

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

        process_handle = self.open_process()
        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.InheritedFromUniqueProcessId
예제 #7
0
    def run(self):
        """Run the pipe dispatcher."""
        buf = create_string_buffer(BUFSIZE)
        bytes_written = c_uint()

        while self.do_run:
            message = self._read_message(buf)
            if not message:
                break

            response = self.dispatcher.dispatch(message) or "OK"

            KERNEL32.WriteFile(self.pipe_handle, response, len(response),
                               byref(bytes_written), None)

        KERNEL32.CloseHandle(self.pipe_handle)
예제 #8
0
    def get_filepath(self):
        """Get process image file path.
        @return: decoded file path.
        """
        process_handle = self.open_process()

        NT_SUCCESS = lambda val: val >= 0

        pbi = create_string_buffer(200)
        size = c_int()

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

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

        KERNEL32.CloseHandle(process_handle)

        if NT_SUCCESS(ret) and size.value > 8:
            try:
                fbuf = pbi.raw[8:]
                fbuf = fbuf[:fbuf.find("\x00\x00") + 1]
                return fbuf.decode("utf16", errors="ignore")
            except:
                return ""

        return ""
예제 #9
0
def click_mouse():
    # Move mouse to top-middle position.
    USER32.SetCursorPos(RESOLUTION["x"] / 2, 0)
    # Mouse down.
    USER32.mouse_event(2, 0, 0, 0, None)
    KERNEL32.Sleep(50)
    # Mouse up.
    USER32.mouse_event(4, 0, 0, 0, None)
예제 #10
0
def set_clock(clock):
    st = SYSTEMTIME()
    st.wYear = clock.year
    st.wMonth = clock.month
    st.wDay = clock.day
    st.wHour = clock.hour
    st.wMinute = clock.minute
    st.wSecond = clock.second
    st.wMilliseconds = 0
    KERNEL32.SetLocalTime(ctypes.byref(st))
예제 #11
0
def grant_privilege(privilege):
    """Grant debug privileges.
    @param pid: PID.
    @return: operation status.
    """
    ADVAPI32.OpenProcessToken.argtypes = (wintypes.HANDLE, wintypes.DWORD,
                                          POINTER(wintypes.HANDLE))

    ADVAPI32.LookupPrivilegeValueW.argtypes = (wintypes.LPWSTR,
                                               wintypes.LPWSTR, POINTER(LUID))

    ADVAPI32.AdjustTokenPrivileges.argtypes = (wintypes.HANDLE, wintypes.BOOL,
                                               POINTER(TOKEN_PRIVILEGES),
                                               wintypes.DWORD,
                                               POINTER(TOKEN_PRIVILEGES),
                                               POINTER(wintypes.DWORD))

    h_process = KERNEL32.GetCurrentProcess()

    h_current_token = wintypes.HANDLE()
    if not ADVAPI32.OpenProcessToken(h_process, TOKEN_ALL_ACCESS,
                                     h_current_token):
        return False

    se_original_luid = LUID()
    if not ADVAPI32.LookupPrivilegeValueW(None, privilege, se_original_luid):
        return False

    luid_attributes = LUID_AND_ATTRIBUTES()
    luid_attributes.Luid = se_original_luid
    luid_attributes.Attributes = SE_PRIVILEGE_ENABLED
    token_privs = TOKEN_PRIVILEGES()
    token_privs.PrivilegeCount = 1
    token_privs.Privileges = luid_attributes

    if not ADVAPI32.AdjustTokenPrivileges(h_current_token, False, token_privs,
                                          0, None, None):
        return False

    KERNEL32.CloseHandle(h_current_token)
    KERNEL32.CloseHandle(h_process)
    return True
예제 #12
0
    def invoke(self, ctlcode, value, outlength=0x1000):
        device_handle = KERNEL32.CreateFileA(
            "\\\\.\\%s" % self.pipepath, GENERIC_READ | GENERIC_WRITE,
            FILE_SHARE_READ | FILE_SHARE_WRITE, None, OPEN_EXISTING, 0,
            None) % 2**32

        if device_handle == 0xffffffff:
            # Only report an error if the error is not "name not found",
            # indicating that no kernel analysis is currently taking place.
            if KERNEL32.GetLastError() != 2:
                log.warning("Error opening handle to driver (%s): %d!",
                            driver_name, KERNEL32.GetLastError())
            return False

        out = ctypes.create_string_buffer(outlength)
        length = ctypes.c_uint()

        ret = KERNEL32.DeviceIoControl(device_handle, ctlcode, value,
                                       len(value), out, ctypes.sizeof(out),
                                       ctypes.byref(length), None)
        KERNEL32.CloseHandle(device_handle)

        if not ret:
            log.warning("Error performing ioctl (0x%08x): %d!", ctlcode,
                        KERNEL32.GetLastError())
            return False

        return out.raw[:length.value]
예제 #13
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
예제 #14
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.init()
                aux.start()
            except (NotImplementedError, AttributeError):
                log.exception("Auxiliary module %s was not implemented",
                              module.__name__)
            except CuckooDisableModule:
                continue
            except Exception as e:
                log.exception("Cannot execute auxiliary module %s: %s",
                              module.__name__, e)
            else:
                log.debug("Started auxiliary module %s", module.__name__)
                aux_enabled.append(aux)

        # Inform zer0m0n of the ResultServer address.
        zer0m0n.resultserver(self.config.ip, self.config.port)

        # Forward the command pipe and logpipe names on to zer0m0n.
        zer0m0n.cmdpipe(self.config.pipe)
        zer0m0n.channel(self.config.logpipe)

        # Hide the Cuckoo Analyzer & Cuckoo Agent.
        zer0m0n.hidepid(self.pid)
        zer0m0n.hidepid(self.ppid)

        # Initialize zer0m0n with our compiled Yara rules.
        zer0m0n.yarald("bin/rules.yarac")

        # Propagate the requested dump interval, if set.
        zer0m0n.dumpint(int(self.config.options.get("dumpint", "0")))

        # Start analysis package. If for any reason, the execution of the
        # analysis package fails, we have to abort the analysis.
        pids = self.package.start(self.target)

        # 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:
                    # We also track the PIDs provided by zer0m0n.
                    self.process_list.add_pids(zer0m0n.getpids())

                    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

        # 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)

        # Dump all the notified files.
        self.files.dump_files()

        # Hell yeah.
        log.info("Analysis completed.")
        return True
예제 #15
0
    def run(self):
        buf = create_string_buffer(BUFSIZE)
        bytes_read = c_uint()
        pid = c_uint()

        # The first four bytes indicate the process identifier. In case the
        # pipe handle is closed in an unknown way, reopening one and
        # specifying the same process identifier will reuse the same socket,
        # thus making it look like as if it was never closed in the first
        # place.
        success = KERNEL32.ReadFile(self.pipe_handle, byref(pid), sizeof(pid),
                                    byref(bytes_read), None)

        if not success or bytes_read.value != sizeof(pid):
            log.warning("Unable to read the process identifier of this "
                        "log pipe instance.")
            KERNEL32.CloseHandle(self.pipe_handle)
            return

        if self.active.get(pid.value):
            log.warning("A second log pipe handler for an active process is "
                        "being requested, denying request.")
            KERNEL32.CloseHandle(self.pipe_handle)
            return

        if pid.value:
            sock = self.sockets.get(pid.value)
            if not sock:
                sock = socket.create_connection(self.destination)
                self.sockets[pid.value] = sock

            self.active[pid.value] = True
        else:
            sock = socket.create_connection(self.destination)

        open_handles.add(sock)

        while self.do_run:
            success = KERNEL32.ReadFile(self.pipe_handle, byref(buf),
                                        sizeof(buf), byref(bytes_read), None)

            if success or KERNEL32.GetLastError() == ERROR_MORE_DATA:
                try:
                    sock.sendall(buf.raw[:bytes_read.value])
                except socket.error as e:
                    if e.errno != errno.EBADF:
                        log.warning("Failed socket operation: %s", e)
                    break

            # If we get the broken pipe error then this pipe connection has
            # been terminated for one reason or another. So break from the
            # loop and make the socket "inactive", that is, another pipe
            # connection can in theory pick it up. (This will only happen in
            # cases where malware for some reason broke our pipe connection).
            elif KERNEL32.GetLastError() == ERROR_BROKEN_PIPE:
                break
            else:
                log.warning("The log pipe handler has failed, last error %d.",
                            KERNEL32.GetLastError())
                break

        if pid.value:
            self.active[pid.value] = False
예제 #16
0
def click(hwnd):
    USER32.SetForegroundWindow(hwnd)
    KERNEL32.Sleep(1000)
    USER32.SendMessageW(hwnd, BM_CLICK, 0, 0)
예제 #17
0
 def Close(self):
     KERNEL32.CloseHandle(self)
예제 #18
0
 def get_system_info(self):
     """Get system information."""
     self.system_info = SYSTEM_INFO()
     KERNEL32.GetSystemInfo(byref(self.system_info))
예제 #19
0
 def open_process(self):
     """Open a process handle."""
     return KERNEL32.OpenProcess(PROCESS_ALL_ACCESS, False, self.pid)
예제 #20
0
 def open_thread(self):
     """Open a thread handle."""
     return KERNEL32.OpenThread(THREAD_ALL_ACCESS, False, self.tid)