Shrug

Convertir une chaine en valeur hex en C

#autres#c#bowie41

Dans le cadre de la résolution du challenge Go go gadgeto du CTF NoBrackets2025, je suis tombé sur une fonction qui convertit une entrée utilisateur en valeur hexadécimale :

int populate_chain(int size, gadget *chain) { int i, j; long addr; char buffer; for (i = 0; i < size; i++) { printf("Utilise ton prochain gadget inspecteur (format deadbeef1337babe) : "); fflush(stdout); addr = 0; for (j = 0; j < 16; j++) { buffer = getchar(); if (buffer > 47 && buffer < 58) { buffer -= 48; } else if (buffer > 96 && buffer < 103) { buffer -= 87; } else { return 0; } addr += pow(16, 15-j) * buffer; } if (getchar() != '\n') { return 0; } chain[i] = addr; } return 1; }

Le début de la fonction est plutot clair : une boucle for qui englobe tout le reste. Le reste du code est donc éxécuté size fois.

A chaque execution le programme imprime une chaine :

printf("Utilise ton prochain gadget inspecteur (format deadbeef1337babe) : ");

Ensuite il répète 16 fois (à l'aide d'une autre boucle for) :

buffer = getchar(); if (buffer > 47 && buffer < 58) { buffer -= 48; } else if (buffer > 96 && buffer < 103) { buffer -= 87; } else { return 0; } addr += pow(16, 15-j) * buffer;

Il récupère un caractere depuis l'entrée standard avec getchar qu'il stocke dans buffer, de type char :

char buffer;

Il compare ensuite sa valeur avec un entier :

buffer > 47

Cela peut paraitre bizarre car d'habitude, on ne peut pas comparer un caractere et un entier. Par exemple en python le code suivant retourne une erreur en nous disant qu'il ne peut pas comparer un str et un int :

Bac à sable Python 3.10.0

Il faut alors comprendre comment C voit les char. En C les deux variables suivantes ont la même valeur :

int a = 97; char b = 'a';

Et je le prouve :

Bac à sable C 10.2.0

Cela s'explique car 'a' en ASCII correspond a 97 en décimal :

ASCIIBase 10

Entrée

a

Résultat

97

Plutôt que de stocker les caracteres en soi, C stocke donc leur valeur ASCII.
Note : il fait quand même la distinction entre leurs types.

Dans le code de notre fonction, si buffer > 47 && buffer < 58 (soit buffer est entre 48 et 57 inclus), on lui enlève 48.

Or, 48 en base 10 équivaut à '0' en ascii et 57 en base 10 équivaut à '9' en ascii.

Base 10ASCII

Entrée

48

Résultat

0
Base 10ASCII

Entrée

57

Résultat

9

En clair, si le caractere entré est un entier de 0 à 9, sa valeur est changée en entier.

Par exemple, si on entre '4' :

ASCIIBase 10

Entrée

4

Résultat

52

52 > 47 && 52 < 58 est vrai, on enlève donc 48 à sa valeur et on obtient : buffer = 52 - 48 soit buffer = 4.

Ensuite, si buffer > 96 && buffer < 103 (soit buffer est entre 97 et 102 inclus), on lui enlève 87.

Base 10ASCII

Entrée

97

Résultat

a
Base 10ASCII

Entrée

102

Résultat

f

Donc si le caractere entré est une lettre de a à f, on lui soustrait 87.

Par exemple, si on entre 'c' :

ASCIIBase 10

Entrée

c

Résultat

99

99 > 96 && 99 < 103 est vrai, on enlève donc 87 à sa valeur et on obtient : buffer = 99 - 87 soit buffer = 12. Or :

Base 16Base 10

Entrée

c

Résultat

12

Puis sinon (si buffer n'est pas dans 0-9 ou a-f) la fonction retourne 0.

En clair

Pour le moment, si le caractère entré dans l'entrée standard est valide en hexadécimal, il est converti en sa valeur héxadécimale. Sinon, la fonction retourne 0.

Ensuite, le programme ajoute pow(16, 15-j) * buffer à addr :

addr += pow(16, 15-j) * buffer;

Pour comprendre pourquoi, il faut revoir comment convertir de base 16 (héxadécimal) à base 10 (décimal). Pour cela, on prends l'example 0xfade :

On peut le décomposer en :

0xe + 0xd0 + 0xa00 + 0xf000

Puis :

Hexadécimal0xe0xd00xa000xf000
Conversion1601416^0 * 141611316^1 * 131621016^2 * 101631516^3 * 15
Résultat14208256061440
Somme64222

Base 16Base 10

Entrée

fade

Résultat

64222

Pour convertir un nombre héxadécimal en décimal, on prends donc pour chaque caractere (en partant de la droite) 16**index * valeur. C'est exactement ce que fait pow(16, 15-j) * buffer mais en partant de la gauche.

Après tout cela

Le programme lis un caractere de plus et s'il est différent de '\n" (retour à la ligne), retourne 0. Cela assure donc une entrée de la forme :

"deadbeef1337babe\n" # Soit 16 caracteres héxadcimaux puis un retour a la ligne

A chaque répétition, il stocke chaque valeur dans la liste chain puis retourne 1 si toutes les entrées étaient correctes.