可以终止线程从另一个进程终止线程

Can TerminateThread terminate a thread from another process?

本文关键字:终止 线程 进程 另一个      更新时间:2023-10-16

在我的Windows服务应用程序中,我可能会在进程中的某些线程上调用TerminateThread API。(请注意,我这样做只是作为使用信令机制和线程同步技术以"正常方式"无法退出线程的最后措施。我在客户提交的事件日志中注意到的是,很少TerminateThread可能会引发STATUS_INVALID_THREAD异常,这仅在属于threadpool的线程上调用该 API 时发生。

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

所以我的问题是,由于我的服务以足够高的特权运行(如localService(,在这种情况下TerminateThread API 是否可以无意中终止属于另一个进程的某些线程?如果是,我该如何防止这种情况(除了找到我现在正在做的竞争条件(?

让我们让

文档自己说话:

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

请勿使用TerminateThread()

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

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

线程不能保护自己免受TerminateThread,除了控制对其句柄的访问。CreateThreadCreateProcess 函数返回的线程句柄具有THREAD_TERMINATE访问权限,因此持有这些句柄之一的任何调用方都可以终止线程。

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

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

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

如果你必须这样做,你可以这样做。

您只需要启动线程句柄。

使用带有 ThreadQuerySetWin32StartAddress 参数的"未记录"NtQueryInformationThread(( 函数调用第一个具有线程句柄作为输入的函数,您将获得线程的 StartAddress。 更多阅读@NTInternals。

它在通过 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,我没有写这段代码,但以前用过它。

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