我有一个针对某些操作的线程,它需要存活,直到标志另有说明。
我使用PsCreateSystemThread创建线程,然后使用ObReferenceObjectByHandle获取ETHREAD对象引用,等待线程在使用KeWaitForSingleObject卸载驱动程序之前终止。< / p>
创建线程并检索对它的引用的函数:
ntStatus = PsCreateSystemThread(
&hThread,
(ACCESS_MASK)0, NULL,
(HANDLE)0, NULL,
ThreadRoutine,
(PVOID)pThreadData
);
if (!NT_SUCCESS(ntStatus))
{
return ntStatus;
}
ntStatus = ObReferenceObjectByHandle(
hThread,
THREAD_ALL_ACCESS,
NULL,
KernelMode,
(PVOID*)&ptThreadObject,
NULL
);
if (!NT_SUCCESS(ntStatus))
{
bStopThread = TRUE;
ptThreadObject = NULL;
return ntStatus;
}
线程例程:
LARGE_INTEGER liSleepTime;
while (FALSE == bThread)
{
liSleepTime.QuadPart = 1000 * RELATIVE_MILLISECOND;
KeDelayExecutionThread(KernelMode, FALSE, (&liSleepTime));
ExAcquireFastMutex(&fmMutex);
//DO SOMTHING
ExReleaseFastMutex(&fmMutex);
}
PsTerminateSystemThread(STATUS_SUCCESS);
卸载驱动程序功能:
if (NULL != ptThreadObject)
{
bStopThread = TRUE;
KeWaitForSingleObject(
(PVOID)ptThreadObject,
Executive,
KernelMode,
FALSE,
(&liTimeOut));
ObDereferenceObject((PVOID)ptThreadObject);
ptThreadObject= NULL;
}
我需要这个线程一直运行。
有没有办法检查线程是否过早终止?(如果由PsTerminateSystemThread完成,我可以添加&#39;布尔&#39;并在之前设置它调用PsTerminateSystemThread来终止线程)。
还有一个问题:
我在程序开始时终止了线程并等待了20秒才调用ObReferenceObjectByHandle并且它没有失败。
ntStatus = PsCreateSystemThread(
&hThread,
(ACCESS_MASK)0, NULL,
(HANDLE)0, NULL,
ThreadRoutine,
(PVOID)pThreadData
);
if (!NT_SUCCESS(ntStatus))
{
return ntStatus;
}
// The ThreadRoutine calling PsTerminateSystemThread first and terminate.
liSleepTime.QuadPart = 20000 * RELATIVE_MILLISECOND;
KeDelayExecutionThread(KernelMode, FALSE, (&liSleepTime));
ntStatus = ObReferenceObjectByHandle(
hThread,
THREAD_ALL_ACCESS,
NULL,
KernelMode,
(PVOID*)&ptThreadObject,
NULL
);
if (!NT_SUCCESS(ntStatus))
{
bStopThread = TRUE;
ptThreadObject = NULL;
return ntStatus;
}
为什么ObReferenceObjectByHandle成功而不失败? - 线程早已消失。
感谢。
答案 0 :(得分:1)
为什么
ObReferenceObjectByHandle
成功而不失败? - 线程 早已不复存在。
但为什么它必须失败? ObReferenceObjectByHandle
只返回返回对象体的相应指针。在你的情况下ETHREAD
。对象的状态 - 在这里不起任何作用。终止线程或不绝对无关。直到你有句柄或引用指向线程体结构(ETHREAD
)的指针 - 该对象将不会被释放。因此,如果hThread
是有效句柄,则ObReferenceObjectByHandle
必须成功。
如何确定线程是否终止?
非常简单 - 只需等待它通过你已经完成的KeWaitForSingleObject
说。因为线程对象本身就是一种调度程序对象,当线程终止时,它设置为信号状态并且KeWaitForSingleObject
返回。
if (ptThreadObject)
{
bStopThread = TRUE;
KeWaitForSingleObject(
ptThreadObject,
Executive,
KernelMode,
FALSE,
0);
ObDereferenceObject(ptThreadObject);
}
注意 - 必须将 Timeout 设置为0才能无限期等待,直到线程终止(调度程序对象设置为信号状态)。你也不需要将ptThreadObject
强制转换为PVOID
- 它已经是指针。 (PVOID)ptThreadObject
不是错误,而是多余且不必要的代码。
有没有办法检查线程是否过早?
操作系统,我不明白你在过早下的意思。检查线程终止,我们可以通过等待它。但过早地只能在你的代码的上下文中有意义。假设您可以通过PsTerminateSystemThread
设置不同的线程退出状态,并且(在线程终止后)通过PsGetThreadExitStatus
获取此状态。如果线程仍在运行PsGetThreadExitStatus
返回STATUS_PENDING
。此例程也可以部分用于检查线程状态 - 如果它返回任何不同于STATUS_PENDING
的状态 - 线程终止。但如果它返回STATUS_PENDING
- 不清楚 - 或线程仍在运行,或者线程存在于PsTerminateSystemThread(STATUS_PENDING)
。当然使用STATUS_PENDING
作为存在状态是坏主意,永远不必使用。在这种情况下,您也可以使用PsGetThreadExitStatus
确定线程状态(运行/终止),但此例程不会等待。但是当线程终止时,你的驱动程序逻辑需要等待,只有在此之后我们才能卸载驱动程序。所以这里只有KeWaitForSingleObject
(或另一个等待函数)是正确的解决方案。如果线程可以以不同的方式存在 - 在调用PsTerminateSystemThread
中使用不同的退出状态,并在线程终止后通过PsGetThreadExitStatus
获取它(因此在KeWaitForSingleObject
之后)
然而,PsTerminateSystemThread
的来电是可选的 - 您只需从ThreadRoutine
返回 - 在这种情况下系统自己调用PsTerminateSystemThread(STATUS_SUCCESS);
- 所以在您的代码调用中PsTerminateSystemThread(STATUS_SUCCESS);
也是多余的不必要的代码如果您希望返回状态与PsTerminateSystemThread
不同,并且检查在线程终止后返回状态,则需要致电STATUS_SUCCESS
。请注意,Windows本身不会解释并使用线程退出状态。它只是将它存储在ETHREAD
对象中。如果您不查询并使用此状态 - 无论退出状态如何。
也可以从循环中设置liSleepTime.QuadPart = 1000 * RELATIVE_MILLISECOND;
行 - 在循环之前设置。
如果在asm代码中使用特殊的线程入口点,我们也不能等待驱动程序卸载中的线程终止。很明显,在线程运行之前,不能卸载驱动程序。为此存在2个解决方案 - 一个是在驱动程序卸载例程中等待,对于所有驱动程序线程终止。但存在和另一个 - 当我们创建线程时,添加对驱动程序对象的引用,并在线程退出时取消引用驱动程序对象。
所以定义全局变量:
PDRIVER_OBJECT gDriverObject;
{p}在DriverEntry
初始化它:gDriverObject = DriverObject;
以下一个方式启动线程:
ObfReferenceObject(gDriverObject);
NTSTATUS status = PsCreateSystemThread(
&hThread,
0, NULL,
0, NULL,
ThreadRoutine,
pThreadData
);
if (!NT_SUCCESS(status))
{
ObfDereferenceObject(gDriverObject);
}
并且在线程退出时需要调用ObfDereferenceObject(gDriverObject);
。但在ObfDereferenceObject(gDriverObject);
之后我们已经无法返回到驱动程序代码 - 它已经可以卸载了。所以这个调用不能从 c / c ++ 代码完成。在用户模式下存在FreeLibraryAndExitThread
,但在内核模式下没有这个api的模拟。唯一的解决方案 - 在asm代码中实现线程入口点 - 此条目调用 c / c ++ 线程例程,最后 jmp (但不是调用)到{ {1}}。
将 c / c ++ proc定义为
ObfDereferenceObject
x64的代码和 c (void NTAPI _ThreadRoutine(PVOID pv)
{
// not call PsTerminateSystemThread here !!
}
)
ml64 /c /Cp $(InputFileName) -> $(InputName).obj
x86的代码和 c (extern _ThreadRoutine : PROC
extern gDriverObject : QWORD
extern __imp_ObfDereferenceObject : QWORD
_TEXT segment 'CODE'
ThreadRoutine proc
sub rsp,28h
call _ThreadRoutine
add rsp,28h
mov rcx,gDriverObject
jmp __imp_ObfDereferenceObject
ThreadRoutine endp
_TEXT ENDS
end
)
ml /c /Cp $(InputFileName) -> $(InputName).obj
这样你可以不等待来工作线程退出 - 只需发信号给他终止(.686
extern _gDriverObject:DWORD
extern __imp_@ObfDereferenceObject@4:DWORD
extern __ThreadRoutine : PROC
_TEXT SEGMENT
_ThreadRoutine proc
mov eax,[esp]
xchg eax,[esp+4]
mov [esp],eax
call __ThreadRoutine
mov ecx,_gDriverObject
jmp __imp_@ObfDereferenceObject@4
_ThreadRoutine endp
_TEXT ENDS
END
)并从驱动程序卸载返回。