Extraire la clé XOR : reverse d'une vérification de licence codée en dur en C
Le défi
Ce keygen en C lit un numéro de série, applique un XOR à chaque octet avec un secret codé en dur, puis compare le résultat à une cible. Lisez la boucle de validation, trouvez quelle chaîne sert de clé XOR, et tapez-la exactement.
Ce que tu vas apprendre
- Lire une boucle de validation en C et identifier l'étape de XOR octet par octet
- Reconnaître une chaîne littérale codée en dur utilisée comme clé cryptographique
- Comprendre pourquoi le XOR est réversible et comment retrouver l'entrée d'origine
- Expliquer pourquoi les secrets livrés dans un binaire client ne sont pas secrets
- Distinguer l'obfuscation d'une véritable protection cryptographique
Compétences testées
Prérequis
- Syntaxe de base en C (tableaux, boucles, pointeurs)
- Le fonctionnement de l'opérateur XOR (^)
- Octets et ASCII
Comment ça marche
De nombreuses vérifications de licence tentent de cacher le numéro de série valide derrière une transformation. Le XOR est une méthode courante : chaque octet de la saisie de l'utilisateur est XORé avec un octet de clé, puis le résultat est comparé à une table précalculée. Le XOR est séduisant car il est rapide et symétrique, mais c'est justement cette symétrie qui en fait une mauvaise protection : la même opération qui brouille les données les débrouille aussi.
Dans keygen.c, check_serial déclare const char *key = "K3yR3v" puis parcourt le numéro de série en boucle, calculant serial[i] ^ key[i % klen] et comparant chaque résultat à expected[i]. Tout ce dont la vérification a besoin se trouve dans le binaire : la chaîne clé, la table attendue, et l'algorithme. Comme (serial ^ key) == expected implique serial == expected ^ key, un attaquant peut lire la clé dans le code source et calculer instantanément un numéro de série valide, sans avoir besoin de brute force.
La leçon à retenir pour le reverse engineering est que la réponse se trouve en pleine vue, sous la forme d'une chaîne littérale. La difficulté consiste à reconnaître que l'opérande XOR de la boucle est la clé, et non la table expected (qui est la cible) ni le numéro de série (qui est la saisie de l'attaquant). Ici, la clé est K3yR3v.
Erreurs fréquentes
- Soumettre les octets de expected[]. Cette table est la cible à laquelle le résultat du XOR est comparé, pas la clé.
- Chercher la clé dans main(). La clé est une variable locale dans
check_serial;mainse contente de lire argv et d'afficher le verdict. - Supposer que la clé est aléatoire ou dérivée. C'est une simple chaîne littérale codée en dur dans le code source.
- Confondre le numéro de série avec la clé. Le numéro de série est la saisie de l'attaquant (argv[1]) ; la clé est la constante avec laquelle il est XORé.
Comment s'en protéger
Le XOR côté client relève de l'obfuscation, pas de la sécurité. Quiconque possède le binaire peut lire la clé. Ne comptez pas sur une constante cachée pour protéger une licence ou des secrets.
- Validez les licences côté serveur, là où le secret et la vérification n'atteignent jamais l'utilisateur.
- Si une vérification locale est indispensable, utilisez des jetons signés (cryptographie asymétrique) pour que le client ne détienne qu'une clé publique et ne puisse pas forger de numéro de série valide.
- Ne stockez jamais de clés d'API ou de clés de signature en clair sous forme de chaînes littérales dans le code livré.
- Considérez que tout ce qui est compilé dans un client est entièrement lisible par un attaquant.