Синхронизация

Тема в разделе "WASM.NT.KERNEL", создана пользователем wasm_test, 10 янв 2010.

  1. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    Навеяно темой про синхронизацию.
    Набросал вот несколько функций для более удобной реализации remove lock.
    Их нужно использовать в хуках. Поддерживается работа и при IRQL<2 и при IRQL>=2.

    Код:
    Код (Text):
    1. typedef struct REMOVELOCK
    2. {
    3.     LONG Count;
    4.     LONG UnloadInitiated;
    5.     BOOLEAN HighIrql;
    6.     KEVENT SynchEvent;
    7. } *PREMOVELOCK;
    8.  
    9. #define InitializeRemoveLock(LOCK, HIGH) \
    10.     { \
    11.         (LOCK)->Count = 0; \
    12.         (LOCK)->UnloadInitiated = FALSE; \
    13.         (LOCK)->HighIrql = (HIGH); \
    14.         KeInitializeEvent (&(LOCK)->SynchEvent, SynchronizationEvent, FALSE); \
    15.     }
    16.  
    17. #define AcquireRemoveLock(LOCK) InterlockedIncrement (&(LOCK)->Count)
    18.  
    19. __declspec(naked)
    20. VOID ReleaseRemoveLock (
    21.     PREMOVELOCK Lock,
    22.     BOOLEAN CallNext,
    23.     BOOLEAN LeaveFromParent,
    24.     ULONG sizeOfArguments OPTIONAL,
    25.     PVOID Next OPTIONAL,
    26.     ULONG_PTR ReturnValue OPTIONAL)
    27. {
    28.     __asm
    29.     {
    30.         push ebp
    31.         mov ebp, esp
    32.         sub esp, __LOCAL_SIZE
    33.         cli
    34.     }
    35.  
    36.     // decrement lock count
    37.     InterlockedDecrement (&Lock->Count);
    38.  
    39.     KdPrint(("Released remove lock: count %d, call next: %s, leave from parent: %s, sizeofargs: %d, next: %p, return: %08x\n",
    40.         Lock->Count,
    41.         CallNext ? "YES" : "NO",
    42.         LeaveFromParent ? "YES" : "NO",
    43.         sizeOfArguments,
    44.         Next,
    45.         ReturnValue));
    46.  
    47.     // driver is being unloaded?
    48.  
    49.     if (Lock->Count == 0 && Lock->UnloadInitiated && Lock->HighIrql == FALSE)
    50.     {
    51.         // Raise synch event.
    52.         __asm
    53.         {
    54.             push ebx
    55.             mov cl, DISPATCH_LEVEL
    56.             call dword ptr [KfRaiseIrql]
    57.             mov ebx, eax
    58.             sti
    59.         }
    60.  
    61.         KeSetEvent (&Lock->SynchEvent, 0, FALSE);
    62.  
    63.         __asm
    64.         {
    65.             cli
    66.             mov ecx, ebx
    67.             pop ebx
    68.             call dword ptr [KfLowerIrql]
    69.         }
    70.     }
    71.  
    72.     if (CallNext == TRUE)
    73.     {
    74.         // pass down
    75.         __asm
    76.         {
    77.             mov ecx, [Next]
    78.             mov al, [LeaveFromParent]
    79.             leave
    80.             add esp, 4 // return address
    81.             test al, al
    82.             jz _1
    83.             leave
    84. _1:
    85.             push ecx
    86.             sti
    87.             retn
    88.         }
    89.     }
    90.     else
    91.     {
    92.         // return
    93.         __asm
    94.         {
    95.             mov al, [LeaveFromParent]
    96.             mov ecx, [sizeOfArguments]
    97.             test al, al
    98.             jz _2
    99.             or ecx, 0x80000000
    100. _2:
    101.             mov eax, [ReturnValue]
    102.  
    103.             // leave from this function
    104.             leave
    105.             add esp, 4 // return address
    106.  
    107.             // leave from parent
    108.             test ecx, 0x80000000
    109.             jz _3
    110.             leave
    111. _3:
    112.             and ecx, 0xFFFF
    113.  
    114.             // we have to save return address and delete arguments from the stack
    115.  
    116.             // save eax
    117.             push eax
    118.  
    119.             // stack layout:
    120.             //  esp+00    saved eax
    121.             //  esp+04    return from HookFunction
    122.             //  esp+08    argument1
    123.             //  esp+0c    argument2
    124.             //  esp+10    argument3
    125.  
    126.             mov eax, [esp+4]
    127.  
    128.             // push return address
    129.             mov edx, esp
    130.             add edx, ecx
    131.             add edx, 4
    132.             mov [edx], eax
    133.  
    134.             // stack layout:
    135.             //  esp+00    saved eax
    136.             //  esp+04    return from HookFunction
    137.             //  esp+08    argument1
    138.             //  esp+0c    argument2
    139.             //  esp+10    return from HookFunction
    140.  
    141.             pop eax // restore eax
    142.  
    143.             // stack layout:
    144.             //  esp+00    return from HookFunction
    145.             //  esp+04    argument1
    146.             //  esp+08    argument2
    147.             //  esp+0c    return from HookFunction
    148.  
    149.  
    150.             add esp, ecx // arguments
    151.  
    152.             // stack layout:
    153.             //  esp+00    return from HookFunction
    154.  
    155.             sti
    156.             retn
    157.         }
    158.     }
    159. }
    160.  
    161. VOID WaitForRemoveLock (PREMOVELOCK Lock)
    162. {
    163.     InterlockedIncrement (&Lock->UnloadInitiated);
    164.  
    165.     for (ULONG Times = 10; Lock->Count > 0; Times --)
    166.     {
    167.         KdPrint(("WaitForRemoveLock: not ready (count %d, highirql: %s)\n",
    168.             Lock->Count,
    169.             Lock->HighIrql ? "YES" : "NO"));
    170.  
    171.         if (Lock->HighIrql)
    172.         {
    173.             LARGE_INTEGER Timeout = {-10000 * 100, -1};
    174.             KeDelayExecutionThread (KernelMode, FALSE, &Timeout);
    175.         }
    176.         else
    177.         {
    178.             LARGE_INTEGER Timeout = {-10000 * 1000, -1};
    179.             if (STATUS_TIMEOUT == KeWaitForSingleObject (&Lock->SynchEvent, Executive, KernelMode, FALSE, &Timeout))
    180.             {
    181.                 KdPrint(("WaitForRemoveLock: timed out, count %d\n", Lock->Count));
    182.             }
    183.         }
    184.  
    185.         if (Times == 0)
    186.         {
    187.             Times = 10;
    188.             KdPrint(("10 timeouts exceeded, break-in\n"));
    189.             ASSERT (FALSE);
    190.         }
    191.     }
    192. }
    193.  
    194. #define ReleaseRemoveLockCallNext(LOCK,LEAVEPARENT,NEXT) ReleaseRemoveLock ((LOCK), TRUE, (LEAVEPARENT), 0, (PVOID)(NEXT), 0)
    195. #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):
    1. PVOID OriginalFunction; // int (*OriginalFunction)(int argument);
    2.  
    3. __declspec(naked) int HookFunction (int argument)
    4. {
    5.     AcquireRemoveLock (&Lock);
    6.  
    7.     __asm push ebp;
    8.     __asm mov ebp, esp;
    9.     __asm sub esp, __LOCAL_SIZE;
    10.  
    11.     KdPrint("in HookFunction(argument = %d)\n", argument);
    12.  
    13.     // для возврата в вызвавшую функцию
    14.     ReleaseRemoveLockReturn (&Lock,
    15.         TRUE, // leave parent
    16.         4, // size of arguments
    17.         0x11223344 // EAX return value
    18.         );
    19.  
    20.     // для передачи управления в оригинальную функцию
    21.     ReleaseRemoveLockCallNext (&Lock,
    22.         TRUE, // leave parent
    23.         OriginalFunction
    24.         );
    25. }
    3. В DriverUnload:
    Код (Text):
    1. void DriverUnload(IN PDRIVER_OBJECT DriverObject)
    2. {
    3.     KdPrint(("[~] DriverUnload()\n"));
    4.  
    5.     WaitForRemoveLock (&Lock);
    6.  
    7.     KdPrint(("Ready to unload\n"));
    8. }
    Реализовано в случае низких IRQL через атомарные инкремент/декремент Count и установку Event'а в случае выгрузки и освобождения всех хуков. WaitForRemoveLock ожидает на этом евенте.
    В случае высоких IRQL реализовано без евента. ReleaseRemoveLock просто делает декремент Count, а WaitForRemoveLock крутится в цикле, пока Count>0
     
  2. T800

    T800 Member

    Публикаций:
    0
    Регистрация:
    7 дек 2006
    Сообщения:
    293
    Адрес:
    Moscow
    Спасибо. Как раз в эту сторону смотрю, т.к. иногда при DriverUnload получаю BSOD (дампы ещё пока не научился корректно просматривать через WinDbg).
     
  3. T800

    T800 Member

    Публикаций:
    0
    Регистрация:
    7 дек 2006
    Сообщения:
    293
    Адрес:
    Moscow
    Great
    Заюзал твой кодес. Всё протестил - работает. Решил смогранить релизную версию и получил странную проблемс.
    Сам кодес:
    Код (Text):
    1. HANDLE GetProcessIdByHandle(HANDLE hProcess, PHANDLE ParentId)
    2. {
    3.     NTSTATUS         ntStatus;
    4.     ULONG            returnedLength;
    5.     PROCESS_BASIC_INFORMATION pi;
    6.        
    7.     if(KeGetCurrentIrql() > PASSIVE_LEVEL) return NULL;
    8.     ntStatus = ZwQueryInformationProcess( hProcess,
    9.                                           ProcessBasicInformation,
    10.                                           &pi,
    11.                                           sizeof(pi),
    12.                                           &returnedLength);
    13.     if (NT_SUCCESS(ntStatus)) {
    14.       if (ParentId) *ParentId = (HANDLE)pi.InheritedFromUniqueProcessId;
    15.       return (HANDLE)pi.UniqueProcessId;
    16.     }  
    17.     return NULL;
    18. }
    19.  
    20. __declspec(naked)
    21. NTSTATUS NewNtCreateThread(
    22.     OUT PHANDLE            ThreadHandle,
    23.     IN  ACCESS_MASK        DesiredAccess,
    24.     IN  POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
    25.     IN  HANDLE             ProcessHandle,
    26.     OUT PCLIENT_ID         ClientId,
    27.     IN  PCONTEXT           ThreadContext,
    28.     IN  PUSER_STACK        UserStack,
    29.     IN  BOOLEAN            CreateSuspended)
    30. {
    31.     NTSTATUS         ntStatus;
    32.     BOOLEAN          OutBack;
    33.     HANDLE           cpid;
    34.     HANDLE           opid;
    35.     AcquireRemoveLock(&syscallLock);
    36.     __asm push ebp;
    37.     __asm mov ebp, esp;
    38.     __asm sub esp, __LOCAL_SIZE;
    39.     DbgPrint("[prot] in NewNtCreateThread\n");
    40.     OutBack = FALSE;
    41.     ntStatus = STATUS_ACCESS_DENIED;
    42.     cpid = PsGetCurrentProcessId();
    43.     if (ProcessHandle) opid = GetProcessIdByHandle(ProcessHandle, NULL);   // <---- problem
    44.     ReleaseRemoveLockCallNext(&syscallLock, TRUE, OldNtCreateThread);
    45. }
    Это хук NtCreateThread, который ничего фактически и не делает.
    Но вот при компиляции Release после запуска дрова не могу запустить exe файл (CreateProcess), т.к. перехват както некорректно работает (BSOD'ов нету как ни странно).

    Вот Debug кодес под IDA:
    Код (Text):
    1. NewNtCreateThread:
    2.                  mov     eax, offset syscallLock
    3.                  mov     ecx, 1
    4.                  lock xadd [eax], ecx
    5.                  push    ebp
    6.                  mov     ebp, esp
    7.                  sub     esp, 10h
    8.                  push    offset aProtInNewntc ; "[prot] in NewNtCreateThread\n"
    9.                  call    _DbgPrint
    10.                  add     esp, 4
    11.                  mov     byte ptr [ebp-1], 0
    12.                  mov     dword ptr [ebp-0Ch], 0C0000022h
    13.                  call    ds:__imp__PsGetCurrentProcessId@0 ; PsGetCurrentProcessId()
    14.                  mov     [ebp-8], eax
    15.                  cmp     dword ptr [ebp+14h], 0
    16.                  jz      short loc_10003FE9
    17.                  push    0
    18.                  mov     edx, [ebp+14h]
    19.                  push    edx
    20.                  call    GetProcessIdByHandle
    21.                  mov     [ebp-10h], eax
    22.  loc_10003FE9:
    23.                  push    0
    24.                  mov     eax, OldNtCreateThread
    25.                  push    eax
    26.                  push    0
    27.                  push    1
    28.                  push    1
    29.                  push    offset syscallLock
    30.                  call    ReleaseRemoveLock
    Вот Release кодес под IDA:
    Код (Text):
    1.  NewCreateThread:
    2.                  mov     eax, offset dword_10005100
    3.                  mov     ecx, 1
    4.                  lock xadd [eax], ecx
    5.                  push    ebp
    6.                  mov     ebp, esp
    7.                  sub     esp, 18h
    8.                  push    offset aProtInNewntc ; "[prot] in NewNtCreateThread\n"
    9.                  call    DbgPrint
    10.                  add     esp, 4
    11.                  call    ds:PsGetCurrentProcessId
    12.                  mov     esi, [ebp+14h]
    13.                  test    esi, esi
    14.                  jz      short loc_1000295B
    15.                  call    ds:KeGetCurrentIrql
    16.                  test    al, al
    17.                  ja      short loc_1000295B
    18.                  lea     edx, [ebp+14h]
    19.                  push    edx
    20.                  push    18h
    21.                  lea     eax, [ebp-18h]
    22.                  push    eax
    23.                  push    0
    24.                  push    esi
    25.                  call    ds:ZwQueryInformationProcess
    26.  loc_1000295B:
    27.                  mov     ecx, dword_100050D8
    28.                  push    0
    29.                  push    ecx
    30.                  push    0
    31.                  push    1
    32.                  push    1
    33.                  push    offset dword_10005100
    34.                  call    sub_100026A0
    Неушто отпимизацию надо отключать в Release режиме? Или как то по другому можно решить эту проблемму?
     
  4. T800

    T800 Member

    Публикаций:
    0
    Регистрация:
    7 дек 2006
    Сообщения:
    293
    Адрес:
    Moscow
    Вот что значит вторую неделю кодить на С. Нифига не Delphi. Столько возможностей что ...
    Разгуглил эту проблему.
    #if !DBG
    #pragma optimize("", off)
    #endif
    // тут наш кодес
    #if !DBG
    #pragma optimize("", on)
    #endif
     
  5. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    T800
    Так я не понял, у тебя заработало?

    Что это за кошмарик?
     
  6. T800

    T800 Member

    Публикаций:
    0
    Регистрация:
    7 дек 2006
    Сообщения:
    293
    Адрес:
    Moscow
    Great
    Без оптимизации NewHook* ф-ий всё нормально работает.
    Ты лучше глянь на asm листинги и скажи где там компилятор VS2005 облажался.

    Это для уверенности, что перехваченный код работает в PASSIVE_LEVEL. А что не так то?
     
  7. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    Нужно точно знать, какие функции при каких IRQL выполняются. Допускать, что функция, которая декларирована работающей на PASSIVE_LEVEL, может быть вызвана при IRQL>PASSIVE - это значит допускать критическую ошибку проектирования своего кода. Предлагаю убрать это
     
  8. AntiFreeze

    AntiFreeze Дмитрий

    Публикаций:
    0
    Регистрация:
    26 июн 2008
    Сообщения:
    65
    Для таких случаев есть чудесные отладочные макросы, типа ASSERT, PAGED_CODE и т.п.
     
  9. n0name

    n0name New Member

    Публикаций:
    0
    Регистрация:
    5 июн 2004
    Сообщения:
    4.336
    Адрес:
    Russia
    да и префаст ловит, если верно настроить.