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.
Code: Select all
not
and
or
xor
shl
shr
rol
ror
bt
bts
btr
Un entier est stocké sous forme de bits.
Par exemple :
Code: Select all
5 = 00000101b
Code: Select all
0 = désactivé
1 = activé
Exemple :
Code: Select all
00000101b
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
Code: Select all
0x1000 | 0x2000 = 0x3000
L'instruction :
Code: Select all
not
Exemple :
Code: Select all
00001111
Code: Select all
11110000
Code: Select all
mov al, 00001111b
not al
Il faut bien distinguer :
Code: Select all
not = inversion bit à bit
neg = changement de signe arithmétique
Code: Select all
mov rax, 5
neg rax
Code: Select all
RAX = -5
Code: Select all
not 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
Code: Select all
11001100
10101010
--------
10001000
Code: Select all
and rax, rbx
4. Les masques avec AND
L'un des usages principaux de AND consiste à sélectionner certains bits.
Supposons :
Code: Select all
RAX = 0x12345678
Code: Select all
and rax, 0xFF
Code: Select all
RAX = 0x78
Parce que :
Code: Select all
0xFF = 11111111b
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
Supposons qu'une valeur contienne plusieurs flags :
Code: Select all
FLAG_READ = 00000001b
FLAG_WRITE = 00000010b
FLAG_EXECUTE = 00000100b
Code: Select all
permissions = 00000101b
Code: Select all
permissions AND 00000100b
Code: Select all
00000100b
En assembleur, on peut rencontrer :
Code: Select all
mov eax, [permissions]
and eax, 4
jnz execute_enabled
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
Code: Select all
10000000
00000100
--------
10000100
On peut retenir :
Code: Select all
OR avec 1 -> force le bit à 1
OR avec 0 -> conserve le bit
Exemple Windows :
Code: Select all
MEM_COMMIT = 0x1000
MEM_RESERVE = 0x2000
Code: Select all
MEM_COMMIT | MEM_RESERVE
Code: Select all
0x3000
En assembleur, si on voulait construire la valeur :
Code: Select all
mov eax, 1000h
or eax, 2000h
Code: Select all
EAX = 3000h
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
Code: Select all
10101010
11110000
--------
01011010
Code: Select all
xor rax, rbx
Une instruction extrêmement fréquente est :
Code: Select all
xor eax, eax
Code: Select all
EAX = 0
Donc :
Code: Select all
xor eax, eax
Code: Select all
RAX = 0
Par exemple :
Code: Select all
xor eax, eax
ret
Code: Select all
return 0;
XOR permet également de basculer certains bits.
Exemple :
Code: Select all
valeur = 10100000b
masque = 00100000b
Code: Select all
10000000b
Code: Select all
10100000b
11. Décalage à gauche avec SHL
L'instruction :
Code: Select all
shl
Exemple :
Code: Select all
00000101b
Code: Select all
shl al, 1
Code: Select all
00001010b
Code: Select all
5 -> 10
Donc :
Code: Select all
shl rax, 1 -> *2
shl rax, 2 -> *4
shl rax, 3 -> *8
`shr` décale les bits vers la droite.
Exemple :
Code: Select all
00010000b
Code: Select all
shr al, 1
Code: Select all
00001000b
Code: Select all
16 -> 8
Exemple :
Code: Select all
shr rax, 1 -> /2
shr rax, 2 -> /4
shr rax, 3 -> /8
Les shifts sont fréquents pour calculer des index.
Supposons un tableau d'entiers 32 bits :
Code: Select all
int array[...]
Pour calculer :
Code: Select all
index * 4
Code: Select all
shl eax, 2
Code: Select all
2^2 = 4
Code: Select all
shl eax, 2
Code: Select all
eax *= 4
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
Code: Select all
shr eax, 8
Puis :
Code: Select all
and eax, 0FFh
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
Code: Select all
and rax, masque_inverse
shl rbx, position
or rax, rbx
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
Exemple :
Code: Select all
10000001b
Code: Select all
00000011b
17. Rotation avec ROR
`ror` effectue l'opération inverse :
Code: Select all
Rotate Right
Code: Select all
10000001b
Code: Select all
ror al, 1
Code: Select all
11000000b
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é
19. Tester un bit avec BT
L'instruction :
Code: Select all
bt
Code: Select all
Bit Test
Exemple :
Code: Select all
bt rax, 4
Le résultat du test est placé dans :
Code: Select all
CF = Carry Flag
Code: Select all
CF = 1
Code: Select all
CF = 0
20. BTS — tester et mettre à 1
`bts` signifie :
Code: Select all
Bit Test and Set
Code: Select all
1. copie l'ancien état du bit dans CF
2. met le bit à 1
Code: Select all
bts rax, 3
21. BTR — tester et remettre à zéro
`btr` signifie :
Code: Select all
Bit Test and Reset
Code: Select all
1. copie l'ancien état du bit dans CF
2. remet le bit à 0
Code: Select all
btr rax, 3
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
Code: Select all
bt
bts
btr
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
Code: Select all
permissions = READ | WRITE
Code: Select all
permissions = 3
Code: Select all
permissions |= EXECUTE
Code: Select all
or eax, 4
Code: Select all
and eax, ~2
Code: Select all
test eax, 1
jnz has_read
Même si le chapitre se concentre sur les opérations bit à bit, il est important de connaître :
Code: Select all
test
Il met seulement à jour les flags.
Exemple :
Code: Select all
test eax, eax
jz zero
Très fréquent en reverse :
Code: Select all
test rax, rax
je failed
Code: Select all
if (rax == 0)
goto failed;
Supposons :
Code: Select all
mov eax, ecx
and eax, 4
test eax, eax
je disabled
Code: Select all
EAX = ECX
garde uniquement le bit correspondant à 4
teste si le résultat est nul
si nul -> disabled
26. Exemple avec XOR
Tu vois :
Code: Select all
xor eax, eax
ret
Code: Select all
EAX = 0
return
Code: Select all
return 0;
Supposons :
Code: Select all
mov eax, ecx
shr eax, 8
and eax, 0FFh
Code: Select all
bits 8 à 15 de ECX
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
Code: Select all
1. effacer bits 8-15
2. placer BL en position 8
3. fusionner le champ avec RAX
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.
Exercice 1
Calcule :
Code: Select all
10101010b AND 11110000b
Calcule :
Code: Select all
10101010b OR 00001111b
Calcule :
Code: Select all
10101010b XOR 11111111b
Que vaut RAX après :
Code: Select all
mov rax, 12345678h
and rax, 0FFh
Que vaut RAX après :
Code: Select all
mov rax, 10
shl rax, 2
Que vaut RAX après :
Code: Select all
mov rax, 64
shr rax, 3
Que fait :
Code: Select all
xor eax, eax
Exercice 8
On a :
Code: Select all
EAX = 00000101b
Code: Select all
00000100b
Explique comment le vérifier avec AND ou TEST.
Exercice 9
Analyse :
Code: Select all
mov eax, ecx
shr eax, 8
and eax, 0FFh
Exercice 10
Explique la différence entre :
Code: Select all
shl
rol
Quelle est la différence entre :
Code: Select all
bt rax, 5
bts rax, 5
btr rax, 5
Dans x64dbg, trouve une séquence contenant :
Code: Select all
and
or
xor
test
shl
shr
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
