linux - x64 - system call asm



Pourquoi les appels système x86-64 Linux modifient-ils RCX et que signifie la valeur? (1)

La valeur de retour de l'appel système est en rax , comme toujours. Voir Quelles sont les conventions d'appel pour les appels système UNIX et Linux sur i386 et x86-64 .

Notez que sys_brk a une interface légèrement différente de celle des fonctions POSIX de brk / sbrk ; reportez-vous à la section Différences entre la bibliothèque et le noyau C de la page de manuel Linux brk(2) . Plus précisément, Linux sys_brk définit la coupure de programme ; les valeurs arg et return sont toutes deux des pointeurs. Voir Assembly x86 brk () call use . Cette réponse nécessite des votes positifs, car elle est la seule bonne sur cette question.

L’autre partie intéressante de votre question est la suivante:

Je ne comprends pas bien la valeur du registre RCX dans ce cas

Vous voyez la mécanique de la manière dont les instructions syscall / sysret sont conçues pour permettre au noyau de reprendre l'exécution de l'espace utilisateur tout en restant rapide.

syscall ne charge ni ne stocke, il ne modifie que les registres. Au lieu d'utiliser des registres spéciaux pour enregistrer une adresse de retour, il utilise simplement des registres d'entiers réguliers.

Ce n'est pas une coïncidence si RCX=RIP et R11=RFLAGS après que le noyau soit revenu à votre code d'espace utilisateur . Le seul moyen d' éviter que cela ne se rcx est si un appel système ptrace modifiait la valeur rcx ou r11 enregistrée du processus alors qu'elle se trouvait dans le noyau. ( ptrace est l'appel système utilisé par gdb). Dans ce cas, Linux utiliserait iret au lieu de sysret pour revenir à l’espace utilisateur, car le cas plus lent iret peut le faire. (Voir Que se passe-t-il si vous utilisez ABI Linux 32 bits int en 0x80 dans du code 64 bits? Pour parcourir les points d'entrée des appels système de Linux. processus binaire, cependant.)

Au lieu de placer une adresse de retour sur la pile du noyau (comme le fait int 0x80 ), syscall :

  • définit RCX = RIP, R11 = RFLAGS (il est donc impossible pour le noyau de voir les valeurs d'origine de ces regs avant que vous ayez exécuté l' syscall ).
  • masque RFLAGS avec un masque préconfiguré à partir d'un registre de configuration (le MSR IA32_FMASK ). Cela permet au noyau de désactiver les interruptions (IF) jusqu'à ce que swapgs terminé et que rsp configuré pour qu'il pointe vers la pile du noyau. Même avec cli comme première instruction au point d’entrée, il y aurait une fenêtre de vulnérabilité. Vous bénéficiez également de cld gratuitement en masquant DF pour que les rep movs / stos aillent vers le haut, même si user-space avait utilisé std .

    syscall : le premier syscall proposé par syscall / swapgs proposé par swapgs ne masquait pas les RFLAGS, mais il a changé après les commentaires des développeurs du noyau sur la liste de diffusion amd64 (en 2000 environ, quelques années avant le premier silicium).

  • syscall au point d’entrée d’ syscall configuré (réglage CS: RIP = IA32_LSTAR ). L'ancienne valeur CS n'est sauvegardée nulle part, je pense.

  • Il ne fait rien d'autre, le noyau doit utiliser swapgs pour avoir accès à un bloc d'informations où il a sauvegardé le pointeur de pile du noyau, car rsp toujours sa valeur depuis l'espace utilisateur.

Ainsi, la conception de syscall nécessite un ABI d'appel système que clobbers enregistre, et c'est pourquoi les valeurs sont ce qu'elles sont.

J'essaie d'allouer de la mémoire sous Linux avec sys_brk sys_brk. Voici ce que j'ai essayé:

BYTES_TO_ALLOCATE equ 0x08

section .text
    global _start

_start:
    mov rax, 12
    mov rdi, BYTES_TO_ALLOCATE
    syscall

    mov rax, 60
    syscall

La chose est comme par convention d'appel de Linux je m'attendais à ce que la valeur de retour soit dans le registre rax (pointeur sur la mémoire allouée). J'ai couru ceci dans gdb et après avoir fait sys_brk syscall, j'ai remarqué le contenu du registre suivant

Avant l'appel système

rax            0xc      12
rbx            0x0      0
rcx            0x0      0
rdx            0x0      0
rsi            0x0      0
rdi            0x8      8

Après l'appel système

rax            0x401000 4198400
rbx            0x0      0
rcx            0x40008c 4194444 ; <---- What does this value mean?
rdx            0x0      0
rsi            0x0      0
rdi            0x8      8

Je ne comprends pas très bien la valeur du registre rcx dans ce cas. Lequel utiliser comme pointeur vers le début de 8 octets que j’ai alloué avec sys_brk ?





system-calls