c++windowswmicreateprocessasuser

CreateProcessAsUser too fast to track process


We call CreateProcessAsUser() and, after having checked the result, we start tracking (WMI) the process that might create other processes.

In 1 case, the first process is so fast that it creates another process and terminates before we can start tracking it.

I even tried to not check the result and start immediately to track after the call to CreateProcessAsUser(), but it's not fast enough.

My idea is to start the process from a launcher.exe so we can track all the generated processes.

Is there any other alternative solution? We have the PID of the terminated process.


Solution

  • if we start child process and want way when it and all the children processes terminate we can use job object. general steps

    of course we need create I/O completion port too by CreateIoCompletionPort and one or more (if only for this task - single thread more than enough) threads which will be call GetQueuedCompletionStatus on port until end signal.

    we can for example use lpCompletionKey as pointer to object with virtual functions, and every object know how handle action event. demo code:

    struct __declspec(novtable) PortTask 
    {
        virtual bool OnIoCompletion(OVERLAPPED* lpOverlapped, ULONG NumberOfBytesTransferred) = 0;
    };
    
    struct EndTask : public PortTask 
    {
        virtual bool OnIoCompletion(OVERLAPPED* /*lpOverlapped*/, ULONG /*NumberOfBytesTransferred*/)
        {
            DbgPrint("%s<%p>\n", __FUNCTION__, this);
            delete this;
            return false;
        }
    };
    
    struct IOPort 
    {
        HANDLE CompletionPort;
        LONG dwRefCount;
    
        IOPort() : dwRefCount(1), CompletionPort(0) {
            DbgPrint("%s<%p>\n", __FUNCTION__, this);
        }
    
        ~IOPort(){
            if (CompletionPort) CloseHandle(CompletionPort);
            DbgPrint("%s<%p>\n", __FUNCTION__, this);
        }
    
        void AddRef(){
            InterlockedIncrementNoFence(&dwRefCount);
        }
    
        void Release(){
            if (!InterlockedDecrement(&dwRefCount)) {
                delete this;
            }
        }
    
        static ULONG WINAPI PortThread(PVOID This)
        {
            union {
                ULONG_PTR CompletionKey;
                PortTask* pTask;
            };
    
            ULONG NumberOfBytesTransferred;
            OVERLAPPED* lpOverlapped;
    
            HANDLE CompletionPort = reinterpret_cast<IOPort*>(This)->CompletionPort;
    
            while (GetQueuedCompletionStatus(CompletionPort, &NumberOfBytesTransferred, &CompletionKey, &lpOverlapped, INFINITE) &&
                pTask->OnIoCompletion(lpOverlapped, NumberOfBytesTransferred)) continue;
    
            reinterpret_cast<IOPort*>(This)->Release();
            return 0;
        }
    
        ULONG Create()
        {
            if (CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1))
            {
                AddRef();
    
                if (HANDLE hThread = CreateThread(0, 0, PortThread, this, 0, 0))
                {
                    CloseHandle(hThread);
                    return NOERROR;
                }
                Release();
            }
    
            return GetLastError();
        }
    
        ULONG Stop()
        {
            if (EndTask* pTask = new EndTask)
            {
                if (!PostQueuedCompletionStatus(CompletionPort, 0, (ULONG_PTR)pTask, 0))
                {
                    ULONG dwError = GetLastError();
                    delete pTask;
                    return dwError;
                }
                return NOERROR;
            }
    
            return ERROR_NO_SYSTEM_RESOURCES;
        }
    };
    
    struct ActiveProcessZeroTask : public PortTask 
    {
        //HWND hwnd; // in real code you send some message to hwnd instead thread
        HANDLE _hJob;
        ULONG _dwThreadId;
    
        ActiveProcessZeroTask() : _hJob(0), _dwThreadId(GetCurrentThreadId()) { }
    
        ~ActiveProcessZeroTask() {
            CloseHandle(_hJob);
            PostThreadMessageW(_dwThreadId, WM_QUIT, 0, 0);
        }
    
        virtual bool OnIoCompletion(OVERLAPPED* dwProcessId, ULONG MessageId)
        {
            DbgPrint("%s<%p>(%x %p)\n", __FUNCTION__, this, MessageId, dwProcessId);
            switch (MessageId)
            {
            case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO:
                DbgPrint("%p - ACTIVE_PROCESS_ZERO\n", dwProcessId);
                delete this;
                break;
            case JOB_OBJECT_MSG_NEW_PROCESS:
                DbgPrint("%p - NEW_PROCESS\n", dwProcessId);
                break;
            case JOB_OBJECT_MSG_EXIT_PROCESS:
                DbgPrint("%p - EXIT_PROCESS\n", dwProcessId);
                break;
            case JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS:
                DbgPrint("%p - ABNORMAL_EXIT_PROCESS\n", dwProcessId);
                break;
            }
            return true;
        }
    
        ULONG Create(HANDLE CompletionPort, PCWSTR ApplicationName)
        {
            if (HANDLE hJob = CreateJobObjectW(0, 0))
            {
                _hJob = hJob;
    
                JOBOBJECT_ASSOCIATE_COMPLETION_PORT jacp = { this, CompletionPort };
    
                if (SetInformationJobObject(hJob, JobObjectAssociateCompletionPortInformation, &jacp, sizeof(jacp)))
                {
                    STARTUPINFO si = { sizeof(si)};
                    PROCESS_INFORMATION pi;
                    if (CreateProcessW(ApplicationName, 0, 0, 0, 0, CREATE_SUSPENDED, 0, 0, &si, &pi))
                    {
                        ULONG dwError = NOERROR;
    
                        if (!AssignProcessToJobObject(hJob, pi.hProcess) || 
                            !ResumeThread(pi.hThread))
                        {
                            dwError = GetLastError();
                            TerminateProcess(pi.hProcess, 0);
                        }
                        CloseHandle(pi.hThread);
                        CloseHandle(pi.hProcess);
    
                        return dwError;
                    }
                }
            }
    
            return GetLastError();
        }
    };
    
    void demo()
    {
        if (IOPort* port = new IOPort)
        {
            if (port->Create() == NOERROR)
            {
                MessageBoxW(0, 0, L"just for demo #1", MB_ICONINFORMATION);
    
                // exec cmd for demo
                WCHAR ApplicationName[MAX_PATH];
                if (GetEnvironmentVariableW(L"ComSpec", ApplicationName, RTL_NUMBER_OF(ApplicationName)))
                {
                    if (ActiveProcessZeroTask* pTask = new ActiveProcessZeroTask)
                    {
                        if (pTask->Create(port->CompletionPort, ApplicationName) != NOERROR)
                        {
                            delete pTask;
                        }
                    }
                }
    
                // wait all childs exit
                MessageBoxW(0, 0, L"Wait for MSG_ACTIVE_PROCESS_ZERO", MB_ICONINFORMATION);
    
                // stop track thread
    
                if (port->Stop() != NOERROR) __debugbreak();
    
            }
    
            port->Release();
        }
    
        {
            MSG msg;
            // remove Wm_QUIT
            while (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) continue;
            MessageBoxW(0, 0, L"just for demo #2", MB_ICONINFORMATION);
        }
    }