Opérations sur les bits en assembleur 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:

Opérations sur les bits en assembleur x86-64

Post by Hydraxx »

Opérations sur les bits en assembleur x86-64

Les opérations sur les bits sont très fréquentes en assembleur, en programmation système et en reverse engineering.

Elles permettent notamment de :
  • tester des flags ;
  • activer ou désactiver des options ;
  • extraire une partie d'une valeur ;
  • combiner plusieurs états dans un entier ;
  • manipuler des permissions ;
  • effectuer certains calculs rapides ;
  • travailler directement avec des champs binaires.
Dans ce chapitre, nous allons voir principalement :

Code: Select all

not
and
or
xor
shl
shr
rol
ror
bt
bts
btr
1. Représentation binaire

Un entier est stocké sous forme de bits.

Par exemple :

Code: Select all

5 = 00000101b
Chaque bit peut être vu comme un état :

Code: Select all

0 = désactivé
1 = activé
Cela permet de stocker plusieurs informations dans une seule valeur.

Exemple :

Code: Select all

00000101b
Ici, les bits 0 et 2 sont activés.

En programmation système, une valeur peut donc représenter plusieurs options à la fois.

Exemple classique :

Code: Select all

MEM_COMMIT  = 0x1000
MEM_RESERVE = 0x2000
On peut les combiner :

Code: Select all

0x1000 | 0x2000 = 0x3000
2. NOT — inversion des bits

L'instruction :

Code: Select all

not
inverse tous les bits de l'opérande.

Exemple :

Code: Select all

00001111
devient :

Code: Select all

11110000
En assembleur :

Code: Select all

mov al, 00001111b
not al
Après `not`, chaque bit à 0 devient 1 et chaque bit à 1 devient 0.

Il faut bien distinguer :

Code: Select all

not = inversion bit à bit
neg = changement de signe arithmétique
Exemple :

Code: Select all

mov rax, 5
neg rax
donne :

Code: Select all

RAX = -5
Alors que :

Code: Select all

not rax
inverse simplement tous les bits de RAX.

3. AND — conserver certains bits

L'opération AND donne 1 uniquement lorsque les deux bits valent 1.

Table de vérité :

Code: Select all

0 AND 0 = 0
0 AND 1 = 0
1 AND 0 = 0
1 AND 1 = 1
Exemple :

Code: Select all

11001100
10101010
--------
10001000
En assembleur :

Code: Select all

and rax, rbx
Le résultat est stocké dans RAX.

4. Les masques avec AND

L'un des usages principaux de AND consiste à sélectionner certains bits.

Supposons :

Code: Select all

RAX = 0x12345678
Pour conserver uniquement l'octet faible :

Code: Select all

and rax, 0xFF
Résultat :

Code: Select all

RAX = 0x78
Pourquoi ?

Parce que :

Code: Select all

0xFF = 11111111b
Les bits placés à 1 dans le masque sont conservés.

Les bits placés à 0 sont supprimés.

On peut retenir :

Code: Select all

AND avec 1 -> conserve le bit
AND avec 0 -> efface le bit
5. Tester un flag avec AND

Supposons qu'une valeur contienne plusieurs flags :

Code: Select all

FLAG_READ    = 00000001b
FLAG_WRITE   = 00000010b
FLAG_EXECUTE = 00000100b
Et :

Code: Select all

permissions = 00000101b
Pour tester EXECUTE :

Code: Select all

permissions AND 00000100b
Résultat :

Code: Select all

00000100b
Le résultat est non nul, donc le flag est présent.

En assembleur, on peut rencontrer :

Code: Select all

mov eax, [permissions]
and eax, 4
jnz execute_enabled
6. OR — activer des bits

L'opération OR donne 1 si au moins un des deux bits vaut 1.

Table :

Code: Select all

0 OR 0 = 0
0 OR 1 = 1
1 OR 0 = 1
1 OR 1 = 1
Exemple :

Code: Select all

10000000
00000100
--------
10000100
L'utilisation classique de OR consiste à mettre certains bits à 1.

On peut retenir :

Code: Select all

OR avec 1 -> force le bit à 1
OR avec 0 -> conserve le bit
7. Combiner des flags avec OR

Exemple Windows :

Code: Select all

MEM_COMMIT  = 0x1000
MEM_RESERVE = 0x2000
On peut obtenir :

Code: Select all

MEM_COMMIT | MEM_RESERVE
soit :

Code: Select all

0x3000
C'est très fréquent avec les API Windows.

En assembleur, si on voulait construire la valeur :

Code: Select all

mov eax, 1000h
or  eax, 2000h
Résultat :

Code: Select all

EAX = 3000h
8. XOR — ou exclusif

XOR produit 1 si les bits sont différents.

Table :

Code: Select all

0 XOR 0 = 0
0 XOR 1 = 1
1 XOR 0 = 1
1 XOR 1 = 0
Exemple :

Code: Select all

10101010
11110000
--------
01011010
En assembleur :

Code: Select all

xor rax, rbx
9. XOR pour mettre un registre à zéro

Une instruction extrêmement fréquente est :

Code: Select all

xor eax, eax
Comme une valeur XOR elle-même vaut toujours 0 :

Code: Select all

EAX = 0
Et en x86-64, écrire dans EAX remet également les 32 bits supérieurs de RAX à zéro.

Donc :

Code: Select all

xor eax, eax
donne :

Code: Select all

RAX = 0
Tu verras cette instruction partout en reverse.

Par exemple :

Code: Select all

xor eax, eax
ret
signifie souvent :

Code: Select all

return 0;
10. XOR pour inverser certains bits

XOR permet également de basculer certains bits.

Exemple :

Code: Select all

valeur = 10100000b
masque = 00100000b
Après XOR :

Code: Select all

10000000b
Si on applique de nouveau le même masque :

Code: Select all

10100000b
Le bit ciblé est donc inversé à chaque opération.

11. Décalage à gauche avec SHL

L'instruction :

Code: Select all

shl
décale les bits vers la gauche.

Exemple :

Code: Select all

00000101b
après :

Code: Select all

shl al, 1
devient :

Code: Select all

00001010b
Soit :

Code: Select all

5 -> 10
Pour des entiers non signés sans débordement, décaler d'un bit vers la gauche revient à multiplier par 2.

Donc :

Code: Select all

shl rax, 1  -> *2
shl rax, 2  -> *4
shl rax, 3  -> *8
12. Décalage à droite avec SHR

`shr` décale les bits vers la droite.

Exemple :

Code: Select all

00010000b
après :

Code: Select all

shr al, 1
donne :

Code: Select all

00001000b
Soit :

Code: Select all

16 -> 8
Pour les valeurs non signées, cela ressemble à une division entière par une puissance de 2.

Exemple :

Code: Select all

shr rax, 1 -> /2
shr rax, 2 -> /4
shr rax, 3 -> /8
13. SHL et calcul d'offset

Les shifts sont fréquents pour calculer des index.

Supposons un tableau d'entiers 32 bits :

Code: Select all

int array[...]
Chaque élément fait 4 octets.

Pour calculer :

Code: Select all

index * 4
le compilateur peut utiliser :

Code: Select all

shl eax, 2
car :

Code: Select all

2^2 = 4
Donc en reverse :

Code: Select all

shl eax, 2
peut souvent signifier :

Code: Select all

eax *= 4
14. Extraire un champ avec SHR + AND

Supposons qu'on souhaite récupérer les bits 8 à 15 d'une valeur.

On peut faire :

Code: Select all

mov eax, valeur
shr eax, 8
and eax, 0FFh
Explication :

Code: Select all

shr eax, 8
place les bits 8-15 dans les bits 0-7.

Puis :

Code: Select all

and eax, 0FFh
efface tout sauf les 8 bits recherchés.

C'est une technique extrêmement importante en reverse.

15. Insérer un champ de bits

Pour insérer une valeur dans un champ, on suit généralement trois étapes :

Code: Select all

1. effacer l'ancien champ
2. décaler la nouvelle valeur
3. combiner avec OR
Exemple conceptuel :

Code: Select all

and rax, masque_inverse
shl rbx, position
or  rax, rbx
La première instruction nettoie la zone.

La deuxième place la nouvelle valeur au bon endroit.

La troisième la fusionne avec la valeur originale.

16. Rotation avec ROL

`rol` signifie :

Code: Select all

Rotate Left
Contrairement à `shl`, les bits qui sortent d'un côté reviennent de l'autre côté.

Exemple :

Code: Select all

10000001b
après une rotation gauche de 1 :

Code: Select all

00000011b
Le bit qui sort à gauche revient à droite.

17. Rotation avec ROR

`ror` effectue l'opération inverse :

Code: Select all

Rotate Right
Exemple :

Code: Select all

10000001b
après :

Code: Select all

ror al, 1
donne :

Code: Select all

11000000b
Contrairement aux shifts, les bits ne sont donc pas perdus.

18. Shift vs rotation

Différence essentielle :

Code: Select all

SHL / SHR
-> les bits sortants sont perdus

ROL / ROR
-> les bits sortants reviennent de l'autre côté
Les rotations apparaissent notamment dans certains algorithmes, transformations binaires et routines optimisées.

19. Tester un bit avec BT

L'instruction :

Code: Select all

bt
signifie :

Code: Select all

Bit Test
Elle teste un bit précis.

Exemple :

Code: Select all

bt rax, 4
teste le bit numéro 4 de RAX.

Le résultat du test est placé dans :

Code: Select all

CF = Carry Flag
Si le bit vaut 1 :

Code: Select all

CF = 1
Sinon :

Code: Select all

CF = 0
On peut ensuite utiliser un saut conditionnel lié à CF.

20. BTS — tester et mettre à 1

`bts` signifie :

Code: Select all

Bit Test and Set
Elle :

Code: Select all

1. copie l'ancien état du bit dans CF
2. met le bit à 1
Exemple :

Code: Select all

bts rax, 3
Le bit 3 de RAX devient 1.

21. BTR — tester et remettre à zéro

`btr` signifie :

Code: Select all

Bit Test and Reset
Elle :

Code: Select all

1. copie l'ancien état du bit dans CF
2. remet le bit à 0
Exemple :

Code: Select all

btr rax, 3
Le bit numéro 3 est effacé.

22. Bitsets

Un entier peut représenter un ensemble d'états.

Par exemple, un entier 64 bits peut représenter 64 éléments :

Code: Select all

bit 0  -> élément 0
bit 1  -> élément 1
...
bit 63 -> élément 63
Avec :

Code: Select all

bt
bts
btr
on peut tester, ajouter ou supprimer un élément du set.

Cette technique permet de stocker de nombreux booléens de manière compacte.

23. Exemple pratique de flags

Supposons :

Code: Select all

READ    = 1
WRITE   = 2
EXECUTE = 4
On crée :

Code: Select all

permissions = READ | WRITE
Donc :

Code: Select all

permissions = 3
Pour ajouter EXECUTE :

Code: Select all

permissions |= EXECUTE
En assembleur :

Code: Select all

or eax, 4
Pour retirer WRITE :

Code: Select all

and eax, ~2
Pour tester READ :

Code: Select all

test eax, 1
jnz has_read
24. TEST — AND sans conserver le résultat

Même si le chapitre se concentre sur les opérations bit à bit, il est important de connaître :

Code: Select all

test
`test` effectue conceptuellement un AND mais ne sauvegarde pas le résultat.

Il met seulement à jour les flags.

Exemple :

Code: Select all

test eax, eax
jz zero
Cela vérifie simplement si EAX vaut zéro.

Très fréquent en reverse :

Code: Select all

test rax, rax
je failed
peut souvent être lu comme :

Code: Select all

if (rax == 0)
    goto failed;
25. Exemple typique de reverse

Supposons :

Code: Select all

mov eax, ecx
and eax, 4
test eax, eax
je disabled
Tu peux lire :

Code: Select all

EAX = ECX
garde uniquement le bit correspondant à 4
teste si le résultat est nul
si nul -> disabled
Donc le programme vérifie probablement si un flag est activé.

26. Exemple avec XOR

Tu vois :

Code: Select all

xor eax, eax
ret
Cela signifie très souvent :

Code: Select all

EAX = 0
return
Comme EAX contient généralement la valeur de retour entière, on peut souvent traduire :

Code: Select all

return 0;
27. Exemple d'extraction

Supposons :

Code: Select all

mov eax, ecx
shr eax, 8
and eax, 0FFh
Le programme récupère :

Code: Select all

bits 8 à 15 de ECX
Cette séquence est typique pour lire un champ contenu dans une valeur plus grande.

28. Exemple d'insertion

Supposons qu'on souhaite remplacer les bits 8-15 de RAX par la valeur contenue dans BL.

Conceptuellement :

Code: Select all

and rax, 0FFFFFFFFFFFF00FFh
movzx rbx, bl
shl rbx, 8
or rax, rbx
Étapes :

Code: Select all

1. effacer bits 8-15
2. placer BL en position 8
3. fusionner le champ avec RAX
29. Ce qu'il faut retenir pour le reverse

Pour le reverse x64, retiens surtout :
  • `not` inverse tous les bits ;
  • `and` sert surtout à masquer / sélectionner des bits ;
  • `or` sert surtout à activer des bits ;
  • `xor` sert à basculer des bits et très souvent à mettre un registre à zéro ;
  • `xor eax, eax` signifie presque toujours EAX = 0 ;
  • `shl` décale vers la gauche et peut correspondre à une multiplication par une puissance de 2 ;
  • `shr` décale vers la droite et peut correspondre à une division non signée par une puissance de 2 ;
  • `shl/shr + and` permet souvent d'extraire un champ ;
  • `rol` et `ror` font des rotations sans perdre les bits ;
  • `bt` teste un bit précis ;
  • `bts` teste puis met le bit à 1 ;
  • `btr` teste puis remet le bit à 0 ;
  • `test` permet souvent de tester un registre ou un flag sans modifier la valeur ;
  • les masques sont très fréquents avec les flags Windows et les structures bas niveau.
30. Exercices

Exercice 1

Calcule :

Code: Select all

10101010b AND 11110000b
Exercice 2

Calcule :

Code: Select all

10101010b OR 00001111b
Exercice 3

Calcule :

Code: Select all

10101010b XOR 11111111b
Exercice 4

Que vaut RAX après :

Code: Select all

mov rax, 12345678h
and rax, 0FFh
Exercice 5

Que vaut RAX après :

Code: Select all

mov rax, 10
shl rax, 2
Exercice 6

Que vaut RAX après :

Code: Select all

mov rax, 64
shr rax, 3
Exercice 7

Que fait :

Code: Select all

xor eax, eax
et pourquoi est-ce très fréquent dans du code compilé ?

Exercice 8

On a :

Code: Select all

EAX = 00000101b
Le flag :

Code: Select all

00000100b
est-il activé ?

Explique comment le vérifier avec AND ou TEST.

Exercice 9

Analyse :

Code: Select all

mov eax, ecx
shr eax, 8
and eax, 0FFh
Quelle partie de ECX est extraite ?

Exercice 10

Explique la différence entre :

Code: Select all

shl
rol
Exercice 11

Quelle est la différence entre :

Code: Select all

bt rax, 5
bts rax, 5
btr rax, 5
Exercice 12

Dans x64dbg, trouve une séquence contenant :

Code: Select all

and
or
xor
test
shl
shr
Place un breakpoint avant l'instruction et observe le registre concerné avant puis après.

L'objectif est de prendre l'habitude de reconnaître immédiatement :

Code: Select all

masque
flag
mise à zéro
extraction de bits
calcul d'offset

Who is online

Users browsing this forum: No registered users and 1 guest