Когда ЦП находится в режиме пользователя, ЦП не может выполнять привилегированные инструкции и не может получить доступ к памяти пространства ядра.
А когда процессор находится в режиме ядра, он может выполнять все инструкции и получать доступ ко всей памяти.
Теперь в Linux программа в режиме пользователя может получить доступ ко всей памяти (используя /dev/mem
) и может выполнить две привилегированные инструкции IN
и OUT
(используя, iopl()
я думаю).
Таким образом, программа в пользовательском режиме в Linux может делать большинство вещей (я думаю, большинство вещей), которые можно делать в режиме ядра.
Разве не позволение программе пользовательского режима иметь всю эту мощь не противоречит цели использования режимов ЦП?
iopl
не разрешает все привилегированные инструкции, поэтому все равно полезно убедиться, что ошибочная программа пользовательского пространства не запускается случайноinvd
, перепрыгивая через поврежденный указатель функции, который указывает на исполняемую память, начиная с0F 08
байтов. Я добавил ответ с некоторыми причинами, не связанными с безопасностью, почему полезно, чтобы процессы в пользовательском пространстве повышали свои привилегии.Только так же, как
modprobe
«побеждает» безопасность, загружая новый код в ядро.По разным причинам иногда имеет смысл иметь полу-привилегированный код (например, графические драйверы внутри X-сервера), работающий в пользовательском пространстве, а не в потоке ядра.
kill
этому легче, если это не запирает HW.Это мало что делает для безопасности, но имеет большие преимущества в надежности и архитектуре программного обеспечения.
Внедрение графических драйверов в ядро может уменьшить переключение контекста между X-клиентами и X-сервером, например, всего лишь на один пользователь-> ядро-> пользователь, вместо того, чтобы загружать данные в другой процесс пространства использования, но исторически X-серверы слишком велики и слишком глючны. хотеть их полностью в ядре.
Да, вредоносный код с этими привилегиями мог бы захватить ядро, если бы захотел, используя
/dev/mem
для модификации кода ядра.Или на x86, например, запустите
cli
инструкцию для отключения прерываний на этом ядре после выполненияiopl
системного вызова, чтобы установить уровень привилегий ввода-вывода в 0.Но даже x86
iopl
"only" предоставляет доступ к некоторым инструкциям : in / out (и строковые версии in / outs) и cli / sti. Он не позволяет вам использоватьrdmsr
илиwrmsr
для чтения или записи «регистров, специфичных для модели» (например,IA32_LSTAR
которые задают адрес точки входа ядра дляsyscall
инструкции x86-64 ), илиlidt
для замены таблицы дескрипторов прерываний (что позволит вам полностью принять на машине из существующего ядра, по крайней мере, на этом ядре.)Вы даже не можете читать управляющие регистры (например, CR3, который содержит физический адрес каталога страниц верхнего уровня, который атакующий процесс может счесть полезным в качестве смещения
/dev/mem
для изменения своих собственных таблиц страниц в качестве альтернативыmmap
большему количеству/dev/mem
. )invd
(аннулируйте все кеши без обратной записи !! ( используйте вариант = ранний BIOS до того, как сконфигурирована RAM)) - еще один забавный, который всегда требует полного CPL 0 (текущий уровень привилегий), а не только IOPL. Дажеwbinvd
привилегирован, потому что он очень медленный (и не прерываемый) и должен сбрасывать все кэши во всех ядрах. (См Есть ли способ , чтобы очистить весь кэш процессора , связанный с программой? И использование инструкции WBINVD )Ошибки, которые приводят к переходу на неверный адрес, выполняющий данные как код, таким образом, не могут выполнить любую из этих инструкций случайно на X-сервере пользовательского пространства.
Текущий уровень привилегий (в защищенном и длинном режиме) - младшие 2 бита
cs
(селектор сегмента кода) .mov eax, cs
/and eax, 3
работает в любом режиме для чтения уровня привилегий.Чтобы записать уровень привилегий, вы должны сделать a
jmp far
илиcall far
установитьCS:RIP
(но запись GDT / LDT для целевого сегмента может ограничить его на основе старого уровня привилегий, поэтому пользовательское пространство не может сделать это, чтобы поднять себя). Или вы используетеint
илиsyscall
для переключения на кольцо 0 в точке входа в ядро.источник