在我的Windows服务应用程序中,我可能会在我的进程中的某些线程上调用TerminateThread API。 (请注意,我只是作为最后的手段,当线程无法以正常方式退出时使用信令机制和线程同步技术。)我在提交的事件日志中注意到了什么客户很少TerminateThread
可能抛出STATUS_INVALID_THREAD
异常,只有在属于threadpool
的线程上调用该API时才会发生此异常。
因为我确信我的线程都没有从线程池启动,所以我调用TerminateThread
尝试关闭的线程必须来自另一个进程。这可能只发生在竞争条件下,我的线程句柄首先被关闭,然后再次传递给TerminateThread
API,而操作系统将其重新用于另一个进程中的其他线程。
所以我的问题是,由于我的服务运行时具有足够高的权限(如localService
),TerminateThread
API在这种情况下无意中终止某个属于另一个进程的线程?如果是的话,我怎么能防止这种情况(除了找到我现在正在做的竞争条件)?
答案 0 :(得分:3)
让我们让文档为自己说话:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms686717(v=vs.85).aspx
请勿使用TerminateThread()
:
TerminateThread
是一种危险的功能,只能在最极端的情况下使用。仅当您确切知道目标线程正在执行的操作时,才应调用TerminateThread
,并且您可以控制目标线程在终止时可能正在运行的所有代码。例如,TerminateThread
可能会导致以下问题:
[...]
您可以终止任何线程,只要您拥有具有足够权限的句柄:
除了通过控制对其句柄的访问权限之外,线程无法保护自己免受
TerminateThread
的攻击。CreateThread
和CreateProcess
函数返回的线程句柄具有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设置为您想要的任何内容。