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

C++ TerminateThread可以从另一个进程终止线程吗?,c++,windows,multithreading,winapi,C++,Windows,Multithreading,Winapi,在我的Windows服务应用程序中,我可能会在进程中的某些线程上调用API。请注意,我这样做只是在线程无法使用信号机制和线程同步技术以正常方式退出时作为最后手段。我在客户提交的事件日志中注意到,TerminateThread很少会抛出STATUS_INVALID_THREAD异常,只有在属于线程池的线程上调用该API时才会发生 因为我确信我的线程都不是从线程池启动的,所以我对TerminateThread的调用试图关闭的线程必须来自另一个进程。这只可能是由于争用条件造成的,在争用条件下,我的线程

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

因为我确信我的线程都不是从线程池启动的,所以我对TerminateThread的调用试图关闭的线程必须来自另一个进程。这只可能是由于争用条件造成的,在争用条件下,我的线程句柄首先关闭,然后再次传递给TerminateThread API,而操作系统在另一个进程中对其他线程重用它


所以我的问题是,因为我的服务运行时具有足够高的权限,因为localService可以在这种情况下终止ThreadAPI,从而无意中终止属于另一个进程的某个线程?如果是的话,除了找到我现在正在做的比赛条件之外,我怎样才能防止这种情况发生呢?

让医生们自己说:

不要使用TerminateThread:

TerminateThread是一个危险的函数,只能在最极端的情况下使用。如果您确切知道目标线程正在做什么,并且您控制目标线程在终止时可能正在运行的所有代码,则应该调用TerminateThread。例如,TerminateThread可能导致以下问题: [……]

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

线程不能保护自己免受TerminateThread的攻击,除非通过控制对其句柄的访问。CreateThread和CreateProcess函数返回的线程句柄具有thread_TERMINATE访问权限,因此持有其中一个句柄的任何调用方都可以终止线程

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

在这种情况下,您不能调用TerminateThread。只有在对要终止的线程及其完全协作具有精确控制的情况下,才能调用TerminateThread。如果线程未能以正常方式退出,那么您就失去了对线程的控制,这与调用TerminateThread所需的条件正好相反


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

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

您只需要启动线程句柄

使用带有参数的未文档化函数调用第一个以线程句柄作为输入的函数,即可获得线程的StartAddress。更多阅读@

通过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,我没有编写此代码,但以前使用过它。

在NTSTATUS ExitStatus中使用未记录的NTSYSAPI NTSTATUS NTAPI NtTerminateThreadIN HANDLE ThreadHandle;其中ThreadHandle是MS函数的结果,ExitStatus设置为您想要的任何值。

是否打开其他进程?@usr:是。它可能会打开其他进程。如果线程无法通过正常方式退出,则它是好的。您的代码中出现了一个缺陷。跟踪并修复它,不要使用TerminateThread。要从另一个进程获得线程池线程句柄的唯一方法是调用OpenThread。如果您不调用OpenThread,那么这不是您的问题。如果这样做,可以将每个此类句柄的副本放入表中。在调用TerminateThread之前,搜索表以查看您使用的句柄是否存在;如果是的话,你的句柄就错了。顺便说一句,你至少做出了两个不安全的假设:首先,不能保证状态无效线程异常的唯一可能原因是你试图终止一个线程池线程;其次,即使您的代码不使用线程池,您的进程中仍可能有线程池线程,由操作系统或第三方软件自动启动。那么答案是肯定的?我还假设在另一个进程特权中没有线程终止,对吗?是的。如果您拥有该特权的句柄,那么您是否处于不同的进程中并不重要
字母S。关于它不安全的免责声明几乎保证了a,但仍然适用。那么我应该在那里做些什么呢?@c00000fd如果不了解您的全部外部问题,很难说,但可能会将不安全代码隔离在自己的过程中。假设代码无法修复。线程必须在进程内协作,否则进程将丢失。@c00000fd您必须修复导致线程挂起的有缺陷的代码。没有其他方法可以实现工作程序。@c0000fd您正在尝试使无法使其工作的东西工作。文档上是这么说的,痛苦的经历也是这么说的。@c00000fd我只是在整个程序未通过此类健全性检查时终止它。它是有缺陷的,可能会导致任何其他问题,TerminateThread是你试图向测试人员和用户隐藏缺陷,而不是修复这些缺陷。伙计,你是不是在对自己的答案进行投票?看起来确实如此。我想我有一个粉丝:无论如何,谢谢。我得试试。我两年前问过这个问题。现在我需要重新阅读我在那里问的wtf: