【问题标题】:Is the first thread that gets to run inside a Win32 process the "primary thread"? Need to understand the semantics在 Win32 进程中运行的第一个线程是“主线程”吗?需要理解语义
【发布时间】:2012-03-14 03:09:06
【问题描述】:

我使用CreateProcess()CREATE_SUSPENDED 创建了一个进程,然后继续在远程进程中创建一小段代码以加载一个DLL 并调用一个函数(由该DLL 导出),使用VirtualAllocEx() (使用..., MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE)、WriteProcessMemory(),然后使用代码在该内存块上调用FlushInstructionCache()

之后我调用CreateRemoteThread() 来调用该代码,为我创建了一个hRemoteThread。我已验证远程代码按预期工作。 注意:此代码仅返回,它不调用除LoadLibrary()GetProcAddress() 之外的任何API,然后调用导出的存根函数,该函数当前仅返回一个值,然后将作为线程的退出状态。

现在出现了一个特殊的观察结果:请记住 PROCESS_INFORMATION::hThread 仍然处于暂停状态。当我简单地忽略hRemoteThread 的退出代码并且也不等待它退出时,一切都会“正常”。调用CreateRemoteThread() 的例程返回,PROCESS_INFORMATION::hThread 被恢复,(远程)程序实际开始运行。

但是,如果我致电WaitForSingleObject(hRemoteThread, INFINITE) 或执行以下操作(效果相同):

DWORD exitCode = STILL_ACTIVE;
while(STILL_ACTIVE == exitCode)
{
    Sleep(500);
    if(!GetExitCodeThread(hRemoteThread, &exitCode))
        break;
}

随后是CloseHandle(),这导致hRemoteThreadPROCESS_INFORMATION::hThread 恢复之前完成,并且该过程只是“消失”。让hRemoteThread 在没有PROCESS_INFORMATION::hThread 的情况下以某种方式完成就足以导致进程终止。

这看起来有点像竞态条件,因为在某些情况下hRemoteThread 可能仍然更快,并且即使我保留代码原样,该过程也可能仍然“消失”。

这是否意味着在进程中运行的第一个线程自动成为主线程,并且该主线程有特殊规则?

我一直认为进程在其最后一个线程死亡时结束,而不是在特定线程死亡时结束。

另请注意:这里没有以任何方式调用ExitProcess(),因为hRemoteThread 只是返回,而PROCESS_INFORMATION::hThread 在我等待hRemoteThread 返回时仍处于暂停状态。

这发生在 32 位 Windows XP SP3 上。

编辑:我刚刚尝试了 Sysinternals Process Monitor 来查看发生了什么,我可以验证我之前的观察结果。注入的代码不会崩溃或任何事情,相反,我会看到,如果我不等待线程它不会在我关闭注入代码的程序之前退出。我正在考虑是否应该推迟对CloseHandle(hRemoteThread) 的调用或其他什么...

编辑+1:不是CloseHandle()。如果我只是为了测试而忽略它,那么在等待线程完成时行为不会改变。

【问题讨论】:

    标签: multithreading winapi code-injection win32-process createremotethread


    【解决方案1】:

    第一个运行的线程并不特殊。

    例如,创建一个控制台应用程序,该应用程序创建一个挂起的线程并终止原始线程(通过调用 ExitThread)。这个过程永远不会终止(无论如何在 Windows 7 上)。

    或者让新线程等待五秒钟然后退出。正如预期的那样,该进程将存活 5 秒,并在辅助线程终止时退出。

    我不知道您的示例发生了什么。避免竞争的最简单方法是让新线程恢复原来的线程。

    现在推测,我确实想知道您正在做的事情是否不太可能导致问题。例如,对于隐式加载的 DLL 的所有 DllMain 调用会发生什么情况?它们是意外发生在错误的线程上,是被跳过,还是被推迟到您的代码运行并且主线程启动之后?

    【讨论】:

    • 请解释最后一点。我知道假设 DLL 在同一个基地址加载会导致问题。在我的进程中就像在远程进程中一样,因此传递给CreateRemoteThread 的显式旧LoadLibrary 不会飞——这就是为什么这样。此外,当主线程在创建进程后挂起时,DLL 已经加载,虽然我不确定他们的DllMain 是否已经被调用。但这并不重要,只要我在主线程启动后不调用我的LoadLibrary 并且可以在DllMain 本身内。谢谢
    • 在静态 DLL 初始化之前,Windows 通常不会看到对 LoadLibrary 的调用。这可能会导致问题。或者,如果在您的线程上调用静态 DllMains,这会混淆假设初始化线程将持续到进程生命周期的 DLL,这通常是正确的。正如我所说,我在推测,但似乎有很多破损的可能性。
    • 嗯,好的。如何验证其他 DLL 的 DllMains 是否已被调用?根据过去的经验,我知道我什至不能成功调用CreateRemoteProcess,除非kernel32.dll 已初始化并建立与Win32 的连接。但是,在这里调用CreateRemoteProcess 是成功的。你仍然有可能是对的,但我现在不这么认为。
    • 您可以通过创建一个静态链接到简单 DLL 的简单目标进程来测试它。简单的 DLL 可以输出调用其 DllMain 的线程的线程 ID(例如,使用 OutputDebugString)。我几乎很想自己尝试一下,但我需要去睡觉了。
    • 我玩得更远了。您似乎是对的,以某种方式使进程保持活动状态所需的初始化阶段到那时还没有结束。我所做的测试是创建未暂停的进程,休眠 500 毫秒,然后暂停它,然后注入我的代码。这变得非常不稳定,因为有时该过程会完全死锁,有时它会按预期启动 - 证明对竞争条件的怀疑。虽然有很多东西需要了解,但我会接受你的回答。
    【解决方案2】:

    具有main(或等效)函数的线程调用ExitProcess(显式或在其运行时库中)的可能性很大。 ExitProcess,嗯,退出整个进程,包括杀死所有线程。由于主线程不知道你注入的代码,它不会等待它完成。

    不知道有什么好办法让主线程等待你的完成...

    【讨论】:

    • 怎么样?如果 ti 无法运行,包含main() 的线程将如何调用ExitProcess()。我在我的问题中写道,当我遇到问题时,主线程不会在注入代码之前运行。当它们“并行”运行时,问题不会发生。
    • 哦,抱歉,我误读了您的问题。这是一个谜。您的线程干净地终止,并且没有退出进程,但进程仍然死亡?奇怪。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-06-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-19
    相关资源
    最近更新 更多