TerminateThread可以从另一个进程终止一个线程吗?

时间:2014-11-08 20:54:37

标签: c++ windows multithreading winapi

在我的Windows服务应用程序中,我可能会在我的进程中的某些线程上调用TerminateThread API。 (请注意,我只是作为最后的手段,当线程无法以正常方式退出时使用信令机制和线程同步技术。)我在提交的事件日志中注意到了什么客户很少TerminateThread可能抛出STATUS_INVALID_THREAD异常,只有在属于threadpool的线程上调用该API时才会发生此异常。

因为我确信我的线程都没有从线程池启动,所以我调用TerminateThread尝试关闭的线程必须来自另一个进程。这可能只发生在竞争条件下,我的线程句柄首先被关闭,然后再次传递给TerminateThread API,而操作系统将其重新用于另一个进程中的其他线程。

所以我的问题是,由于我的服务运行时具有足够高的权限(如localService),TerminateThread API在这种情况下无意中终止某个属于另一个进程的线程?如果是的话,我怎么能防止这种情况(除了找到我现在正在做的竞争条件)?

4 个答案:

答案 0 :(得分:3)

让我们让文档为自己说话:

http://msdn.microsoft.com/en-us/library/windows/desktop/ms686717(v=vs.85).aspx

请勿使用TerminateThread()

  

TerminateThread是一种危险的功能,只能在最极端的情况下使用。仅当您确切知道目标线程正在执行的操作时,才应调用TerminateThread,并且您可以控制目标线程在终止时可能正在运行的所有代码。例如,TerminateThread可能会导致以下问题:
  [...]

您可以终止任何线程,只要您拥有具有足够权限的句柄:

  

除了通过控制对其句柄的访问权限之外,线程无法保护自己免受TerminateThread的攻击。 CreateThreadCreateProcess函数返回的线程句柄具有THREAD_TERMINATE访问权限,因此任何持有其中一个句柄的调用者都可以终止您的线程。

答案 1 :(得分:3)

  

请注意,当线程无法以正常方式退出时,我这样做只是作为最后的手段。使用信令机制和线程同步技术。

这是无法调用TerminateThread的情况。如果您可以精确控制正在终止的线程并完全合作,则只能调用TerminateThread。如果线程无法以正常方式退出,那么您已经失去了对线程的控制,这与您可以调用TerminateThread所需的条件完全相反。

如果进程失去了对其中一个线程的控制权,则无法保存该进程。这是线程的基本属性 - 除了流量控制之外,它们不提供任何隔离。

答案 2 :(得分:1)

如果必须执行此操作,则可以这样做。

您需要启动的只是线程句柄。

使用带有NtQueryInformationThread()参数的“未公开说明的” ThreadQuerySetWin32StartAddress函数,将第一个函数以线程句柄作为输入来调用,即可获取线程的StartAddress。 {@ {3}}中的更多内容。

在通过GetProcAddress获取地址之后,它将通过函数地址调用NTQueryInformationThread。然后,它使用 ThreadQuerySetWin32StartAddress 参数调用它,获得线程的 StartAddress

然后,您调用第二个函数,该函数通过 CreateToolHelp32Snapshot 遍历所有线程,并与提供的 StartAddress 比较。找到它后,它将调用 TerminateThread

enum THREADINFOCLASS
{
    ThreadQuerySetWin32StartAddress = 9,
};

typedef NTSTATUS(__stdcall * f_NtQueryInformationThread)(HANDLE, THREADINFOCLASS, void*, ULONG_PTR, ULONG_PTR*);

ULONG_PTR GetThreadStartAddress(HANDLE hThread)
{
    auto NtQueryInformationThread = reinterpret_cast<f_NtQueryInformationThread>(GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueryInformationThread"));
    if (!NtQueryInformationThread)
        return 0;

    ULONG_PTR ulStartAddress = 0;
    NTSTATUS Ret = NtQueryInformationThread(hThread, ThreadQuerySetWin32StartAddress, &ulStartAddress, sizeof(ULONG_PTR), nullptr);

    if (NT_FAIL(Ret))
        return 0;

    return ulStartAddress;
}


bool TerminateThreadByStartaddress(ULONG_PTR StartAddress)
{
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
    if (hSnap == INVALID_HANDLE_VALUE)
        return false;

    THREADENTRY32 TE32 = { 0 };
    TE32.dwSize = sizeof(THREADENTRY32);

    BOOL Ret = Thread32First(hSnap, &TE32);
    while (Ret)
    {
        HANDLE hTempThread = OpenThread(THREAD_ALL_ACCESS, FALSE, TE32.th32ThreadID);
        if (!hTempThread)
        {
            Ret = Thread32Next(hSnap, &TE32);
            continue;
        }

        if (StartAddress == GetThreadStartAddress(hTempThread))
        {
            TerminateThread(hTempThread, 0);
            CloseHandle(hTempThread);
            CloseHandle(hSnap);
            return true;
        }

        CloseHandle(hTempThread);

        Ret = Thread32Next(hSnap, &TE32);
    }

    CloseHandle(hSnap);

    return false;
}

感谢我的朋友Broihon,我没有写这段代码,但是以前用过它。

答案 3 :(得分:0)

使用http://undocumented.ntinternals.net中未记载的NTSYSAPI NTSTATUS NTAPI NtTerminateThread(IN HANDLE ThreadHandle, IN NTSTATUS ExitStatus);,其中ThreadHandle是OpenThread MS函数的结果,并且ExitStatus设置为您想要的任何内容。