CreateProcessAsUser 太快无法跟踪进程

CreateProcessAsUser too fast to track process

我们调用 CreateProcessAsUser(),检查结果后,我们开始跟踪 (WMI) 可能创建其他进程的进程。

在 1 种情况下,第一个进程太快了,它创建了另一个进程并在我们开始跟踪它之前终止。

我什至尝试检查结果并在调用CreateProcessAsUser()后立即开始跟踪,但速度不够快

我的想法是从一个 launcher.exe 开始进程,这样我们就可以跟踪所有生成的进程。

还有其他替代方案吗?我们有终止进程的 PID。

您需要实现显式同步机制(信号量)。下面是一个算法:

在父进程中:

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)

在shild进程中:

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

如果我们启动子进程并希望在它和所有子进程终止时有办法,我们可以使用作业对象。一般步骤

当然我们也需要通过 CreateIoCompletionPort and one or more (if only for this task - single thread more than enough) threads which will be call GetQueuedCompletionStatus 在端口上创建 I/O 完成端口直到结束信号。

我们可以例如使用lpCompletionKey作为指向具有虚函数的对象的指针,并且每个对象都知道如何处理动作事件。演示代码:

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);
    }
}