Face à un SDK de signature obfusqué, on a vite l’impression d’être devant un mécanisme ésotérique dont aucune ligne ne peut être comprise isolément.
Après avoir déroulé tout le processus de rétro-ingénierie, le constat est généralement bien plus simple : 90 % du code correspond à des algorithmes standard. Le hachage est public, l’encodage est public, le chiffrement par flot est public. Il ne s’agit même pas de « comprendre » ces briques : il suffit d’identifier l’algorithme, de reprendre l’implémentation de la RFC ou une référence fiable, et le résultat sera strictement identique octet pour octet.
Le danger se trouve dans les 10 % restants : les endroits où un algorithme connu a été très légèrement altéré. Un nombre de tours normalement constant devient variable ; une constante de base est remplacée par une valeur voisine ; les indices de mots d’un tour sont permutés ; la clé est dissimulée dans le texte chiffré. Chaque écart paraît minime, mais il suffit d’en manquer un pour que votre réécriture ne corresponde jamais à la cible. Et comme les 90 % restants sont justes, localiser l’erreur devient particulièrement pénible.
Le vrai enjeu de la rétro-ingénierie d’une signature obfusquée n’est donc pas de lire les 90 % standards, mais d’isoler les 10 % de points de divergence. C’est précisément là qu’un grand modèle est le plus difficile à remplacer : dans la chasse méthodique à ces modifications.
(Le prérequis — identifier une famille d’algorithmes, reconnaître « c’est du ChaCha » ou « c’est du FNV » à partir des constantes et de la structure — est abordé dans l’article précédent. Ici, je pars du principe que le squelette est déjà identifié et je vais directement à la recherche des divergences.)
Pourquoi les divergences échappent si facilement à l’œil humain
Quand il reconnaît un motif, le cerveau humain a un réflexe très fort : dès qu’il a compris l’idée générale, il cesse d’examiner les détails.
Vous voyez un bloc de code, les constantes portent l’empreinte de ChaCha, la structure semble correcte : votre cerveau coche « ChaCha20 » et passe à autre chose. Il ne va pas vérifier octet par octet le nombre de tours, chaque indice ou le bit de poids faible de chaque constante. La satisfaction d’avoir déjà reconnu le mécanisme coupe l’attention.
Les auteurs d’obfuscation comptent exactement là-dessus. Ils ne réécrivent pas l’algorithme entier — ce serait coûteux et propice aux bugs — mais apportent la modification la plus infime possible à un standard : un nombre changé, un indice échangé, une étape ajoutée. C’est assez discret pour que la reconnaissance visuelle le laisse passer, mais suffisant pour faire échouer complètement toute réimplémentation conforme au standard.
Le rapport de force est asymétrique : l’auteur n’a besoin de cacher qu’une seule divergence ; vous devez toutes les trouver. Or, l’humain est naturellement mauvais pour « rester méfiant face à quelque chose qui paraît correct ».
Le modèle possède ici un avantage contre-intuitif : il n’éprouve pas cette satisfaction de reconnaissance qui lui ferait relâcher son attention. Si votre prompt lui demande explicitement de chercher les écarts, il peut contrôler chaque point sans s’arrêter à mi-chemin — à condition de bien le guider, ce qui sera le sujet de la section 5. Voyons d’abord les quatre formes que prennent le plus souvent ces divergences.
Les quatre formes de divergence les plus courantes
Pour chaque cas, je partirai d’un algorithme public afin de montrer à quoi ressemble l’implémentation standard, puis j’expliquerai l’écart possible. En pratique, ces quatre techniques se cumulent régulièrement dans un même signataire.
1. Une constante transformée en valeur calculée à l’exécution
Un chiffrement par flot standard utilise un nombre de tours fixe. ChaCha20 en exécute 20, sans exception ; toute implémentation standard le code en dur. RFC 8439 le rappelle dès son introduction : elle ne décrit que ChaCha à 20 tours, les variantes à 8 et 12 tours étant définies ailleurs. Le nombre de tours est donc, par nature, une constante du standard.
La divergence consiste à faire dépendre ce nombre de tours, normalement fixe, d’un calcul effectué à partir de la clé. La logique des quarter rounds reste identique, mais leur nombre varie selon certains octets de la clé. Changez de clé, et le nombre de tours peut changer lui aussi.
Pourquoi c’est facile à rater : vous avez reconnu les quarter rounds, reconnu les constantes σ, conclu à « ChaCha20 », puis recopié 20 tours. Vous n’avez même pas vérifié si la variable qui contrôle la boucle est une constante ou une expression, parce que dans tous les ChaCha vus jusque-là, c’était une constante.
Le signal à rechercher est simple : là où le standard impose une constante, le code contient une expression dépendante de l’entrée. Une passe de recherche des contre-indices bien cadrée vérifiera précisément ce point : « le standard fixe cette valeur, mais est-elle calculée ici ? »
Pour le confirmer par test différentiel, exécutez volontairement deux clés ne différant que d’un seul octet. Si l’écart entre les sorties dépasse largement l’influence attendue d’un seul octet, cet octet alimente probablement un paramètre global — par exemple le nombre de tours — plutôt que de se contenter d’être XORé dans le flot de clés.
2. Une constante de base légèrement modifiée
Le hachage FNV-1a repose sur deux nombres magiques publics : l’offset basis et le prime. Toute implémentation FNV-1a correcte utilise exactement ces deux valeurs, publiées dans le standard. La page de référence FNV, maintenue par le co-auteur Landon Curt Noll, donne notamment l’offset basis 32 bits à 2166136261 et le prime à 16777619, chiffre par chiffre.
La divergence : remplacer l’une de ces constantes fondamentales par une valeur voisine, qui ne s’écarte du standard que très légèrement. La structure globale du hachage reste identique — XOR, multiplication et boucle sont corrects — mais la base initiale a subi une modification presque invisible.
C’est probablement le cas le plus vicieux des quatre. Vous reconnaissez la structure de FNV, repérez une grande constante qui ressemble à l’offset basis, et concluez « FNV-1a standard ». Vous ne la comparez pas bit à bit avec la valeur de référence : qui revérifie un nombre magique qu’il pense avoir déjà identifié ?
Ce type d’écart se détecte presque uniquement avec des tests différentiels, car comparer visuellement de grands nombres est profondément peu fiable. Prenez une entrée connue, exécutez-la dans l’implémentation standard puis dans la boîte noire cible, et comparez les sorties. Si la structure est identique mais que les résultats diffèrent, le problème se trouve très probablement dans une constante de base. Donnez alors la constante suspecte au modèle et demandez-lui de la comparer avec celle du standard : une machine est bien plus fiable que vous pour cette tâche.
La valeur du modèle est ici très concrète : il mémorise l’offset basis standard de FNV-1a jusqu’au dernier bit, pas vous. Demandez simplement « cette constante est-elle exactement égale à l’offset basis standard de FNV-1a ? », et il signalera immédiatement l’écart.
3. Une modification locale dans la structure de l’algorithme
Un double tour de ChaCha comprend 8 quarter rounds : les 4 premiers opèrent sur les colonnes, les 4 suivants sur les diagonales. Les indices des mots utilisés par chaque quarter round sont fixés par le standard ; RFC 8439 §2.3 décrit la fonction de bloc et indique précisément quels mots d’état sont manipulés à chaque tour.
La divergence consiste à échanger discrètement un ou deux indices de mots dans l’un des tours. La grande majorité des tours reste conforme ; quelque part au milieu, un mot qui devrait être manipulé est remplacé par un autre. Le code ressemble toujours à ChaCha, s’exécute sans erreur, mais produit un flot de clés totalement différent du ChaCha standard.
Pourquoi l’œil humain passe à côté : les indices des quarter rounds forment une longue suite de nombres — huit groupes du type (0,4,8,12)(1,5,9,13)…. On les survole en vérifiant seulement « oui, tours de colonnes puis tours diagonaux », sans contrôler que les quatre nombres de chaque groupe occupent leur position standard. Cacher une modification dans une suite d’indices déjà pénible à lire est très efficace.
Ce n’est pas non plus un simple coup d’œil pour un modèle : il faut lui demander explicitement de lister les indices tour par tour et de les comparer à ChaCha standard. C’est une vérification mécanique, exactement le type de tâche où le modèle garde sa rigueur et l’humain décroche. Demandez un tableau « indices standard / indices réels » : la divergence ressortira d’elle-même.
Le test différentiel permet de confirmer : si les deux premiers cas sont écartés — bon nombre de tours, bonnes constantes — mais que la sortie reste différente, le problème est structurel. Exportez l’état intermédiaire après chaque tour ; le premier tour qui diverge de ChaCha standard est celui qui a été modifié.
4. Des données de clé intégrées au résultat
Les trois premiers cas modifient l’algorithme ; celui-ci modifie l’organisation des données.
Technique fréquente : la clé de chiffrement n’est pas transmise par un canal distinct, mais découpée puis insérée directement dans le texte chiffré. Le récepteur la reconstitue selon la même règle. Dans la version plus rusée, la position d’insertion n’est pas fixe : elle est calculée à partir du contenu des données. Le texte chiffré change, et l’emplacement où la clé est cachée change avec lui. Une couche externe enveloppe ensuite le tout dans un Base64 à alphabet personnalisé avec un octet de préfixe marqueur.
Pourquoi c’est souvent manqué : vous luttez contre l’algorithme de chiffrement sans réaliser que la clé n’a peut-être pas besoin d’être cassée du tout. Elle est déjà présente dans le texte chiffré que vous avez sous les yeux ; vous ignorez simplement quelle tranche la contient. Les débutants restent fréquemment bloqués à essayer de « casser » un élément dont le clair est déjà devant eux.
Le signe distinctif est une zone d’un bloc de données dont les caractéristiques statistiques diffèrent du reste. Une clé contient généralement des octets aléatoires à forte entropie ; insérée au milieu d’un texte chiffré, elle forme un « segment étranger » reconnaissable. Le modèle peut aider à déterminer « quel intervalle de cette sortie a une distribution d’octets différente du reste », afin de localiser les limites du matériau injecté.
Pour confirmer, une fois la règle d’insertion trouvée — par exemple un modulo appliqué à une somme d’octets — prenez quelques paires entrée/sortie connues, remontez la formule de position d’insertion, puis vérifiez-la dans le sens direct. Le modèle peut aider à induire la règle à partir de quelques échantillons, mais la validation finale revient toujours à assert.
La recherche des contre-indices : le véritable terrain du raisonnement
Ces quatre cas ont un point commun : identifier la famille d’algorithmes, donc le candidat, est facile ; repérer la divergence, donc le contre-indice, est difficile.
L’étape du candidat est à la portée de presque tous les modèles : les constantes σ de ChaCha ou la structure de FNV sont reconnaissables par tout modèle ayant vu les données d’entraînement. Ce qui sépare réellement les modèles, c’est la passe de contre-indices : leur capacité et leur volonté de continuer à examiner un algorithme qu’ils viennent d’identifier, puis de dire « cette partie ne correspond pas au standard ».
Un modèle moins solide perd son élan à ce stade. Après avoir reconnu « c’est ChaCha20 », sa passe de contre-indices se résume souvent à reformuler le candidat : « l’implémentation suit la structure standard de ChaCha20 et emploie les quarter rounds classiques… ». C’est une répétition de l’hypothèse, pas une vérification des écarts. Une telle analyse ne peut pas guider l’étape suivante.
La passe de contre-indices d’un modèle de raisonnement robuste est d’une autre nature. Il écrira par exemple : « le candidat est ChaCha20, mais l’implémentation s’écarte du standard sur trois points : premièrement, le nombre de tours dépend d’une expression liée à la clé alors que ChaCha20 standard impose 20 tours ; deuxièmement, les indices de mots du tour N ne correspondent pas au tour diagonal standard ; troisièmement… ». Chaque observation pointe vers une divergence concrète et vérifiable. Une passe de contre-indices de ce niveau est la checklist de tests différentiels que vous écrirez à l’étape 3.
C’est pourquoi l’identification de la famille algorithmique mérite d’être confiée à un modèle de raisonnement, et testée par vous-même avant tout engagement. La qualité des contre-indices varie directement d’un modèle à l’autre ; elle détermine le nombre de tests inutiles et de détours que vous ferez.
Voici les quatre niveaux que j’utilise dans ce workflow :
Étape | Capacité requise | Choix | model id |
|---|---|---|---|
Cartographie structurelle après découpage | Long contexte, lecture d’un module entier en une passe | Kimi K3 |
|
Identification de la famille algorithmique et contre-indices | Raisonnement solide, capable de contester sa propre hypothèse | Claude Opus 5 |
|
Renommage massif des symboles | Faible coût, forte concurrence | Claude Sonnet 5 |
|
Attribution des différences | Raisonnement intermédiaire, explication à partir d’octets précis | GPT-5.6 Sol |
|
Le deuxième niveau est le cœur de cet article. Ne me croyez pas sur parole : testez-le. Le protocole est simple :
Choisissez 2 à 3 fonctions feuilles de votre propre bundle obfusqué, dont au moins une dont vous connaissez déjà la réponse afin de servir de contrôle.
Avec le prompt en trois parties « candidat / indices / contre-indices » présenté dans l’article précédent, envoyez la même entrée à
claude-opus-5etgpt-5.6-solséparément.Ne regardez que la passe de contre-indices : vérifie-t-elle réellement les divergences une par une, ou reformule-t-elle simplement le candidat ? Sur le cas de contrôle, combien de divergences chaque modèle a-t-il détectées ?
Le nombre et la qualité des divergences repérées constituent votre critère de sélection.
Une seule exécution suffit pour voir l’écart, bien plus directement qu’avec n’importe quel classement de benchmark.
Le vrai frein : le coût du changement de modèle
Quatre modèles répartis chez trois fournisseurs. L’approche naïve consiste à connecter trois SDK, gérer trois systèmes d’authentification et trois traitements d’erreurs. Beaucoup font le calcul, concluent que cela ne vaut pas l’effort, puis exécutent tout avec un seul modèle. Ils utilisent alors un niveau faible en contre-indices pour identifier la famille algorithmique et accumulent les détours sans comprendre pourquoi.
AIReiter simplifie cette couche : une seule clé, une interface compatible OpenAI, les quatre modèles derrière, et le changement se limite au champ model du corps de requête.
# Identification de la famille algorithmique + contre-indices : niveau raisonnement
curl https://aireiter.com/api/v1/chat/completions \
-H "Authorization: Bearer $AIREITER_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-opus-5",
"messages": [{"role": "user", "content": "<prompt en trois parties + fonction feuille + constantes>"}]
}'
# Renommage massif des symboles : modifiez un seul champ
# "model": "claude-sonnet-5"
# Attribution des différences :
# "model": "gpt-5.6-sol"
Vous utilisez déjà le SDK OpenAI ? Faites pointer base_url vers https://aireiter.com/api/v1, sans rien changer d’autre. Vous utilisez le SDK Anthropic ? Envoyez une requête POST /api/v1/messages avec la même clé.
Côté prix, les modèles Claude sont proposés avec 30 % de réduction sur le tarif public, et les modèles GPT à moitié prix. Dans ce workflow, la remise cible les étapes les plus coûteuses : l’identification de la famille algorithmique vous fait itérer le prompt encore et encore sur la même fonction, sur de nombreux tours, et concentre le plus grand nombre d’appels ; le renommage massif des symboles commence, lui, à plusieurs centaines d’appels. Ce sont ces deux étapes qui représentent l’essentiel du coût.
Essayer sans inscription — lancez quelques analyses à la main, comparez vous-même les passes de contre-indices des deux modèles, puis décidez.
Pour conclure
La réalité de la rétro-ingénierie des signatures obfusquées est simple : la plupart du code reprend des algorithmes standard à recopier tels quels ; le vrai travail consiste à trouver les endroits où le standard a été discrètement modifié.
Les quatre formes de divergence — constante devenue variable, constante perturbée, modification structurelle et matériau intégré — ont toutes le même trait : elles sont assez petites pour échapper à la reconnaissance de motifs humaine, mais assez importantes pour faire échouer entièrement une réécriture. L’humain peine à rester soupçonneux devant ce qui paraît correct ; c’est précisément la force d’un modèle, lorsqu’il est bien guidé.
Mais le modèle ne produit qu’un soupçon, pas une confirmation. Toute hypothèse sur un point de divergence doit finir en test différentiel — le sujet de l’article consacré aux tests différentiels. Le modèle vous remet une liste des « endroits qui ont pu être modifiés » ; assert vous dit lesquels l’ont réellement été.
