Example #1
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]
Example #2
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:
            if pid.value not in self.sockets:
                self.sockets[pid.value] = (socket.create_connection(
                    self.destination))

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

        while True:
            success = KERNEL32.ReadFile(self.pipe_handle, byref(buf),
                                        sizeof(buf), byref(bytes_read), None)
            #log.error(" [*] Thunder pipe.py@PipeForwarder@run buffer: Len[%d]", bytes_read.value)
            #import traceback
            #log.error(" [*] Thunder pipe.py: [%s]", str(traceback.format_stack()))
            #log.error(buf[:bytes_read.value])
            if success or KERNEL32.GetLastError() == ERROR_MORE_DATA:
                #log.info("Sending data")
                sock.sendall(buf.raw[:bytes_read.value])
            # 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
Example #3
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
Example #4
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:
            self.active[pid.value] = True

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

            if success or KERNEL32.GetLastError() == ERROR_MORE_DATA:
                d = os.path.join(os.getcwd(), 'stuff', 'logs')
                if not os.path.exists(d):
                    os.makedirs(d)
                fn = os.path.join(d, '{0}.bson'.format(str(pid.value)))
                with open(fn, 'ab') as fa:
                    fa.write(buf.raw[:bytes_read.value])
            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

        return
Example #5
0
File: log.py Project: xme/CAPEv2
    def run(self):
        """Create and run Log Server.
        @return: operation status.
        """
        try:
            while self.do_run:
                # Create the Named Pipe.
                # If we receive a connection to the pipe, we invoke the handler.
                if KERNEL32.ConnectNamedPipe(self.h_pipe,
                                             None) or KERNEL32.GetLastError(
                                             ) == ERROR_PIPE_CONNECTED:
                    self.handle_logs()

                KERNEL32.CloseHandle(self.h_pipe)
                self.resultserver_socket.close()

            return True
        except Exception as e:
            print(e)
            self.resultserver_socket.close()
            if self.h_pipe != 0:
                KERNEL32.CloseHandle(self.h_pipe)
            error_exc = traceback.format_exc()
            log.exception(error_exc)
            return True
    def run(self):
        """Create and run PIPE server.
        @return: operation status.
        """
        while self.do_run:
            # Create the Named Pipe.
            h_pipe = KERNEL32.CreateNamedPipeA(
                self.pipe_name, PIPE_ACCESS_DUPLEX,
                PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
                PIPE_UNLIMITED_INSTANCES, BUFSIZE, BUFSIZE, 0, None)

            if h_pipe == INVALID_HANDLE_VALUE:
                return False

            # If we receive a connection to the pipe, we invoke the handler.
            if KERNEL32.ConnectNamedPipe(
                    h_pipe,
                    None) or KERNEL32.GetLastError() == ERROR_PIPE_CONNECTED:
                handler = PipeHandler(h_pipe)
                handler.daemon = True
                handler.start()
            else:
                KERNEL32.CloseHandle(h_pipe)

        return True
Example #7
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()
            else:
                KERNEL32.CloseHandle(pipe_handle)
Example #8
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 & subprocess.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)
Example #9
0
    def execute(self, path, args=None, suspended=False, kernel_analysis=False):
        """Execute sample process.
        @param path: sample path.
        @param args: process args.
        @param suspended: is suspended.
        @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

        startup_info = STARTUPINFO()
        startup_info.cb = sizeof(startup_info)
        # STARTF_USESHOWWINDOW
        startup_info.dwFlags = 1
        # SW_SHOWNORMAL
        startup_info.wShowWindow = 1
        process_info = PROCESS_INFORMATION()

        arguments = '"' + path + '" '
        if args:
            arguments += args

        creation_flags = CREATE_NEW_CONSOLE
        if suspended:
            self.suspended = True
            creation_flags += CREATE_SUSPENDED

        created = KERNEL32.CreateProcessW(path, arguments, None, None, None,
                                          creation_flags, None,
                                          os.getenv("TEMP"),
                                          byref(startup_info),
                                          byref(process_info))

        if created:
            self.pid = process_info.dwProcessId
            self.h_process = process_info.hProcess
            self.thread_id = process_info.dwThreadId
            self.h_thread = process_info.hThread
            log.info(
                'Successfully executed process from path "%s" with '
                'arguments "%s" with pid %d', path, args or "", self.pid)
            if kernel_analysis:
                return self.kernel_analyze()
            return True
        else:
            log.error(
                'Failed to execute process from path "%s" with '
                'arguments "%s" (Error: %s)',
                path,
                args,
                get_error_string(KERNEL32.GetLastError()),
            )
            return False
Example #10
0
    def execute(self, path, args=None, suspended=False):
        """Execute sample process.
        @param path: sample path.
        @param args: process args.
        @param suspended: is suspended.
        @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

        startup_info = STARTUPINFO()
        startup_info.cb = sizeof(startup_info)
        process_info = PROCESS_INFORMATION()

        if args:
            arguments = "\"" + path + "\" " + args
        else:
            arguments = None

        creation_flags = CREATE_NEW_CONSOLE
        if suspended:
            self.suspended = True
            creation_flags += CREATE_SUSPENDED

        created = KERNEL32.CreateProcessA(path,
                                          arguments,
                                          None,
                                          None,
                                          None,
                                          creation_flags,
                                          None,
                                          os.getenv("TEMP"),
                                          byref(startup_info),
                                          byref(process_info))

        if created:
            self.pid = process_info.dwProcessId
            self.h_process = process_info.hProcess
            self.thread_id = process_info.dwThreadId
            self.h_thread = process_info.hThread
            log.info("Successfully executed process from path \"%s\" with "
                     "arguments \"%s\" with pid %d", path, args, self.pid)
            return True
        else:
            log.error("Failed to execute process from path \"%s\" with "
                      "arguments \"%s\" (Error: %s)", path, args,
                      get_error_string(KERNEL32.GetLastError()))
            return False
Example #11
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
Example #12
0
    def run(self):
        while self.do_run:
            # 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)
            # flags = FILE_FLAG_WRITE_THROUGH

            if self.message:
                pipe_handle = KERNEL32.CreateNamedPipeW(
                    self.pipe_name,
                    PIPE_ACCESS_DUPLEX,  # | flags,
                    PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
                    PIPE_UNLIMITED_INSTANCES,
                    BUFSIZE,
                    BUFSIZE,
                    0,
                    byref(sa),  # None,
                )
            else:
                pipe_handle = KERNEL32.CreateNamedPipeW(
                    self.pipe_name,
                    PIPE_ACCESS_INBOUND,  # | flags,
                    PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
                    PIPE_UNLIMITED_INSTANCES,
                    0,
                    BUFSIZE,
                    0,
                    byref(sa),  # 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)
Example #13
0
File: log.py Project: xme/CAPEv2
    def handle_logs(self):
        # Read the data submitted to the Log Server.
        while True:
            data = b""
            while True:
                bytes_read = c_int(0)
                buf = create_string_buffer(LOGBUFSIZE)
                success = KERNEL32.ReadFile(self.h_pipe, buf, sizeof(buf),
                                            byref(bytes_read), None)

                data += buf.raw[:bytes_read.value]
                if success or KERNEL32.GetLastError() != ERROR_MORE_DATA:
                    break

            # got an entire message, send it off to the resultserver
            if data:
                self.resultserver_socket.sendall(data)
Example #14
0
    def run(self):
        """Create and run PIPE server.
        @return: operation status.
        """
        try:
            while self.do_run:
                # 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(self.pipe_name,
                                                   PIPE_ACCESS_DUPLEX,
                                                   PIPE_TYPE_MESSAGE |
                                                   PIPE_READMODE_MESSAGE |
                                                   PIPE_WAIT,
                                                   PIPE_UNLIMITED_INSTANCES,
                                                   BUFSIZE,
                                                   BUFSIZE,
                                                   0,
                                                   byref(sa))

                if h_pipe == INVALID_HANDLE_VALUE:
                    return False

                # If we receive a connection to the pipe, we invoke the handler.
                if KERNEL32.ConnectNamedPipe(h_pipe, None) or KERNEL32.GetLastError() == ERROR_PIPE_CONNECTED:
                    handler = PipeHandler(h_pipe, self.config, self.options)
                    handler.daemon = True
                    handler.start()
                else:
                    KERNEL32.CloseHandle(h_pipe)

            return True
        except Exception as e:
            error_exc = traceback.format_exc()
            log.exception(error_exc)
            return True
Example #15
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
    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
Example #17
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
Example #18
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
Example #19
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
Example #20
0
    def run(self):
        """Run handler.
        @return: operation status.
        """
        global MONITORED_SERVICES
        global MONITORED_WMI
        global MONITORED_DCOM
        global MONITORED_TASKSCHED
        global MONITORED_BITS
        global LASTINJECT_TIME
        global NUM_INJECTED
        try:
            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":
                    hidepids = set()
                    hidepids.update(HIDE_PIDS)
                    hidepids.update([PID, PPID])
                    response = struct.pack("%dI" % len(hidepids), *hidepids)

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

                elif command.startswith("INTEROP:"):
                    if not MONITORED_DCOM:
                        MONITORED_DCOM = True
                        dcom_pid = pid_from_service_name("DcomLaunch")
                        if dcom_pid:
                            servproc = Process(pid=dcom_pid,suspended=False)
                            servproc.set_critical()
                            filepath = servproc.get_filepath()
                            servproc.inject(dll=DEFAULT_DLL, interest=filepath, nosleepskip=True)
                            LASTINJECT_TIME = datetime.now()
                            servproc.close()
                            KERNEL32.Sleep(2000)

                elif command.startswith("WMI:"):
                    if not MONITORED_WMI:
                        MONITORED_WMI = True
                        si = subprocess.STARTUPINFO()
                        # STARTF_USESHOWWINDOW
                        si.dwFlags = 1
                        # SW_HIDE
                        si.wShowWindow = 0
                        log.info("Stopping WMI Service")
                        subprocess.call(['net', 'stop', 'winmgmt', '/y'], startupinfo=si)
                        log.info("Stopped WMI Service")
                        subprocess.call("sc config winmgmt type= own", startupinfo=si)

                        if not MONITORED_DCOM:
                            MONITORED_DCOM = True
                            dcom_pid = pid_from_service_name("DcomLaunch")
                            if dcom_pid:
                                servproc = Process(pid=dcom_pid,suspended=False)
                                servproc.set_critical()
                                filepath = servproc.get_filepath()
                                servproc.inject(dll=DEFAULT_DLL, interest=filepath, nosleepskip=True)
                                LASTINJECT_TIME = datetime.now()
                                servproc.close()
                                KERNEL32.Sleep(2000)

                        log.info("Starting WMI Service")
                        subprocess.call("net start winmgmt", startupinfo=si)
                        log.info("Started WMI Service")

                        wmi_pid = pid_from_service_name("winmgmt")
                        if wmi_pid:
                            servproc = Process(pid=wmi_pid,suspended=False)
                            servproc.set_critical()
                            filepath = servproc.get_filepath()
                            servproc.inject(dll=DEFAULT_DLL, interest=filepath, nosleepskip=True)
                            LASTINJECT_TIME = datetime.now()
                            servproc.close()
                            KERNEL32.Sleep(2000)

                elif command.startswith("TASKSCHED:"):
                    if not MONITORED_TASKSCHED:
                        MONITORED_TASKSCHED = True
                        si = subprocess.STARTUPINFO()
                        # STARTF_USESHOWWINDOW
                        si.dwFlags = 1
                        # SW_HIDE
                        si.wShowWindow = 0
                        log.info("Stopping Task Scheduler Service")
                        subprocess.call(['net', 'stop', 'schedule', '/y'], startupinfo=si)
                        log.info("Stopped Task Scheduler Service")
                        subprocess.call("sc config schedule type= own", startupinfo=si)

                        log.info("Starting Task Scheduler Service")
                        subprocess.call("net start schedule", startupinfo=si)
                        log.info("Started Task Scheduler Service")

                        sched_pid = pid_from_service_name("schedule")
                        if sched_pid:
                            servproc = Process(pid=sched_pid,suspended=False)
                            servproc.set_critical()
                            filepath = servproc.get_filepath()
                            servproc.inject(dll=DEFAULT_DLL, interest=filepath, nosleepskip=True)
                            LASTINJECT_TIME = datetime.now()
                            servproc.close()
                            KERNEL32.Sleep(2000)

                elif command.startswith("BITS:"):
                    if not MONITORED_BITS:
                        MONITORED_BITS = True
                        si = subprocess.STARTUPINFO()
                        # STARTF_USESHOWWINDOW
                        si.dwFlags = 1
                        # SW_HIDE
                        si.wShowWindow = 0
                        log.info("Stopping BITS Service")
                        subprocess.call(['net', 'stop', 'BITS', '/y'], startupinfo=si)
                        log.info("Stopped BITS Service")
                        subprocess.call("sc config BITS type= own", startupinfo=si)

                        if not MONITORED_DCOM:
                            MONITORED_DCOM = True
                            dcom_pid = pid_from_service_name("DcomLaunch")
                            if dcom_pid:
                                servproc = Process(pid=dcom_pid,suspended=False)
                                servproc.set_critical()
                                filepath = servproc.get_filepath()
                                servproc.inject(dll=DEFAULT_DLL, interest=filepath, nosleepskip=True)
                                LASTINJECT_TIME = datetime.now()
                                servproc.close()
                                KERNEL32.Sleep(2000)

                        log.info("Starting BITS Service")
                        subprocess.call("net start BITS", startupinfo=si)
                        log.info("Started BITS Service")

                        bits_pid = pid_from_service_name("BITS")
                        if bits_pid:
                            servproc = Process(pid=bits_pid,suspended=False)
                            servproc.set_critical()
                            filepath = servproc.get_filepath()
                            servproc.inject(dll=DEFAULT_DLL, interest=filepath, nosleepskip=True)
                            LASTINJECT_TIME = datetime.now()
                            servproc.close()
                            KERNEL32.Sleep(2000)

                # 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()
                    # STARTF_USESHOWWINDOW
                    si.dwFlags = 1
                    # SW_HIDE
                    si.wShowWindow = 0
                    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)
                            servproc.set_critical()
                            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 attempted shutdowns/restarts -- flush logs for all monitored processes
                # additional handling can be added later
                elif command.startswith("SHUTDOWN:"):
                    log.info("Received shutdown request")
                    PROCESS_LOCK.acquire()
                    for process_id in PROCESS_LIST:
                        event_name = TERMINATE_EVENT + str(process_id)
                        event_handle = KERNEL32.OpenEventA(EVENT_MODIFY_STATE, False, event_name)
                        if event_handle:
                            KERNEL32.SetEvent(event_handle)
                            KERNEL32.CloseHandle(event_handle)
                            if self.options.get("procmemdump"):
                                p = Process(pid=process_id)
                                p.dump_memory()
                            dump_files()
                    PROCESS_LOCK.release()
                # 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:
                            log.info("Notified of termination of process with pid %u.", process_id)
                            # dump the memory of exiting processes
                            if self.options.get("procmemdump"):
                                p = Process(pid=process_id)
                                p.dump_memory()
                            # 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()
                    NUM_INJECTED += 1
                    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()
                                # if it's a URL analysis, provide the URL to all processes as
                                # the "interest" -- this will allow cuckoomon to see in the
                                # child browser process that a URL analysis is occurring
                                if self.config.category == "file" or NUM_INJECTED > 1:
                                    interest = filepath
                                else:
                                    interest = self.config.target

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

                                if not in_protected_path(filename) and proc.check_inject():
                                    log.info("Announced %s process name: %s pid: %d", "64-bit" if is_64bit else "32-bit", filename, process_id)
                                    proc.inject(dll, interest)
                                    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:"):
                    FILES_LIST_LOCK.acquire()
                    # We extract the file path.
                    file_path = unicode(command[9:].decode("utf-8"))
                    # We add the file to the list.
                    add_file(file_path)
                    FILES_LIST_LOCK.release()
                # 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:"):
                    FILES_LIST_LOCK.acquire()
                    # Extract the file path.
                    file_path = unicode(command[9:].decode("utf-8"))
                    # Dump the file straight away.
                    del_file(file_path)
                    FILES_LIST_LOCK.release()
                elif command.startswith("FILE_MOVE:"):
                    FILES_LIST_LOCK.acquire()
                    # 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")))
                    FILES_LIST_LOCK.release()
                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
        except Exception as e:
            error_exc = traceback.format_exc()
            log.exception(error_exc)
            return True
Example #21
0
    def execute(self, path, args=None, suspended=False, kernel_analysis=False):
        """Execute sample process.
        @param path: sample path.
        @param args: process args.
        @param suspended: is suspended.
        @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

        startup_info = STARTUPINFO()
        startup_info.cb = sizeof(startup_info)
        # STARTF_USESHOWWINDOW
        startup_info.dwFlags = 1
        # SW_SHOWNORMAL
        startup_info.wShowWindow = 1
        process_info = PROCESS_INFORMATION()

        arguments = f'"{path}" '
        if args:
            arguments += args

        creation_flags = CREATE_NEW_CONSOLE
        if suspended:
            self.suspended = True
            creation_flags += CREATE_SUSPENDED

        # Use the custom execution directory if provided, otherwise launch in the same location
        # where the sample resides (default %TEMP%)
        if "executiondir" in self.options.keys():
            execution_directory = self.options["executiondir"]
        elif "curdir" in self.options.keys():
            execution_directory = self.options["curdir"]
        else:
            execution_directory = os.getenv("TEMP")

        # Try to create the custom directories so that the execution path is deemed valid
        create_custom_folders(execution_directory)

        created = KERNEL32.CreateProcessW(path, arguments, None, None, None,
                                          creation_flags, None,
                                          execution_directory,
                                          byref(startup_info),
                                          byref(process_info))

        if created:
            self.pid = process_info.dwProcessId
            self.h_process = process_info.hProcess
            self.thread_id = process_info.dwThreadId
            self.h_thread = process_info.hThread
            log.info(
                'Successfully executed process from path "%s" with arguments "%s" with pid %d',
                path, args or "", self.pid)
            if kernel_analysis:
                return self.kernel_analyze()
            return True
        else:
            log.error(
                'Failed to execute process from path "%s" with arguments "%s" (Error: %s)',
                path,
                args,
                get_error_string(KERNEL32.GetLastError()),
            )
            return False
Example #22
0
    def run(self, waiting_time):

        # Open driver device
        self.hdevice = KERNEL32.CreateFileA(self.device_name,
                                            GENERIC_READ | GENERIC_WRITE, 0,
                                            None, OPEN_EXISTING, 0, None)
        if self.hdevice == INVALID_HANDLE_VALUE:
            self.log.error("CreateFileA failed with error : 0x%x" %
                           KERNEL32.GetLastError())
            quit()

        self.log.info("Driver device file opened, handle = %x" % self.hdevice)

        #todo : build command line
        self.process = Process(self.command_line, self.log)

        self.process.create_suspended()

        self.pre_run()

        self.log.info("Target process handle value is 0x%x" %
                      self.process.process_handle)

        self.thread_running = True
        thread = Thread(target=self.pipe_reader_thread, args=())

        #Create an unpack event which will be signaled when the
        self.hUnpackEvent = KERNEL32.CreateEventA(NULL, 0, 0, "DaEvent")
        self.UserlandNotidyEvent = KERNEL32.CreateEventA(
            NULL, 0, 0, "UserlandNotidyEvent")

        #Struct sent to the driver
        MyPidStruct = PID_STRUCT()
        MyPidStruct.do_log = self.kernel_log
        MyPidStruct.RWEPolicy = self.rwe_policy
        MyPidStruct.InitialNXState = self.initial_nx_state
        MyPidStruct.UserlandNotidyEvent = self.UserlandNotidyEvent
        MyPidStruct.TargetProcessHandle = self.process.process_handle

        #Initiate driver's state and communication mecanisms
        BytesReturned = DWORD(0)
        success = KERNEL32.DeviceIoControl(self.hdevice, IOCTL_SETUP_STUFF,
                                           ctypes.byref(MyPidStruct),
                                           ctypes.sizeof(MyPidStruct), NULL, 0,
                                           ctypes.byref(BytesReturned), 0)
        if not (success):
            self.log.error("DeviceIoControl failed")
            raise UnpackerException("DeviceIoControl failed")

        thread.start()

        #Resume main process thtread
        self.process.resume()
        self.log.info("Main thread resumed")

        #Wait for unpacking to terminate
        r = KERNEL32.WaitForSingleObject(self.hUnpackEvent,
                                         self.max_unpack_time)
        if (r == WAIT_ABANDONED):
            self.log.error("Wait abandoned, something went wrong")
            raise UnpackerException("Wait abandoned, something went wrong")

        if (r == WAIT_TIMEOUT):
            self.log.info("Wait timed out")
            self.log.info("Thread suspended")

        if (r == WAIT_OBJECT_0):
            self.log.info("Event signaled")

        BytesReturned = DWORD(0)
        success = KERNEL32.DeviceIoControl(self.hdevice, IOCTL_SUSPEND_TRACKED,
                                           NULL, 0, NULL, 0,
                                           ctypes.byref(BytesReturned), 0)
        if not (success):
            self.log.error("DeviceIoControl failed")
            raise UnpackerException("DeviceIoControl failed")

        self.thread_running = False

        result = self.post_treatment()

        BytesReturned = DWORD(0)
        success = KERNEL32.DeviceIoControl(self.hdevice,
                                           IOCTL_UNTRACK_AND_RESUME_PROCESSES,
                                           NULL, 0, NULL, 0,
                                           ctypes.byref(BytesReturned), 0)
        if not (success):
            self.log.error("DeviceIoControl failed")
            raise UnpackerException("DeviceIoControl failed")

        BytesReturned = DWORD(0)
        success = KERNEL32.DeviceIoControl(self.hdevice, IOCTL_CLEANUP, NULL,
                                           0, NULL, 0,
                                           ctypes.byref(BytesReturned), 0)
        if not (success):
            self.log.error("DeviceIoControl failed")
            raise UnpackerException("DeviceIoControl failed")

        KERNEL32.CloseHandle(self.hdevice)
        KERNEL32.CloseHandle(self.UserlandNotidyEvent)

        self.process.terminate()

        KERNEL32.ExitProcess(0)
Example #23
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