Странности работы PsGetContexThread в ядре

Тема в разделе "WASM.NT.KERNEL", создана пользователем TermoSINteZ, 16 сен 2011.

  1. TermoSINteZ

    TermoSINteZ Синоби даоса Команда форума

    Публикаций:
    2
    Регистрация:
    11 июн 2004
    Сообщения:
    3.546
    Адрес:
    Russia
    Здравствуйте. Исследовал я как то на днях одну тему, с контекстами. И заметил некоторую странность.
    Начну с кода:
    Код (Text):
    1.     CONTEXT ctx;
    2.  
    3.     RtlZeroMemory(&ctx,sizeof(ctx));
    4.     ctx.ContextFlags = CONTEXT_ALL;
    5.  
    6.     ntStatus = PsGetContextThread(PsGetCurrentThread(), &ctx, KernelMode);
    7.     DbgPrint("PsGetContextThread st = %x\n",ntStatus);
    Вызываем данную функцию в ядре, например в DriverEntry.
    Получаем немного странное поведение - на WinXP все ок - возвращается STATUS_SUCCESS. В Windows Vista и Windows 7 - возвращает STATUS_UNSUCCESSFUL.
    Исследование показало место, где идет возврат данного статуса. Эта функция PspGetSetContextSpecialApc. Место передачи статуса:
    Код (Text):
    1. pkTrapFrame = pThread->Tcb.TrapFrame;
    2. if ( !pkTrapFrame || pkTrapFrame->EFlags & EFLAGS_V86_MASK || pkTrapFrame->SegCs & 1 )
    3. {
    4.      pkApc[1].SpareLong0 = 0xC0000001u;
    5.      goto LABEL_15;
    6. }
    Если допустить, что у потока нет KTRAP_FRAME , то возможно ясно такое поведение, но смотрим дальше.
    Eсли открыть чужой поток, пусть даже юзермодный, открывая его по ID (при чем даже тот, который уже работает) - функция PspSetContextThreadInternal в своих недрах идет немного по другой схеме там идет KeInsetQueueApc и потом KeWaitForGate . Адрес процедуры APC совпадает с адресом PspGetSetContextSpecialApc (что и следовало ожидать - раз возвращает тоже самое - 0xC0000001). Но если посудить - в юзермодном потоке точно есть KTRAP_FRAME , тогда в чем проблема?

    Ну и послесловие - из юзермода вызвав GetThreadContext - мы все же получим контекст без проблем (пока не трейсил поведение функции из юзермода).

    В общем если у кого-нибудь есть какие мысли - велком обсуждать.
    Если нужен бинарник драйвера с вышеприведенным кодом - говорите.
     
  2. shchetinin

    shchetinin Member

    Публикаций:
    0
    Регистрация:
    27 май 2011
    Сообщения:
    715
    KernelMode врялти чего с этим здравого получится ... ((
     
  3. TermoSINteZ

    TermoSINteZ Синоби даоса Команда форума

    Публикаций:
    2
    Регистрация:
    11 июн 2004
    Сообщения:
    3.546
    Адрес:
    Russia
    shchetinin
    не забывайте что в XP работает.
    И я пробовал для юзермодных потоков устанавливать UserMode. Возвращалось C0000005.
     
  4. shchetinin

    shchetinin Member

    Публикаций:
    0
    Регистрация:
    27 май 2011
    Сообщения:
    715
    Ладно покажи код ...
     
  5. TermoSINteZ

    TermoSINteZ Синоби даоса Команда форума

    Публикаций:
    2
    Регистрация:
    11 июн 2004
    Сообщения:
    3.546
    Адрес:
    Russia
    shchetinin
    Я вам дал код. А вызвать PsLookupThreadByThreadId думаю сами сможете.
     
  6. shchetinin

    shchetinin Member

    Публикаций:
    0
    Регистрация:
    27 май 2011
    Сообщения:
    715
    PsGetContextThread(PsGetCurrentThread(), &ctx, KernelMode);
    В DriverEntry ? и если поставить UserMode то error 5? и что здесь удивительного? Где поток берете?
     
  7. TermoSINteZ

    TermoSINteZ Синоби даоса Команда форума

    Публикаций:
    2
    Регистрация:
    11 июн 2004
    Сообщения:
    3.546
    Адрес:
    Russia
    shchetinin
    Вы вообще между строк читаете походу?
    Специально для ваc объясняю, что, для ЮЗЕРМОДНЫХ потоков - ETHREAD я беру через PsLookupThreadByThreadId предварительно узнав его ID.
    Что вы не поняли? С чего вы взяли что я UserMode ставлю для системного потока???
    И кстати ошибка не 5, а 0xC0000005. Читайте внимательнее.
     
  8. shchetinin

    shchetinin Member

    Публикаций:
    0
    Регистрация:
    27 май 2011
    Сообщения:
    715
    А не одно и тоже ? Или у вас 5 не ассоциируется с ACCESS_VIOLATION?

    а access THREAD_GET_CONTEXT ?
    Юзать надо ObReferenceObjectByHandle(hThread, THREAD_GET_CONTEXT ) или же
    ObReferenceObjectByHandle(hThread, THREAD_ALL_ACCESS )
    А сюда PsGetContextThread давать UserMode
     
  9. TermoSINteZ

    TermoSINteZ Синоби даоса Команда форума

    Публикаций:
    2
    Регистрация:
    11 июн 2004
    Сообщения:
    3.546
    Адрес:
    Russia
    shchetinin
    Вы побовали? Или просто так написали для вида? Получите вы хендл, в PsGetContextThread все равно передавать ETHREAD. А то что вы зареференсили хендл - ничем не поможет.
    Код (Text):
    1. ObReferenceObjectByHandle(hTargetThread, THREAD_ALL_ACCESS, *PsThreadType, KernelMode, &pObject, NULL );
    В результате, юзая далее предыдущий код, возвратиться тоже самое. Поставив в PsGetContextThread параметр UserMode - вернестя C000005, а поставив KernelMode - вернется C000001.
    Вызвав NtGetContextThread, при этом передав тот самый рефнутый хендл - вернется C000001.

    Пожалуйста, не пишите если не проверяли и не знаете о чем речь. Экономьте и ваше и мое время.
     
  10. TermoSINteZ

    TermoSINteZ Синоби даоса Команда форума

    Публикаций:
    2
    Регистрация:
    11 июн 2004
    Сообщения:
    3.546
    Адрес:
    Russia
    В общем проблему с возвратом C0000005 статуса, из кернела, при получении контекста юзермодного потока, я решил.
    Возвращался этот статус потому, что в PsGetContextThread(..UserMode) передается адрес структуры конекста, из диапазона кернела.
    А там есть проверка внутри PspGetContextThreadInternal:
    Код (Text):
    1.     v6 = pCtx;
    2.     if ( (unsigned int)pCtx >= (unsigned int)MmUserProbeAddress )
    3.       v6 = MmUserProbeAddress;
    4.     CtxFlag = *(_DWORD *)v6;  //здесь сработает SEH если условие выполнилось.
    Решение достаточно простое.
    Аттачимся в процесс, выделяем там память (ZwAllocateVirtualMemory), заполняем поле ContextFlag и передаем адрес памяти в PsGetContextThread. Контекст юзермодного потока из ядра получен.
     
  11. FoxB

    FoxB Member

    Публикаций:
    0
    Регистрация:
    10 июл 2003
    Сообщения:
    105
    а проблему с возвратом C0000001 статуса решили?
     
  12. Indy_

    Indy_ Well-Known Member

    Публикаций:
    4
    Регистрация:
    29 апр 2011
    Сообщения:
    4.775
    FoxB,

    Не имеет значения причина фейла и прочие тех детали. Не нужно трогать контекст задачи, тоесть вмешиваться в её работу. Если вы хотите через это залиться в приложение - это самое большое палево, иных причин в произвольный момент времени захватывать задачи нет, ну если только отслеживать какое то ав событие.
     
  13. FoxB

    FoxB Member

    Публикаций:
    0
    Регистрация:
    10 июл 2003
    Сообщения:
    105
    да
     
  14. Indy_

    Indy_ Well-Known Member

    Публикаций:
    4
    Регистрация:
    29 апр 2011
    Сообщения:
    4.775
    FoxB,

    Что значит да ?
    Получаем контекст и из него прямо указатель в код, парсить стек слишком накладно!?
    Это имеет смысл если вы например захватили системный шедулер и каждое переключение контекста или например страничная ошибка приводит к накоплению лога. Но выполнить скан системы в произвольный момент времени - вы получите только поточный стек и его нужно парсить. А как следствие у этой задачи цель одна - обнаружить память, за пределами штатного набора. Зачем юлить, ведь всё равно всё очевидно, можно было сразу прямо описать задачу, всё давно понятно и решено.
     
  15. TermoSINteZ

    TermoSINteZ Синоби даоса Команда форума

    Публикаций:
    2
    Регистрация:
    11 июн 2004
    Сообщения:
    3.546
    Адрес:
    Russia
    FoxB, это прямо супер некропост . Это было так давно, что не то, что даже не помню, а щас это даже неактуально (как правильно заметил инди).

    Конкретно проблема была в получении контекста юзер потока. Делайте как я написал выше, и вы получите контекст. Если будете продолжать передавать флаг KernelMode, то так и будете получать C0000001 . Что вполне логично.
     
  16. FoxB

    FoxB Member

    Публикаций:
    0
    Регистрация:
    10 июл 2003
    Сообщения:
    105
    получается как-то так
    Код (Text):
    1.  
    2. .....
    3. Status = ZwAllocateVirtualMemory(NtCurrentProcess(), (void**)&BaseAddress, 0, &Size, MEM_COMMIT, PAGE_READWRITE);
    4.         if (NT_SUCCESS(Status))
    5.         {
    6.             BaseAddress->ContextFlags = CONTEXT_FULL;
    7.             if (0 <= (Status = PsGetContextThread(pethr, BaseAddress, UserMode)))
    8.             {
    9.                 memcpy(&t_ctx, BaseAddress, sizeof(CONTEXT));
    10.             #ifdef _AMD64_
    11.                 res = t_ctx.Rsp;
    12.             #endif
    13.             #ifdef _X86_
    14.                 res = t_ctx.Esp;
    15.             #endif
    16.             dprintf((__FUNCTION__": thread stack %p\n", res));
    17.             }
    18.             ZwFreeVirtualMemory(NtCurrentProcess(), (void**)&BaseAddress, &Size, MEM_RELEASE);
    19.         } else {
    20.             dprintf((__FUNCTION__": ZwAllocateVirtualMemory failed, err 0x%x\n", Status));
    21.         }
    22. .....
    23.  
     
  17. comrade

    comrade Константин Ёпрст

    Публикаций:
    0
    Регистрация:
    16 сен 2002
    Сообщения:
    232
    Адрес:
    Russian Federation
    Так же проверьте alignment струкуры.

    Судя по коду который использует ZwAllocateVm, alignment должен быть достаточным.
     
  18. TermoSINteZ

    TermoSINteZ Синоби даоса Команда форума

    Публикаций:
    2
    Регистрация:
    11 июн 2004
    Сообщения:
    3.546
    Адрес:
    Russia
    Эта ошибка не из за выравнивания. А из за самой идеи - нельзя пытаться у юзер потока взять кернел контекст. Я в первом посте давал вырезку из IDA.
    Почему это работало в XP - ну значит там этой проверки не было.
     
  19. FoxB

    FoxB Member

    Публикаций:
    0
    Регистрация:
    10 июл 2003
    Сообщения:
    105
    Приведенный мною пример позволяет получать из кернела юзер контекст. проверял на вынь7 сп1 х32. позже проверю и на х64
     
  20. TermoSINteZ

    TermoSINteZ Синоби даоса Команда форума

    Публикаций:
    2
    Регистрация:
    11 июн 2004
    Сообщения:
    3.546
    Адрес:
    Russia
    Я не говорил, что это невозможно. Я говорил, что не получится если передать PsGetContextThread(pethr, BaseAddress, UserMode))) - вместо UserMode, KernelMode