PsSetLoadImageNotifyRoutine и выделение памяти

Тема в разделе "WASM.NT.KERNEL", создана пользователем XshStasX, 15 окт 2010.

  1. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    XshStasX
    Нет, апк механизм асинхронный и доставляется она при обработке прерываний(точнее при свопинге). А момент прихода железячного прерывания рандомный. Вот и будет доставлена апк когда не ждали.
     
  2. XshStasX

    XshStasX New Member

    Публикаций:
    0
    Регистрация:
    9 авг 2008
    Сообщения:
    991
    Clerk
    ясно, почитал тут: http://www.wasm.ru/article.php?article=scheduler

    Вопрос такой, а как лочится в ZwAllocateVirtualMemory ? и когда этот лок будет снят ??

    Сразу после выхода из проц. обработчика PsSetLoadImageNotifyRoutine ?...
     
  3. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    XshStasX
    Это макро LOCK_ADDRESS_SPACE:
    Код (Text):
    1. #define LOCK_ADDRESS_SPACE(PROCESS)                                  \
    2.             KeAcquireGuardedMutex (&((PROCESS)->AddressCreationLock));
    3.  
    4. #define LOCK_WS_AND_ADDRESS_SPACE(THREAD, PROCESS)                  \
    5.         LOCK_ADDRESS_SPACE(PROCESS);                                \
    6.         LOCK_WS_UNSAFE(THREAD, PROCESS)
    7.  
    8. #define UNLOCK_WS_AND_ADDRESS_SPACE(THREAD, PROCESS)                \
    9.         UNLOCK_WS_UNSAFE(THREAD, PROCESS);                          \
    10.         UNLOCK_ADDRESS_SPACE(PROCESS);
    11.  
    12. #define UNLOCK_ADDRESS_SPACE(PROCESS)                               \
    13.             KeReleaseGuardedMutex (&((PROCESS)->AddressCreationLock));
    Не имеет значения как выполняется синхронизация, достоп к ресурсу должен быть открыт после окончания его инициализации, поэтому нет смысла вручную освобождать мьютекс.
     
  4. XshStasX

    XshStasX New Member

    Публикаций:
    0
    Регистрация:
    9 авг 2008
    Сообщения:
    991
    Clerk
    хм, все равно не пойму тоесть доступ к памяти закрыт пока мы находимся в обработчике PsSetLoadImageNotifyRoutine ?

    Просто не понятно почему через АРС можно получить память а так нет..
     
  5. x64

    x64 New Member

    Публикаций:
    0
    Регистрация:
    29 июл 2008
    Сообщения:
    1.370
    Адрес:
    Россия
    Да.

    APC вызовется после выхода из сервиса, т.е. блокировка уже будет освобождена.
     
  6. XshStasX

    XshStasX New Member

    Публикаций:
    0
    Регистрация:
    9 авг 2008
    Сообщения:
    991
    сервис это процедурка на которую переходит код из юзер мода в кернел мод ?
     
  7. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    x64
    Поправка: пользовательская апк доставляется при выходе из сервиса, тоесть в юзермоде.

    XshStasX
    Ресурс освобождается при снижении SFN.
     
  8. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    XshStasX
    Код (Text):
    1. ;   Check for pending APC interrupts, if found, dispatch to them
    2. ;   (saving eax in frame first).
    3. ;
    4.         public  _KiServiceExit
    5. _KiServiceExit:
    6.  
    7.         cli                                         ; disable interrupts
    8.         DISPATCH_USER_APC   ebp, ReturnCurrentEax
    9.  
    10. ;
    11. ; Exit from SystemService
    12. ;
    13.  
    14.         EXIT_ALL    NoRestoreSegs, NoRestoreVolatile
    15.  
    16. ;
    17. ; The address of the argument list is not a user address. If the previous mode
    18. ; is user, then return an access violation as the status of the system service.
    19. ; Otherwise, copy the argument list and execute the system service.
    20. ;
    21.  
    22. kss80:  test    byte ptr [ebp].TsSegCs, MODE_MASK ; test previous mode
    23.         jz      KiSystemServiceCopyArguments ; if z, previous mode kernel
    24.         mov     eax, STATUS_ACCESS_VIOLATION ; set service status
    25.         jmp     kss60                   ;
    26.  
    27. ;++
    28. ;
    29. ;   _KiServiceExit2 - same as _KiServiceExit BUT the full trap_frame
    30. ;       context is restored
    31. ;
    32. ;--
    33.         public  _KiServiceExit2
    34. _KiServiceExit2:
    35.  
    36.         cli                             ; disable interrupts
    37.         DISPATCH_USER_APC   ebp
    38.  
    39. ;
    40. ; Exit from SystemService
    41. ;
    42.  
    43.         EXIT_ALL                            ; RestoreAll
    Код (Text):
    1. ;++
    2. ;
    3. ;   Kei386EoiHelper
    4. ;
    5. ;   Routine Description:
    6. ;
    7. ;       This code is transferred to at the end of an interrupt.  (via the
    8. ;       exit_interrupt macro).  It checks for user APC dispatching and
    9. ;       performs the exit_all for the interrupt.
    10. ;
    11. ;       NOTE: This code is jumped to, not called.
    12. ;
    13. ;   Arguments:
    14. ;
    15. ;       (esp) -> base of trap frame.
    16. ;       interrupts are disabled
    17. ;
    18. ;   Return Value:
    19. ;
    20. ;       None.
    21. ;
    22. ;--
    23.  
    24. align 4
    25.         public  _KiExceptionExit
    26. cPublicProc Kei386EoiHelper, 0
    27. _KiExceptionExit:
    28. .FPO (0, 0, 0, 0, 0, FPO_TRAPFRAME)
    29.  
    30.         cli                             ; disable interrupts
    31.         DISPATCH_USER_APC   ebp
    32.  
    33. ;
    34. ; Exit from Exception
    35. ;
    36.  
    37. kee99:
    38.  
    39.         EXIT_ALL       ,,NoPreviousMode
    40.  
    41. stdENDP Kei386EoiHelper
    Код (Text):
    1.         subttl "Macro to dispatch user APC"
    2.  
    3. ;++
    4. ;
    5. ; Macro Description:
    6. ;
    7. ;    This macro is called before returning to user mode.  It dispatches
    8. ;    any pending user mode APCs.
     
  9. x64

    x64 New Member

    Публикаций:
    0
    Регистрация:
    29 июл 2008
    Сообщения:
    1.370
    Адрес:
    Россия
    Ну да, можно и так сказать.
     
  10. XshStasX

    XshStasX New Member

    Публикаций:
    0
    Регистрация:
    9 авг 2008
    Сообщения:
    991
    Код (Text):
    1. BOOLEAN test()
    2. {
    3.    
    4.  
    5.     NTSTATUS        stat;
    6.     LARGE_INTEGER   interval = {0};
    7.     PULONG          p        = 0;
    8.  
    9.    
    10.     PKAPC  startApc  =(PKAPC) ExAllocatePool(PagedPool,sizeof(KAPC));
    11.  
    12.     if ( ! startApc )
    13.         return FALSE;
    14.  
    15.     KeInitializeApc(    startApc,
    16.                             KeGetCurrentThread(),
    17.                             OriginalApcEnvironment,
    18.                             &KKERNEL,
    19.                             &KRUNDOWN,
    20.                             (PKNORMAL_ROUTINE)loadDllThunkRoutine,
    21.                             UserMode,
    22.                             (PVOID)0x1234567
    23.                         );
    24.  
    25.    
    26.  
    27.     stat = KeInsertQueueApc( startApc,
    28.                              NULL,
    29.                              NULL,
    30.                              1);
    31.  
    32.     if (!NT_SUCCESS(stat))
    33.         return FALSE;
    34.    
    35.     stat = KeDelayExecutionThread(UserMode,TRUE,&interval);
    36.        
    37.     if (!NT_SUCCESS(stat))
    38.         return FALSE;
    39.  
    40.  
    41. return TRUE;
    42. };
    43.  
    44.  
    45. VOID
    46. loadDllThunkRoutine (
    47.     __in_opt PVOID NormalContext,
    48.     __in_opt PVOID SystemArgument1,
    49.     __in_opt PVOID SystemArgument2
    50.     )
    51. {
    52. DbgPrint("loadDllThunkRoutine\n");
    53.     INT3;      
    54. };
    55. VOID
    56. KRUNDOWN (
    57.     __in struct _KAPC *Apc
    58.     )
    59. {
    60.     DbgPrint("KRUNDOWN\n");
    61.     INT3;
    62. };
    63.  
    64. VOID
    65. KKERNEL (
    66.     __in struct _KAPC *Apc,
    67.     __deref_inout_opt PKNORMAL_ROUTINE *NormalRoutine,
    68.     __deref_inout_opt PVOID *NormalContext,
    69.     __deref_inout_opt PVOID *SystemArgument1,
    70.     __deref_inout_opt PVOID *SystemArgument2
    71.     )
    72. {
    73.     DbgPrint("KKERNEL\n");
    74.     INT3;
    75. };
    KKERNEL вызывается а вот loadDllThunkRoutine так и не получается добиться вызова.
     
  11. x64

    x64 New Member

    Публикаций:
    0
    Регистрация:
    29 июл 2008
    Сообщения:
    1.370
    Адрес:
    Россия
    Адрес функции loadDllThunkRoutine() должен располагаться в а.п. целевого процесса, не в ядре.
     
  12. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    XshStasX
    Это пользовательская апк, говорил ведь. Она доставляется при возврате в юзермод из системных сервисов, есть соответствующий сервис:
    Код (Text):
    1. NTSYSAPI
    2. NTSTATUS
    3. NTAPI
    4. NtQueueApcThread(
    5.     __in HANDLE ThreadHandle,
    6.     __in PPS_APC_ROUTINE ApcRoutine,
    7.     __in_opt PVOID ApcArgument1,
    8.     __in_opt PVOID ApcArgument2,
    9.     __in_opt PVOID ApcArgument3
    10.     )
    11.  
    12. /*++
    13.  
    14. Routine Description:
    15.  
    16.     This function is used to queue a user-mode APC to the specified thread. The APC
    17.     will fire when the specified thread does an alertable wait
    18.  
    19. Arguments:
    20.  
    21.     ThreadHandle - Supplies a handle to a thread object.  The caller
    22.         must have THREAD_SET_CONTEXT access to the thread.
    23.  
    24.     ApcRoutine - Supplies the address of the APC routine to execute when the
    25.         APC fires.
    26.  
    27.     ApcArgument1 - Supplies the first PVOID passed to the APC
    28.  
    29.     ApcArgument2 - Supplies the second PVOID passed to the APC
    30.  
    31.     ApcArgument3 - Supplies the third PVOID passed to the APC
    32.  
    33. Return Value:
    34.  
    35.     Returns an NT Status code indicating success or failure of the API
    36.  
    37. --*/
    38.  
    39. {
    40.     PETHREAD Thread;
    41.     NTSTATUS st;
    42.     KPROCESSOR_MODE Mode;
    43.     PKAPC Apc;
    44.  
    45.     PAGED_CODE();
    46.  
    47.     Mode = KeGetPreviousMode ();
    48.  
    49.     st = ObReferenceObjectByHandle (ThreadHandle,
    50.                                     THREAD_SET_CONTEXT,
    51.                                     PsThreadType,
    52.                                     Mode,
    53.                                     &Thread,
    54.                                     NULL);
    55.     if (NT_SUCCESS (st)) {
    56.         st = STATUS_SUCCESS;
    57.         if (IS_SYSTEM_THREAD (Thread)) {
    58.             st = STATUS_INVALID_HANDLE;
    59.         } else {
    60.             Apc = ExAllocatePoolWithQuotaTag (NonPagedPool | POOL_QUOTA_FAIL_INSTEAD_OF_RAISE,
    61.                                               sizeof(*Apc),
    62.                                               'pasP');
    63.  
    64.             if (Apc == NULL) {
    65.                 st = STATUS_NO_MEMORY;
    66.             } else {
    67.                 KeInitializeApc (Apc,
    68.                                  &Thread->Tcb,
    69.                                  OriginalApcEnvironment,
    70.                                  PspQueueApcSpecialApc,
    71.                                  NULL,
    72.                                  (PKNORMAL_ROUTINE)ApcRoutine,
    73.                                  UserMode,
    74.                                  ApcArgument1);
    75.  
    76.                 if (!KeInsertQueueApc (Apc, ApcArgument2, ApcArgument3, 0)) {
    77.                     ExFreePool (Apc);
    78.                     st = STATUS_UNSUCCESSFUL;
    79.                 }
    80.             }
    81.         }
    82.         ObDereferenceObject (Thread);
    83.     }
    84.  
    85.     return st;
    86. }
    Поток должен быть алертабельным, можно вручную произвести необходимые манипуляции. Но это хак, нет смысла его описывать.

    Проблема доступа к ресурсам. Допустим есть код:
    Код (Text):
    1. Routine1:
    2.    AcquireResource1()
    3.    Routine2()
    4.    ReleaseResource1()
    5.  
    6. Routine2:
    7.    AcquireResource2()
    8.    Routine3()
    9.    ReleaseResource2()
    10.  
    11. Routine3:
    12.    AcquireResource3()
    13.    Notify()
    14.    ReleaseResource3()
    Пока выполняется Notify(), тоесть из неё не доступны ресурсы 1, 2 и 3.
    Соответствующая цепочка стековых фреймов(SFC) или вложенность следующая:
    Код (Text):
    1. NL
    2. X   Routine1()
    3. X+1      Routine2()
    4. X+2         Routine3()
    5. X+3            Notify()
    NL - Nesting Level.

    Если NL = X - 1, то доступны ресурсы 1, 2 и 3.
    Если NL = X, то доступны ресурсы 2 и 3.
    Если NL = X + 1, то доступен ресурс 3.
    Если NL = X + 2, то все ресурсы заняты.

    Уменьшаем NL(он соответствует SFN) до необходимого и имеем полный доступ к ресурсам. Не обязательно для этого использовать сложные методы, если необходимо получить управление на нулевом NL, то это значительно упрощает задачу:
    Код (Text):
    1. TACK_FRAME struct
    2. Next        PVOID ?
    3. Ip      PVOID ?
    4. STACK_FRAME ends
    5.  
    6. TsDbgArgMark equ 00008H ; KTRAP_FRAME.DbgArgMark
    7.  
    8. PcStackBase     equ 4
    9. PcStackLimit        equ 8
    10.  
    11. Notify:
    12.     mov eax,ebp
    13.     xor ecx,ecx
    14.     assume fs:nothing
    15.     assume eax:PTR STACK_FRAME
    16.     assume edx:PTR STACK_FRAME
    17.     mov edx,eax
    18. Scan:
    19.     cmp eax,-1
    20.     je Stop
    21.    
    22.     cmp fs:[PcStackBase],eax
    23.     jna Error
    24.     cmp fs:[PcStackLimit],eax
    25.     ja Error
    26.  
    27.     cmp dword ptr [eax + TsDbgArgMark],0BADB0D00H   ; For ring transitions.
    28.     je Stop ; PKTRAP_FRAME
    29.    
    30.     mov edx,eax
    31.    
    32.     mov eax,[eax].Next
    33.     inc ecx
    34.     jmp Scan
    35.    
    36. Stop:
    37.     jecxz Error
    38. ;   Tls.Ip = [edx].Ip
    39. ;   [edx].Ip = @PayloadRoutine
     
  13. IceCrashLdr

    IceCrashLdr New Member

    Публикаций:
    0
    Регистрация:
    29 июн 2010
    Сообщения:
    193
    Прежде чем использовать в PsSetLoadImageNotifyRoutine APC,
    Кто уверен что APC отработает раньше, чем процесс получит состояния смерти (не полное освобождения ресурсов,но родительский процесс получил уведомления по HANDLE )?
     
  14. x64

    x64 New Member

    Публикаций:
    0
    Регистрация:
    29 июл 2008
    Сообщения:
    1.370
    Адрес:
    Россия
    В чём конкретно тут проблема, по-твоему?
     
  15. IceCrashLdr

    IceCrashLdr New Member

    Публикаций:
    0
    Регистрация:
    29 июн 2010
    Сообщения:
    193
    x64
    Проблема в том что APC отработает позже, то и есть процесс уже умрет ...
     
  16. IceCrashLdr

    IceCrashLdr New Member

    Публикаций:
    0
    Регистрация:
    29 июн 2010
    Сообщения:
    193
    П.С.
    Я не проводил испытаний, но если есть у кого сведенья ...
     
  17. __MAX__

    __MAX__ New Member

    Публикаций:
    0
    Регистрация:
    21 май 2007
    Сообщения:
    19
    если KeInsertQueueApc вернёт TRUE - гарантированно будет вызвана или Kernel или Rundown функция. Rundown функция вызывается если поток умрёт раньше. в противном случае Kernel будет вызвана. освобождения ресурсов нужно проводить в Kernel и Rundown функциях.
    если память под Apc веделена с помощью ExAllocatePool(NonPagedPool,..))
    (кстати память под Apc обязательно должна быть NonPaged, потому что Apc(другая,не ваша) может быть вставлена в поток и на DISPATCH_LEVEL - а так как Apc линкуются в список - ваша Apc может быть accessed на DISPATCH_LEVEL тоже. вероятность этого конечно крайне низка - но всё таки я понял это после blue screen )
    и ничего больше освобождать ненадо - то Rundown можно := 0 - так как система в этом случае(поток завершается,в его очереди Apc, Rundown == 0) - просто вызывает ExFreePool(Apc).

    поражаюсь глупостью Clerka. то что он пишет об Apc полная чушь. незнать что либо совершенно нормально. но когда ничего не знаешь(об Apc) но при этом споришь об этом, да ещё с таким апломбом. :)
     
  18. __MAX__

    __MAX__ New Member

    Публикаций:
    0
    Регистрация:
    21 май 2007
    Сообщения:
    19
    и ещё - Driver не должен быть выгружен пока не вызовется Kernel или Rundown функция - надеюсь понятно почему. если Driver не выгружается вообще - это не проблема. в противном случае можно сделать следуещее - вызвать ObfReferenceObject(DriverObject); перед вызовом KeInsertQueueApc. если KeInsertQueueApc вернёт FALSE(Apc не вставлена) - сразу вызвать ObfDereferenceObject(DriverObject). в противном случае нужно вызывать ObfDereferenceObject(DriverObject) из Kernel и Rundown функций. но так чтобы после вызова ObfDereferenceObject вернутся сразу в ntos. так что приходится писать небольшой stub на asm
     
  19. x64

    x64 New Member

    Публикаций:
    0
    Регистрация:
    29 июл 2008
    Сообщения:
    1.370
    Адрес:
    Россия
    У пользовательской APC есть три важных момента - 1) постановка в очередь, 2) выполнение Kernel-функции и 3) выполнение User-функции. Есть ещё Rundown-функция, но это сейчас не имеет значения, т.к. если дело дойдёт до неё, то APC уже точно не будет обработана и поток считай что уже мёртвый. Постановка APC в очередь текущего потока в указанном нотификаторе есть операция абсолютно безопасная, т.к. на время выполнения нотификатора текущий поток никуда не денется ибо пользовательские потоки так не убиваются, как минимум, до обработки APCs он доживёт. Второй и третий моменты можно объединить, т.к. они вызываются в одной и той же функции ядра, - KiDeliverApc(). Относительно завершения процесса это также безопасная операция. По той простой причине, что любые APC не будут выполнены, если в очереди присутствует специальная Exit APC (т.к. внутри неё происходит flush, что означает, что выполнение всех прочих APC после неё отменяется). Ну а если наша APC уже успела выполниться до запуска механизма завершения процесса, то всё остальное уже не важно.
     
  20. IceCrashLdr

    IceCrashLdr New Member

    Публикаций:
    0
    Регистрация:
    29 июн 2010
    Сообщения:
    193
    x64
    При обычной работе, думаю все окей.
    Проблема может возникнуть при циклическом параждению процессов, может возникнуть рост ядерной памяти из простого бат файла.
    :loop
    call helloworld
    goto :loop

    И это может положить всю систему ...