简体   繁体   English

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

[英]Windows Kernel Driver: How to determine if thread terminated?

I have a thread that to some operations and it needs to be alive until a flag says otherwise. 我有一个线程,对某些操作,它需要活着,直到一个标志说不然。

I use 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 . 我使用PsCreateSystemThread创建线程,然后使用ObReferenceObjectByHandle获取ETHREAD对象引用,等待线程在使用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;
}

I need this thread to run all the time. 我需要这个线程一直运行。

Is there a way to check if the thread is terminated prematurely? 有没有办法检查线程是否过早终止? (If it was done by PsTerminateSystemThread I can add a 'boolean' and set it before calling PsTerminateSystemThread to terminate the thread). (如果它是由PsTerminateSystemThread完成的,我可以添加一个'boolean'并在调用PsTerminateSystemThread来终止线程之前设置它)。

One more question: 还有一个问题:

I terminated the thread at the start of its routine and waited for 20 seconds before calling ObReferenceObjectByHandle and it didn't fail. 我在程序开始时终止了线程并等待了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;
}

Why does ObReferenceObjectByHandle succeed and not fail? 为什么ObReferenceObjectByHandle成功而不会失败? -The thread is long gone. - 线程早已消失。

Thanks. 谢谢。

Why does ObReferenceObjectByHandle succeed and not fail? 为什么ObReferenceObjectByHandle成功而不会失败? -The thread is long gone. - 线程早已消失。

but why it must fail ?? 但为什么它必须失败? ObReferenceObjectByHandle simply return returns the corresponding pointer to the object's body. ObReferenceObjectByHandle只返回返回对象主体的相应指针。 ETHREAD in your case. ETHREAD在你的情况下。 the state of object - not play any role here. 对象的状态 - 在这里不起任何作用。 terminated thread or not absolute unrelated. 终止线程或不绝对无关。 until you have handles or referenced pointers to thread body structure ( ETHREAD ) - the object will be not freed. 直到你有句柄或引用指向线程体结构( ETHREAD )的指针 - 该对象将不会被释放。 so, if hThread is valid handle - ObReferenceObjectByHandle must succeed. 所以,如果hThread是有效的句柄 - ObReferenceObjectByHandle必须成功。

How to determine if thread terminated? 如何判断线程是否终止?

very easy - simply wait on it say via KeWaitForSingleObject , which you already done. 非常简单 - 只需通过您已经完成的KeWaitForSingleObject等待它。 because thread objects are themselves a type of dispatcher object, when thread terminated, it set to signal state and KeWaitForSingleObject return. 因为线程对象本身就是一种调度程序对象,当线程终止时,它设置为信号状态并返回KeWaitForSingleObject

if (ptThreadObject)
{
    bStopThread = TRUE;

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

note - you must set Timeout to 0 for waits indefinitely until the thread terminated (dispatcher object is set to the signaled state). 注意 - 必须将Timeout设置为0才能无限期等待,直到线程终止(调度程序对象设置为信号状态)。 also you not need cast ptThreadObject to PVOID - it is already pointer. 你也不需要将ptThreadObjectPVOID - 它已经是指针。 (PVOID)ptThreadObject is not error, but superfluous and unnecessary code. (PVOID)ptThreadObject不是错误,而是多余且不必要的代码。

Is there a way to check if the thread is terminated prematurely ? 有没有办法检查线程是否过早终止?

the operation system and i not understand what you mean under prematurely . 操作系统,我不明白你的意思是过早的 check that thread terminated we can via wait on it. 检查线程终止,我们可以通过等待它。 but prematurely can have sense only in context of your code. 但过早地只能在你的代码的上下文中有意义。 say you can set different thread exit status via PsTerminateSystemThread and than (after thread terminated) get this exist status via PsGetThreadExitStatus . 假设您可以通过PsTerminateSystemThread设置不同的线程退出状态,并且(在线程终止后)通过PsGetThreadExitStatus获取此存在状态。 if thread still running the PsGetThreadExitStatus return STATUS_PENDING . 如果线程仍在运行PsGetThreadExitStatus返回STATUS_PENDING this routine can be partially used to check thread state too - if it return any status different from STATUS_PENDING - thread is terminated. 此例程也可以部分用于检查线程状态 - 如果它返回任何与STATUS_PENDING不同的状态 - 线程终止。 but if it return STATUS_PENDING - unclear - or thread still running, or thread exist via PsTerminateSystemThread(STATUS_PENDING) . 但如果它返回STATUS_PENDING - 不清楚 - 或线程仍在运行,或者线程通过PsTerminateSystemThread(STATUS_PENDING)存在。 of course use STATUS_PENDING as exist status is bad idea and never must be used. 当然使用STATUS_PENDING作为存在状态是坏主意,永远STATUS_PENDING使用。 under this condition you can determine thread state (running/terminated) with PsGetThreadExitStatus too, but this routine not wait. 在这种情况下,您也可以使用PsGetThreadExitStatus确定线程状态(运行/终止),但此例程不等待。 but your driver logic require wait when thread terminated, and only after this we can unload driver. 但是你的驱动程序逻辑需要在线程终止时等待 ,并且只有在此之后我们才能卸载驱动程序。 so only KeWaitForSingleObject (or another wait function) is correct solution here. 所以只有KeWaitForSingleObject (或其他等待函数)才是正确的解决方案。 if thread can exist in different ways - use different exit status in call PsTerminateSystemThread and get it back via PsGetThreadExitStatus after thread terminated (so after KeWaitForSingleObject ) 如果线程可以以不同的方式存在 - 在调用PsTerminateSystemThread使用不同的退出状态,并在线程终止后通过PsGetThreadExitStatus返回(因此在KeWaitForSingleObject之后)

however call to PsTerminateSystemThread is optional - you can simply return from ThreadRoutine - in this case system yourself call PsTerminateSystemThread(STATUS_SUCCESS); 但是对PsTerminateSystemThread调用是可选的 - 你可以简单地从ThreadRoutine返回 - 在这种情况下系统自己调用PsTerminateSystemThread(STATUS_SUCCESS); - so in your code call PsTerminateSystemThread(STATUS_SUCCESS); - 所以在你的代码中调用PsTerminateSystemThread(STATUS_SUCCESS); also superfluous and unnecessary code. 也是多余的和不必要的代码。 you need call PsTerminateSystemThread only in case you want return status different from STATUS_SUCCESS and check return status after thread terminated. 您需要调用PsTerminateSystemThread ,以防您希望返回状态与STATUS_SUCCESS不同,并在线程终止后检查返回状态。 note that windows itself not interpret and use thread exit status. 请注意,Windows本身不会解释并使用线程退出状态。 it simply store it in ETHREAD object. 它只是将它存储在ETHREAD对象中。 if you not query and use this status - does not matter what its exit status. 如果您不查询并使用此状态 - 无论退出状态如何。

also liSleepTime.QuadPart = 1000 * RELATIVE_MILLISECOND; 还有liSleepTime.QuadPart = 1000 * RELATIVE_MILLISECOND; line you can out from loop - set before loop, if you use constant timeout. 如果你使用常量超时,你可以从循环中设置 - 在循环之前设置。

also we can not wait for thread termination in driver unload at all, if use special thread entry point in asm code. 如果在asm代码中使用特殊的线程入口点,我们也不能等待驱动程序卸载中的线程终止。 obviously that driver must not be unloaded, until thread is running. 很明显,在线程运行之前,不能卸载驱动程序。 for this exist 2 solution - one is wait in driver unload routine, for all drivers thread termination. 为此存在2个解决方案 - 一个是在驱动程序卸载例程中等待,对于所有驱动程序线程终止。 but exist and another - add reference to driver object, when we create thread, and de-reference driver object, when thread exit. 但存在和另一个 - 当我们创建线程时,添加对驱动程序对象的引用,并在线程退出时取消引用驱动程序对象。

so define global variable: 所以定义全局变量:

PDRIVER_OBJECT gDriverObject;

in DriverEntry initialize it: gDriverObject = DriverObject; DriverEntry初始化它: gDriverObject = DriverObject;

and start thread in next way: 并以下一个方式启动线程:

ObfReferenceObject(gDriverObject);

NTSTATUS status = PsCreateSystemThread(
    &hThread,
    0, NULL,
    0, NULL,
    ThreadRoutine,
    pThreadData
    );
if (!NT_SUCCESS(status))
{
    ObfDereferenceObject(gDriverObject);
}

and on thread exit need call ObfDereferenceObject(gDriverObject); 并且在线程退出时需要调用ObfDereferenceObject(gDriverObject); . but after ObfDereferenceObject(gDriverObject); 但是在ObfDereferenceObject(gDriverObject); we already can not return to driver code - it can be already unloaded. 我们已经无法返回驱动程序代码 - 它已经可以卸载了。 so this call can not be done from c/c++ code. 所以这个调用不能用c / c ++代码完成。 in user mode exist FreeLibraryAndExitThread , but in kernel mode no analog of this api. 在用户模式下存在FreeLibraryAndExitThread ,但在内核模式下没有这个api的模拟。 only solution - implement thread entry point in asm code - this entry call c/c++ thread routine and finally jmp (but not call ) to ObfDereferenceObject . 唯一的解决方案 - 在asm代码中实现线程入口点 - 此条目调用c / c ++线程例程,最后调用 ObfDereferenceObject jmp (但不调用 )。

define your c/c++ proc as 将你的c / c ++ proc定义为

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

code for x64 and c ( ml64 /c /Cp $(InputFileName) -> $(InputName).obj ) 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

code for x86 and c ( ml /c /Cp $(InputFileName) -> $(InputName).obj ) 代码为x86cml /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

with this you can not wait fot working thread exit - simply signal to him terminate ( bStopThread = TRUE; ) and return from driver unload. 有了这个你不能等待工作线程退出 - 只是发信号给他终止( bStopThread = TRUE; )并从驱动程序卸载返回。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM