Навеяно темой про синхронизацию. Набросал вот несколько функций для более удобной реализации remove lock. Их нужно использовать в хуках. Поддерживается работа и при IRQL<2 и при IRQL>=2. Код: Код (Text): typedef struct REMOVELOCK { LONG Count; LONG UnloadInitiated; BOOLEAN HighIrql; KEVENT SynchEvent; } *PREMOVELOCK; #define InitializeRemoveLock(LOCK, HIGH) \ { \ (LOCK)->Count = 0; \ (LOCK)->UnloadInitiated = FALSE; \ (LOCK)->HighIrql = (HIGH); \ KeInitializeEvent (&(LOCK)->SynchEvent, SynchronizationEvent, FALSE); \ } #define AcquireRemoveLock(LOCK) InterlockedIncrement (&(LOCK)->Count) __declspec(naked) VOID ReleaseRemoveLock ( PREMOVELOCK Lock, BOOLEAN CallNext, BOOLEAN LeaveFromParent, ULONG sizeOfArguments OPTIONAL, PVOID Next OPTIONAL, ULONG_PTR ReturnValue OPTIONAL) { __asm { push ebp mov ebp, esp sub esp, __LOCAL_SIZE cli } // decrement lock count InterlockedDecrement (&Lock->Count); KdPrint(("Released remove lock: count %d, call next: %s, leave from parent: %s, sizeofargs: %d, next: %p, return: %08x\n", Lock->Count, CallNext ? "YES" : "NO", LeaveFromParent ? "YES" : "NO", sizeOfArguments, Next, ReturnValue)); // driver is being unloaded? if (Lock->Count == 0 && Lock->UnloadInitiated && Lock->HighIrql == FALSE) { // Raise synch event. __asm { push ebx mov cl, DISPATCH_LEVEL call dword ptr [KfRaiseIrql] mov ebx, eax sti } KeSetEvent (&Lock->SynchEvent, 0, FALSE); __asm { cli mov ecx, ebx pop ebx call dword ptr [KfLowerIrql] } } if (CallNext == TRUE) { // pass down __asm { mov ecx, [Next] mov al, [LeaveFromParent] leave add esp, 4 // return address test al, al jz _1 leave _1: push ecx sti retn } } else { // return __asm { mov al, [LeaveFromParent] mov ecx, [sizeOfArguments] test al, al jz _2 or ecx, 0x80000000 _2: mov eax, [ReturnValue] // leave from this function leave add esp, 4 // return address // leave from parent test ecx, 0x80000000 jz _3 leave _3: and ecx, 0xFFFF // we have to save return address and delete arguments from the stack // save eax push eax // stack layout: // esp+00 saved eax // esp+04 return from HookFunction // esp+08 argument1 // esp+0c argument2 // esp+10 argument3 mov eax, [esp+4] // push return address mov edx, esp add edx, ecx add edx, 4 mov [edx], eax // stack layout: // esp+00 saved eax // esp+04 return from HookFunction // esp+08 argument1 // esp+0c argument2 // esp+10 return from HookFunction pop eax // restore eax // stack layout: // esp+00 return from HookFunction // esp+04 argument1 // esp+08 argument2 // esp+0c return from HookFunction add esp, ecx // arguments // stack layout: // esp+00 return from HookFunction sti retn } } } VOID WaitForRemoveLock (PREMOVELOCK Lock) { InterlockedIncrement (&Lock->UnloadInitiated); for (ULONG Times = 10; Lock->Count > 0; Times --) { KdPrint(("WaitForRemoveLock: not ready (count %d, highirql: %s)\n", Lock->Count, Lock->HighIrql ? "YES" : "NO")); if (Lock->HighIrql) { LARGE_INTEGER Timeout = {-10000 * 100, -1}; KeDelayExecutionThread (KernelMode, FALSE, &Timeout); } else { LARGE_INTEGER Timeout = {-10000 * 1000, -1}; if (STATUS_TIMEOUT == KeWaitForSingleObject (&Lock->SynchEvent, Executive, KernelMode, FALSE, &Timeout)) { KdPrint(("WaitForRemoveLock: timed out, count %d\n", Lock->Count)); } } if (Times == 0) { Times = 10; KdPrint(("10 timeouts exceeded, break-in\n")); ASSERT (FALSE); } } } #define ReleaseRemoveLockCallNext(LOCK,LEAVEPARENT,NEXT) ReleaseRemoveLock ((LOCK), TRUE, (LEAVEPARENT), 0, (PVOID)(NEXT), 0) #define ReleaseRemoveLockReturn(LOCK,LEAVEPARENT,ARGS,RETURNVALUE) ReleaseRemoveLock((LOCK), FALSE, (LEAVEPARET), (ARGS), NULL, (ULONG_PTR)(RETURNVALUE)) Описание: 1. void InitializeRemoveLock (PREMOVELOCK Lock, BOOLEAN HighIrql); Инициализирует REMOVELOCK - Lock: указатель на REMOVELOCK - HighIrql: TRUE для работы на высоких IRQL, FALSE для работы на низких IRQL 2. void AcquireRemoveLock (PREMOVELOCK Lock); Захватывает REMOVELOCK - Lock: указатель на REMOVELOCK 3. void ReleaseRemoveLockCallNext (PREMOVELOCKLock[b/], BOOLEAN LeaveParent, PVOID OriginalFunction) Освобождает REMOVELOCK и передает управление в оригинальную функцию хука. - Lock: указатель на REMOVELOCK - LeaveParent: выполнять leave для функции хука или нет - OriginalFunction: указатель на оригинальную функцию, куда нужно передать управление 4. void ReleaseRemoveLockReturn (PREMOVELOCK Lock, BOOLEAN LeaveParent, int sizeOfArguments, ULONG_PTR ReturnValue); Освобождает REMOVELOCK и возвращает управление в функцию, вызвавшую похуканную функцию. - Lock: указатель на REMOVELOCK - LeaveParent: выполнять leave для функции хука или нет. - sizeOfArguments: суммарный размер аргументов для удаления из стека вызвавшей функции (stdcall) или 0 (cdecl) - ReturnValue: значение EAX, которое нужно вернуть в вызвавшую функцию. 5. void WaitForRemoveLock (PREMOVELOCK Lock); Подождать, пока выполнятся все хуки. Хук должен быть уже снят. - Lock: указатель на REMOVELOCK Использовать: 0. Переменная: REMOVELOCK Lock; 1. Инициализация (в DriverEntry): а) для работы при высоких IRQL: InitializeRemoveLock (&Lock, TRUE); б) для работы при низких IRQL: InitializeRemoveLock (&Lock, FALSE); 2. В хуках: Код (Text): PVOID OriginalFunction; // int (*OriginalFunction)(int argument); __declspec(naked) int HookFunction (int argument) { AcquireRemoveLock (&Lock); __asm push ebp; __asm mov ebp, esp; __asm sub esp, __LOCAL_SIZE; KdPrint("in HookFunction(argument = %d)\n", argument); // для возврата в вызвавшую функцию ReleaseRemoveLockReturn (&Lock, TRUE, // leave parent 4, // size of arguments 0x11223344 // EAX return value ); // для передачи управления в оригинальную функцию ReleaseRemoveLockCallNext (&Lock, TRUE, // leave parent OriginalFunction ); } 3. В DriverUnload: Код (Text): void DriverUnload(IN PDRIVER_OBJECT DriverObject) { KdPrint(("[~] DriverUnload()\n")); WaitForRemoveLock (&Lock); KdPrint(("Ready to unload\n")); } Реализовано в случае низких IRQL через атомарные инкремент/декремент Count и установку Event'а в случае выгрузки и освобождения всех хуков. WaitForRemoveLock ожидает на этом евенте. В случае высоких IRQL реализовано без евента. ReleaseRemoveLock просто делает декремент Count, а WaitForRemoveLock крутится в цикле, пока Count>0
Спасибо. Как раз в эту сторону смотрю, т.к. иногда при DriverUnload получаю BSOD (дампы ещё пока не научился корректно просматривать через WinDbg).
Great Заюзал твой кодес. Всё протестил - работает. Решил смогранить релизную версию и получил странную проблемс. Сам кодес: Код (Text): HANDLE GetProcessIdByHandle(HANDLE hProcess, PHANDLE ParentId) { NTSTATUS ntStatus; ULONG returnedLength; PROCESS_BASIC_INFORMATION pi; if(KeGetCurrentIrql() > PASSIVE_LEVEL) return NULL; ntStatus = ZwQueryInformationProcess( hProcess, ProcessBasicInformation, &pi, sizeof(pi), &returnedLength); if (NT_SUCCESS(ntStatus)) { if (ParentId) *ParentId = (HANDLE)pi.InheritedFromUniqueProcessId; return (HANDLE)pi.UniqueProcessId; } return NULL; } __declspec(naked) NTSTATUS NewNtCreateThread( OUT PHANDLE ThreadHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, IN HANDLE ProcessHandle, OUT PCLIENT_ID ClientId, IN PCONTEXT ThreadContext, IN PUSER_STACK UserStack, IN BOOLEAN CreateSuspended) { NTSTATUS ntStatus; BOOLEAN OutBack; HANDLE cpid; HANDLE opid; AcquireRemoveLock(&syscallLock); __asm push ebp; __asm mov ebp, esp; __asm sub esp, __LOCAL_SIZE; DbgPrint("[prot] in NewNtCreateThread\n"); OutBack = FALSE; ntStatus = STATUS_ACCESS_DENIED; cpid = PsGetCurrentProcessId(); if (ProcessHandle) opid = GetProcessIdByHandle(ProcessHandle, NULL); // <---- problem ReleaseRemoveLockCallNext(&syscallLock, TRUE, OldNtCreateThread); } Это хук NtCreateThread, который ничего фактически и не делает. Но вот при компиляции Release после запуска дрова не могу запустить exe файл (CreateProcess), т.к. перехват както некорректно работает (BSOD'ов нету как ни странно). Вот Debug кодес под IDA: Код (Text): NewNtCreateThread: mov eax, offset syscallLock mov ecx, 1 lock xadd [eax], ecx push ebp mov ebp, esp sub esp, 10h push offset aProtInNewntc ; "[prot] in NewNtCreateThread\n" call _DbgPrint add esp, 4 mov byte ptr [ebp-1], 0 mov dword ptr [ebp-0Ch], 0C0000022h call ds:__imp__PsGetCurrentProcessId@0 ; PsGetCurrentProcessId() mov [ebp-8], eax cmp dword ptr [ebp+14h], 0 jz short loc_10003FE9 push 0 mov edx, [ebp+14h] push edx call GetProcessIdByHandle mov [ebp-10h], eax loc_10003FE9: push 0 mov eax, OldNtCreateThread push eax push 0 push 1 push 1 push offset syscallLock call ReleaseRemoveLock Вот Release кодес под IDA: Код (Text): NewCreateThread: mov eax, offset dword_10005100 mov ecx, 1 lock xadd [eax], ecx push ebp mov ebp, esp sub esp, 18h push offset aProtInNewntc ; "[prot] in NewNtCreateThread\n" call DbgPrint add esp, 4 call ds:PsGetCurrentProcessId mov esi, [ebp+14h] test esi, esi jz short loc_1000295B call ds:KeGetCurrentIrql test al, al ja short loc_1000295B lea edx, [ebp+14h] push edx push 18h lea eax, [ebp-18h] push eax push 0 push esi call ds:ZwQueryInformationProcess loc_1000295B: mov ecx, dword_100050D8 push 0 push ecx push 0 push 1 push 1 push offset dword_10005100 call sub_100026A0 Неушто отпимизацию надо отключать в Release режиме? Или как то по другому можно решить эту проблемму?
Вот что значит вторую неделю кодить на С. Нифига не Delphi. Столько возможностей что ... Разгуглил эту проблему. #if !DBG #pragma optimize("", off) #endif // тут наш кодес #if !DBG #pragma optimize("", on) #endif
Great Без оптимизации NewHook* ф-ий всё нормально работает. Ты лучше глянь на asm листинги и скажи где там компилятор VS2005 облажался. Это для уверенности, что перехваченный код работает в PASSIVE_LEVEL. А что не так то?
Нужно точно знать, какие функции при каких IRQL выполняются. Допускать, что функция, которая декларирована работающей на PASSIVE_LEVEL, может быть вызвана при IRQL>PASSIVE - это значит допускать критическую ошибку проектирования своего кода. Предлагаю убрать это