Многие из вас, уважаемые хабровцы, пользуются функциями ядра Windows. Однако не все понимают, как называются эти функции.
А знание некоторых механизмов ядра может оказаться весьма полезным.
Этот текст не предназначен для авторов драйверов, которые (теоретически) знают это даже на более глубоком уровне.
Начнем с того, что режим ядра имеет гораздо больше функций, чем всем известный WinAPI. Это связано с тем, что ядро содержит множество недокументированных функций (однако с выходом Win7 и Win8 ситуация улучшилась, многие такие функции попадают в MSDN).
Список таких функций можно посмотреть, например, Здесь.
Эти функции можно разделить на несколько групп:
- API ядра - функции расположены в ntoskrnl.exe (фактически обычная dll), основные функции ядра.
- Оконный API - функции находятся в gdi32.dll, функции для работы с окнами и графикой.
- API обмена сообщениями - функции расположены в user32.dll, функции обработки сообщений.
- Прежде всего, мы обратимся к NtDll.dll — «прослойка» между пользовательским режимом и режимом ядра.
Он содержит нужную нам функцию ядра.
Ээта функция помещает в регистр ЕАХ твой номер.
Это индекс двух таблиц, известных ядру: ServiceTable и ArgumentTable. Характерно, что это число специфично для каждой версии ОС.
Он часто меняется не только при переходе, скажем, с WinXP на Vista, но даже при переходе с Vista на Vista SP1. - Дальше, в ЭДКС размещается адрес вершины стека параметров, в большинстве случаев это значение регистра ESP .
Казалось бы, зачем копировать уже существующее значение? Дело в том, что после выполнения прерывания все состояние процессора сбросится в стек, и ESP уже не будет тем, что нам нужно.
— Наконец, он переключается в режим ядра.
На старых процессорах и системах это делается с помощью прерывания 0x2e, но на современных для этого существует специальная инструкция процессора: SysEnter для Интел, системный вызов для АМД.
— Результатом вызова является выполнение процессором кода, адрес которого находится в глобальной таблице прерываний в конкретной ячейке.
Этот код вызывает либо КиСистемСервис(), в случае прерывания или КиFastCallEntry(), в случае специальной инструкции процессору.
Кстати, второй прописан в реестре MSR, что обеспечивает быстрый доступ к нему.
— Далее код, работающий в ядре, выполняется немного по-разному, в зависимости от того, откуда поступил вызов функции, из режима ядра или из пользовательского режима.
Задача кода ядра: используйте внутреннюю таблицу функций для вызова функции из EAX. Эта таблица называется KiServiceDescriptorTable .
Это структура с тремя полями:
- обращение к таблице ServiceTable (nt!KiServiceTable) — массив функций
- адрес в ArgumentTable (nt!KiArgumentTable) — количество байт, занимаемых параметрами
- количество элементов в этих таблицах
Каждый поток имеет стек пользовательского режима и режима ядра.
ОС берет значение ячейки таблицы ArgumentTable, которая показывает, сколько байт параметры занимают в стеке (для этого мы вспомнили EDX).
Затем он копирует это количество байтов в стек ядра.
И после этого он вызывает функцию через ServiceTable. Вся эта заморочка с двумя стеками нужна, конечно, для того, чтобы никакие изменения в пользовательском режиме не повредили выполнению функции в ядре.
- После вызова и выполнения функции, а также завершения ее работы, происходит возврат из режима ядра с помощью инструкции ирет или СисВыход/СисРет. Вызов функций ядра в режиме ядра.
В принципе у нас всё то же самое, но.
Если вы внимательно посмотрите на список функций ядра, то заметите, что там есть функции, полностью похожие на нт- функции, но с префиксом zw- .
Все они однозначно сопоставлены с функцией nt. Однако в режиме ядра они работают по-другому.
Разница в том, что функции zw не проверяют параметры.
Другими словами, при вызове из пользовательского режима система тщательно отслеживает передаваемые в функцию параметры — не дай бог программист что-то упустит, и здравствуй, синий экран.
В режиме ядра в основном работают с драйверами, а там система уже предполагает, что вы сами проверите все параметры и гарантируете их достоверность, потому что дополнительные проверки съедят драгоценное время.
Работает это так: если вызывается zw, то он загружает номер функции в EAX, помещает указатель на стек ядра в EDX, а затем вызывает опцию nt, которая не выполняет проверку.
Возникает вопрос: что, если я удалю функцию zw из пользовательского режима? Простит ли система все? Не совсем.
Если zw вызывается в пользовательском режиме, он просто вызывает своего двойника nt. Вот, в общем-то, и вся основная информация о вызовах функций ядра.
Надеюсь, вам было интересно.
Теги: #Windows #режим ядра #программирование
-
Nexus Prime Уже Доступен Для Предзаказа
19 Oct, 24 -
Полезные Уроки Для Программиста
19 Oct, 24 -
Из Топ-10 Исчезли 4 Человека! Вас Повысили?
19 Oct, 24