簡體   English   中英

Windows內核驅動程序:如何確定線程是否終止?

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

我有一個線程,對某些操作,它需要活着,直到一個標志說不然。

我使用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完成的,我可以添加一個'boolean'並在調用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成功而不會失敗? - 線程早已消失。

謝謝。

為什么ObReferenceObjectByHandle成功而不會失敗? - 線程早已消失。

但為什么它必須失敗? ObReferenceObjectByHandle只返回返回對象主體的相應指針。 ETHREAD在你的情況下。 對象的狀態 - 在這里不起任何作用。 終止線程或不絕對無關。 直到你有句柄或引用指向線程體結構( ETHREAD )的指針 - 該對象將不會被釋放。 所以,如果hThread是有效的句柄 - ObReferenceObjectByHandle必須成功。

如何判斷線程是否終止?

非常簡單 - 只需通過您已經完成的KeWaitForSingleObject等待它。 因為線程對象本身就是一種調度程序對象,當線程終止時,它設置為信號狀態並返回KeWaitForSingleObject

if (ptThreadObject)
{
    bStopThread = TRUE;

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

注意 - 必須將Timeout設置為0才能無限期等待,直到線程終止(調度程序對象設置為信號狀態)。 你也不需要將ptThreadObjectPVOID - 它已經是指針。 (PVOID)ptThreadObject不是錯誤,而是多余且不必要的代碼。

有沒有辦法檢查線程是否過早終止?

操作系統,我不明白你的意思是過早的 檢查線程終止,我們可以通過等待它。 但過早地只能在你的代碼的上下文中有意義。 假設您可以通過PsTerminateSystemThread設置不同的線程退出狀態,並且(在線程終止后)通過PsGetThreadExitStatus獲取此存在狀態。 如果線程仍在運行PsGetThreadExitStatus返回STATUS_PENDING 此例程也可以部分用於檢查線程狀態 - 如果它返回任何與STATUS_PENDING不同的狀態 - 線程終止。 但如果它返回STATUS_PENDING - 不清楚 - 或線程仍在運行,或者線程通過PsTerminateSystemThread(STATUS_PENDING)存在。 當然使用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;

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 ++線程例程,最后調用 ObfDereferenceObject jmp (但不調用 )。

將你的c / c ++ proc定義為

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

代碼為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

有了這個你不能等待工作線程退出 - 只是發信號給他終止( bStopThread = TRUE; )並從驅動程序卸載返回。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM