CreateProcessAsUser too fast to track process

210 Views Asked by At

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.

2

There are 2 best solutions below

2
RbMm On BEST ANSWER

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);
    }
}
24
Landstalker On

You need to implement an explicit synchronization mechanism (semaphore). Bellow an algorithm :

In the parent process :

semaphore my_semaphore = CreateSemaphore ("my_semphaore");  
args my_arguments ("my_semphaore");  
CreateProcessAsUser (my_arguments); 
Create_Asynchronous_Thread { releasesemaphore (my_semaphore );} // unblock shild process}
waitforSingleObject (shild_process_PID)

In the shild process :

// do something ...
semaphore my_semaphore = CreateSemaphore ("my_semphaore");  
// do something (parent is blocked)
waitforsingleobject (my_semaphore);