Skip to main content

Системные вещи. Ответы Часть 2.

Режим ядра и режим пользователя

Продолжаю публиковать ответы на вопросы на наших собеседованиях. Вопрос про режимы ядра и многие последующие вопросы привязаны к той ОС, в которой вы работаете. В нашей компании активно разрабатываются проекты в Windows и Linux. Однако, так как большинство кандидатов лучше знают Windows, то и ответы будут публиковаться для Windows, если это не оговорено дополнительно. Кстати, по всем системным вещам Windows стоит обращаться к замечательной книге М. Руссинович, Д. Соломон — Внутреннее устройство Microsoft Windows. В ней даны подробные ответы на все системные вопросы нашего собеседования. Если же вам больше интересно как все работает внутри Linux, то я рекомендую обратиться к книге Таненбаум Э. — Современные операционные системы.

Что такое режим ядра и режим пользователя?

Для предотвращения доступа приложений к критически важным данным операционной системы и устранения риска их модификации Windows использует два режима доступа к процессору: пользовательский (user mode) и ядра (kernel mode). Код приложений работает в пользовательском режиме, тогда как код операционной системы (например, системные сервисы и драйверы устройств) — в режиме ядра. B режиме ядра предоставляется доступ ко всей системной памяти и разрешается выполнять любые машинные команды процессора. Предоставляя операционной системе более высокий уровень привилегий, чем прикладным программам, процессор позволяет разработчикам операционных систем реализовать такие архитектуры, которые не дают возможности сбойным приложениям нарушать стабильность работы всей системы.

Windows не предусматривает никакой защиты системной памяти от компонентов, работающих в режиме ядра. Иначе говоря, код операционной системы и драйверов устройств в режиме ядра получает полный доступ к системной памяти и может обходить средства защиты Windows для обращения к любым объектам. Поскольку основная часть кода Windows выполняется в режиме ядра, крайне важно, чтобы компоненты, работающие в этом режиме, были тщательно продуманы и протестированы.

CPL, RPL, DPL?

Существует четыре уровня привилегий (PL) сегментов (0-3). Привилегированность увеличивается с уменьшением номера. На нулевом уровне позволяется использование привилегированных инструкций.

Уровень привилегий сегмента (Descriptor Privilege Level) соответствует значению поля DPL в дескрипторе сегмента.

Текущий уровень привилегий (Current Privilege Level) соответствует уровню привилегий сегмента кода, селектор которого загружен в регистр CS (то есть уровню привилегий выполняющегося сегмента кода).

Запрашиваемый уровень привилегий (Requested Privilege Level) находится в двух младших битах селектора (задаётся программой).

Как происходит взаимодействие между режимом пользователя и ядра? SYSENTER, int2e?

  1. Вызов функции, например, CreateFile разворачивается в вызов функции из  NtDll.dll — «прослойке» между режимом пользователя и режимом ядра. В ней находится нужная нам функция ядра. Эта функция помещает в регистр EAX свой уникальный номер. Этот номер специфичен для каждой версии ОС и может измениться при установке Service Pack или другого обновления системы, которое затрагивает ядро.
  2. Далее, в EDX помещается адрес вершины стека параметров, в большинстве случаев это значение регистра ESP. Дело в том, что в режиме ядра и режиме пользователя используется разный стек, и при переходе в ядро стек будет переключен.
  3. На старых процессорах и ОС переключение выполняется с помощью прерывания 0x2e, на современных же для этого существует специальная инструкция процессора: SysEnter для Intel, SysCall для AMD. Int2e поддерживается в режиме совместимости современными процессорами.
  4. Процессор переключает EIP(RIP), на специальное значение, которое хранится в регистре MSR_SYSENTER_EIP. Этот регистр заполняется системой при загрузке.
  5. В результате происходит переход в специальную функцию — KiFastCallEntry().
  6. После завершения выполнении функции, происходит возврат из режима ядра с помощью iret или Sy
  7. Ядро, используя значение в регистре EAX, находит запись в таблице  KiServiceDescriptorTable и осуществляет вызов нужной функции ядра.
  8. sysExit/SysRet

На этом на сегодня всё, продолжение читайте в следующем посте.