Les registres x86-64

Salut ce forum est dédié à comprendre les entrailles de l'ordinateur au plus bas niveau bienvenue dans le monde du reverse et de l'assembleur

Moderator: Rick

Post Reply
Hydraxx
Site Admin
Posts: 114
Joined: Mon Jan 12, 2026 4:04 pm
Location: France
Contact:

Les registres x86-64

Post by Hydraxx »

Les registres x86-64

Les registres sont de très petites zones de stockage directement accessibles par le processeur. Ils sont essentiels en assembleur : presque toutes les opérations importantes passent par eux.

En reverse engineering, comprendre les registres permet de suivre les paramètres d'une fonction, les valeurs de retour, les pointeurs, les calculs et l'évolution du programme instruction par instruction.

1. Pourquoi utiliser des registres ?

La RAM permet de stocker énormément de données, mais son accès est beaucoup plus lent qu'un accès aux registres du CPU.

On peut retenir cette hiérarchie simplifiée :

Code: Select all

Registres CPU
     |
     v
Caches CPU
     |
     v
RAM
Les registres sont très peu nombreux, mais extrêmement rapides.

Le processeur les utilise donc en permanence pour conserver temporairement les valeurs sur lesquelles il travaille.

Par exemple :

Code: Select all

mov rax, 10
add rax, 5
Ici, `RAX` contient directement la valeur utilisée par l'opération arithmétique.

Après l'addition :

Code: Select all

RAX = 15
2. Registres généraux x86-64

L'architecture x86-64 possède notamment 16 registres généraux 64 bits :

Code: Select all

RAX
RBX
RCX
RDX
RSI
RDI
RBP
RSP

R8
R9
R10
R11
R12
R13
R14
R15
Historiquement, certains registres avaient des rôles privilégiés :

Code: Select all

AX -> accumulateur
BX -> registre de base
CX -> compteur
DX -> registre de données

SI -> source index
DI -> destination index
BP -> base pointer
SP -> stack pointer
Sur les processeurs modernes, beaucoup de ces registres sont utilisables de manière générale, même si certaines instructions et conventions d'appel leur donnent encore des rôles particuliers.

3. Les sous-registres

Un même registre peut être accédé avec différentes tailles.

Prenons `RAX` :

Code: Select all

RAX = 64 bits
EAX = 32 bits
AX  = 16 bits
AL  = 8 bits faibles
AH  = 8 bits suivants
Schématiquement :

Code: Select all

RAX
+---------------------------------------------------------------+
|                           64 bits                             |
+--------------------------------+------------------------------+
                                 |             EAX              |
                                 +---------------+---------------+
                                                 |      AX       |
                                                 +-------+-------+
                                                 |  AH   |  AL   |
                                                 +-------+-------+
Le même principe existe pour plusieurs registres historiques :

Code: Select all

RBX -> EBX -> BX -> BL/BH
RCX -> ECX -> CX -> CL/CH
RDX -> EDX -> DX -> DL/DH
Pour les autres registres, on trouve par exemple :

Code: Select all

RSI -> ESI -> SI -> SIL
RDI -> EDI -> DI -> DIL
RBP -> EBP -> BP -> BPL
RSP -> ESP -> SP -> SPL

R8  -> R8D  -> R8W  -> R8B
R9  -> R9D  -> R9W  -> R9B
...
R15 -> R15D -> R15W -> R15B
4. Effet d'une écriture dans EAX

Une particularité extrêmement importante en x86-64 est qu'une écriture dans un registre 32 bits met automatiquement à zéro les 32 bits supérieurs du registre 64 bits correspondant.

Exemple :

Code: Select all

RAX = 0xFFFFFFFFFFFFFFFF

mov eax, 1
Après l'instruction :

Code: Select all

RAX = 0x0000000000000001
Ce comportement est très fréquent dans le code désassemblé.

En revanche, écrire uniquement dans `AX` ou `AL` ne remet pas automatiquement tout le reste de `RAX` à zéro.

5. RIP — Instruction Pointer

RIP est le registre contenant l'adresse de l'instruction en cours d'exécution / de la prochaine instruction selon le contexte d'observation.

Dans un debugger, RIP permet donc de savoir où se trouve actuellement l'exécution.

Exemple conceptuel :

Code: Select all

RIP -> 00007FF6...1000

00007FF6...1000   mov eax, 5
00007FF6...1005   add eax, 2
00007FF6...1008   ret
En avançant instruction par instruction, RIP progresse vers les instructions suivantes ou change davantage lors d'un saut, d'un `call` ou d'un `ret`.

6. RSP et RBP

RSP est le stack pointer.

Il pointe vers le sommet courant de la pile.

Il est particulièrement important pour :
  • les appels de fonctions ;
  • les adresses de retour ;
  • les sauvegardes de registres ;
  • les variables locales placées sur la stack ;
  • certains paramètres.
Exemple :

Code: Select all

sub rsp, 20h
Cette instruction descend le pointeur de pile et réserve 32 octets.

Puis :

Code: Select all

add rsp, 20h
restaure cet espace.

RBP peut servir de base stable pour une stack frame.

Exemple classique :

Code: Select all

push rbp
mov rbp, rsp
Cependant, les compilateurs modernes n'utilisent pas obligatoirement RBP comme frame pointer dans toutes les fonctions.

7. RFLAGS

Le registre RFLAGS contient plusieurs indicateurs décrivant notamment le résultat de certaines opérations.

Parmi les flags importants :

Code: Select all

ZF = Zero Flag
CF = Carry Flag
SF = Sign Flag
OF = Overflow Flag
Exemple :

Code: Select all

cmp eax, 10
je equal
`cmp` effectue conceptuellement une soustraction pour mettre à jour les flags sans conserver le résultat.

Si :

Code: Select all

EAX == 10
alors `ZF` est positionné et `je` peut effectuer le saut.

C'est fondamental en reverse engineering, car beaucoup de conditions apparaissent sous la forme :

Code: Select all

cmp ...
je  ...
jne ...
jg  ...
jl  ...
8. Observer les registres dans un debugger

Un debugger affiche généralement une fenêtre contenant l'état des registres.

On peut y retrouver :

Code: Select all

RAX
RBX
RCX
RDX
RSI
RDI
RSP
RBP
R8-R15
RIP
RFLAGS
La méthode la plus utile consiste à placer un breakpoint puis à avancer instruction par instruction.

Par exemple :

Code: Select all

mov rax, 100
add rax, 20
Avant :

Code: Select all

RAX = valeur quelconque
Après le premier `mov` :

Code: Select all

RAX = 100
Après le `add` :

Code: Select all

RAX = 120
En reverse, il faut prendre l'habitude de regarder simultanément :

Code: Select all

instruction
registres
stack
mémoire
9. L'instruction MOV

`mov` est l'une des instructions les plus fréquentes en assembleur.

Son principe général est :

Code: Select all

mov destination, source
Elle copie une valeur depuis la source vers la destination.

Exemple :

Code: Select all

mov rax, 100
signifie :

Code: Select all

RAX <- 100
La source n'est pas détruite. `mov` effectue une copie de la valeur.

10. Constante vers registre

Une valeur directement présente dans l'instruction est appelée une valeur immédiate.

Exemple :

Code: Select all

mov rax, 100
Le registre reçoit directement 100.

Autre exemple :

Code: Select all

mov eax, 10
donne :

Code: Select all

EAX = 10
et, en mode 64 bits, l'écriture dans EAX met également à zéro la moitié supérieure de RAX :

Code: Select all

RAX = 000000000000000Ah
11. Mémoire vers registre

Une opération extrêmement importante consiste à charger une valeur depuis la mémoire.

Exemple :

Code: Select all

mov rax, [a]
Les crochets indiquent que l'on veut accéder au contenu situé à l'adresse.

C'est une notion essentielle.

Compare :

Code: Select all

adresse de a
et :

Code: Select all

[a]
Le premier concept désigne l'emplacement.

Le second désigne la valeur stockée à cet emplacement.

En C/C++, on peut rapprocher cela de :

Code: Select all

int* p = &a;

adresse : p
valeur  : *p
En assembleur, les crochets représentent donc souvent une forme de déréférencement mémoire.

Exemple :

Code: Select all

a dq 175

mov rax, [a]
Après l'instruction :

Code: Select all

RAX = 175
12. Registre vers mémoire

L'opération inverse consiste à prendre une valeur contenue dans un registre et à l'écrire en mémoire.

Exemple :

Code: Select all

mov [a], rax
Si :

Code: Select all

RAX = 175
alors la valeur 175 est écrite à l'emplacement mémoire correspondant à `a`, avec la taille imposée par l'opérande/instruction.

Conceptuellement :

Code: Select all

RAX
 |
 | valeur
 v
mémoire [a]
Dans un debugger, on peut observer directement la modification dans la fenêtre mémoire.

13. Registre vers registre

On peut également copier une valeur d'un registre vers un autre.

Exemple :

Code: Select all

mov rax, rbx
Si :

Code: Select all

RBX = 42
alors après l'instruction :

Code: Select all

RAX = 42
RBX = 42
RBX reste inchangé.

C'est une copie, pas un déplacement destructif malgré le nom `mov`.

14. Suivre une suite de MOV

Prenons :

Code: Select all

mov rax, 100
mov rbx, rax
mov [value], rbx
mov rcx, [value]
On peut suivre les valeurs :

Code: Select all

1. RAX = 100

2. RBX = RAX
   RBX = 100

3. [value] = RBX
   mémoire[value] = 100

4. RCX = [value]
   RCX = 100
Ce type de raisonnement est exactement celui utilisé lorsqu'on suit des données dans un désassemblage.

15. Attention à la taille des données

Le processeur doit savoir quelle quantité de mémoire est manipulée.

Exemples :

Code: Select all

mov al,  [value]   ; 8 bits
mov ax,  [value]   ; 16 bits
mov eax, [value]   ; 32 bits
mov rax, [value]   ; 64 bits
Selon la syntaxe assembleur et le contexte, on peut également rencontrer des indications explicites comme :

Code: Select all

byte
word
dword
qword
Soit :

Code: Select all

byte  = 8 bits
word  = 16 bits
dword = 32 bits
qword = 64 bits
En reverse, la taille de l'opération est très importante : lire 1 octet et lire 8 octets à la même adresse ne donne évidemment pas la même chose.

16. Adresse ou contenu ?

C'est probablement l'une des distinctions les plus importantes à maîtriser.

Supposons :

Code: Select all

RAX = 000001F000001000h
RAX peut contenir une simple valeur numérique.

Mais cette valeur peut également être interprétée comme une adresse.

Si le code fait :

Code: Select all

mov rbx, [rax]
le processeur considère alors RAX comme une adresse et lit la mémoire située à cet emplacement.

On peut retenir :

Code: Select all

RAX     -> valeur contenue dans RAX

[RAX]   -> mémoire pointée par RAX
C'est exactement ce qu'il faut comprendre lorsqu'on suit des pointeurs dans x64dbg.

17. Registres et fonctions Windows x64

En reverse Windows x64, certains registres apparaissent constamment à cause de la convention d'appel Microsoft x64.

Pour les arguments entiers ou pointeurs, les quatre premiers sont généralement passés dans :

Code: Select all

RCX = premier argument
RDX = deuxième argument
R8  = troisième argument
R9  = quatrième argument
Les arguments supplémentaires sont généralement passés via la stack.

La valeur de retour entière ou pointeur est généralement placée dans :

Code: Select all

RAX
Exemple conceptuel :

Code: Select all

VirtualAlloc(
    lpAddress,
    dwSize,
    flAllocationType,
    flProtect
)
Juste avant un appel, on pourra typiquement chercher :

Code: Select all

RCX -> lpAddress
RDX -> dwSize
R8  -> flAllocationType
R9  -> flProtect
Puis après le retour :

Code: Select all

RAX -> adresse retournée
Cette connaissance est extrêmement utile lorsqu'on place un breakpoint sur une API Windows et qu'on veut retrouver rapidement ses arguments.

18. Exemple de reverse

Supposons que le debugger affiche :

Code: Select all

mov edx, 1000h
mov r8d, 3000h
mov r9d, 4
call VirtualAlloc
En connaissant la convention d'appel Windows x64, on peut commencer à interpréter immédiatement :

Code: Select all

RDX = 0x1000
R8  = 0x3000
R9  = 0x4
Ces valeurs peuvent ensuite être comparées aux paramètres documentés de l'API.

Par exemple, selon le contexte :

Code: Select all

0x3000 = MEM_COMMIT | MEM_RESERVE
0x4    = PAGE_READWRITE
Après le `call`, on peut examiner :

Code: Select all

RAX
pour récupérer la valeur de retour.

C'est précisément pour ce genre d'analyse que la maîtrise des registres est beaucoup plus importante en reverse user mode que de connaître par cœur tous les détails des tables de pages.

19. Points essentiels à retenir

Pour être à l'aise en reverse x86-64, retiens surtout :
  • les registres sont des zones de stockage extrêmement rapides du CPU ;
  • RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP et R8-R15 sont les principaux registres généraux ;
  • RAX est 64 bits, EAX 32 bits, AX 16 bits et AL 8 bits ;
  • écrire dans EAX remet à zéro les 32 bits supérieurs de RAX ;
  • RIP permet de suivre l'exécution ;
  • RSP pointe vers le sommet de la stack ;
  • RFLAGS contient notamment ZF, CF, SF et OF ;
  • `mov destination, source` copie une valeur ;
  • les crochets indiquent généralement un accès au contenu mémoire ;
  • `mov rax, [adresse]` lit la mémoire ;
  • `mov [adresse], rax` écrit en mémoire ;
  • `mov rax, rbx` copie RBX dans RAX sans modifier RBX ;
  • la taille 8/16/32/64 bits de l'opération est importante ;
  • sous Windows x64, RCX, RDX, R8 et R9 sont essentiels pour suivre les quatre premiers arguments entiers/pointeurs ;
  • RAX contient généralement la valeur de retour entière ou pointeur.
20. Exercices

Exercice 1

Après :

Code: Select all

mov rax, 10
mov rbx, rax
mov rcx, 20
donne les valeurs finales de :

Code: Select all

RAX
RBX
RCX
Exercice 2

On a :

Code: Select all

RAX = FFFFFFFFFFFFFFFFh
Puis :

Code: Select all

mov eax, 5
Quelle est la nouvelle valeur complète de RAX ?

Exercice 3

Explique la différence entre :

Code: Select all

mov rax, rbx
mov rax, [rbx]
Exercice 4

On a :

Code: Select all

value dq 50

mov rax, [value]
mov rbx, rax
add rbx, 10
mov [value], rbx
Quelle est la valeur finale :

Code: Select all

RAX = ?
RBX = ?
[value] = ?
Exercice 5

Tu observes avant un appel Windows :

Code: Select all

RCX = 0
RDX = 1000h
R8  = 3000h
R9  = 4
call VirtualAlloc
Identifie les quatre arguments à partir de la convention d'appel Windows x64.

Exercice 6

Dans x64dbg, place un breakpoint sur une fonction simple puis avance instruction par instruction.

À chaque instruction, note :

Code: Select all

RIP
RAX
RCX
RDX
RSP
RFLAGS
Observe quelles instructions modifient quels registres.

Exercice 7

Explique avec tes propres mots la différence entre :

Code: Select all

une valeur
une adresse
le contenu d'une adresse
Puis donne un exemple utilisant un registre comme pointeur.

Who is online

Users browsing this forum: No registered users and 0 guests