При написании дров всегда юзаю Zw* функции (Nt* функции обошёл стороной).
На счёт Nt* функций знаю следующее:
1) всю полезную работу выполняет Nt* функция
2) Nt* функцию нужно выполнять в контексте системного потока
3) Nt* функция вызывается из функции KiFastSystemCall, которую в свою очередь вызывает каждая Zw* функция
Так вот хочется понять почему для Nt* функции необходим системный поток?
Что творит функция KiFastSystemCall по обеспечению нормального вызова Nt* функции? И что KiFastSystemCall делает со стеком?
Re: Что происходит между вызовом Zw* и Nt* функции?
От:
Аноним
Дата:
18.01.13 17:38
Оценка:
Здравствуйте, meto, Вы писали:
M>При написании дров всегда юзаю Zw* функции (Nt* функции обошёл стороной). M>На счёт Nt* функций знаю следующее: M>1) всю полезную работу выполняет Nt* функция M>2) Nt* функцию нужно выполнять в контексте системного потока M>3) Nt* функция вызывается из функции KiFastSystemCall, которую в свою очередь вызывает каждая Zw* функция
M>Так вот хочется понять почему для Nt* функции необходим системный поток? M>Что творит функция KiFastSystemCall по обеспечению нормального вызова Nt* функции? И что KiFastSystemCall делает со стеком?
Что мешает скачать WRK и посмотреть самому?
Re: Что происходит между вызовом Zw* и Nt* функции?
M>При написании дров всегда юзаю Zw* функции (Nt* функции обошёл стороной). M>На счёт Nt* функций знаю следующее: M>1) всю полезную работу выполняет Nt* функция M>2) Nt* функцию нужно выполнять в контексте системного потока M>3) Nt* функция вызывается из функции KiFastSystemCall, которую в свою очередь вызывает каждая Zw* функция
M>Так вот хочется понять почему для Nt* функции необходим системный поток? M>Что творит функция KiFastSystemCall по обеспечению нормального вызова Nt* функции? И что KiFastSystemCall делает со стеком?
У потока есть в ETHREAD такой флажок — PreviousMode. Он бывает KernelMode или UserMode. Прямого API для модификации этого поля нету, а читать его — да пажалста — ExGetPreviousMode().
Когда вы создаете ядреный поток — он у него проставлен в KernelMode. Когда создается юзермодный поток (на самом деле просто поток, который потом пошлют APC-шкой исполнять юзермодный код) ему это поле выставляется в UserMode.
Далее — когда приходит управление в Ntфункцию, она первым делом проверяет какой PreviousMode у текущего потока. Если он UserMode — она проверяет чтобы все аргументы-указатели лежали юзерском в диапазоне адресов и при необходимости, проводит проверки безопасности (обычно это effective token vs opened object's SECURITY_DESCRIPTOR). Либо Nt* функция может вызвать другую функцию ядра, которая хавает previousmode в качестве аргумента — тогда эта другая функция и будет делать эти проверки. Если же PreviousMode у потока == KernelMode — Nt* функции обычно не проводят проверки валидности аргументов и безопасности.
Zw* заглушки соответственно выставляют PreviousMode в KernelMode, передают управление на Nt* функции, и по возврату — возвращают все назад.
Как много веселых ребят, и все делают велосипед...
Re[2]: Что происходит между вызовом Zw* и Nt* функции?
Здравствуйте, ononim, Вы писали:
O>Далее — когда приходит управление в Ntфункцию, она первым делом проверяет какой PreviousMode у текущего потока. Если он UserMode — она проверяет чтобы все аргументы-указатели лежали юзерском в диапазоне адресов и при необходимости, проводит проверки безопасности (обычно это effective token vs opened object's SECURITY_DESCRIPTOR). Либо Nt* функция может вызвать другую функцию ядра, которая хавает previousmode в качестве аргумента — тогда эта другая функция и будет делать эти проверки. Если же PreviousMode у потока == KernelMode — Nt* функции обычно не проводят проверки валидности аргументов и безопасности.
Действительно. Глянул под Идой несколько Nt* функций и увидел сиё действо.
O>Zw* заглушки соответственно выставляют PreviousMode в KernelMode, передают управление на Nt* функции, и по возврату — возвращают все назад.
Можно ли самому менять флаг Tcb.PreviousMode ?
Кроме этого что ещё нужно сделать для возможности вызова Nt* функции из user-потока (в драйвере) ?
Re[3]: Что происходит между вызовом Zw* и Nt* функции?
O>>Zw* заглушки соответственно выставляют PreviousMode в KernelMode, передают управление на Nt* функции, и по возврату — возвращают все назад. M>Можно ли самому менять флаг Tcb.PreviousMode ?
Ну тока если захардкодить смещение на ETHREAD ли анализировать ExGetPreviousMode(). Но зачем удалять гланды через задницу хачить если есть документированный рабочий способ — вызывать Zw* ?
M>Кроме этого что ещё нужно сделать для возможности вызова Nt* функции из user-потока (в драйвере) ?
да можете и без этого вызывать Nt* функции из user-mode потока. Просто учитывайте все вышеописанные ограничения. Но опять же — зачем?
Как много веселых ребят, и все делают велосипед...
Re[4]: Что происходит между вызовом Zw* и Nt* функции?
Здравствуйте, ononim, Вы писали:
O>Ну тока если захардкодить смещение на ETHREAD ли анализировать ExGetPreviousMode(). Но зачем удалять гланды через задницу хачить если есть документированный рабочий способ — вызывать Zw* ?
Ну если нужно обойти SSDT хуки антивирусов, то это, наверное, самый лучший метод. Али нет?
O>да можете и без этого вызывать Nt* функции из user-mode потока. Просто учитывайте все вышеописанные ограничения.
Так, к примеру, вызов NtCreateFile из user-потока порождает BSOD (по дампу где то стек бъётся). Из kernel-потока таже функция нормально отрабатывает (Zw вариант работает из любого потока). Вот и хочу понять почему такое может происходить.
Re[5]: Что происходит между вызовом Zw* и Nt* функции?
O>>Ну тока если захардкодить смещение на ETHREAD ли анализировать ExGetPreviousMode(). Но зачем удалять гланды через задницу хачить если есть документированный рабочий способ — вызывать Zw* ? M>Ну если нужно обойти SSDT хуки антивирусов, то это, наверное, самый лучший метод. Али нет?
Нет. Например если хочется открыть файл — лучше воспользоваться IoCreateFile, а еще лучшее — послать IRP прямо в драйвер FS. Ключи реестра открыть мона при помощи ObOpenObjectByName, впрочем, тут по хорошему тока два варианта — или ZwOpenKey или ObOpenObjectByName. NtOpenKey — не экспортируется. А вот value читать придецца черз Zw, тут без ваориантов
Как много веселых ребят, и все делают велосипед...
Re[6]: Что происходит между вызовом Zw* и Nt* функции?
Здравствуйте, ononim, Вы писали:
O>Нет. Например если хочется открыть файл — лучше воспользоваться IoCreateFile ...
Попробовал. Хэндл возвращается. Но появилась проблема с освобождением полученного хендла.
Функция NtClose возвращает ошибку STATUS_INVALID_HANDLE (ZwClose даже не пробовал, т.к. хочу отказаться от Zw*).
Сейчаз попробовал перед NtClose менять этот Tcb.PreviousMode на KernelMode — хэндл удачно освободился.
Так что, видимо, буду "удалять гланды через задницу". У меня уже дров в пару местах сканит опкоды — добавим ещё 1 скан ))
Re[7]: Что происходит между вызовом Zw* и Nt* функции?
Тестирование показало, что изменение флага Tcb.PreviousMode и вызов Nt* функций часто влечёт за собой BSOD. Также может вызвать зацикливание в Nt* функции.
Так что даже и не знаю как эмулировать системный трэд без использования IoQueueWorkItem. Печалька.
Re[8]: Что происходит между вызовом Zw* и Nt* функции?
M>Тестирование показало, что изменение флага Tcb.PreviousMode и вызов Nt* функций часто влечёт за собой BSOD.
А назад вы его возвращаете после возврата?
M>Так что даже и не знаю как эмулировать системный трэд без использования IoQueueWorkItem. Печалька.
А чем не устраивает IoQueueWorkItem?
Как много веселых ребят, и все делают велосипед...
Re[9]: Что происходит между вызовом Zw* и Nt* функции?
M>>Тестирование показало, что изменение флага Tcb.PreviousMode и вызов Nt* функций часто влечёт за собой BSOD. O>А назад вы его возвращаете после возврата?
Да, конечно же. Написал такие вот FORCEINLINE функи: