Cuando la CPU está en modo de usuario, la CPU no puede ejecutar instrucciones privilegiadas y no puede acceder a la memoria de espacio del kernel.
Y cuando la CPU está en modo kernel, la CPU puede ejecutar todas las instrucciones y puede acceder a toda la memoria.
Ahora en Linux, un programa en modo de usuario puede acceder a toda la memoria (usando /dev/mem
) y puede ejecutar las dos instrucciones privilegiadas IN
y OUT
(usando iopl()
creo).
Entonces, un programa de modo de usuario en Linux puede hacer la mayoría de las cosas (creo que la mayoría de las cosas) que se pueden hacer en modo kernel.
¿No permite que un programa de modo de usuario tenga toda esta potencia frustra el propósito de tener modos de CPU?
iopl
no permite todas las instrucciones privilegiadas, por lo que sigue siendo útil para asegurarse de que un programa de espacio de usuario con errores no se ejecute accidentalmenteinvd
saltando a través de un puntero de función dañado que apunta a la memoria ejecutable que comienza con0F 08
bytes. Agregué una respuesta con algunas de las razones no relacionadas con la seguridad por las que es útil que los procesos de espacio de usuario eleven sus privilegios.Solo de la misma manera que
modprobe
"derrota" la seguridad al cargar código nuevo en el núcleo.Por varias razones, a veces tiene más sentido tener un código semi-privilegiado (como los controladores gráficos dentro del servidor X) ejecutándose en el espacio del usuario en lugar de un hilo del núcleo.
kill
hacerlo más fácilmente, a menos que bloquee el HW.No hace mucho por la seguridad, pero existen grandes ventajas de fiabilidad y arquitectura de software.
La incorporación de controladores de gráficos al núcleo podría reducir los cambios de contexto entre los clientes X y el servidor X, como un solo usuario-> núcleo-> usuario en lugar de tener que ingresar datos en otro proceso de espacio de uso, pero los servidores X históricamente son demasiado grandes y con errores. quererlos completamente en el núcleo.
Sí, el código malicioso con estos privs podría hacerse cargo del núcleo si así lo deseara, utilizando
/dev/mem
para modificar el código del núcleo.O, por ejemplo, en x86, ejecute una
cli
instrucción para deshabilitar las interrupciones en ese núcleo después de hacer unaiopl
llamada al sistema para establecer su nivel de privilegio de E / S para que suene 0.Pero incluso x86
iopl
"solo" da acceso a algunas instrucciones : entrada / salida (y las versiones de cadena ins / outs) y cli / sti. No le permite usarrdmsr
owrmsr
leer o escribir "registros específicos del modelo" (p. Ej.IA32_LSTAR
que establece la dirección del punto de entrada del kernel para lasyscall
instrucción x86-64 ), o usarlidt
para reemplazar la tabla de descriptor de interrupción (que le permitiría tomar totalmente sobre la máquina desde el núcleo existente, al menos en ese núcleo).Ni siquiera puede leer los registros de control (como CR3, que contiene la dirección física del directorio de página de nivel superior, que un proceso de ataque puede encontrar útil como compensación
/dev/mem
para modificar sus propias tablas de páginas como alternativa ammap
más)/dev/mem
. )invd
(¡invalidar todas las memorias caché sin reescritura ! ( caso de uso = BIOS anterior antes de que se configure la RAM)) es otra diversión que siempre requiere CPL 0 completo (nivel de privilegio actual), no solo IOPL. Evenwbinvd
tiene privilegios porque es muy lento (y no interrumpible) y tiene que vaciar todos los cachés en todos los núcleos. (Consulte ¿Hay alguna forma de vaciar todo el caché de la CPU relacionado con un programa? Y el uso de la instrucción WBINVD )Los errores que resultan en un salto a una dirección incorrecta ejecutando datos como código, por lo tanto, no pueden ejecutar ninguna de estas instrucciones por accidente en un servidor X de espacio de usuario.
El nivel de privilegio actual (en modo protegido y largo) son los 2 bits bajos de
cs
(el selector de segmento de código) .mov eax, cs
/and eax, 3
funciona en cualquier modo para leer el nivel de privilegio.Para escribir el nivel de privilegio, debe hacer
jmp far
ocall far
configurarCS:RIP
(pero la entrada GDT / LDT para el segmento de destino puede restringirlo según el nivel de privilegio anterior, razón por la cual el espacio de usuario no puede hacer esto para elevarse). O usaint
osyscall
para cambiar al anillo 0 en un punto de entrada del núcleo.fuente