![](/img/trans.png)
[英]Windows Kernel Driver: ZwAllocateVirtualMemory causing thread to terminate
[英]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才能無限期等待,直到線程終止(調度程序對象設置為信號狀態)。 你也不需要將ptThreadObject
為PVOID
- 它已經是指針。 (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 !!
}
x64和c的代碼( 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
代碼為x86和c ( 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;
)並從驅動程序卸載返回。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.