KeUserApcDispatcher(PspSystemDll.LoaderInitRoutine).

Тема в разделе "WASM.WIN32", создана пользователем klzlk, 6 июн 2011.

  1. klzlk

    klzlk New Member

    Публикаций:
    0
    Регистрация:
    2 июн 2011
    Сообщения:
    449
    Здрасте.
    Размышляя над темой http://kitrap08.blogspot.com/2011/05/apc-dispatchera.html появилась мысль использовать V8086-mod. Автор того поста сказал что ничего не выйдет. Создаётся остановленный тред, взводится EFlags.VF и тред ресумится. APC-диспетчер не вызывается, управление получает непосредственно стартап-код, это ошибка в ядре:
    Код (Text):
    1. VOID
    2. KiInitializeUserApc (
    3.     IN PKEXCEPTION_FRAME ExceptionFrame,
    4.     IN PKTRAP_FRAME TrapFrame,
    5.     IN PKNORMAL_ROUTINE NormalRoutine,
    6.     IN PVOID NormalContext,
    7.     IN PVOID SystemArgument1,
    8.     IN PVOID SystemArgument2
    9.     )
    10.  
    11. /*++
    12.  
    13. Routine Description:
    14.  
    15.     This function is called to initialize the context for a user mode APC.
    16.  
    17. Arguments:
    18.  
    19.     ExceptionFrame - Supplies a pointer to an exception frame.
    20.  
    21.     TrapFrame - Supplies a pointer to a trap frame.
    22.  
    23.     NormalRoutine - Supplies a pointer to the user mode APC routine.
    24.  
    25.     NormalContext - Supplies a pointer to the user context for the APC
    26.         routine.
    27.  
    28.     SystemArgument1 - Supplies the first system supplied value.
    29.  
    30.     SystemArgument2 - Supplies the second system supplied value.
    31.  
    32. Return Value:
    33.  
    34.     None.
    35.  
    36. --*/
    37.  
    38. {
    39.  
    40.     EXCEPTION_RECORD ExceptionRecord;
    41.     CONTEXT ContextFrame;
    42.     LONG Length;
    43.     ULONG UserStack, TopOfStack;
    44.     PKAPC_RECORD ApcRecord;
    45.  
    46.     //
    47.     // APCs are not defined for V86 mode; however, it is possible a
    48.     // thread is trying to set it's context to V86 mode - this isn't
    49.     // going to work, but we don't want to crash the system so we
    50.     // check for the possibility before hand.
    51.     //
    52.  
    53.     if (TrapFrame->EFlags & EFLAGS_V86_MASK) {
    54.         return ;
    55.     }
    56.  
    57.     //
    58.     // Move machine state from trap and exception frames to the context frame.
    59.     //
    60.  
    61.     ContextFrame.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
    62.     KeContextFromKframes(TrapFrame, ExceptionFrame, &ContextFrame);
    63.  
    64.     //
    65.     // Transfer the context information to the user stack, initialize the
    66.     // APC routine parameters, and modify the trap frame so execution will
    67.     // continue in user mode at the user mode APC dispatch routine.
    68.     //
    69.  
    70.  
    71.     try {        
    72.         C_ASSERT (CONTEXT_ALIGN >= __alignof(EXCEPTION_REGISTRATION_RECORD));
    73.         C_ASSERT (CONTEXT_ALIGN >= __alignof(KAPC_RECORD));
    74.  
    75.         ASSERT ((TrapFrame->SegCs & MODE_MASK) != KernelMode); // Assert usermode frame
    76.  
    77.         //
    78.         // Compute length of context record and new aligned user stack pointer.
    79.         // Make sure to include space for a double-word aligned exception registration
    80.         // record as well. For compatibility the exception registration must follow the
    81.         // context record.
    82.         //
    83.  
    84.         TopOfStack = (ContextFrame.Esp & ~(__alignof(EXCEPTION_REGISTRATION_RECORD)-1));
    85.         Length = CONTEXT_ALIGNED_SIZE + sizeof(KAPC_RECORD);
    86.         UserStack = ((TopOfStack - sizeof (EXCEPTION_REGISTRATION_RECORD)) & ~CONTEXT_ROUND) -
    87.             Length;
    88.  
    89.         //
    90.         // Probe user stack area for writability and then transfer the context
    91.         // record to the user stack. Note the minimum alignment is used as
    92.         // the above code ensures the proper alignment of the addresses.
    93.         //
    94.  
    95.         ProbeForWrite ((PCHAR)UserStack, TopOfStack-UserStack, 1);
    96.         NT_ASSERT (((ULONG_PTR)(UserStack + sizeof(KAPC_RECORD)) & CONTEXT_ROUND) == 0);
    97.         RtlCopyMemory ((PULONG)(UserStack + sizeof(KAPC_RECORD)),
    98.                      &ContextFrame, sizeof(CONTEXT));
    99.  
    100.         //
    101.         // Force correct R3 selectors into TrapFrame.
    102.         //
    103.  
    104.         TrapFrame->SegCs = SANITIZE_SEG(KGDT_R3_CODE, UserMode);
    105.         TrapFrame->HardwareSegSs = SANITIZE_SEG(KGDT_R3_DATA, UserMode);
    106.         TrapFrame->SegDs = SANITIZE_SEG(KGDT_R3_DATA, UserMode);
    107.         TrapFrame->SegEs = SANITIZE_SEG(KGDT_R3_DATA, UserMode);
    108.         TrapFrame->SegFs = SANITIZE_SEG(KGDT_R3_TEB, UserMode);
    109.         TrapFrame->SegGs = 0;
    110.         TrapFrame->EFlags = SANITIZE_FLAGS( ContextFrame.EFlags, UserMode );
    111.  
    112.         //
    113.         // If thread is supposed to have IOPL, then force it on in eflags
    114.         //
    115.  
    116.         if (KeGetCurrentThread()->Iopl) {
    117.             TrapFrame->EFlags |= (EFLAGS_IOPL_MASK & -1);  // IOPL = 3
    118.         }
    119.  
    120.         //
    121.         // Set the address of the user APC routine, the APC parameters, the
    122.         // new frame pointer, and the new stack pointer in the current trap
    123.         // frame. Set the continuation address so control will be transferred
    124.         // to the user APC dispatcher.
    125.         //
    126.  
    127.         TrapFrame->HardwareEsp = UserStack;
    128.         TrapFrame->Eip = (ULONG)KeUserApcDispatcher;
    129.         TrapFrame->ErrCode = 0;
    130.  
    131.         ApcRecord = (PKAPC_RECORD)UserStack;
    132.         ApcRecord->NormalRoutine = NormalRoutine;
    133.         ApcRecord->NormalContext = NormalContext;
    134.         ApcRecord->SystemArgument1 = SystemArgument1;
    135.         ApcRecord->SystemArgument2 = SystemArgument2;
    136.     } except (KiCopyInformation(&ExceptionRecord,
    137.                                 (GetExceptionInformation())->ExceptionRecord)) {
    138.  
    139.         //
    140.         // Lower the IRQL to PASSIVE_LEVEL, set the exception address to
    141.         // the current program address, and raise an exception by calling
    142.         // the exception dispatcher.
    143.         //
    144.         // N.B. The IRQL is lowered to PASSIVE_LEVEL to allow APC interrupts
    145.         //      during the dispatching of the exception. The current thread
    146.         //      will be terminated during the dispatching of the exception,
    147.         //      but lowering of the IRQL is required to enable the debugger
    148.         //      to obtain the context of the current thread.
    149.         //
    150.  
    151.         KeLowerIrql(PASSIVE_LEVEL);
    152.         ExceptionRecord.ExceptionAddress = (PVOID)(TrapFrame->Eip);
    153.         KiDispatchException(&ExceptionRecord,
    154.                             ExceptionFrame,
    155.                             TrapFrame,
    156.                             UserMode,
    157.                             TRUE);
    158.  
    159.     }
    160.     return;
    161. }
    T-фрейм не инициализируется, а используется первичный, заполненный в Get/Set- контекст апи. Изза валидации TrapFrame->EFlags & EFLAGS_V86_MASK формирование обоих фреймов не выполняется.
    Из за мода адреса урезаются, тоесть Ip и стек - младшее слово 32-хбитных регистров. Для этого в нуле аллоцируется память, туда можно записать стартап код и расположить стек.
    Вопрос - как откатать состояние треда в нормальный мод.
     
  2. TSS

    TSS New Member

    Публикаций:
    0
    Регистрация:
    13 апр 2009
    Сообщения:
    494
    Автор таки посмотрел более внимательно и увидел что трапфрейм используется прежний ( тот который был получен в начале работы ядерного апк диспатчера ), так что работать будет.

    Надо подумать =]
     
  3. klzlk

    klzlk New Member

    Публикаций:
    0
    Регистрация:
    2 июн 2011
    Сообщения:
    449
    Вызов прерываний возможен на IOPL = 3. Вызов Sysenter допустим, но в ядре проверка на V8086 мод и генерация #UD. Можно попробовать использовать LDT, тогда шедулер настроит 0x21 гейт для вызова DPMI-сервисов, но это видимо ничего не даст. Остаётся только манипуляция контекстом из другого потока. Вот пруфкод, запускающий тред без вызова диспетчера APC(без проверок успешности выполнения апи):
    Код (Text):
    1. CreateHiddenThread proc uses ebx Ip:PVOID
    2. Local Context:CONTEXT
    3. Local RegionAddress:PVOID, RegionSize:ULONG
    4. Local ThreadHandle:HANDLE
    5. Local ClientId:CLIENT_ID
    6.     mov RegionAddress,4
    7.     mov RegionSize,PAGE_SIZE
    8.     invoke ZwAllocateVirtualMemory, NtCurrentProcess, addr RegionAddress, 0, addr RegionSize, MEM_COMMIT or MEM_RESERVE or MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE
    9.     invoke RtlCreateUserThread, NtCurrentProcess, NULL, TRUE, 0, NULL, NULL, -1, 0, addr ThreadHandle, addr ClientId
    10.     mov Context.ContextFlags,CONTEXT_CONTROL or CONTEXT_SEGMENTS
    11.     invoke ZwGetContextThread, ThreadHandle, addr Context
    12. ; Cs:Ip
    13.     mov Context.regEip,1
    14.     mov Context.regSegCs,0
    15.     mov Context.regEFlags,EFLAGS_VM or EFLAGS_MASK
    16.     mov ebx,Context.regEsp
    17.     mov dword ptr ds:[1],90FEEBF9H  ; Stc/Jmp $
    18.     invoke ZwSetContextThread, ThreadHandle, addr Context
    19.     invoke ZwResumeThread, ThreadHandle, NULL
    20. @@:
    21.     invoke ZwGetContextThread, ThreadHandle, addr Context
    22.     test Context.regEFlags,EFLAGS_CF
    23.     jz @b
    24.     invoke ZwSuspendThread, ThreadHandle, NULL
    25.     mov ecx,Ip
    26.     mov Context.regSegCs,KGDT_R3_CODE or RPL_MASK
    27.     mov Context.regSegDs,KGDT_R3_DATA or RPL_MASK
    28.     mov Context.regSegEs,KGDT_R3_DATA or RPL_MASK
    29.     mov Context.regSegSs,KGDT_R3_DATA or RPL_MASK
    30.     mov Context.regSegFs,KGDT_R3_TEB or RPL_MASK
    31.     mov Context.regEsp,ebx
    32.     mov Context.regEip,ecx
    33.     mov Context.regEFlags,EFLAGS_MASK
    34.     invoke ZwSetContextThread, ThreadHandle, addr Context
    35.     invoke ZwFreeVirtualMemory, NtCurrentProcess, addr RegionAddress, addr RegionSize, MEM_RELEASE
    36.     invoke ZwResumeThread, ThreadHandle, NULL
    37.     ret
    38. CreateHiddenThread endp
    TSS
    :P
     
  4. klzlk

    klzlk New Member

    Публикаций:
    0
    Регистрация:
    2 июн 2011
    Сообщения:
    449
    Как детектить такой поток ?
     
  5. TSS

    TSS New Member

    Публикаций:
    0
    Регистрация:
    13 апр 2009
    Сообщения:
    494
    Хук юзермодного диспетчера исключений. Чтобы выполнить нормальный код нужно будет выбраться из V8086, сгенерировав исключение, ядерный диспетчер исключений передаст управление на юзермодный (т.к. v8086 код выполняется с cpl = 3).
     
  6. klzlk

    klzlk New Member

    Публикаций:
    0
    Регистрация:
    2 июн 2011
    Сообщения:
    449
    TSS
    Обратите внимание на проверку в KiDispatchException():
    Код (Text):
    1.                 if (TrapFrame->HardwareSegSs != (KGDT_R3_DATA | RPL_MASK) ||
    2.                     TrapFrame->EFlags & EFLAGS_V86_MASK ) {
    3.                     ExceptionRecord2.ExceptionCode = STATUS_ACCESS_VIOLATION;
    4.                     ExceptionRecord2.ExceptionFlags = 0;
    5.                     ExceptionRecord2.NumberParameters = 0;
    6.                     ExRaiseException(&ExceptionRecord2);
    7.                 }
    Видимо поток будет крутиться в бесконечном цикле на одной инструкции. Будет возникать фолт, ядро возвращать управление на туже самую инструкцию. При этом кстате не сбрасывается VF.
     
  7. TSS

    TSS New Member

    Публикаций:
    0
    Регистрация:
    13 апр 2009
    Сообщения:
    494
    Если такой поток не может выбраться из режима эмуляции, то зачем его ловить? Он ничего не сможет сделать
     
  8. klzlk

    klzlk New Member

    Публикаций:
    0
    Регистрация:
    2 июн 2011
    Сообщения:
    449
    У потока есть другой поток-родитель. Он может выполнить определённые манипуляции с создаваемым потоком, как например код в #3.
     
  9. TSS

    TSS New Member

    Публикаций:
    0
    Регистрация:
    13 апр 2009
    Сообщения:
    494
    Это понятно что может. Я говорю про то, что созданный таким образом поток ограничен в возможностях, заперт в своем 1Мб АП, сервисы звать не может, cpl остается юзермодным, привелегированные инструкции эмулируются через обработчик #GP. Что создали поток, что не создали, результат примерно один :) Поэтому и ловить бессмысленно. А с технической точки зрения интересно, да.
     
  10. klzlk

    klzlk New Member

    Публикаций:
    0
    Регистрация:
    2 июн 2011
    Сообщения:
    449
    TSS
    В #3 после ZwResumeThread() поток обычный, а не V86. Ну разве что нужно зарегать в csrss, но тут это не важно. Оно работает же. Родительский тред выполняет необходимые настройки, например при создании треда(CreateThread()/RtlCreateUserThread()) стартап-контекст ядро не формирует, это делает поток-родитель.