Windows内核驱动程序:如何确定线程是否终止?

时间:2018-02-23 18:07:30

标签: c windows multithreading driver windows-kernel

我有一个针对某些操作的线程,它需要存活,直到标志另有说明。

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

感谢。

1 个答案:

答案 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 )并从驱动程序卸载返回。