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

Windows Kernel Driver: How to determine if thread terminated?

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

我用PsCreateSystemThread to create the thread and then use ObReferenceObjectByHandle to get ETHREAD object reference to waiting for the thread to terminate before the driver unloaded using KeWaitForSingleObject.

The function that creates the thread and retrieves a reference to it:

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;
}

The thread routine:

LARGE_INTEGER liSleepTime;
while (FALSE == bThread)
{
    liSleepTime.QuadPart = 1000 * RELATIVE_MILLISECOND;
    KeDelayExecutionThread(KernelMode, FALSE, (&liSleepTime));

    ExAcquireFastMutex(&fmMutex);
    //DO SOMTHING
    ExReleaseFastMutex(&fmMutex);
}

PsTerminateSystemThread(STATUS_SUCCESS);

The unload driver function:

if (NULL != ptThreadObject)
{
    bStopThread = TRUE;

    KeWaitForSingleObject(
        (PVOID)ptThreadObject,
        Executive,
        KernelMode,
        FALSE,
        (&liTimeOut));
    ObDereferenceObject((PVOID)ptThreadObject);
    ptThreadObject= NULL;
}

我一直需要这个帖子 运行。

有没有办法检查线程是否过早终止?(如果它是由 PsTerminateSystemThread 完成的,我可以添加一个 'boolean' 并设置它在调用 PsTerminateSystemThread 终止线程之前)。

One more question:

我在例程开始时终止了线程并等待了 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成功而不失败?-线程早已消失

谢谢。

Why does ObReferenceObjectByHandle succeed and not fail? -The thread is long gone.

但为什么它一定会失败?? ObReferenceObjectByHandle 只是 return return 指向对象主体的相应指针。 ETHREAD 你的情况。对象的状态——在这里没有任何作用。终止线程与否绝对无关。直到您拥有指向线程主体结构 (ETHREAD) 的句柄或引用指针 - 该对象将不会被释放。因此,如果 hThread 是有效句柄 - ObReferenceObjectByHandle 必须成功。

How to determine if thread terminated?

非常简单 - 只需等待它说通过 KeWaitForSingleObject,您已经完成了。因为线程对象本身是一种调度程序对象,当线程终止时,它设置为信号状态和 KeWaitForSingleObject return.

if (ptThreadObject)
{
    bStopThread = TRUE;

    KeWaitForSingleObject(
        ptThreadObject,
        Executive,
        KernelMode,
        FALSE,
        0);
    ObDereferenceObject(ptThreadObject);
}

注意 - 您必须将 Timeout 设置为 0 以无限期等待直到线程终止(调度程序对象设置为信号状态)。您也不需要将 ptThreadObject 转换为 PVOID - 它已经是指针。 (PVOID)ptThreadObject 不是错误,而是多余和不必要的代码。

Is there a way to check if the thread is terminated prematurely?

操作系统,我不明白你在过早下的意思。检查线程终止我们可以通过等待它。但过早地只有在您的代码上下文中才有意义。假设您可以通过 PsTerminateSystemThread 设置不同的线程退出状态,而不是(在线程终止后)通过 PsGetThreadExitStatus 获得此存在状态。如果线程仍然运行 PsGetThreadExitStatus return STATUS_PENDING。这个例程也可以部分地用于检查线程状态 - 如果它 return 任何不同于 STATUS_PENDING 的状态 - 线程被终止。但如果它 return STATUS_PENDING - 不清楚 - 或者线程仍然 运行,或者线程通过 PsTerminateSystemThread(STATUS_PENDING) 存在。当然使用 STATUS_PENDING 作为存在状态是个坏主意,永远不要使用。在这种情况下,您也可以使用 PsGetThreadExitStatus 确定线程状态 (running/terminated),但此例程不会等待。但是您的驱动程序逻辑需要 wait 当线程终止时,只有在此之后我们才能卸载驱动程序。所以这里只有 KeWaitForSingleObject (或另一个等待函数)是正确的解决方案。如果线程可以以不同的方式存在 - 在调用 PsTerminateSystemThread 中使用不同的退出状态,并在线程终止后通过 PsGetThreadExitStatus 取回它(所以在 KeWaitForSingleObject 之后)

但是调用 PsTerminateSystemThread 是可选的 - 你可以简单地从 ThreadRoutine return - 在这种情况下系统自己调用 PsTerminateSystemThread(STATUS_SUCCESS); - 所以在你的代码调用 PsTerminateSystemThread(STATUS_SUCCESS); 也是多余和不必要的代码。仅当您希望 return 状态不同于 STATUS_SUCCESScheck return 线程终止后的状态时,才需要调用 PsTerminateSystemThread。请注意 windows 本身不解释和使用线程退出状态。它只是将它存储在 ETHREAD 对象中。如果您不查询和使用此状态 - 它的退出状态是什么并不重要。

还有 liSleepTime.QuadPart = 1000 * RELATIVE_MILLISECOND; 行你可以从循环中退出 - 在循环之前设置,如果你使用 constant 超时。

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

所以定义全局变量:

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); 之后我们已经无法 return 驱动程序代码 - 它可能已经被卸载了。所以这个调用不能从 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;)和return从驱动程序卸载。