
Documentation et rétro-ingénierie de reCAPTCHA
Ce dépôt contient une analyse technique de l'antibot de Google (reCAPTCHA), en se concentrant sur :
@g_recaptcha@lyxlobyxreCAPTCHA est l'un des systèmes anti-bot dotés des techniques d'obscurcissement les plus sophistiquées, employant une série de transformations qui rendent le code moins lisible et plus difficile à rétro-ingénier. La plupart des obscurcissements peuvent être facilement manipulés à l'aide de l'arbre syntaxique abstrait (AST), mais certains sont traités au moment de l'exécution, rendant l'AST inutile dans ce cas. Le polymorphisme est également appliqué au code pour modifier sa structure dans chaque version du script. Par exemple, le code n'effectue pas l'action directement. Au lieu de cela, il utilise des objets ou des fonctions qui changent de forme
Expressions de séquence
Le code est aplati en convertissant chaque bloc d'instructions en une expression continue séparée par des virgules. Cela peut apparaître dans les instructions if, les arguments de fonction, et même à l'intérieur d'objets

Arithmétique booléenne mixte
Entremêle des opérations arithmétiques (addition, soustraction, multiplication) avec des opérations bit à bit (ET, OU, XOR, NON) pour masquer la logique originale, par exemple -2 * ~(h & H) + -2 + (h ^ H)
Table de fonctions indirecte
Chaque fonction est construite dans une table et est appelée en utilisant son index, comme functions[index](https://github.com/elyelysiox/recaptcha/blob/main/args)
Tableau de constantes en ligne Un littéral de tableau local est affecté en ligne, au milieu d'une expression. Le tableau regroupe des constantes (nombres, chaînes) qui sont réutilisées dans tout le corps de la fonction via un accès par index
// b = [14, 1, "call"] assigné en ligne dans une expression de séquence
function(Y, Q, c, l, G, X, W, J, b, P) {
(Y & 94) == Y && (b = [14, 1, "call"], ...)
W[b[2]](J, G) // W.call(J, G)
Y >> b[1] & b[0] // Y >> 1 & 14
}
Multiplexage de fonctions Plusieurs fonctions logiquement distinctes sont fusionnées en une seule, en utilisant un paramètre numérique comme sélecteur de bloc. Le bloc actif est déterminé en évaluant le paramètre par rapport à des conditions bit à bit. Les appelants passent un littéral numérique comme sélecteur.
function(N, y, U, Y, h, H, m, C, u) {
C = [26, 47, 6];
// bloc 1
if ((N - 2 ^ 14) < N && (N - C[2] | 28) >= N) {
// logique de conversion de valeur en chaîne
}
// bloc 2
if ((N + 4 & 40) >= N && (N + 5 & C[0]) < N) {
Y = bB();
throw Error(Y === void 0 ? "valeur inattendue " + U + y : Y);
}
return u;
}
Branchement par opérateurs logiques
Remplace les blocs if et if/else par l'évaluation en court-circuit des opérateurs logiques, convertissant le flux de contrôle en expressions. Combiné avec des expressions de séquence, plusieurs branches apparaissent comme une seule expression continue séparée par des virgules
// if (a) { bloc }
a && (bloc)
// if (!a) { bloc }
a || (bloc)
// if (a) { x } else { y }
a ? x : y
// combiné avec CFF et expressions de séquence :
(Y | 1) & 14 || (c = Q.O, J = c.O.length + c.g.length),
(Y ^ 59) >> 3 == 3 && (Q.classList
? Q.classList.add(c)
: Z[31](31, Q, c) || (l = f[0](84, "string", "", Q), ...)),
Liaison de constantes de méthodes natives Lie les méthodes natives du navigateur à leurs récepteurs d'origine, les stockant en tant que constantes pour empêcher toute modification
LO = (Tw = self) == null ? void 0 :
(K9 = Tw.Math) == null ? void 0 :
(v4 = K9.floor) == null ? void 0 :
(mF = v4.bind) == null ? void 0 :
mF.call(v4, Math) // Math.floor.bind(Math)
LO(x) // Math.floor(x)
U4() // Math.random()
Ge(obj, prop) // Object.defineProperty(obj, prop)
Code mort Des blocs de code inaccessibles ou inutilisés sont injectés dans tout le fichier, augmentant sa taille à plus de 60 000 lignes, ce qui rend l'analyse statique et la rétro-ingénierie par LLM difficiles
Aplatissement du flux de contrôle Transforme chaque partie du code (déclarations et boucles) en une machine d'état plate. Cela masque la logique d'exécution d'origine en acheminant tous les blocs de code via un bloc « répartiteur » central
Les répartiteurs peuvent changer de forme ; certains ont 2-3 variables d'état, et le type de boucle/condition change. Ils ressemblent à ceci

Ceci est un CFF avec 2 variables d'état, l'une gère le bloc catch et l'autre le bloc try.
Pool de chaînes chiffrées Tous les littéraux de chaîne (API DOM, propriétés du navigateur, valeurs CSS, messages d'erreur, etc.) sont chiffrés en un seul pool de chaînes massif. Une fonction de déchiffrement utilise une graine et un chiffrement XOR basé sur LCG pour extraire chaque chaîne au moment de l'exécution
Il y a plus de 1990 sites d'appel répartis dans le code. La fonction de déchiffrement utilise une clé courante qui accumule les points de code décodés, rendant chaque caractère dépendant de tous les précédents
X = function(J, b, P, F, U) {
U = ["codePointAt", 127, "pool chiffré de chars"];
for (F = (P = 0, b = "", l); P < Q; P++)
J = (U[2][U[0]](c + P) ^ F) & U[1], // XOR avec clé courante
b += String.fromCodePoint(J),
F += J; // accumule la clé
return G = b;
}
// les sites d'appel passent une graine pour localiser et déchiffrer chaque chaîne
Z[23](64, 4, 54961, 103)() // → "lang"
Z[23](66, 4, 54961, 103)() // → "addEventListener"
Z[23](32, 12, 20287, 852)() // → "inline-block"

Itérateur de valeurs avec état reCAPTCHA utilise une fonction avec état qui renvoie une séquence d'objets et de valeurs d'exécution (comme window, document.body, des constantes numériques) dans un ordre fixe ; chaque appel avance un curseur interne. L'appeler dans le désordre ou trop de fois corrompt toutes les lectures ultérieures. Un mécanisme d'expiration invalide l'état après un intervalle fixe, renvoyant null pour toutes les lectures tardives
// les appels séquentiels renvoient des valeurs différentes :
c() // → window
c() // → document.body
c() // → 123
c() // → null (expiration dépassée)
l(c(), G[2], G[W[1]], G[1]) + l(c(), G[2], G[W[1]], 12)
// ↑ window ↑ window
10 * l(c(), G[2], G[W[1]], G[1]) + l(c(), G[2], G[W[1]], 12))
c().querySelectorAll(a[X[2]](98, X[1], X[1]))
// ↑ document.body