Quelqu'un peut-il expliquer ce que fait le code d'assembly suivant?
int 0x80
Quelqu'un peut-il expliquer ce que fait le code d'assembly suivant?
int 0x80
Réponses:
Il passe le contrôle pour interrompre le vecteur 0x80
Voir http://en.wikipedia.org/wiki/Interrupt_vector
Sur Linux, jetez un œil à ceci : il était utilisé pour gérer system_call
. Bien sûr, sur un autre système d'exploitation, cela pourrait signifier quelque chose de totalement différent.
int 0x80
comme une sorte spéciale de call
fonction dans le noyau (sélectionnée par eax
).
int
signifie interruption, et le nombre 0x80
est le numéro d'interruption. Une interruption transfère le flux de programme à celui qui gère cette interruption, ce qui est une interruption 0x80
dans ce cas. Sous Linux, le 0x80
gestionnaire d'interruption est le noyau et est utilisé pour faire des appels système au noyau par d'autres programmes.
Le noyau est informé de l'appel système que le programme souhaite effectuer, en examinant la valeur dans le registre %eax
(syntaxe AT&T et EAX dans la syntaxe Intel). Chaque appel système a des exigences différentes concernant l'utilisation des autres registres. Par exemple, une valeur de 1
in %eax
signifie un appel système de exit()
et la valeur de %ebx
contient la valeur du code d'état pour exit()
.
Gardez à l'esprit que 0x80
= 80h
=128
Vous pouvez voir ici que ce INT
n'est qu'une des nombreuses instructions (en fait la représentation en langage d'assemblage (ou devrais-je dire «mnémonique») de celui-ci) qui existe dans le jeu d'instructions x86. Vous pouvez également trouver plus d'informations sur cette instruction dans le manuel d'Intel disponible ici .
Pour résumer à partir du PDF:
INT n / INTO / INT 3 - Procédure d'appel pour interruption
L'instruction INT n génère un appel au gestionnaire d'interruption ou d'exception spécifié avec l'opérande de destination. L'opérande de destination spécifie un vecteur de 0 à 255, codé en tant que valeur intermédiaire non signée de 8 bits. L'instruction INT n est le mnémonique général pour exécuter un appel généré par logiciel à un gestionnaire d'interruption.
Comme vous pouvez le voir, 0x80 est l' opérande de destination dans votre question. À ce stade, le CPU sait qu'il doit exécuter du code qui réside dans le noyau, mais quel code? Cela est déterminé par le vecteur d'interruption sous Linux.
L'une des interruptions les plus utiles du logiciel DOS était l'interruption 0x21. En l'appelant avec différents paramètres dans les registres (principalement ah et al), vous pouvez accéder à diverses opérations d'E / S, à la sortie de chaîne et plus encore.
La plupart des systèmes Unix et dérivés n'utilisent pas d'interruptions logicielles, à l'exception de l'interruption 0x80, utilisée pour effectuer des appels système. Ceci est accompli en entrant une valeur de 32 bits correspondant à une fonction du noyau dans le registre EAX du processeur , puis en exécutant INT 0x80.
Jetez un œil à ceci s'il vous plaît où d'autres valeurs disponibles dans les tables de gestionnaire d'interruption sont affichées:
Comme vous pouvez le voir, le tableau indique à la CPU d'exécuter un appel système. Vous pouvez trouver le tableau des appels système Linux ici .
Ainsi, en déplaçant la valeur 0x1 vers le registre EAX et en appelant le INT 0x80 dans votre programme, vous pouvez faire en sorte que le processus exécute le code dans le noyau qui arrêtera (quittera) le processus en cours d'exécution (sous Linux, processeur Intel x86).
Une interruption matérielle ne doit pas être confondue avec une interruption logicielle. Voici une très bonne réponse à ce sujet.
C'est aussi une bonne source.
int 0x80
appel système ABI i386 Linux est extrêmement similaire à l' int 0x21
ABI DOS . Mettez un numéro d'appel dans un registre (AH pour DOS, EAX pour Linux) et d'autres arguments dans d'autres registres, puis exécutez une instruction d'interruption de logiciel. La principale différence réside dans ce que les appels système vous permettent de faire (accéder directement au matériel sous DOS mais pas sous Linux), pas dans la façon dont vous les appelez.
/usr/include/x86_64-linux-gnu/asm/unistd_64.h
Exemple d'appel système Linux exécutable minimal
Linux configure le gestionnaire d'interruption pour 0x80
qu'il implémente les appels système, un moyen pour les programmes utilisateur de communiquer avec le noyau.
.data
s:
.ascii "hello world\n"
len = . - s
.text
.global _start
_start:
movl $4, %eax /* write system call number */
movl $1, %ebx /* stdout */
movl $s, %ecx /* the data to print */
movl $len, %edx /* length of the buffer */
int $0x80
movl $1, %eax /* exit system call number */
movl $0, %ebx /* exit status */
int $0x80
Compilez et exécutez avec:
as -o main.o main.S
ld -o main.out main.o
./main.out
Résultat: le programme imprime sur stdout:
hello world
et sort proprement.
Vous ne pouvez pas définir vos propres gestionnaires d'interruptions directement depuis le userland car vous n'avez que le ring 3 et Linux vous en empêche .
GitHub en amont . Testé sur Ubuntu 16.04.
Meilleures alternatives
int 0x80
a été remplacé par de meilleures alternatives pour faire des appels système: d'abord sysenter
, puis VDSO.
x86_64 a une nouvelle syscall
instruction .
Voir aussi: Qu'est-ce qui est mieux "int 0x80" ou "syscall"?
Exemple 16 bits minimal
Apprenez d'abord à créer un système d'exploitation de chargeur de démarrage minimal et à l'exécuter sur QEMU et du matériel réel comme je l'ai expliqué ici: https://stackoverflow.com/a/32483545/895245
Vous pouvez maintenant exécuter en mode réel 16 bits:
movw $handler0, 0x00
mov %cs, 0x02
movw $handler1, 0x04
mov %cs, 0x06
int $0
int $1
hlt
handler0:
/* Do 0. */
iret
handler1:
/* Do 1. */
iret
Cela ferait dans l'ordre:
Do 0.
Do 1.
hlt
: arrêter l'exécutionNotez comment le processeur recherche le premier gestionnaire à l'adresse 0
et le second à 4
: c'est une table de gestionnaires appelée IVT , et chaque entrée a 4 octets.
Exemple minimal qui effectue des E / S pour rendre les gestionnaires visibles.
Exemple de mode protégé minimal
Les systèmes d'exploitation modernes fonctionnent en mode dit protégé.
La manipulation a plus d'options dans ce mode, donc c'est plus complexe, mais l'esprit est le même.
L'étape clé consiste à utiliser les instructions LGDT et LIDT, qui pointent l'adresse d'une structure de données en mémoire (le tableau des descripteurs d'interruption) qui décrit les gestionnaires.
int 0x80 est l'instruction en langage d'assemblage qui est utilisée pour appeler les appels système sous Linux sur les processeurs x86 (c'est-à-dire compatibles Intel).
L'instruction "int" provoque une interruption.
Réponse simple: une interruption, en termes simples, est un événement qui interrompt le processeur et lui dit d'exécuter une tâche spécifique.
Réponse détaillée :
L'UC a une table des routines de service d'interruption (ou ISR) stockées en mémoire. En réel (16 bits) mode, cela est stocké en tant que IVT , ou je nterrupt V ecteur T mesure. L'IVT est généralement situé à 0x0000:0x0000
(adresse physique 0x00000
), et c'est une série d'adresses de décalage de segment qui pointent vers les ISR. Le système d'exploitation peut remplacer les entrées IVT préexistantes par ses propres ISR.
(Remarque: la taille de l'IVT est fixée à 1024 (0x400) octets.)
En mode protégé (32 bits), la CPU utilise un IDT. L'IDT est une structure de longueur variable qui se compose de descripteurs (autrement appelés portes), qui indiquent au CPU les gestionnaires d'interruption. La structure de ces descripteurs est beaucoup plus complexe que les simples entrées de décalage de segment de l'IVT; C'est ici:
bytes 0, 1: Lower 16 bits of the ISR's address.
bytes 2, 3: A code segment selector (in the GDT/LDT)
byte 4: Zero.
byte 5: A type field consisting of several bitfields.
bit 0: P (Present): 0 for unused interrupts, 1 for used interrupts.*
bits 1, 2: DPL (Descriptor Privilege Level): The privilege level the descriptor (bytes 2, 3) must have.
bit 3: S (Storage Segment): Is 0 for interrupt and trap gates. Otherwise, is one.
bits 4, 5, 6, 7: GateType:
0101: 32 bit task gate
0110: 16-bit interrupt gate
0111: 16-bit trap gate
1110: 32-bit interrupt gate
1111: 32-bit trap gate
* L'IDT peut être de taille variable, mais il doit être séquentiel, c'est-à-dire que si vous déclarez que votre IDT va de 0x00 à 0x50, vous devez avoir chaque interruption de 0x00 à 0x50. Le système d'exploitation ne les utilise pas nécessairement tous, de sorte que le bit Présent permet au processeur de gérer correctement les interruptions que le système d'exploitation n'a pas l'intention de gérer.
Lorsqu'une interruption se produit (soit par un déclencheur externe (par exemple un périphérique matériel) dans une IRQ, soit par l' int
instruction d'un programme), la CPU pousse EFLAGS, puis CS, puis EIP. (Ceux-ci sont automatiquement restaurés par iret
l'instruction de retour d'interruption.) Le système d'exploitation stocke généralement plus d'informations sur l'état de la machine, gère l'interruption, restaure l'état de la machine et continue.
Dans de nombreux systèmes d'exploitation * NIX (y compris Linux), les appels système sont basés sur des interruptions. Le programme met les arguments de l'appel système dans les registres (EAX, EBX, ECX, EDX, etc.), et appelle l'interruption 0x80. Le noyau a déjà défini l'IDT pour qu'il contienne un gestionnaire d'interruption sur 0x80, qui est appelé lorsqu'il reçoit l'interruption 0x80. Le noyau lit ensuite les arguments et invoque une fonction de noyau en conséquence. Il peut stocker un retour dans EAX / EBX. Les appels système ont été largement remplacés par les instructions sysenter
et sysexit
(ou syscall
et sysret
sur AMD), qui permettent une entrée plus rapide dans l'anneau 0.
Cette interruption peut avoir une signification différente dans un autre système d'exploitation. Assurez-vous de vérifier sa documentation.
eax
est utilisé pour le numéro syscall. asm.sourceforge.net/intro/hello.html
Comme mentionné, cela fait sauter le contrôle pour interrompre le vecteur 0x80. En pratique, cela signifie (au moins sous Linux) qu'un appel système est appelé; l'appel système exact et les arguments sont définis par le contenu des registres. Par exemple, exit () peut être appelé en définissant% eax sur 1 suivi de «int 0x80».
Il indique au processeur d'activer le vecteur d'interruption 0x80, qui sur les OS Linux est l'interruption d'appel système, utilisée pour appeler des fonctions système comme open()
pour les fichiers, et cetera.
int n'est rien d'autre qu'une interruption, c'est-à-dire que le processeur mettra son exécution en cours en attente.
0x80 n'est rien d'autre qu'un appel système ou un appel du noyau. c'est-à-dire que la fonction système sera exécutée.
Pour être précis, 0x80 représente rt_sigtimedwait / init_module / restart_sys, il varie d'une architecture à l'autre.
Pour plus de détails, consultez https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.md