¿Por qué las llamadas al sistema Linux x86-64 modifican RCX, y qué significa el valor?
assembly system-calls (1)
Estoy tratando de asignar algo de memoria en Linux con
sys_brk
syscall.
Esto es lo que probé:
BYTES_TO_ALLOCATE equ 0x08
section .text
global _start
_start:
mov rax, 12
mov rdi, BYTES_TO_ALLOCATE
syscall
mov rax, 60
syscall
La cuestión es que, según la convención de llamadas de Linux, esperaba que el valor de retorno estuviera en el registro
rax
(puntero a la memoria asignada).
sys_brk
esto en gdb y después de hacer
sys_brk
syscall noté los siguientes contenidos de registro
Antes de syscall
rax 0xc 12
rbx 0x0 0
rcx 0x0 0
rdx 0x0 0
rsi 0x0 0
rdi 0x8 8
Después de syscall
rax 0x401000 4198400
rbx 0x0 0
rcx 0x40008c 4194444 ; <---- What does this value mean?
rdx 0x0 0
rsi 0x0 0
rdi 0x8 8
No entiendo muy bien el valor en el registro
rcx
en este caso.
¿Cuál usar como puntero al comienzo de 8 bytes que
sys_brk
con
sys_brk
?
El valor de retorno de la llamada del sistema está en
rax
, como siempre.
Consulte
Cuáles son las convenciones de llamadas para las llamadas al sistema UNIX y Linux en i386 y x86-64
.
Tenga en cuenta que
sys_brk
tiene una interfaz ligeramente diferente que las funciones POSIX de
brk
/
sbrk
;
consulte la
sección de diferencias de la biblioteca C / kernel de la página del
brk(2)
man
brk(2)
Linux
.
Específicamente,
Linux
sys_brk
establece
el salto del programa
;
el argumento y el valor de retorno son ambos punteros.
Ver
Ensamblaje x86 uso de la llamada brk ()
.
Esa respuesta necesita votos positivos porque es la única buena en esa pregunta.
La otra parte interesante de su pregunta es:
No entiendo bien el valor en el registro RCX en este caso
Está viendo la mecánica de cómo las instrucciones
syscall
/
sysret
están diseñadas para permitir que el núcleo reanude la ejecución en el espacio del usuario pero que sea rápido.
syscall
no carga ni almacena nada, solo modifica los registros.
En lugar de usar registros especiales para guardar una dirección de retorno, simplemente usa registros enteros regulares.
No es una coincidencia que
RCX=RIP
y
R11=RFLAGS
después de que el núcleo vuelva a su código de espacio de usuario
.
La única forma de que este
no
sea el caso es si una
ptrace
sistema
ptrace
modifica el valor
rcx
o
r11
guardado del proceso mientras estaba dentro del núcleo.
(
ptrace
es la llamada al sistema que usa gdb).
En ese caso, Linux usaría
iret
lugar de
sysret
para volver al espacio del usuario, porque el
iret
caso general más lento puede hacer eso.
(Consulte
¿Qué sucede si utiliza la ABI de Linux int 0x80 de 32 bits en el código de 64 bits?
Para conocer algunos de los puntos de entrada de llamadas al sistema de Linux. Principalmente los puntos de entrada de procesos de 32 bits, no de
syscall
en un 64 proceso de bits, sin embargo.)
En lugar de insertar una dirección de retorno en la pila del kernel (como lo hace
int 0x80
),
syscall
:
-
establece RCX = RIP, R11 = RFLAGS (por lo que es imposible que el núcleo vea los valores originales de esos registros antes de ejecutar
syscall
). -
enmascara
RFLAGS
con una máscara preconfigurada de un registro de configuración (elIA32_FMASK
MSR). Esto permite que el kernel deshabilite las interrupciones (IF) hasta queswapgs
y configurarrsp
para que apunte a la pila del kernel. Incluso concli
como la primera instrucción en el punto de entrada, habría una ventana de vulnerabilidad. También puede obtenercld
de forma gratuita al enmascararDF
para que losrep movs
/rep movs
vayan hacia arriba incluso si el espacio de usuario ha usadostd
.Dato
syscall
: el primer diseño desyscall
/swapgs
propuesto porswapgs
no enmascaraba RFLAGS, pero lo cambiaron después de los comentarios de los desarrolladores del kernel en la lista de correo amd64 (en ~ 2000, un par de años antes del primer silicio). -
salta al punto de entrada de
syscall
configurado (configuración CS: RIP =IA32_LSTAR
). El antiguo valorCS
no se guarda en ningún lado, creo. -
No hace nada más, el núcleo tiene que usar
swapgs
para obtener acceso a un bloque de información donde guardó el puntero de la pila del núcleo, porquersp
todavía tiene su valor desde el espacio de usuario.
Por lo tanto, el diseño de
syscall
requiere una llamada de sistema ABI que registra los registros, y es por eso que los valores son lo que son.