Когда вы создаете поток, который использовал ваш драйвер, драйвер, конечно, не должен выгружаться, пока поток не выйдет. Для этого необходимо вызвать ObfReferenceObject
для вашего объекта драйвера, прежде чем создавать поток. если создать поток не удалось - позвоните ObfDereferenceObject
. а при выходе из нити - нужно позвонить ObfDereferenceObject
. но вот проблема - как / откуда это назвать? Вызывать ObfDereferenceObject
с конца подпрограммы потока не имеет смысла - драйвер может быть выгружен внутри ObfDereferenceObject
, и мы возвращаемся из вызова в несуществующее место в памяти. в идеале это будет, если внешний код (сама Windows) вызовет это, сразу после возврата потока.
ищите IoAllocateWorkItem
для хорошего примера. рабочий элемент - как нить, и драйвер не должен выгружаться, пока WorkerRoutine
не вернется. и здесь система заботится об этом - для этого мы передаем DeviceObject
IoAllocateWorkItem
: Указатель на объект драйвера вызывающего или на один из объектов устройства вызывающего. - система ссылается на этот объект (устройство или драйвер) ) когда мы вызываем IoQueueWorkItem
, и это является гарантией того, что драйвер не будет выгружен во время выполнения WorkerRoutine
. когда он возвращается - Windows вызывает ObfDereferenceObject
для переданного объекта устройства или драйвера. и здесь все в порядке, потому что после этого мы возвращаемся к коду ядра системы (не к драйверу). но, к сожалению, PsCreateSystemThread
не берет указатель на объект драйвера и не реализует такой функционал.
еще один хороший пример FreeLibraryAndExitThread
- драйвер по сути является режимом ядра dll, который можно загружать и выгружать. и FreeLibraryAndExitThread
точно реализуют функционал, который нам нужен, но только для пользовательских режимов. опять нет такого API в режиме ядра.
но в любом случае решение возможно. Вы можете перейти (не вызывать) к ObfDereferenceObject
в конце выполнения потока, но для этого нужно использовать ассемблерный код. невозможно сделать этот трюк в c / c ++ .
прежде всего позвольте объявить указатель на объект драйвера в глобальной переменной - мы инициализируем его в допустимое значение в точке входа драйвера.
extern "C" PVOID g_DriverObject;
чем некоторые макросы для искаженных c ++ имен, это необходимо использовать в файле asm:
#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 ++ мы объявляем 2 функции для потока:
VOID _AThread(IN PVOID Context)_ASM_FUNCTION;
VOID __fastcall AThread(IN PVOID Context)
{
CPP_FUNCTION;
// some code here
// but not call PsTerminateSystemThread !!
}
( не забудьте __fastcall
на AThread
- для x86 это нужно )
теперь мы создаем тему со следующим кодом:
ObfReferenceObject(g_DriverObject);
HANDLE hThread;
if (0 > PsCreateSystemThread(&hThread, 0, 0, 0, 0, _AThread, ctx))
{
ObfDereferenceObject(g_DriverObject);
}
else
{
NtClose(hThread);
}
поэтому вы устанавливаете точку входа потока в _AThread
, которая будет реализована в файле asm . в начале вы звоните ObfReferenceObject(g_DriverObject);
. _AThread
назовет вас фактической реализацией потока AThread
в c ++ . наконец, он возвращается обратно к _AThread
(потому что это не должно вызывать PsTerminateSystemThread
. В любом случае вызывать этот API вообще необязательно - когда подпрограмма потока возвращает управление системе - это будет вызываться автоматически). и _AThread
в конце разыменования g_DriverObject
и возврата в систему.
так что главный трюк в файлах asm. здесь 2 asm для x86 и 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
64
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