Assembleur

De Justine's wiki
Aller à la navigation Aller à la recherche


LANGAGE ASSEMBLEUR

Fichier récapitulatif

<pdf>Fichier:SensePost crash course in x86 assembly-.pdf</pdf>

Boutisme

Page wikipedia

Le boutisme (endianness) désigne la façon dont un processeur (ou de façon plus générale n'importe quel système de stockage de nombres) enregistre un nombre : pour simplifier, de gauche à droite ou de droite à gauche.

Ainsi, on parle de gros-boutisme (big-endian) ou de petit-boutisme (little-endian).

Gros-boutisme

C'est le sens *traditionnel* en notation décimale courante, c'est-à-dire que le bit de poids fort est situé à gauche. Ainsi, si je prends un nombre noté en hexa 0xA0B70708, et que l'unité atomique (soit le plus petit découpage que va faire le processeur) est de 1 octet, mon nombre sera stocké sous la forme suivante:

adresse | 0 | 1 | 2 |
valeur  | A0| B7| 07| etc

...puisque deux caractères hexa font un octet (0-255). Tous les protocoles TCP/IP parlent en gros-boutiste, tout comme un certain nombre de processeurs et le protocole PCI Express.

Petit-boutisme

Pour le petit-boutisme, c'est tout simplement l'inverse : on commence par le bit de poids faible.

adresse | 0 | 1 | 2 |
valeur  | 08| 07| B7| etc

Mais pourquoi faire ? Le petit-boutisme a à l'origine été utilisé parce qu'il simplifiait la circuiterie de décodage d'adresse dans les processeurs (ce qui était utile dans les années 70). De nos jours, le gain est négligeable. Les processeurs x86 sont cependant petit-boutistes.

bi-boutisme

Les processeurs bi-boutistes supportent les deux. Ils sont surnommés *bytesexual* en anglais :D

REGISTRES

Les CPU x86 et + ont 8 registres de 32 bits, qui sont des cases où sont stockées les données.


Les quatre premier ont des sous sections.


MEMOIRE ET MODES D’ADRESSAGE

Static data region == variable globale


Commence par la directive .DATA puis


DB pour un octet

DW Pour d’eux octets

DD pour quatre octets


#
.DATA
var DB 64 → Déclare un octet initialisé à 64
#


Les arrays sont un nombre de cellules contigües


#
Z DD 1, 2, 3 → Déclare trois fois quatre octets initialisés à 1, 2, et 3
octets DB 10 DUP(?) → Déclare 10 octets commencant à l’endroit nommé « octets »
#


Dup == duplicate


Adressage mémoire:

Un cpu moderne peut adresser 2^32 octets : les adresses mémoire font 32 bits. Au dessus quand j’appelle un emplacement « octets » (label octet) il est remplacé par l’assembleur par une addresse en 32 bits. On peut additionner un registre 32 bits + une constante 32 bits pour former une adresse mémoire. L’un des registres peut être pré multiplié par 2, 4, 8

#


mov eax, [ebx] ; Move the 4 bytes in memory at the address contained in EBX into EAX
mov [var], ebx ; Move the contents of EBX into the 4 bytes at memory address var. (Note, var is a 32-bit constant).
mov eax, [esi-4] ; Move 4 bytes at memory address ESI + (-4) into EAX
mov [esi+eax], cl ; Move the contents of CL into the byte at address ESI+EAX
mov edx, [esi+4*ebx]     ; Move the 4 bytes of data at address ESI+4*EBX into EDX
#


On ne peut pas soustraire de valeurs de registres, et on ne peut en additionner que 2


DIRECTIVES DE TAILLE

La taille d’une donnée à une adresse mémoire est inférée par l’assembleur : si je prend EAX et que je le bouge quelque part, l’assembleur infère que la taille de l’emplacement destination sera de 32 bits. Seulement parfois c’est ambigü :#mov [ebx], 2# peut soit mettre 2 dans une seul octet l’adresse EBX, ou mettre le représentation 32 bits de 2 dans les quatre octets commencant à l’adresse EBX. C’est pour ça qu’il faut des directives : BYTE PTR, WORD PTR et DWORD PTR indiquent 1, 2 et 4 octets respectivement.

mov BYTE PTR [ebx], 2 ; Move 2 into the single byte at the address stored in EBX.
mov WORD PTR [ebx], 2 ; Move the 16-bit integer representation of 2 into the 2 bytes starting at the address in EBX.
mov DWORD PTR [ebx], 2     ; Move the 32-bit integer representation of 2 into the 4 bytes starting at the address in EBX.

INSTRUCTIONS

Trois catégories :

  • mouvement de données
  • arithmétique, logique
  • contrôle de flux

La liste complète est dans la Intel Instruction Set Reference.

Ici on notera par praticité:

<reg32>     Any 32-bit register (EAX, EBX, ECX, EDX, ESI, EDI, ESP, or EBP)
<reg16> Any 16-bit register (AX, BX, CX, or DX)
<reg8> Any 8-bit register (AH, BH, CH, DH, AL, BL, CL, or DL)
<reg> Any register
<mem> A memory address (e.g., [eax], [var + 4], or dword ptr [eax+ebx])
<con32> Any 32-bit constant
<con16> Any 16-bit constant
<con8> Any 8-bit constant
<con> Any 8-, 16-, or 32-bit constant


Chaque instruction commence par un « opcode » (code opération) qui détermine sa nature, en général un hextet (8 bits notés en hexa) comme 0x6A


Mouvement de données :

mov  (opcode 88 89 8A 8B 8C 8E…)

copie les données dans le second opérand vers le premier opérand :

#
mov eax, ebx → copie ebx dans eax
#
Syntaxmov <reg>,<reg>mov <reg>,<mem>mov <mem>,<reg>mov <reg>,<const>mov <mem>,<const>
push (opcode FF 89 8A 8b 8C 8E…)


place l’instruction dans son opérande au sommet de la pile mémoire. Il décrément ESP de 4 octets et place le contenu de son opérande dans ESP. On peut faire du registre-à-registre mais pas du mémoire-à-mémoire directement (il faut d’abord passer par un registre).


Syntaxpush <reg32>push <mem>push <con32>


Exemple :

#

push eax → push eax au sommet de la stack

push [var] → push les 4 octets à l’@ var au sommet de la stack


pop (pop stack)


Déplace les 4 octets au sommet de la stack vers l’opérande ( puis incrément SP – Stack pointer de 4 octets)


Syntaxpop <reg32>pop <mem>

Examplespop edi — pop the top element of the stack into EDI.pop [ebx] — pop the top element of the stack into memory at the four bytes starting at location EBX.


Lea (load Effective Address)

place l’adresse donnée par le second operand dans le registre donné par le premier operand. (Pas le contenu, juste l’adresse : ça sert à faire des pointeurs mémoire!)


Syntaxlea <reg32>,<mem>

Exampleslea edi, [ebx+4*esi] — the quantity EBX+4*ESI is placed in EDI.lea eax, [var] — the value in var is placed in EAX.lea eax, [val] — the value val is placed in EAX.


Arithmétique, Logique

add (addition d’entiers)

additionne ses operands et stocke le résultat dans le premier (on peut avoir deux registres mais un seul emplacement mémoire).

Syntaxadd <reg>,<reg>add <reg>,<mem>add <mem>,<reg>add <reg>,<con>add <mem>,<con>

Examplesadd eax, 10 — EAX ← EAX + 10add BYTE PTR [var], 10 — add 10 to the single byte stored at memory address var


sub (soustraction d’entiers)

comme add, mais en soustrayant


inc, dec (increment, decrement)

incrémente ou décrémente le contenu de son operand de 1.

Syntaxinc <reg>inc <mem>dec <reg>dec <mem>

Examplesdec eax — subtract one from the contents of EAX.inc DWORD PTR [var] — add one to the 32-bit integer stored at location var

imul (multiplication d’entiers)

Peut utiliser deux ou trois operands :

-soit multiplie o1 par o2 et stocke dans o2

-soit multiplie o2 par o3 et stocke dans o1


Syntaximul <reg32>,<reg32>imul <reg32>,<mem>imul <reg32>,<reg32>,<con>imul <reg32>,<mem>,<con>

Examples

imul eax, [var] — multiply the contents of EAX by the 32-bit contents of the memory location var. Store the result in EAX.

imul esi, edi, 25 — ESI → EDI * 25


idiv (division d’entiers)

Prend le contenu des 64 bits EDX:EAX (EDX les 4 octets de poids fort, EAX les 4 octets de poids faible) et le divise par l’operand. Le quotient va dans EAX et le reste va dans EDX.

Syntaxidiv <reg32>idiv <mem>

Examples

idiv ebx — divide the contents of EDX:EAX by the contents of EBX. Place the quotient in EAX and the remainder in EDX.

idiv DWORD PTR [var] — divide the contents of EDX:EAX by the 32-bit value stored at memory location var. Place the quotient in EAX and the remainder in EDX.


and, or, xor (bitwise)


Ces instructions effectuent l’opération demandée sur les opérands et enregistre le résultat dans o1

Syntaxand <reg>,<reg>and <reg>,<mem>and <mem>,<reg>and <reg>,<con>and <mem>,<con>

or <reg>,<reg>or <reg>,<mem>or <mem>,<reg>or <reg>,<con>or <mem>,<con>

xor <reg>,<reg>xor <reg>,<mem>xor <mem>,<reg>xor <reg>,<con>xor <mem>,<con>

Examplesand eax, 0fH — clear all but the last 4 bits of EAX.xor edx, edx — set the contents of EDX to zero.

Not (not logique bitwise)

Inverse chaque bit de l’operand

Syntaxnot <reg>not <mem>

Examplenot BYTE PTR [var] — negate all bits in the byte at the memory location var.


Neg (negate)

Equivalent en algèbre de boole à P devient (non P)

Syntaxneg <reg>neg <mem>

Exampleneg eax — EAX → - EAX

shl, shr (shift left, shift right)

Déplace un nombre de bits vers la gauche ou la droite ; les emplacements devenus vides recoivent 0, l’operand peut être shifté jusqu’à 31 places ; le nombre de bits à shifter est donné par o2, qui peut être une constante de 8 bits ou le contenu de CL. Les shift counts > 31 sont faits en modulo 32.

Syntaxshl <reg>,<con8>shl <mem>,<con8>shl <reg>,<cl>shl <mem>,<cl>

shr <reg>,<con8>shr <mem>,<con8>shr <reg>,<cl>shr <mem>,<cl>
Examples

shl eax, 1 — Multiply the value of EAX by 2 (if the most significant bit is 0)

shr ebx, cl — Store in EBX the floor of result of dividing the value of EBX by 2n wheren is the value in CL.

INSTRUCTIONS DE CONTROLE DE FLUX

Un CPU x86 a un registre appellé IP (Instruction Pointer) en 32 bits qui indique l’emplacement mémoire du début de l’instruction en cours. Normalement il s’incrémente pour pointer à l’instruction suivante après execution. Il ne peut pas être manipulé directement mais peut être mis à jour implicitement avec des instructions de contrôle de flux. On utilise ici la notation <label> pour les locations avec label dans le texte du programme. Un label est donné avec un mot suivi d’un double point (:) par exemple :

      mov esi, [ebp+8]

begin: xor ecx, ecx

      mov eax, [esi]

Le deuxième instruction s’appelle begin, et on peut référer à l’emplacement de cette instruction en utilisant son label begin.

jmp (jump)

Passe le contrôle de flux à l’instruction donnée par l’operand

Syntaxjmp <label>

Examplejmp begin — Jump to the instruction labeled begin.


jcondition (saut conditionnel) :

Ces instructions effectuent un saut en se basant sur un registre spécial : le machine status word. Le contenu de ce registre contient des informations sur les dernières opération arithmétiques effectuées. Par ex l’un de ses bits dit si le derniers résultat était 0, un autre si il était négtif, etc. Un certains nombre de conditions ont un nom basé sur le fait que la dernière opération était une instruction de comparaison cmp.

Syntaxje <label> (jump when equal)jne <label> (jump when not equal)jz <label> (jump when last result was zero)jg <label> (jump when greater than)jge <label> (jump when greater than or equal to)jl <label> (jump when less than)jle <label> (jump when less than or equal to)

Examplecmp eax, ebxjle done

If the contents of EAX are less than or equal to the contents of EBX, jump to the label done. Otherwise, continue to the next instruction.


cmp (compare)


compare les valeurs de o1 et o2, et mets à jour le machine status word. Identique à un sub sauf que le résultat est mis à la poubelle.


Syntaxcmp <reg>,<reg>cmp <reg>,<mem>cmp <mem>,<reg>cmp <reg>,<con>

Examplecmp DWORD PTR [var], 10jeq loop

If the 4 bytes stored at location var are equal to the 4-byte integer constant 10, jump to the location labeled loop.


call, ret (subroutine call and return)


Ces instruction mettent en place des sous routines call et return.


Call fait un push de l’adresse du code actuel et jump à l’adresse donnée par l’operand.


Return pop le code location au sommet de la stack, et jumpe au location code qu’elle retrouve.



Syntaxcall <label>ret

La Call Stack et les conventions de nommage


Une call stack est une structure qui stocke des informations sur les sous routines d’un program (c’est une pile). Elle est aussi connue sous les noms execution stack, program stack, control stack, run-time stack, or machine stack, ou juste « la stack ». Elle sert à :

-Stocker les adresses de retour : a chaque sous routine qui se termine, l’adresse de retour, c’est-à-dire l’endroit ou le programme doit revenir, est push au sommet de la stack.

-Le stockage de données : en faisant de la place au sommet de la stack on peut stocker les variables nécessaires à une fonction (les vraiables qui ne sont que dans la fonction et pas ailleurs).

-Les paramètres : La stack est idéale pour stocker les paramètres nécessaires à une fonction, sachant que chaque appel à une subroutine fait de la place dans la stack pour stocker ces valeurs.

Sa structure :

Structure LIFO : Last In, First Out

La stack est composée de stack frames, des structures qui stockent des infos sur l’état de chaque subroutine. comme ses paramètres, ses variables locales, l’adresse retour et l’adresse à la base de la frame. Chaque stack frame correspond à un appel de subroutine non terminé. La stack frame au sommet est celle de la routine en cours. Elle a généralement, par ordre de push :

-Les arguments, si il y’en a ;-L’adresse de retour ;-L’espace pour les variables locales.

Stack et frame pointers

RAPPEL ESP est le stack pointer ; il renvoie toujours au sommet de la stack.

Quand des valeurs sont push sur la stack, ESP diminue. Si les valeurs sont poppées de la stack elle augmente. Ce registre change constamment. Durant l’execution d’une fonction on peut vouloir accéder au données de celle-ci ; vu que l’ESP change tout le temps on ne peut pas s’en servir. On prend donc la valeur de l’ESP au moment de l’appel à la fonction et on la stocke dans EBP – le base pointer, comme ça on peut s’en servir. Vu qu’on efface EBP en faisant, on stocke sa valeur en le poussant au dessus de la stack à ce moment là.


Cf schéma bloc notes


Liste des registres

Un registre est une unité de stockage de données du CPU, dont le temps d’accès est le plus rapide


Registres Généraux

EAX (AX, AH, AL) (EAX = 32 bits, AX = 16 bits faibles, AH = 8 bits forts de AX, AL = 8 bits faibles de AX) (Contient les valeurs retour des fonctions)EBX (BX, BH, BL)ECX (CX, CH, CL) (Contient les compteurs)EDX (DX, DH, DL)EBP (BP)ESP (SP)ESI (SI)EDI (DI)

Registres de segment

CS (Code Section)SS (Stack Section)DS (Data Section)ES, FS, GS (Général)

EFLAGSTBC ( Un registre avec des flags 32 bits contenant des infos sur les opérations précédentes)Flags : Zero, Carry (Résultat trop grand ou trop petit), Sign (Si le resultat est négatif)

EIP (Ce registre contient toujours le registre de la prochaine instruction à exécuter


NB

(EAX = 32 bits, AX = 16 bits faibles, AH = 8 bits forts de AX, AL = 8 bits faibles de AX) (Contient les valeurs retour des fonctions)(Pareil pour les autres)

EAX contient le quotient et EDX le reste d’une division