c语言 - Windows 内核驱动程序:如何确定线程是否已终止



我有一个线程,对于某些操作,它需要处于活动状态,直到标志另有说明。

我使用 PsCreateSystemThread 创建线程,然后使用 ObReferenceObjectByHandle 获取 ETHREAD 对象引用,以便在使用 KeWaitForSingleObject 卸载驱动程序之前等待线程终止。

创建线程并检索对其引用的函数:

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 完成的,我可以添加一个"布尔值"并在调用 PsTerminateSystemThread 终止线程之前设置它)。

还有一个问题:

我在线程例程开始时终止了线程,并在调用 ObReferenceObjectByHandle 之前等待了 20 秒,它没有失败。

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 成功而不失败?-线程早已消失。

谢谢。

为什么ObReferenceObjectByHandle成功而不失败? -线程 早已不复存在。

但为什么它必须失败??ObReferenceObjectByHandle简单地返回返回指向对象主体的相应指针。ETHREAD在你的情况下。对象的状态 - 在这里不发挥任何作用。终止线程或不是绝对无关的。直到您有句柄或指向线程体结构 (ETHREAD) 的引用指针 - 对象将不会释放。因此,如果hThread句柄有效 -ObReferenceObjectByHandle必须成功。

如何确定线程是否已终止?

非常简单 - 只需等待它,比如说通过KeWaitForSingleObject,你已经完成了。 由于线程对象本身就是一种调度程序对象,因此当线程终止时,它会设置为信号状态并KeWaitForSingleObject返回。

if (ptThreadObject)
{
bStopThread = TRUE;
KeWaitForSingleObject(
ptThreadObject,
Executive,
KernelMode,
FALSE,
0);
ObDereferenceObject(ptThreadObject);
}

注意 - 您必须将超时设置为 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);也是多余和不必要的代码。 仅当您希望返回状态与STATUS_SUCCESS不同并在线程终止后检查返回状态时,才需要调用PsTerminateSystemThread。 请注意,Windows 本身不会解释和使用线程退出状态。 它只是将其存储在ETHREAD对象。如果您不查询并使用此状态 - 则其退出状态无关紧要。

此外,如果您使用常量超时,您还可以从循环中输出liSleepTime.QuadPart = 1000 * RELATIVE_MILLISECOND;行 - 在循环之前设置。

此外,如果在 ASM 代码中使用特殊的线程入口点,我们根本无法在驱动程序卸载中等待线程终止。 显然,在线程运行之前,不得卸载该驱动程序。 为此存在 2 个解决方案 - 一个是在驱动程序卸载例程中等待,用于所有驱动程序线程终止。 但存在和另一个 - 添加对驱动程序对象的引用, 当我们创建线程时,当线程退出时,取消引用驱动程序对象。

所以定义全局变量:

PDRIVER_OBJECT gDriverObject;

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(但不调用)ObfDereferenceObject.

C/C++进程定义为

void NTAPI _ThreadRoutine(PVOID pv)
{
// not call PsTerminateSystemThread here !!
}

x64c的代码 (ml64 /c /Cp $(InputFileName) -> $(InputName).obj)

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

x86c的代码 (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

有了这个,您就不能等待工作线程退出 - 只需向他发出终止信号(bStopThread = TRUE;)并从驱动程序卸载返回。

相关内容

  • 没有找到相关文章

最新更新