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);
如果我们启动子进程并希望在它和所有子进程终止时有办法,我们可以使用作业对象。一般步骤
- 创建新的作业对象
设置 JobObjectAssociateCompletionPortInformation
为
SetInformationJobObject
在挂起状态创建新进程(使用CREATE_SUSPENDED
标志)
- 向作业添加进程
使用 ResumeThread
恢复新进程
现在系统会在新进程启动或退出时向我们的完成端口发送通知。当作业中没有更多进程时 - 将 JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO
消息 post 到端口 - 表示活动进程计数已减少到 0
当然我们也需要通过 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);
}
}
我们调用 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);
如果我们启动子进程并希望在它和所有子进程终止时有办法,我们可以使用作业对象。一般步骤
- 创建新的作业对象
设置
JobObjectAssociateCompletionPortInformation
为SetInformationJobObject
在挂起状态创建新进程(使用
CREATE_SUSPENDED
标志)- 向作业添加进程
使用
恢复新进程ResumeThread
现在系统会在新进程启动或退出时向我们的完成端口发送通知。当作业中没有更多进程时 - 将
JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO
消息 post 到端口 - 表示活动进程计数已减少到 0
当然我们也需要通过 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);
}
}