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

Posted

技术标签:

【中文标题】TerminateThread 可以终止另一个进程的线程吗?【英文标题】:Can TerminateThread terminate a thread from another process? 【发布时间】:2014-11-08 20:54:36 【问题描述】:

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

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

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

【问题讨论】:

你在打开其他进程吗? @usr:是的。它可能会打开其他进程。 如果线程无法正常退出,那很好。您的代码中出现了缺陷。跟踪它并修复它,不要使用 TerminateThread。 从另一个进程获得线程池线程句柄的唯一方法是调用 OpenThread。如果您不调用 OpenThread,那么这不是您的问题。如果这样做,您可以将每个此类句柄的副本放入表中。在调用 TerminateThread 之前,先搜索表看看你正在使用的句柄是否在那里;如果是,则说明您的句柄有误。 顺便说一句,您至少做了两个不安全的假设:首先,不能保证 STATUS_INVALID_THREAD 异常的唯一可能原因是您正在尝试终止线程池线程;其次,即使您的代码不使用线程池,您的进程中可能仍然存在线程池线程,由操作系统或第三方软件自动启动。 【参考方案1】:

让我们让文档自己说话:

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

不要使用TerminateThread():

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

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

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

【讨论】:

所以答案是yes?我还假设没有THREAD_TERMINATE_IN_ANOTHER_PROCESS 特权,对吗? 是的。如果您拥有该权限的句柄,那么您是否处于不同的进程中并不重要。关于它不安全的免责声明(几乎可以保证但)仍然适用。【参考方案2】:

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

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

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

【讨论】:

那我该怎么办呢? @c00000fd 如果不了解您的全部外部问题,这很难说,但可能会将不安全的代码隔离在自己的进程中。 (假设无法修复代码。)线程必须在进程内部协作,否则进程将丢失。 @c00000fd 您必须修复导致线程挂起的缺陷代码。没有其他方法可以实现工作程序。 @c00000fd 您正在尝试使某些无法正常工作的工作。文档是这么说的,痛苦的经历也是这么说的。 @c00000fd 我只是在整个程序未通过此类健全性检查时终止它。它是有缺陷的,可能会导致任何其他问题,TerminateThread 是您试图向测试人员和用户隐藏缺陷而不是修复这些缺陷。【参考方案3】:

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

你只需要启动线程句柄。

您使用带有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,这段代码不是我写的,但以前用过。

【讨论】:

老兄,你在为自己的答案投票吗 L) 看起来确实如此。我想我有一个粉丝 :) 无论如何,谢谢。我得试试看。 2年前我问过这个问题。现在我需要重新阅读我在那里问的 wtf :)【参考方案4】:

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

【讨论】:

以上是关于TerminateThread 可以终止另一个进程的线程吗?的主要内容,如果未能解决你的问题,请参考以下文章

获取/检查进程 win32 的内部 kernel32 状态(为了安全使用 TerminateThread )

C语言如何终止线程?

线程天敌TerminateThread与SuspendThread

如何等待进程终止以在批处理文件中执行另一个进程

从另一个 Java 应用程序中查找并终止特定的 Java 进程

IPC:共享内存终止进程通知