Ori*_*avi 5 c windows multithreading driver windows-kernel
我有一个线程用于某些操作,它需要保持活动状态,直到标志另有说明为止。
我使用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;
}
Run Code Online (Sandbox Code Playgroud)
线程例程:
LARGE_INTEGER liSleepTime;
while (FALSE == bThread)
{
liSleepTime.QuadPart = 1000 * RELATIVE_MILLISECOND;
KeDelayExecutionThread(KernelMode, FALSE, (&liSleepTime));
ExAcquireFastMutex(&fmMutex);
//DO SOMTHING
ExReleaseFastMutex(&fmMutex);
}
PsTerminateSystemThread(STATUS_SUCCESS);
Run Code Online (Sandbox Code Playgroud)
卸载驱动函数:
if (NULL != ptThreadObject)
{
bStopThread = TRUE;
KeWaitForSingleObject(
(PVOID)ptThreadObject,
Executive,
KernelMode,
FALSE,
(&liTimeOut));
ObDereferenceObject((PVOID)ptThreadObject);
ptThreadObject= NULL;
}
Run Code Online (Sandbox Code Playgroud)
我需要这个线程一直运行。
有没有办法检查线程是否提前终止?(如果它是由PsTerminateSystemThread完成的,我可以添加一个“布尔值”并在调用 PsTerminateSystemThread 终止线程之前设置它)。
还有一个问题:
我在它的例程开始时终止了线程,并在调用 ObReferenceObjectByHandle 之前等待了 20 秒,但它没有失败。
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;
}
Run Code Online (Sandbox Code Playgroud)
为什么 ObReferenceObjectByHandle 成功而不失败?- 线程早已不复存在。
谢谢。
为什么会
ObReferenceObjectByHandle成功,不会失败?- 线索早已消失。
但为什么它一定会失败?ObReferenceObjectByHandle只需 return 返回指向对象主体的相应指针。ETHREAD在你的情况下。对象的状态 - 在这里不发挥任何作用。终止线程或不是绝对无关的。直到您拥有指向线程体结构的句柄或引用指针 ( ETHREAD) - 该对象将不会被释放。因此,如果hThread句柄有效,则ObReferenceObjectByHandle必须成功。
如何判断线程是否终止?
非常简单 - 只需等待它说 via KeWaitForSingleObject,您已经完成了。因为线程对象本身是一种调度程序对象,所以当线程终止时,它设置为信号状态并KeWaitForSingleObject返回。
if (ptThreadObject)
{
bStopThread = TRUE;
KeWaitForSingleObject(
ptThreadObject,
Executive,
KernelMode,
FALSE,
0);
ObDereferenceObject(ptThreadObject);
}
Run Code Online (Sandbox Code Playgroud)
注意 - 您必须将Timeout设置为 0,以便无限期等待,直到线程终止(调度程序对象设置为有信号状态)。你也不需要强制转换ptThreadObject-PVOID它已经是指针了。(PVOID)ptThreadObject不是错误,而是多余和不必要的代码。
有没有办法检查线程是否提前终止?
操作系统和我不明白你的意思下过早。我们可以通过等待来检查线程是否终止。但过早地只能在您的代码上下文中有意义。假设您可以通过PsTerminateSystemThread和 设置不同的线程退出状态(在线程终止后)通过 获取此存在状态PsGetThreadExitStatus。如果线程仍在运行则PsGetThreadExitStatus返回STATUS_PENDING。该例程也可以部分用于检查线程状态 - 如果它返回与STATUS_PENDING线程终止不同的任何状态。但如果它返回STATUS_PENDING- 不清楚 - 或线程仍在运行,或线程通过PsTerminateSystemThread(STATUS_PENDING). 当然,STATUS_PENDING作为存在状态使用是个坏主意,绝对不能使用。在这种情况下,您也可以确定线程状态(运行/终止)PsGetThreadExitStatus,但此例程不等待。但是您的驱动程序逻辑需要在线程终止时等待,只有在此之后我们才能卸载驱动程序。所以只有KeWaitForSingleObject(或另一个等待函数)是这里的正确解决方案。如果线程可以以不同的方式存在 - 在调用中使用不同的退出状态并通过线程终止后将PsTerminateSystemThread其返回(因此 after )PsGetThreadExitStatusKeWaitForSingleObject
然而,调用PsTerminateSystemThread是可选的 - 您可以简单地返回ThreadRoutine- 在这种情况下系统您自己调用PsTerminateSystemThread(STATUS_SUCCESS);- 因此在您的代码中调用PsTerminateSystemThread(STATUS_SUCCESS);也是多余和不必要的代码。PsTerminateSystemThread仅当您希望返回状态不同于STATUS_SUCCESS线程终止后检查返回状态时才需要调用。请注意,Windows 本身不解释和使用线程退出状态。它只是将其存储在ETHREAD对象中。如果您不查询和使用此状态 - 它的退出状态是什么并不重要。
如果您使用恒定超时,您还liSleepTime.QuadPart = 1000 * RELATIVE_MILLISECOND;可以从循环中退出 - 在循环之前设置。
如果在 asm 代码中使用特殊的线程入口点,我们也根本不能在驱动程序卸载中等待线程终止。显然,在线程运行之前不得卸载该驱动程序。对于这个存在两种解决方案 - 一种是在驱动程序卸载例程中等待,以终止所有驱动程序线程。但存在另一个 - 当我们创建线程时添加对驱动程序对象的引用,并在线程退出时取消引用驱动程序对象。
所以定义全局变量:
PDRIVER_OBJECT gDriverObject;
Run Code Online (Sandbox Code Playgroud)
在DriverEntry初始化它:gDriverObject = DriverObject;
并以下面的方式启动线程:
ObfReferenceObject(gDriverObject);
NTSTATUS status = PsCreateSystemThread(
&hThread,
0, NULL,
0, NULL,
ThreadRoutine,
pThreadData
);
if (!NT_SUCCESS(status))
{
ObfDereferenceObject(gDriverObject);
}
Run Code Online (Sandbox Code Playgroud)
并在线程退出时需要调用ObfDereferenceObject(gDriverObject);。但之后ObfDereferenceObject(gDriverObject);我们已经无法返回驱动程序代码 - 它可能已经被卸载。所以这个调用不能从C/C++代码中完成。在用户模式下存在FreeLibraryAndExitThread,但在内核模式下没有类似的 api。唯一的解决方案 - 在 asm 代码中实现线程入口点 - 该入口调用c/c++线程例程,最后jmp(但不调用)到ObfDereferenceObject.
将您的c/c++过程定义为
void NTAPI _ThreadRoutine(PVOID pv)
{
// not call PsTerminateSystemThread here !!
}
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
这样你就不能等待工作线程退出 - 只需向他发出终止(bStopThread = TRUE;)信号并从驱动程序卸载中返回。
| 归档时间: |
|
| 查看次数: |
2522 次 |
| 最近记录: |