web-dev-qa-db-fra.com

Que signifie "int 0x80" dans le code d'assemblage?

Quelqu'un peut-il expliquer ce que le code d'assemblage suivant fait?

 int 0x80  
73
Josh Curren

Il passe le contrôle pour interrompre le vecteur 0x80

Voir http://en.wikipedia.org/wiki/Interrupt_vector

Sous Linux, regardez this : il était utilisé pour gérer system_call. Bien sûr, sur un autre OS, cela pourrait signifier quelque chose de totalement différent.

54
jldupont

int signifie interruption et le nombre 0x80 est le numéro de l'interruption. Une interruption transfère le flux de programme à la personne qui gère cette interruption, qui est une interruption 0x80 Dans ce cas. Sous Linux, le gestionnaire d'interruptions 0x80 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 veut effectuer, en examinant la valeur dans le registre %eax (Syntaxe gas et EAX en syntaxe Intel). Chaque appel système a des exigences différentes concernant l’utilisation des autres registres. Par exemple, une valeur de 1 Dans %eax Signifie un appel système de exit(), et la valeur dans %ebx Contient la valeur du code d'état pour exit().

106
Polat Tuzla

Gardez à l'esprit que 0x80 = 80h = 128

Vous pouvez voir ici que [~ # ~] int [~ # ~] n'est qu'une des nombreuses instructions (en réalité la représentation en langage d'assemblage (ou devrais-je dire "mnémonique") de celle-ci) qui existe dans le jeu d'instructions x86. Vous pouvez également trouver plus d’informations sur cette instruction dans le propre manuel d’Intel, trouvé ici .

Pour résumer à partir du PDF:

INT n/INTO/INT 3 — Appel de la procédure d'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 compris entre 0 et 255, codé comme une valeur intermédiaire non signée de 8 bits. L'instruction INT n est la mnémonique générale pour l'exécution d'un appel généré par logiciel vers un gestionnaire d'interruptions.

Comme vous pouvez le voir , 0x80 est l'opérande de destination de votre question. À ce stade, la CPU sait qu'elle doit exécuter du code résidant dans le noyau, mais quel code? Cela est déterminé par le vecteur d'interruption sous Linux.

L’une des interruptions logicielles DOS les plus utiles a été 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 IO, sortie chaîne, etc.).

La plupart des systèmes Unix et leurs 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 32 bits correspondant à une fonction du noyau dans le registre EAX du processeur , puis en exécutant INT 0x80.

Jetez un coup d'œil à ceci s'il vous plaît où d'autres valeurs disponibles dans les tables de gestionnaire d'interruption sont affichées:

enter image description here

Comme vous pouvez le voir sur les points de la table, la CPU exécute un appel système. Vous pouvez trouver la table des appels système Linux ici .

Donc, en déplaçant la valeur 0x1 dans le registre EAX et en appelant l'INT 0x80 dans votre programme, vous pouvez obliger le processus à exécuter le code dans le noyau, ce qui arrêtera (quittera) le processus en cours d'exécution (sous Linux, x86 Intel CPU).

Une interruption matérielle ne doit pas être confondue avec une interruption logicielle. Ici est une très bonne réponse à cet égard.

This est aussi une bonne source.

Vous pouvez voir int 80h en action ici .

35
Koray Tugay

int 0x80 est l'instruction de langue d'assemblage utilisée pour appeler des appels système sous Linux sur des processeurs x86 (c'est-à-dire compatibles Intel).

http://www.linfo.org/int_0x80.html

9
Tom

Exemple d'appel de système Linux exécutable minimal

Linux configure le gestionnaire d'interruptions pour 0x80 de telle sorte qu'il implémente des 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

Compiler et exécuter avec:

as -o main.o main.S
ld -o main.out main.o
./main.out

Résultat: le programme imprime sur la sortie standard:

hello world

et sort proprement.

Vous ne pouvez pas définir vos propres gestionnaires d'interruptions directement à partir de l'utilitaire parce que vous n'avez que l'anneau 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 passer des appels système: d'abord sysenter, puis VDSO.

x86_64 a ne nouvelle instruction syscall .

Voir aussi: Quel est le meilleur "int 0x80" ou "syscall"?

Exemple minimal sur 16 bits

Tout d’abord, apprenez à créer un système d’exploitation de chargeur de démarrage minimal et à l’exécuter sur QEMU et sur du matériel réel, comme je l’ai expliqué ici: https://stackoverflow.com/a/32483545/895245

Maintenant, vous pouvez 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ête l'exécution

Notez 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 faisant quelques 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 (la table de descripteur d'interruption) qui décrit les gestionnaires.

exemple minimal

L'instruction "int" provoque une interruption.

Qu'est-ce qu'une interruption?

Réponse simple: Une interruption, en termes simples, est un événement qui interrompt le processeur et lui indique d'exécuter une tâche spécifique.

Réponse détaillée:

La CPU dispose d'une table de routines de service d'interruption (ou ISR) stockée en mémoire. En mode réel (16 bits), il est stocké sous le nom IVT , ou I nterrupt V ector T capable. L'IVT est généralement situé à 0x0000:0x0000 (adresse physique 0x00000), et il s’agit d’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 composée de descripteurs (autrement appelés portes), qui renseignent la CPU sur les gestionnaires d'interruptions. 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 toutes les interruptions de 0x00 à 0x50. Le système d'exploitation ne les utilise pas nécessairement tous, de sorte que le bit Present permet au CPU 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'instruction int d'un programme), la CPU envoie EFLAGS, puis CS et EIP. (Celles-ci sont automatiquement restaurées 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 place 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 une interruption 0x80. Le noyau lit ensuite les arguments et appelle une fonction du noyau en conséquence. Il peut stocker un retour dans EAX/EBX. Les appels système ont été en grande partie 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 pourrait avoir une signification différente dans un système d'exploitation différent. Assurez-vous de vérifier sa documentation.

3
Adrian Zhang

Comme mentionné précédemment, le contrôle saute au vecteur d'interruption 0x80. En pratique, cela signifie (du 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'.

2
Steve Smith

Il indique au processeur d'activer le vecteur d'interruption 0x80, qui, sous les systèmes d'exploitation Linux, correspond à l'interruption d'appel système, utilisé pour appeler des fonctions système telles que open() pour les fichiers, etc.

1
Amber