Les opérations arithmétiques sont extrêmement fréquentes en assembleur. Elles apparaissent aussi bien dans du code simple que dans des boucles, des calculs d'offsets, des comparaisons, des index de tableaux ou encore dans le calcul d'adresses.
Pour le reverse engineering, il faut surtout être capable de lire rapidement une suite d'instructions et de déterminer la valeur finale des registres ou de la mémoire.
Dans ce chapitre, nous allons voir les principales opérations arithmétiques :
Code: Select all
neg
add
inc
sub
dec
mul
imul
div
idiv
cmovcc
L'instruction :
Code: Select all
neg
Exemple :
Code: Select all
mov rax, 10
neg rax
Code: Select all
RAX = -10
Code: Select all
neg rax
Code: Select all
RAX = 10
Elle peut être utilisée sur un registre :
Code: Select all
neg rax
Code: Select all
neg dword [x]
neg byte [x]
2. Les tailles : byte, word, dword, qword
Les tailles classiques sont :
Code: Select all
byte = 1 octet = 8 bits
word = 2 octets = 16 bits
dword = 4 octets = 32 bits
qword = 8 octets = 64 bits
Code: Select all
neg byte [x]
neg word [x]
neg dword [x]
neg qword [x]
En reverse, cette taille est très importante.
Par exemple :
Code: Select all
neg byte [rax]
Alors que :
Code: Select all
neg qword [rax]
3. NEG et les flags
Comme beaucoup d'instructions arithmétiques, `neg` modifie certains bits du registre `RFLAGS`.
Parmi les plus importants :
Code: Select all
ZF = Zero Flag
SF = Sign Flag
CF = Carry Flag
OF = Overflow Flag
Code: Select all
mov eax, 0
neg eax
Code: Select all
ZF = 1
Ces flags peuvent ensuite être utilisés par une instruction conditionnelle.
4. Addition avec ADD
L'instruction `add` réalise une addition.
Syntaxe :
Code: Select all
add destination, source
Code: Select all
mov rax, 10
add rax, 5
Code: Select all
RAX = 15
- une constante à un registre ;
- un registre à un autre registre ;
- une valeur mémoire à un registre ;
- un registre à une valeur mémoire.
Code: Select all
add rax, 10
add rax, rbx
add rax, [x]
add [x], rax
Supposons :
Code: Select all
x dq 100
Code: Select all
mov rax, 20
add rax, [x]
Code: Select all
RAX = 120
Code: Select all
mov rax, 20
add [x], rax
Code: Select all
x = 120
Code: Select all
add rax, [x]
Alors que :
Code: Select all
add [x], rax
6. INC — incrémenter
L'instruction :
Code: Select all
inc
Exemple :
Code: Select all
mov rax, 10
inc rax
Code: Select all
RAX = 11
Code: Select all
add rax, 1
Exemple :
Code: Select all
inc rcx
cmp rcx, 10
jne boucle
7. Soustraction avec SUB
L'instruction :
Code: Select all
sub destination, source
Exemple :
Code: Select all
mov rax, 20
sub rax, 5
Code: Select all
RAX = 15
Code: Select all
sub rax, rbx
sub rax, [x]
sub [x], rax
8. SUB et la stack
Tu verras très souvent :
Code: Select all
sub rsp, 20h
Cette instruction sert généralement à réserver de l'espace sur la stack.
Puis, à la fin de la fonction :
Code: Select all
add rsp, 20h
Exemple :
Code: Select all
sub rsp, 20h
...
add rsp, 20h
ret
9. DEC — décrémenter
`dec` soustrait 1.
Exemple :
Code: Select all
mov rcx, 10
dec rcx
Code: Select all
RCX = 9
Code: Select all
sub rcx, 1
Exemple :
Code: Select all
dec rcx
jnz boucle
`add` et `sub` modifient plusieurs flags importants.
Par exemple :
Code: Select all
mov eax, 10
sub eax, 10
Code: Select all
EAX = 0
ZF = 1
Code: Select all
je egal
Code: Select all
mov eax, 5
sub eax, 10
Le Sign Flag est alors utile pour les conditions signées.
En reverse, tu n'as pas forcément besoin de mémoriser tous les flags immédiatement. Mais retiens bien :
Code: Select all
une opération arithmétique modifie souvent RFLAGS
```
et les instructions suivantes peuvent prendre une décision en fonction de ces flags.
[size=150][b][color=#FF0000]11. Multiplication avec IMUL[/color][/b][/size]
L'instruction :
[code]
imul
C'est celle que tu rencontreras très souvent dans du code compilé.
Exemple :
Code: Select all
mov rax, 10
imul rax, 5
Code: Select all
RAX = 50
Code: Select all
imul rax, rbx
Code: Select all
RAX = 10
RBX = 20
Code: Select all
RAX = 200
Code: Select all
imul rax, rbx, 10
Code: Select all
RAX = RBX * 10
12. MUL — multiplication non signée
`mul` réalise une multiplication non signée.
Sa syntaxe est différente de certaines formes de `imul`.
Exemple :
Code: Select all
mov rax, 10
mov rbx, 20
mul rbx
Pour une opération 64 bits :
Code: Select all
RDX:RAX = RAX * RBX
La partie basse du résultat est placée dans :
Code: Select all
RAX
Code: Select all
RDX
13. MUL ou IMUL ?
La différence principale est :
Code: Select all
mul = multiplication non signée
imul = multiplication signée
Code: Select all
imul eax, eax, 5
Code: Select all
EAX = EAX * 5
Prenons :
Code: Select all
mov rax, 5
imul rax, rax
Code: Select all
RAX = 25
Code: Select all
mov rbx, 12
imul rbx, 2
Code: Select all
RBX = 24
15. Division avec DIV
L'instruction :
Code: Select all
div
Pour une division 64 bits, le dividende est placé dans :
Code: Select all
RDX:RAX
Code: Select all
RAX
Code: Select all
RDX
Code: Select all
mov rax, 100
xor rdx, rdx
mov rbx, 10
div rbx
Code: Select all
RAX = 10
RDX = 0
Code: Select all
xor rdx, rdx
16. Pourquoi RDX:RAX ?
Certaines instructions utilisent une paire de registres afin de représenter une valeur plus grande.
Pour `div`, le dividende 64 bits ou 128 bits selon la taille de l'opération peut être réparti sur deux registres.
En 64 bits :
Code: Select all
RDX = partie haute
RAX = partie basse
Code: Select all
RDX : RAX
high low
Code: Select all
RAX = quotient
RDX = reste
`idiv` réalise la division signée.
Exemple conceptuel :
Code: Select all
-100 / 10 = -10
Code: Select all
cqo
Exemple :
Code: Select all
mov rax, -100
cqo
mov rbx, 10
idiv rbx
Code: Select all
RAX = -10
RDX = 0
Code: Select all
cdq
18. Division par zéro
Une division par zéro n'est pas autorisée.
Exemple :
Code: Select all
mov rbx, 0
div rbx
En reverse, si tu vois un test avant une division :
Code: Select all
test rbx, rbx
je erreur
div rbx
19. Les instructions CMOVcc
Les instructions :
Code: Select all
cmovcc
Code: Select all
Conditional MOV
```
Elles permettent de copier une valeur uniquement si une condition est vraie.
Exemple :
[code]
cmp rax, rbx
cmovg rcx, rdx
Code: Select all
si RAX > RBX
alors RCX = RDX
```
Sinon :
[code]
RCX reste inchangé
```
Contrairement à un saut conditionnel, `cmov` ne change pas RIP vers une autre branche de code.
Il effectue simplement ou non la copie.
[size=150][b][color=#FF0000]20. Quelques CMOV utiles[/color][/b][/size]
On peut rencontrer :
[code]
cmove / cmovz -> égal / zéro
cmovne / cmovnz -> différent / non zéro
cmovg -> supérieur signé
cmovge -> supérieur ou égal signé
cmovl -> inférieur signé
cmovle -> inférieur ou égal signé
cmova -> supérieur non signé
cmovb -> inférieur non signé
Code: Select all
cmp
test
sub
add
Supposons :
Code: Select all
mov rax, 20
mov rbx, 10
mov rcx, 1
mov rdx, 2
cmp rax, rbx
cmovg rcx, rdx
Code: Select all
20 > 10
Donc :
Code: Select all
RCX = 2
Code: Select all
RCX serait resté égal à 1
Une condition classique pourrait être écrite avec un saut :
Code: Select all
cmp rax, rbx
jle no_change
mov rcx, rdx
no_change:
Code: Select all
cmp rax, rbx
cmovg rcx, rdx
Les compilateurs modernes utilisent parfois ce type d'instruction pour produire du code plus compact ou éviter certains coûts liés aux branches.
23. Pourquoi déplacer une valeur dans un registre ?
Tu verras souvent du code qui charge d'abord une valeur depuis la mémoire :
Code: Select all
mov rax, [x]
add rax, 10
mov [x], rax
Code: Select all
add qword [x], 10
Parce que les instructions et calculs sur registres sont généralement simples et efficaces, et parce que certaines opérations imposent l'utilisation de registres spécifiques.
Les registres servent donc souvent d'intermédiaires :
Code: Select all
mémoire -> registre -> calcul -> mémoire
24. Exemple complet de suivi de valeurs
Prenons :
Code: Select all
mov rax, 10
add rax, 5
imul rax, 2
sub rax, 4
Code: Select all
RAX = 10
add rax, 5
RAX = 15
imul rax, 2
RAX = 30
sub rax, 4
RAX = 26
25. Exemple avec mémoire
Supposons :
Code: Select all
x dq 100
y dq 20
Code: Select all
mov rax, [x]
sub rax, [y]
imul rax, 2
mov [x], rax
Code: Select all
RAX = 100
RAX = 100 - 20
RAX = 80
RAX = 80 * 2
RAX = 160
x = 160
Code: Select all
x = 160
y = 20
RAX = 160
Supposons que tu rencontres :
Code: Select all
mov eax, ecx
imul eax, 2
add eax, 5
cmp eax, 25
jne failed
Code: Select all
EAX = ECX
EAX = EAX * 2
EAX = EAX + 5
compare EAX avec 25
si différent -> failed
Code: Select all
2 * ECX + 5 = 25
Code: Select all
ECX = 10
27. Ce qu'il faut retenir pour le reverse
Pour ton usage, retiens surtout :
- `neg` inverse le signe ;
- `add` additionne ;
- `inc` ajoute 1 ;
- `sub` soustrait ;
- `dec` retire 1 ;
- `imul` multiplie des valeurs signées ;
- `mul` multiplie des valeurs non signées ;
- `div` divise des valeurs non signées ;
- `idiv` divise des valeurs signées ;
- après `div/idiv`, le quotient est généralement dans RAX et le reste dans RDX ;
- les opérations arithmétiques mettent souvent à jour RFLAGS ;
- les instructions suivantes peuvent utiliser ces flags ;
- `cmovcc` effectue une copie uniquement si une condition est vraie ;
- les calculs d'offset et d'indices utilisent souvent `add`, `sub` et `imul` ;
- il faut savoir suivre une valeur registre par registre.
Exercice 1
Quelle est la valeur finale de RAX ?
Code: Select all
mov rax, 10
add rax, 5
sub rax, 3
Quelle est la valeur finale de RAX ?
Code: Select all
mov rax, 8
imul rax, 4
Quelle est la valeur finale ?
Code: Select all
mov rax, 100
mov rbx, 25
sub rax, rbx
imul rax, 2
On a :
Code: Select all
x dq 10
y dq 20
Code: Select all
mov rax, [x]
add rax, [y]
mov [x], rax
Code: Select all
RAX = ?
x = ?
y = ?
Analyse :
Code: Select all
mov eax, ecx
imul eax, 3
add eax, 2
cmp eax, 20
jne failed
Exercice 6
Que fait ce code ?
Code: Select all
mov rax, 100
xor rdx, rdx
mov rbx, 30
div rbx
Code: Select all
RAX = quotient
RDX = reste
Explique la différence entre :
Code: Select all
mul
imul
div
idiv
Analyse :
Code: Select all
mov rax, 20
mov rbx, 10
mov rcx, 1
mov rdx, 2
cmp rax, rbx
cmovg rcx, rdx
Exercice 9
Dans x64dbg, trouve une instruction :
Code: Select all
add
sub
imul
Observe la valeur du registre avant et après.
L'objectif est de prendre l'habitude de lire une instruction assembleur comme une transformation directe de l'état du CPU.
