
reCAPTCHA का दस्तावेज़ीकरण और रिवर्स इंजीनियरिंग
इस रिपॉज़िटरी में Google के एंटीबॉट (reCAPTCHA) का तकनीकी विश्लेषण है, जो निम्न पर केंद्रित है:
@g_recaptcha@lyxlobyxreCAPTCHA उन एंटी-बॉट सिस्टमों में से एक है जिनमें सबसे परिष्कृत ऑबफस्केशन तकनीकें होती हैं, जो अनेक परिवर्तनों का उपयोग करती हैं जो कोड को कम पठनीय और रिवर्स इंजीनियरिंग के लिए अधिक कठिन बनाते हैं। अधिकांश ऑबफस्केशन को Abstract Syntax Tree (AST) का उपयोग करके आसानी से हेरफेर किया जा सकता है, लेकिन कुछ को रनटाइम पर प्रोसेस किया जाता है, जिससे ऐसे मामलों में AST बेकार हो जाता है। कोड में पॉलीमॉर्फिज्म भी लागू किया जाता है ताकि स्क्रिप्ट के हर संस्करण में इसकी संरचना बदलती रहे। उदाहरण के लिए, कोड क्रिया को सीधे नहीं करता। इसके बजाय, यह ऐसी वस्तुओं या फ़ंक्शनों का उपयोग करता है जो आकार बदलते हैं
अनुक्रम अभिव्यक्तियाँ
कोड को प्रत्येक ब्लॉक स्टेटमेंट को एक सतत कॉमा-पृथक अभिव्यक्ति में परिवर्तित करके समतल (flatten) किया जाता है; यह if स्टेटमेंट, फ़ंक्शन आर्गुमेंट्स और यहाँ तक कि ऑब्जेक्ट्स के भीतर भी प्रकट हो सकता है

मिश्रित बूलियन अंकगणित
अंकगणितीय संक्रियाओं (जोड़, घटाव, गुणा) को बिटवाइज़ संक्रियाओं (AND, OR, XOR, NOT) के साथ इस प्रकार उलझाता है कि मूल तर्क छिप जाता है, उदाहरण के लिए -2 * ~(h & H) + -2 + (h ^ H)
अप्रत्यक्ष फ़ंक्शन तालिका
प्रत्येक फ़ंक्शन एक तालिका के भीतर बनाया जाता है, और उसके इंडेक्स का उपयोग करके कॉल किया जाता है, जैसे functions[index](https://github.com/elyelysiox/recaptcha/blob/main/args)
इनलाइन कॉन्स्टेंट सरणी एक स्थानीय array लिटरल को अभिव्यक्ति के बीच में ही इनलाइन असाइन किया जाता है; array उन स्थिरांकों (संख्याएँ, स्ट्रिंग्स) को समूहबद्ध करता है जिन्हें फ़ंक्शन बॉडी में इंडेक्स एक्सेस के माध्यम से पुनः उपयोग किया जाता है
// b = [14, 1, "call"] assigned inline inside a sequence expression
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
}
फ़ंक्शन मल्टीप्लेक्सिंग कई तार्किक रूप से भिन्न फ़ंक्शनों को एक में मिला दिया जाता है, और ब्लॉक चयनकर्ता के रूप में एक संख्यात्मक पैरामीटर का उपयोग किया जाता है। सक्रिय ब्लॉक पैरामीटर का बिटवाइज़ शर्तों के विरुद्ध मूल्यांकन करके निर्धारित किया जाता है। कॉल करने वाले पक्ष चयनकर्ता के रूप में एक संख्यात्मक लिटरल पास करते हैं
function(N, y, U, Y, h, H, m, C, u) {
C = [26, 47, 6];
// block 1
if ((N - 2 ^ 14) < N && (N - C[2] | 28) >= N) {
// convert value to string logic
}
// block 2
if ((N + 4 & 40) >= N && (N + 5 & C[0]) < N) {
Y = bB();
throw Error(Y === void 0 ? "unexpected value " + U + y : Y);
}
return u;
}
लॉजिकल ऑपरेटर ब्रांचिंग if स्टेटमेंट और if/else ब्लॉकों को लॉजिकल ऑपरेटर शॉर्ट-सर्किट मूल्यांकन द्वारा प्रतिस्थापित किया जाता है, जिससे नियंत्रण प्रवाह अभिव्यक्तियों में बदल जाता है। अनुक्रम अभिव्यक्तियों के साथ संयुक्त होने पर, कई शाखाएँ एक सतत कॉमा-पृथक अभिव्यक्ति के रूप में दिखाई देती हैं
// if (a) { block }
a && (block)
// if (!a) { block }
a || (block)
// if (a) { x } else { y }
a ? x : y
// combined with CFF and sequence expressions:
(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), ...)),
बाइंड नेटिव मेथड कॉन्स्टेंट्स नेटिव ब्राउज़र मेथड्स को उनके मूल रिसीवर्स से बाइंड करके स्थिरांक के रूप में संग्रहीत करता है, ताकि छेड़छाड़ (tampering) को रोका जा सके
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)
डेड कोड अप्राप्य या अप्रयुक्त कोड ब्लॉक पूरी फ़ाइल में इंजेक्ट किए जाते हैं, जिससे इसका आकार 60,000 से अधिक पंक्तियों तक बढ़ जाता है; यह स्थैतिक विश्लेषण और LLM-आधारित रिवर्स इंजीनियरिंग को कठिन बना देता है
कंट्रोल फ्लो फ्लैटनिंग कोड के प्रत्येक भाग (घोषणाएँ और लूप) को एक समतल स्टेट मशीन में रूपांतरित करता है। यह सभी कोड ब्लॉकों को एक केंद्रीय 'डिस्पैचर' ब्लॉक के माध्यम से मार्गबद्ध करके मूल निष्पादन तर्क को छिपाता है
डिस्पैचर आकार बदल सकते हैं; कुछ में 2-3 स्टेट वेरिएबल होते हैं, और लूप/कंडीशन का प्रकार बदलता रहता है। वे इस प्रकार दिखते हैं

यह एक CFF है जिसमें 2 स्टेट वेरिएबल हैं; एक catch ब्लॉक को संभालता है और दूसरा try ब्लॉक को।
एन्क्रिप्टेड स्ट्रिंग पूल सभी string लिटरल (DOM APIs, ब्राउज़र प्रॉपर्टीज़, CSS मान, त्रुटि संदेश, आदि) एक विशाल स्ट्रिंग पूल में एन्क्रिप्ट कर दिए जाते हैं। एक डिक्रिप्शन फ़ंक्शन प्रत्येक स्ट्रिंग को रनटाइम पर निकालने के लिए एक सीड और LCG-आधारित XOR साइफर का उपयोग करता है
पूरे कोड में 1990+ कॉल साइटें फैली हुई हैं; डिक्रिप्शन फ़ंक्शन एक रनिंग key का उपयोग करता है जो डिकोड किए गए कोडपॉइंट्स को संचित करता है, जिससे प्रत्येक अक्षर पिछले सभी अक्षरों पर निर्भर हो जाता है
X = function(J, b, P, F, U) {
U = ["codePointAt", 127, "char encrypted pool"];
for (F = (P = 0, b = "", l); P < Q; P++)
J = (U[2][U[0]](c + P) ^ F) & U[1], // XOR with running key
b += String.fromCodePoint(J),
F += J; // accumulate key
return G = b;
}
// call sites pass a seed to locate and decrypt each string
Z[23](64, 4, 54961, 103)() // → "lang"
Z[23](66, 4, 54961, 103)() // → "addEventListener"
Z[23](32, 12, 20287, 852)() // → "inline-block"

स्टेटफुल वैल्यू इटरेटर reCAPTCHA एक स्टेटफुल फ़ंक्शन का उपयोग करता है जो रनटाइम ऑब्जेक्ट्स और मानों (जैसे window, document.body, संख्यात्मक स्थिरांक) का एक क्रम एक निश्चित क्रम में लौटाता है; प्रत्येक कॉल एक आंतरिक कर्सर को आगे बढ़ाता है, इसे क्रम से बाहर या बहुत अधिक बार कॉल करने से बाद के सभी रीड दूषित हो जाते हैं। एक टाइमआउट तंत्र एक निश्चित अंतराल के बाद स्थिति को अमान्य कर देता है, जिससे किसी भी देर से रीड के लिए null लौटता है
// sequential calls return different values:
c() // → window
c() // → document.body
c() // → 123
c() // → null (timeout expired)
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
कम्प्यूटेड फ़ंक्शन तालिका
यह Indirect Function Table के समान है, लेकिन यहाँ प्राप्त किए जाने वाले फ़ंक्शन का इंडेक्स रनटाइम पर एक सीड के साथ, XOR और मॉडुलस का उपयोग करके गणना किया जाता है
c = ((Q ^ no | U[1]) >> 5) + no
A = mN[(c % U[2] + U[2]) % U[2]] // mN is the function table (50+ functions)
q[29](5, 6977) // seed=6977 → index resolves to function at mN[X]
q[29](53, 6187) // seed=6187 → different index, different function

रनटाइम वैल्यू एन्क्रिप्शन
कुछ मान (captcha कॉन्फ़िगरेशन पैरामीटर, ऐंकर पैरामीटर, आदि) कभी भी सादे पाठ में संग्रहीत नहीं किए जाते; वे संग्रह के तुरंत बाद एन्क्रिप्ट किए जाते हैं और केवल उपयोग के समय डिक्रिप्ट होते हैं; उनकी शुरुआत में B प्रीफिक्स होता है