---
title: "Sécurité logicielle : L'assembleur - approfondissement"
date: 2023-02-01
tags: ["assembleur", "intel"]
categories: ["Sécurité logicielle", "Cours"]
---

## Un premier exemple

Prenons `IP` comme compteur et `A` , `B`, `C` et `D` comme registres.

```asm {linenos=inline}
0x00: mov $1, A
0x05: mov $2, B
0x0a: mov $3, C
0x0f: add A, B
0x10: cmp B, C
0x11: jz zero
0x12: mov $41, A
0x17: jmp end
0x18: zero:
0x18: mov $42, A
0x1d: end:
```

La première colonne représente le compteur d'instruction, nous pouvons vois que
chaque *instruction prend 5 octets*.

Ligne 4, la seconde opérande de l'instruction `add` est aussi le résultat

Ligne 5, l'instruction `cmp` fait une comparaison, en fait il fait une
soustraction des deux opérandes. Si le résultat de cette sourtraction (et dons
que les opérandes sont égales) alors l'instruction `jz` (*jump zero*) réalisera
un saut vers l'étiquette `zero`.

## Le langage

### Les registres

Il étaient de 8 bits à l'origine et nommés `A`, `B`, `C` et `D`, lors du passage
en 16 bits, ils se sont appelés `Ax`, `Bx`, `Cx`, `Dx` (pour *extended*). Lors
du passage à 32 bits ils sont passé à `eAx`, `eBx`, `eCx`et `'eDx`. Enfin ils
sont passé à `rAx`, `rBx`, `rCx` et `rDx` pour la version 64 bits.

Lors de ce cours, nous utiliseront principalement les versions 32 bits.

### Les suffixes d'instructions

Toute comme pour les registres, il existe des version de certaines instructins
en fonction de la taille des opérandes :

 * `movb` : 8bits
 * `movb` : 16 bits
 * `movl` : 32 bits
 * `movq` : 64 bits

### Les usages

Il existe plusieurs type de registres en fonction de leur usage

 * registres **généraux** : `rAx`, `rBx`, ...
 * registres **d'indexation** : `RSI` (source) et `RDI` (destination)
 * registres supplémentaire 64 bits : de `R8` à `R15`

 Nous en verrons d''autre un peu plus tard.

### Flags de résultats

C'est un registre de 32 bits, chaque bit correspond à un drapeau spécifique :

Les 16 premier bits repente les *flags* : 

position | flag | fonction
---------|------|----------
 0 | CF | Carry flag
 2 | PF | Parity flag
 4 | AF | Adjust flag
 6 | ZF | Zero flag (si le résultat est 0)
 7 | SF | Sign flag (valeur de bit de poids fort)
 8 | TF | Trap flag
 9 | IF | Interrupt enable flag
 10 | DF | Direction flag (sens de lecture d'une chaine de caractère)
 11 | OF | Overflow flag (positionné en cas d'overflow)
 12 13| IOPL | IO privilege number
 14 | NT | Nested tag flag

Et les 16 suivants les *eflags*:

position | flag | fonction
---------|------|----------
 16 | RF | Resume flag (Défini la réponse CPU pour la reprise à l'exception pour
 debug)
 17 | VM | Virtual 8086 mode
 18 | AC | Alignement check
 19 | VIF | Virtual interrupt flag
 20 | ID | CPUID flag (possibilité d'utiliser  le CPUID)
 21-31  | Réservé

### Les instructions

#### Addition / soustraction / incrémentation / décrémentation

 * `add <src> <dest>` : addition de `<src>` et `<dst>`, positionnement du
   résultat dans `<dst>`
 * `sub <src> <dst>` : soustraction de `<src>` et `<dst>`, positionnement du
   résultat dans `<dst>`
 * `inc <val>` : incrémenter `<val>`
 * `dec <val>` : décrémenter `<val>`
 * `neg <val>` : renvoie le complément à deux de `<val>`

```asm
movl $20 %rax # rax = 15
addl $10 %rax # rax = rax + 10 = 25
subl $7  %rax # rax = rax - 7 = 18
incl %rax     # rax += 1 = 19
decl %rax     # rax -= 1 = 19
```

#### Division / Multiplication

 * `mul <src>` : multiplication de `%eax` par `<src>`, `%eax` est choisi en dur,
   le résultat est écrit sur deux registres `%edx` et `%eax`. `mul` retroune un
   résultat **non signé**.
 * `imul` : multiplication comme précédement, mais le résultat est cette fois
   **signé**
 * `div <dst>` : divise le nombre contenu dans `%eax` et `%edx` par `<dst>`. Le
   quotien sera positionné dans `%eax` et le reste dans `%edx`. Le résultat est
   **non signé**.
 * `idiv <dst>` : division comme précédement mais cette fois le résultat est
   **signé**.

```asm
movl $0 %edx
movl $8 %eax
movl $2 %ebx
divl %ebx    # %eax = %edx.%eax / %ebx remainer : %edx

movl $0 %edx
movl $8 %eax
movl $2 %ebx
mull %ebx    # %eax = %ebx * %eax
```

#### Shift, rotate

On parle ici de décalage de bits que se soir à gauche (multilication par 2
puissance X) ou à droite (division par 2 puissance X).

 * `shl <n> <dst>` : décalage à gauche de `<dst>` de `<n>` bits (`<dst> << n`).
 * `shr <n> <dst>` : décalage à droite de `<dst>` de `<n>` bits (`<dst> >> n`).

Ces deux instructions sont **non signées**, les versions **signées** sont `sal`
et `sar`.

 * `sol <n> <dst>` : applique une rotation de `n` bits vers la gauche de `<dst>`
 * `sor <n> <dst>` : applique une rotation de `n` bits vers la droite de `<dst>`

```asm
movl $0xa %eax
rol $0x2 %eax  # 00001010 -> 00101000 = 0x28
```

#### Opération bit à bit

 * `and <src> <dst>` : réalisation de l'opération `<src> & <dst>`,
   positionnement de résultat dans `<dst>`
 * `or <src> <dst>` : réalisation de l'opėration `<src> | <dst>`, positinnement
   du résultat dans `<dst>`
 * `xor <src> <dst>` : réalisation de l'opėration `<src> ^ <dst>`, positinnement
   du résultat dans `<dst>`
 * `not <dst>` : réalisation de l'opération `<~dst>` et poitionnement du résultat
   dans `<dst>`
 * `cmp <1> <2>` : réalisation de l'opération `<1> - <2>`, le résultat n'est pas
   affecté mais les drapeaux OF, ZF AF, CF PF son positionné en fonction du
   résultat.

#### Instruction de branchement

Certaines de ces instructions on plusieurs notations possibles.

 * `jmp <addr>` : saut vers l'adresse `<addr>` sans condition
 * `ja <addr>` : saut vers l'adresse `<addr>` si la comparaison au dessus donne
   comme résultat *strictement supérieur à*
 * `jae <addr>` : saut vers l'adresse `<addr>` si la comparaison au dessus donne
   comme résultat *supérieur ou égal à*
 * `jnae <addr>` : saut vers l'adresse `<addr>` si la comparaison au dessus donne
   comme résultat *strictement inférieur à* (not above or equal) -- peut être
   aussi `jng` (not greater)
 * `jz <addr>` : saut vers l'adresse `<addr>` si la comparaison au dessus donne
   comme résultat *égal à* 

#### Instruction diverses

 * `loop <addr>`: décrémente `%ecx` et effectue un saut vers `<addr>` si `%ecx`
   et supérieur à 0.
 * `loope <addr>`: décrémente `%ecx` et effectue un saut vers `<addr>` si `%ecx`
   et supérieur à 0 et `ZF` = 1. Peut aussi être nommé `loopz`
 * `loopne <addr>`: décrémente `%ecx` et effectue un saut vers `<addr>` si `%ecx`
   et supérieur à 0 et `ZF` = 0. Peut aussi être nommé `loopnz`
 * `noop`: ne fait rien...

### Gestion de la Mémoire

Nous avons différents types d'opérandes pour la mémoire. que se soit sur les
registres : 

```asm
# affecter le contenu de %eax dans %ebx
movl %eax %ebx
```

Pour les immediats:

```asm
# Affecter 1 à %ebx
movl $1 %ebx

# Affecter 0xa à %ebx
movl $0xa %ebx

# Affecter l'adresse `a` à %ebx
movl $a %ebx
```
Et pour l'acces direct à la mémoire

```asm
# Erreur !!
movl a %ebx

# Référence à l'adresse `a`
movl $a %ebx
```

Et enfin les accès indirects à la mémoire. Le principe ici est base + index * 
type + déplacement. Si le type est omit alors il vaut 1 si le déplacement est 
omis il vaut 0. Concrètement l'adresse est organisée comme suit:  
`<deplacement>(<base>, <index>, <type>)`


```asm
# Copie le contenu de la case mémoire pointé par le contenu de %ebx dans %eax
movl (%ebx) %eax

# Copie le contenu à l'adresse %ebx + 4 octets dans %eax
movl 4(%ebx) %eax

# Cette fois c'est le contenu à l'adresse %ebx - 4
movl -4(%ebx) %eax


# le contenu à l'adresse %ecx + %ebx
movl (%ebx, %ecx) %eax

# contenu à l'adresse %ebx +t
movl t(%ebx) %eax

# contenu à l'adresse t + %ebx + %ecx * 4
movl t(%ebx, %ecx, 4) %eax
```

#### Move, xchg, lea

Ces instructions servent à copier une valeur d'un endroit (registre, mémoire ) à 
un autre.

 * `movl <src> <dst>` : copie le contenu de `<src>` vers `<dst>`, `<src>` peut
   être une constante, un registre ou un adresse (label) et `<dst>` une adresse
   ou un registre.
 * `xchg <a> <b>` : ;echange le contenu de `<a>` et de `<b>`
 * `leal <src> <dst>` : calcule l'adresse source (`<src>`) et place le résultat
   dans `<dst>`. Contrairement à `movl`, `leal` ne traite pas la valeur mais
   bien **l'adresse**

Il est possible d'utiliser `leal` pour effectuer des calculs arithmétiques non
signé. Ainsi on peut bénéficier d'un avantage : **les drapeaux ne sont pas
impactés**.

 * `cmov<cmp> <src> <dst>`: copie `<src>` vers `<dst>` en fonction de
   l'instruction de comparaison précédente et de l'instruction donnée par
   `<cmp>`.
   ```asm
   cmpl %eax %ebx  # %ebx - %eax
   cmovgel $1 %edx # si %ebx > ebx (res > 0) alors %edx = 1
   ```

### Diverses instruction

 * `nop` : ne fait rien à part incrémenter le *pointeur d'instruction*. Elle
   peut servier à remplir un espace mémoire afin d'éviter de décaller le
   restrant du code.
 * `halt`: met en pause le CPU



### Travail sur les flottants

Les flotants ont une longueur de *32bits*, les doubles de *64bits*, le
processeur lui les traite sur *80bits* :

 * **un bit** de signe
 * **15 bits** d'exposants
 * **64 bits** de mantisse

Pour information, nombre = mantisse = 10^exposant.

Le processeur dispose de 8 registres (`st0` à `st7`). Leurs accès ne se fait pas
directement mais via `push` / `pop`

ils sont gérés dans une unité à part , qui dispose aussi des ses propres *flags*
sur *16bits*. Les 6 premiers bits sont réservés aux exceptions:

position | flag | fonction
---------|------|----------
 0 | IE | Invalid Operation
 1 | DE | Denormalized Operand Exception
 2 | FE | Zero Divide Exception
 3 | OE | Overflow Exception
 4 | UE | Underflow Exception
 5 | PE | Precision Exception

Voici les suivants:

position | flag | fonction
---------|------|----------
 7 | ES | Error Summary Status
 8 9 10 14 | C1-C4 | Condition Code 
 11 12 13 | TOP | Top of stack pointer
 15 | B | FPU busy

#### Les instructions

Le FPU dispose de ses propres instructions par exemple:

 * `fadd`, `faddp` : addition flotante :
   ```asm
   faddp    # Ajouter st1 à  st0
   fadd a   # Ajouter a à st0
   fadd a b # Ajouter a à b
   ```
 * `fsub`, `fsubp`: soustraction flottante
 * `fmul`, `fmulp`: multiplication flottante
 * `fdiv`, `fdivp`: division flottante
 * `fchs` : changement de signe (`st(i)`)
 * `fabs`: retourne la valeur absolue (`st(i)`)
 * `fsqrt`: retourne la racine carré (`st(i)`)
 * `fsin`, `fcos` retourne le sinus, le cosinus (`st(i)`)
 * `fcomi`: compare l'opérande avec `st0`
   ```asm
   fcomi st2
   ```
 * `fcmovb`, `fcmove`, `fcmovbe`: move si en dessous, egal, en dessous ou égal

### SSE (Streaming SIMD Extentions)

C'est un ensemble de 70 instructions ajoutées au processeurs Intel en 1999, 8
registres supplémentaires sont implémentés : `xmm0` à `xmm7` d'une longueur de
*128bits*. La version 64 bits ajoute 8 registes de plus (`xmm8` à `xmm16`).

#### Instruction

Voici quelques exemples de mnémonique spécifiques à cette partie:

 * `movss` qui permet de déplacement, deux opérandes la source (adresse ou
   registre `xmm`) et une destination (resitre `xmm`).
 * `addss`: additions, s'utilise comme `movss`.
 * `subss`, `mulss`, `divss`: respectivement soustrasction, multiplication et
   division. Ces opération acceptent les même opérandes que `movss`.

### Les appels systèmes en assembleur

Pour passer en mode noyau, nous pouvons utiliser une interruption spécifique :
`0x80`, le numéro d'appel système doit être positionné dans `%eax` et les
paramètres dasn `ebx`, `ecx`, `edx`, `esi` et `edi`. La valeur de retour est
positionnée dans `eax`.

En version *32bits* l'appel se fait via `int 0x80`. La version *64 bits* dispose
de l'instruction `syscall`.

```asm
.data

# Notre message et sa taille, elle nous sont nécessaire pour appeler 
# notre appel système
msg:
  .asciz "Hello World\n"
  len = . - msg

.text
.globl main
main:
  # placement de nos paramètres dans nos différents registres
  movl $len, %edx 
  movl $msg, %ecx
  movl $1, %ebx

  # numéro de l'appel système sys_write dans eax
  movl $4, %eax 

  # Et lancement de notre interruption
  int $0x80

  # Relançons un appel système pour exit, avec 0 comme paramètre
  movl $0, %ebx
  movl $1, %eax
  int $0x80
```
