когда вы создаете поток, который использует ваш драйвер, драйвер, конечно, не должен быть выгружен, пока поток не завершится. для этого необходимо вызвать ObfReferenceObject
для вашего объекта драйвера перед созданием потока. если создать поток не удалось - вызовите ObfDereferenceObject
. и при выходе из потока - нужно вызвать ObfDereferenceObject
. но вот проблема - как/откуда это вызвать? вызывать ObfDereferenceObject
из конца подпрограммы потока нет смысла - драйвер можно выгрузить внутри ObfDereferenceObject
и мы возвращаемся из вызова в несуществующее место в памяти. в идеале будет, если внешний код (сама Windows) вызовет это сразу после возврата потока.
найдите IoAllocateWorkItem
для хорошего примера. рабочий элемент - как нить, и драйвер не должен быть выгружен, пока WorkerRoutine
не вернется. и здесь система заботится об этом - для этого мы передаем DeviceObject
в IoAllocateWorkItem
: Указатель на объект драйвера вызывающей стороны или на один из объектов устройства вызывающей стороны. - система ссылается на этот объект (устройство или драйвер), когда мы вызов IoQueueWorkItem
и это гарантия того, что драйвер не будет выгружен во время выполнения WorkerRoutine
. когда он возвращается - Windows вызывает ObfDereferenceObject
для переданного устройства или объекта драйвера. и здесь все ок, потому что после этого мы возвращаемся к коду ядра системы (а не к драйверу). но, к сожалению, PsCreateSystemThread
не берет указатель на объект драйвера и не реализует такой функционал.
еще один хороший пример FreeLibraryAndExitThread
- Драйвер по сути является dll режима ядра, которую можно загружать и выгружать. и FreeLibraryAndExitThread
точно реализуем нужный нам функционал, но только для dll пользовательского режима. опять нет такого 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
в 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
и вернуться в систему.
так что главный трюк в ассемблерных файлах. здесь 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
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
person
RbMm
schedule
05.09.2018