when you create thread which used your driver, the driver of course must not be unloaded, until thread not exit. for do this need call ObfReferenceObject
for your driver object, before create thread. if create thread fail - call ObfDereferenceObject
. and when thread exit - need call ObfDereferenceObject
. but here is problem - how / from where call this ? call ObfDereferenceObject
from the end of thread routine no sense - the driver can be unloaded inside ObfDereferenceObject
and we return from call to not existing memory place. ideally will be if external code (windows itself) call this, just after thread return.
look for IoAllocateWorkItem
for good example. work item - like thread, and driver must not be unloaded, until WorkerRoutine
not return. and here system care about this - for this we pass DeviceObject
to IoAllocateWorkItem
: Pointer to the caller's driver object or to one of the caller's device objects. - the system reference this object (device or driver) when we call IoQueueWorkItem
and this is guarantee that driver will be not unloaded during WorkerRoutine
execution. when it return - windows call ObfDereferenceObject
for passed device or driver object. and here all ok, because we return to system kernel code (not to driver) after this. but unfortunately PsCreateSystemThread
not take pointer to driver object and not implement such functional.
another good example FreeLibraryAndExitThread
- the driver is kernel mode dll by fact, which can be loaded and unloaded. and FreeLibraryAndExitThread
exactly implement functional which we need, but for user mode dlls only. again no such api in kernel mode.
but anyway solution is possible. possible yourself jump (not call) to ObfDereferenceObject
at the end of thread execution, but for this need use assembler code. not possible do this trick in c/c++.
first of all let declare pointer to driver object in global variable - we initialize it to valid value in driver entry point.
extern "C" PVOID g_DriverObject;
than some macros for get mangled c++ names, this need for use it in asm file:
#if 0
#define __ASM_FUNCTION __pragma(message(__FUNCDNAME__" proc\r\n" __FUNCDNAME__ " endp"))
#define _ASM_FUNCTION {__ASM_FUNCTION;}
#define ASM_FUNCTION {__ASM_FUNCTION;return 0;}
#define CPP_FUNCTION __pragma(message("extern " __FUNCDNAME__ " : PROC ; " __FUNCSIG__))
#else
#define _ASM_FUNCTION
#define ASM_FUNCTION
#define CPP_FUNCTION
#endif
in c++ we declare 2 functions for thread:
VOID _AThread(IN PVOID Context)_ASM_FUNCTION;
VOID __fastcall AThread(IN PVOID Context)
{
CPP_FUNCTION;
// some code here
// but not call PsTerminateSystemThread !!
}
(don't forget __fastcall
on AThread
- for x86 this need)
now we create thread with next code:
ObfReferenceObject(g_DriverObject);
HANDLE hThread;
if (0 > PsCreateSystemThread(&hThread, 0, 0, 0, 0, _AThread, ctx))
{
ObfDereferenceObject(g_DriverObject);
}
else
{
NtClose(hThread);
}
so you set thread entry point to _AThread
which will be implemented in asm file. at begin you call ObfReferenceObject(g_DriverObject);
. the _AThread
will call you actual thread implementation AThread
in c++. finally it return back to _AThread
(because this you must not call PsTerminateSystemThread
. anyway call this api is optional at all - when thread routine return control to system - this will be auto called). and _AThread
at the end de-reference g_DriverObject
and return to system.
so main trick in asm files. here 2 asm for x86 and x64:
x86:
.686p
extern _g_DriverObject:DWORD
extern __imp_@ObfDereferenceObject@4:DWORD
extern ?AThread@@YIXPAX@Z : PROC ; void __fastcall AThread(void *)
_TEXT segment
?_AThread@@YGXPAX@Z proc
pop ecx
xchg ecx,[esp]
call ?AThread@@YIXPAX@Z
mov ecx,_g_DriverObject
jmp __imp_@ObfDereferenceObject@4
?_AThread@@YGXPAX@Z endp
_TEXT ends
END
x64:
extern g_DriverObject:QWORD
extern __imp_ObfDereferenceObject:QWORD
extern ?AThread@@YAXPEAX@Z : PROC ; void __cdecl AThread(void *)
_TEXT segment 'CODE'
?_AThread@@YAXPEAX@Z proc
sub rsp,28h
call ?AThread@@YAXPEAX@Z
add rsp,28h
mov rcx,g_DriverObject
jmp __imp_ObfDereferenceObject
?_AThread@@YAXPEAX@Z endp
_TEXT ENDS
END