Le contrôle de flux permet à un programme de choisir quelles instructions exécuter et combien de fois les exécuter.
En assembleur, les structures de haut niveau comme :
Code: Select all
if
else
while
do while
break
continue
Elles sont traduites en :
Code: Select all
comparaisons
flags
sauts conditionnels
sauts inconditionnels
labels
1. Exécution séquentielle
Par défaut, le processeur exécute les instructions les unes après les autres.
Exemple :
Code: Select all
mov eax, 1
add eax, 2
imul eax, 3
Code: Select all
1 -> 2 -> 3
C'est le rôle des branchements.
2. Les labels
Un label représente une position dans le code.
Exemple :
Code: Select all
start:
mov eax, 10
jmp end
middle:
mov eax, 20
end:
ret
Code: Select all
start
middle
end
Ils ne correspondent pas à une instruction exécutée : ce sont des noms associés à des adresses dans le code.
3. JMP — saut inconditionnel
L'instruction :
Code: Select all
jmp label
Exemple :
Code: Select all
mov eax, 1
jmp skip
mov eax, 2
skip:
ret
Code: Select all
mov eax, 2
Le processeur saute directement vers `skip`.
En reverse, `jmp` peut servir à :
- sortir d'une branche ;
- revenir au début d'une boucle ;
- rejoindre une zone commune de code ;
- implémenter des structures if/else.
L'instruction :
Code: Select all
cmp destination, source
Conceptuellement :
Code: Select all
destination - source
Exemple :
Code: Select all
cmp eax, 10
Code: Select all
EAX == 10
EAX != 10
EAX > 10
EAX < 10
...
Les sauts conditionnels utilisent les bits du registre :
Code: Select all
RFLAGS
Code: Select all
ZF = Zero Flag
SF = Sign Flag
CF = Carry Flag
OF = Overflow Flag
Code: Select all
cmp eax, ebx
je equal
Code: Select all
ZF = 1
Les instructions :
Code: Select all
je
jz
Elles sautent lorsque :
Code: Select all
ZF = 1
Code: Select all
cmp eax, 10
je equal
Code: Select all
if (eax == 10)
goto equal;
Les instructions :
Code: Select all
jne
jnz
Code: Select all
ZF = 0
Code: Select all
cmp eax, 10
jne different
Code: Select all
if (eax != 10)
goto different;
8. Comparaisons signées
Pour les entiers signés, on utilise principalement :
Code: Select all
jg = greater
jge = greater or equal
jl = less
jle = less or equal
Code: Select all
cmp eax, ebx
jg greater
Code: Select all
si EAX > EBX en signé
saute vers greater
Pour les entiers non signés, on rencontre :
Code: Select all
ja = above
jae = above or equal
jb = below
jbe = below or equal
Code: Select all
cmp eax, ebx
ja greater_unsigned
Code: Select all
jg
ja
`jg` interprète les valeurs comme signées.
`ja` les interprète comme non signées.
10. JC / JNC
Les instructions :
Code: Select all
jc
jnc
Code: Select all
jc -> CF = 1
jnc -> CF = 0
11. Traduction d'un IF simple
Code C :
Code: Select all
if (a == b)
{
x = 1;
}
Code: Select all
mov eax, [a]
cmp eax, [b]
jne end_if
mov dword [x], 1
end_if:
Au lieu de dire :
Code: Select all
si égal -> exécuter
Code: Select all
si différent -> sauter la branche
12. Traduction d'un IF / ELSE
Code C :
Code: Select all
if (a < b)
{
max = b;
}
else
{
max = a;
}
Code: Select all
mov eax, [a]
cmp eax, [b]
jge else_part
mov eax, [b]
mov [max], eax
jmp end_if
else_part:
mov eax, [a]
mov [max], eax
end_if:
Code: Select all
jcc -> choisir la branche
jmp -> éviter d'exécuter l'autre branche
Code C :
Code: Select all
if (a == 1)
{
result = 10;
}
else if (a == 2)
{
result = 20;
}
else
{
result = 0;
}
Code: Select all
cmp eax, 1
jne check_two
mov ebx, 10
jmp end
check_two:
cmp eax, 2
jne other
mov ebx, 20
jmp end
other:
xor ebx, ebx
end:
Code: Select all
cmp
jcc
cmp
jcc
cmp
jcc
14. Boucle WHILE
Code C :
Code: Select all
while (i < 10)
{
i++;
}
Code: Select all
while_start:
cmp ecx, 10
jge while_end
inc ecx
jmp while_start
while_end:
Code: Select all
test
|
| vrai
v
corps
|
v
retour au test
Un indice très fort est un saut vers une adresse plus basse.
Exemple :
Code: Select all
401000:
...
cmp eax, 10
jl 401000
Cela indique souvent une boucle.
En x64dbg, les lignes de saut permettent généralement de visualiser ce contrôle de flux.
16. BREAK
`break` n'existe pas directement comme instruction assembleur.
Il est généralement traduit par un saut vers la fin de la boucle.
Code C :
Code: Select all
while (1)
{
if (x == 10)
break;
x++;
}
Code: Select all
loop_start:
cmp eax, 10
je loop_end
inc eax
jmp loop_start
loop_end:
Code: Select all
saut depuis l'intérieur d'une boucle -> vers après la boucle
17. CONTINUE
`continue` revient directement à l'itération suivante.
Exemple C :
Code: Select all
while (i < 10)
{
i++;
if (i == 5)
continue;
x++;
}
Code: Select all
loop_start:
cmp ecx, 10
jge loop_end
inc ecx
cmp ecx, 5
je loop_start
inc eax
jmp loop_start
loop_end:
18. Boucle DO WHILE
Une boucle `do while` exécute son corps au moins une fois.
Code C :
Code: Select all
do
{
i++;
}
while (i < 10);
Code: Select all
loop_start:
inc ecx
cmp ecx, 10
jl loop_start
19. Compter des bits avec une boucle
Exemple logique :
Code: Select all
sum = 0;
for (i = 0; i < 64; i++)
{
sum += value & 1;
value >>= 1;
}
Code: Select all
xor edx, edx
xor ecx, ecx
loop_start:
cmp ecx, 64
jge end_loop
mov rbx, rax
and ebx, 1
add edx, ebx
shr rax, 1
inc ecx
jmp loop_start
end_loop:
20. Code source vs code compilé
Il ne faut pas s'attendre à retrouver exactement la structure C originale.
Un compilateur peut :
- inverser une condition ;
- déplacer le test ;
- supprimer un saut ;
- fusionner plusieurs branches ;
- réorganiser une boucle ;
- utiliser `cmov` au lieu d'un branchement ;
- dérouler une boucle.
Code: Select all
if (x == 0)
```
peut devenir :
[code]
test eax, eax
jne ...
Code: Select all
cmp eax, 0
je ...
Le loop unrolling consiste à répéter plusieurs fois le corps d'une boucle pour réduire le nombre de branchements.
Boucle classique :
Code: Select all
for (i = 0; i < 4; i++)
sum += array[i];
Code: Select all
sum += array[0];
sum += array[1];
sum += array[2];
sum += array[3];
Code: Select all
moins de comparaisons
moins de sauts
```
Inconvénient :
[code]
plus de code machine
22. Recherche dans une chaîne
Exemple C :
Code: Select all
while (data[i] != 0)
{
if (data[i] == target)
break;
i++;
}
Code: Select all
xor ecx, ecx
loop:
mov al, [data+rcx]
test al, al
je end
cmp al, bl
je found
inc ecx
jmp loop
found:
...
end:
Code: Select all
test al, al
```
comme un test du caractère nul.
[size=150][b][color=#FF0000]23. Instructions de chaînes x86[/color][/b][/size]
x86 possède plusieurs instructions spécialisées pour manipuler des blocs mémoire ou chaînes.
Les plus importantes :
[code]
movs
stos
lods
scas
cmps
En 64 bits :
Code: Select all
RSI = source
RDI = destination
RCX = compteur avec REP
`movsb` copie un octet :
Code: Select all
[RSI] -> [RDI]
Exemple :
Code: Select all
movsb
Code: Select all
[RDI] = [RSI]
RSI++
RDI++
25. REP MOVSB
Le préfixe :
Code: Select all
rep
Exemple :
Code: Select all
cld
mov rcx, 100
rep movsb
Code: Select all
[RSI]
```
vers :
[code]
[RDI]
26. STOSB
`stosb` écrit AL à l'adresse :
Code: Select all
[RDI]
Code: Select all
mov al, 0
mov rcx, 100
rep stosb
On peut rapprocher cela d'un `memset`.
27. LODSB
`lodsb` charge :
Code: Select all
AL = [RSI]
Cela permet de lire une suite d'octets depuis une source.
28. SCASB
`scasb` compare :
Code: Select all
AL
```
avec :
[code]
[RDI]
Cette instruction est très utile pour rechercher un octet dans une zone mémoire.
Exemple :
Code: Select all
mov al, 0
repne scasb
29. CMPSB
`cmpsb` compare :
Code: Select all
[RSI]
```
avec :
[code]
[RDI]
Avec :
Code: Select all
repe cmpsb
C'est proche du comportement d'une comparaison de blocs mémoire.
30. REP / REPE / REPNE
Les préfixes principaux sont :
Code: Select all
rep
repe
repz
repne
repnz
`repe` / `repz` répète tant que :
Code: Select all
ZF = 1
```
et que RCX n'est pas nul.
`repne` / `repnz` répète tant que :
[code]
ZF = 0
```
et que RCX n'est pas nul.
[size=150][b][color=#FF0000]31. CLD et STD[/color][/b][/size]
Le Direction Flag détermine le sens d'avancement des instructions de chaînes.
`cld` :
[code]
DF = 0
```
Les pointeurs avancent vers les adresses supérieures.
`std` :
[code]
DF = 1
```
Les pointeurs avancent vers les adresses inférieures.
En pratique, beaucoup de code utilise :
[code]
cld
32. Exemple typique en reverse
Tu vois :
Code: Select all
cmp eax, 25
jne failed
mov eax, 1
jmp end
failed:
xor eax, eax
end:
ret
Code: Select all
si EAX == 25
return 1
sinon
return 0
33. Reconstruire une boucle
Tu observes :
Code: Select all
loop_start:
cmp ecx, 10
jge end
add eax, ecx
inc ecx
jmp loop_start
end:
Code: Select all
while (ecx < 10)
{
eax += ecx;
ecx++;
}
34. Ce qu'il faut retenir pour le reverse
Pour ton usage, retiens surtout :
- `jmp` effectue un saut inconditionnel ;
- `cmp` compare deux valeurs en modifiant les flags ;
- `je/jz` signifie égal / zéro ;
- `jne/jnz` signifie différent / non zéro ;
- `jg/jge/jl/jle` servent aux comparaisons signées ;
- `ja/jae/jb/jbe` servent aux comparaisons non signées ;
- un saut vers une adresse plus basse indique souvent une boucle ;
- un saut vers après une boucle peut correspondre à `break` ;
- un saut vers le début ou le test peut correspondre à `continue` ;
- une boucle `while` teste généralement avant le corps ;
- une boucle `do while` teste après le corps ;
- le compilateur peut réorganiser les structures source ;
- `rep movsb` ressemble à une copie mémoire ;
- `rep stosb` ressemble à un remplissage mémoire ;
- `repne scasb` peut servir à rechercher un octet ;
- `repe cmpsb` peut servir à comparer des zones mémoire ;
- `RSI`, `RDI`, `RCX` sont importants avec les instructions de chaînes.
Exercice 1
Que fait :
Code: Select all
cmp eax, 10
je equal
Quelle est la différence entre :
Code: Select all
jg
ja
Reconstitue le pseudo-C :
Code: Select all
cmp eax, ebx
jne skip
mov ecx, 1
skip:
Analyse :
Code: Select all
xor ecx, ecx
loop:
cmp ecx, 5
jge end
inc ecx
jmp loop
end:
Exercice 5
Quelle structure C ressemble le plus à :
Code: Select all
loop:
inc eax
cmp eax, 10
jl loop
Explique la différence entre :
Code: Select all
while
do while
Exercice 7
Que fait :
Code: Select all
cld
mov rcx, 100
rep movsb
Que fait :
Code: Select all
mov al, 0
repne scasb
Exercice 9
Analyse :
Code: Select all
cmp eax, 25
jne failed
mov eax, 1
ret
failed:
xor eax, eax
ret
Et sinon ?
Exercice 10
Dans x64dbg, trouve une séquence contenant :
Code: Select all
cmp
jcc
jmp
Exercice 11
Trouve une boucle dans un programme désassemblé et repère :
Code: Select all
le test
le saut de sortie
le corps
le saut vers le début
