
Документация и обратная разработка reCAPTCHA
Этот репозиторий содержит технический анализ антибота Google (reCAPTCHA), сфокусированный на:
@g_recaptcha@lyxlobyxreCAPTCHA — одна из антибот-систем с наиболее изощрёнными техниками обфускации, применяющая ряд преобразований, которые делают код менее читаемым и усложняют обратную разработку. Большинство обфускаций легко обрабатываются с помощью абстрактного синтаксического дерева (AST), но некоторые выполняются во время выполнения, делая AST в этом случае бесполезным. К коду также применяется полиморфизм, изменяющий его структуру в каждой версии скрипта. Например, код не выполняет действие напрямую. Вместо этого используются объекты или функции, меняющие форму.
Последовательные выражения (Sequence Expressions)
Код уплощается путём преобразования каждого блока инструкций в непрерывное выражение, разделённое запятыми; может встречаться в операторах if, аргументах функций и даже внутри объектов.

Смешанная булева арифметика (Mixed Boolean Arithmetic)
Переплетает арифметические операции (сложение, вычитание, умножение) с побитовыми (AND, OR, XOR, NOT), чтобы скрыть исходную логику, например -2 * ~(h & H) + -2 + (h ^ H).
Косвенная таблица функций (Indirect Function Table)
Каждая функция помещается в таблицу и вызывается по индексу, например functions[index](https://github.com/elyelysiox/recaptcha/blob/main/args).
Встроенный постоянный массив (Inline Constant Array) Литерал локального массива присваивается прямо внутри выражения; массив группирует константы (числа, строки), которые многократно используются в теле функции через доступ по индексу.
// b = [14, 1, "call"] присвоено внутри последовательного выражения
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 Multiplexing) Несколько логически различных функций объединяются в одну, используя числовой параметр как селектор блока. Активный блок определяется путём проверки параметра на побитовые условия. Вызывающие передают числовой литерал в качестве селектора.
function(N, y, U, Y, h, H, m, C, u) {
C = [26, 47, 6];
// блок 1
if ((N - 2 ^ 14) < N && (N - C[2] | 28) >= N) {
// логика преобразования значения в строку
}
// блок 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;
}
Ветвление через логические операторы (Logical Operator Branching) Заменяет блоки if и if/else на короткое замыкание логических операторов, превращая поток управления в выражения. В сочетании с последовательными выражениями множество ветвей выглядят как одно непрерывное выражение, разделённое запятыми.
// if (a) { block }
a && (block)
// if (!a) { block }
a || (block)
// if (a) { x } else { y }
a ? x : y
// в сочетании с CFF и последовательными выражениями:
(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), ...)),
Привязка нативных методов как констант (Bind Native Methods Constants) Привязывает нативные методы браузера к их исходным получателям, сохраняя их как константы для предотвращения модификации.
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)
Мёртвый код (Dead Code) Недоступные или неиспользуемые блоки кода вставляются по всему файлу, увеличивая его размер до более чем 60 000 строк; это затрудняет статический анализ и обратную разработку с помощью LLM.
Уплощение потока управления (Control Flow Flattening) Преобразует каждую часть кода (объявления и циклы) в плоский конечный автомат. Скрывает исходную логику выполнения, направляя все блоки кода через центральный блок-диспетчер.
Диспетчеры могут менять форму; некоторые имеют 2-3 переменных состояния, а тип цикла/условия меняется. Выглядят они так:

Это CFF с двумя переменными состояния: одна управляет блоком catch, другая — блоком try.
Зашифрованный пул строк (Encrypted String Pool) Все строковые литералы (DOM API, свойства браузера, CSS-значения, сообщения об ошибках и т.д.) зашифрованы в одном огромном пуле строк. Функция дешифрования использует seed и XOR-шифр на основе LCG для извлечения каждой строки во время выполнения.
По всему коду разбросано более 1990 мест вызова; функция дешифрования использует нарастающий ключ, накапливающий декодированные кодовые точки, что делает каждый символ зависимым от всех предыдущих.
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 с нарастающим ключом
b += String.fromCodePoint(J),
F += J; // накопление ключа
return G = b;
}
// места вызова передают seed для поиска и дешифровки каждой строки
Z[23](64, 4, 54961, 103)() // → "lang"
Z[23](66, 4, 54961, 103)() // → "addEventListener"
Z[23](32, 12, 20287, 852)() // → "inline-block"

Итератор значений с состоянием (Stateful Value Iterator) reCAPTCHA использует функцию с состоянием, которая возвращает последовательность объектов и значений среды выполнения (например, window, document.body, числовые константы) в фиксированном порядке; каждый вызов сдвигает внутренний курсор; вызов не по порядку или слишком много раз портит все последующие чтения. Механизм тайм-аута аннулирует состояние через заданный интервал, возвращая null для любых поздних чтений.
// последовательные вызовы возвращают разные значения:
c() // → window
c() // → document.body
c() // → 123
c() // → null (тайм-аут истёк)
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
Вычисляемая таблица функций (Computed Function Table)
Похоже на Indirect Function Table, но здесь индекс получаемой функции вычисляется во время выполнения на основе seed с использованием XOR и Modulus.
c = ((Q ^ no | U[1]) >> 5) + no
A = mN[(c % U[2] + U[2]) % U[2]] // mN — таблица функций (50+ функций)
q[29](5, 6977) // seed=6977 → индекс разрешается в функцию mN[X]
q[29](53, 6187) // seed=6187 → другой индекс, другая функция

Шифрование значений во время выполнения (Runtime Value Encryption)
Некоторые значения (параметры конфигурации капчи, параметры якоря и т.д.) никогда не хранятся в открытом виде; они шифруются сразу после сбора и расшифровываются только в момент использования; имеют префикс B в начале.

Обфускация асинхронного потока управления (Async Control Flow Obfuscation) Синхронная логика преобразуется в конечный автомат на основе генераторов, обёрнутый в рекурсивные цепочки Promise; отслеживание любого значения через отладчик вынуждает проходить через несколько асинхронных обработчиков, теряя исходный контекст выполнения на каждой границе .then().
Компоненты для инициализации reCAPTCHA``` ar: <WidgetInit?> k: co: hl: v: size: sa: anchor-ms: execute-ms: cb:
### Response
Ответ содержит:
- Дизайн iframe-окна CAPTCHA
- Якорный токен, используемый для проверки полезной нагрузки (/reload)
- Основную конфигурацию для инициализации, выполняемую в методе `recaptcha.anchor.Main.init` для её получения в recaptcha_en.js
### Структура:
Примечание: Recaptcha BotGuard был удалён `04/01/2026`. Вы можете увидеть некоторые примеры [здесь](https://github.com/elyelysiox/recaptcha-payload/tree/main/botguard_scripts)```javascript
[
"ainput",
[
"bgdata",
"",
"LyogQW50aS1zcGFtLiBXYW50IHRvIHNheSBoZWxsbz8gQ29ud...", // BotGuard Script Base64-Encoded
"YWVtZ0h5MGNCWXJDQ2lidGh0RW13RmhWdlc3aU5yeVpzSmx6U...", // BotGuard VM Bytecode Double Base64-Encoded
"wrB8fsOVU8K0YAzDsyQpw7ZHw6jDnMK1AMO6SRcvw53CsGlQw6/DuMO0wqr" // VM Config Bytecode Base64-Encoded
],
null,
[
"conf",
null,
"6LfTV4gkAAAAACDVrUvp9_DalxUPvFSU7M2HJDO-", // Website Key
0,
null,
null,
null,
1,
// Indexes for fingerprinting or something else
[16, 21, 125, 63, 73, 95, 87, 41, 43, 42, 83, 102, 105, 109, 121],
[-7614991, 137],
0,
null,
null,
null,
null,
0,
null,
0,
null,
700, // Time Start for Time Variances of Fingerprint Values
1,
null,
0,
// Encoded metadata for the VM's main bytecode constructs
"CvsCEg8I8ajhFRgAOgZUOU5CNWISDwjmjuIVGAA6BlFCb29IYxIPCMfm1DgYADoGZHhkTmlkEg8Is4qgOBgBOgZMV0o1a2ISDwiB7OgVGAA6Bkh1dlBqZhIPCK6e6zcYADoGR2JpT1FkEg8I94jmNxgAOgZvaWxlRGQSDwjwzeMVGAE6BmZJVkloYhIPCOLKoDcYAToGZ0xOQ0hjEg8I3r+3NxgBOgZlYXp1NmQSDwjY0oEyGAA6BkFxNzd2ZhIOCLjllDIYAToFUUJPRDASDwio7784GAA6BkdGVU5jZhIPCLWzqzgYAToGb3JZVjZkEg8I0tuVNxgAOgZmZmFXQWUSDwiV2JQyGAE6BlBxNjBuZBIPCKuRyjgYADoGQVo0TmJiEg8IxfOINxgAOgZhWG9pYWMSDwiNyoYyGAE6Bk93TjR0ZhIOCIr9iDcYADoFTWZJSTQaJwgDEiMdJv3NgTUZp4oJGYQKGZzijAIZzPMRGa+VQBnqsCYZ5a8MGQ==",
0,
1,
null,
null,
1,
null,
null,
0,
null,
null,
0,
0,
"cf5e3c3a6ab6c494c829a7932d9577c1e6de862dd4912e55dc7fe2e40a1f7604"
],
"https://who.clickbait.team:443", // Website URL:Port
null,
[
3,
1,
1
],
null,
null,
null,
1,
3600,
[
"https://www.google.com/intl/es/policies/privacy/",
"https://www.google.com/intl/es/policies/terms/"
],
"vQWNjTpMwdevQPWmE8akDzp8jsOZfa1VqTzVHaPiQ/c=",
1,
0,
null,
1,
1777669303203, // Fingerprint Data Encryption Key
0,
0,
[203], // Config Bytecode Key
null,
[185], // Config Bytecode Key
"RC--rg1OSRnOD0ayA",
null,
null,
null,
null,
null,
// Bft Token, Used in reCAPTCHA V2
"0dAFcWeA4RepD9zDjeMQE73pAT27pXZ7Nz_419U2K36QNqzHaKtLIDkwZQLi-Ud8OvSZHbDEcxQxusBQnsF5QQErMRpJMcUplaFg",
1777752103288 // "oc" Data Encryption Key
]
Значения основаны на расшифрованных значениях из первого образца отпечатка, смотрите здесь
Каждое подполе значений отпечатка имеет базовый формат:
[value, key, elapsed]
Где key — ключ шифрования, а elapsed — время, которое потребовалось сборщику для получения и шифрования значения. Это позволяет reCAPTCHA обнаруживать:
Система использует внутренний планировщик:``` [42, 45, 53, 30, 28, 54, 29, 31, 32, 33, 34, 35, 37, 36, 38, 39, 43, 40, 41, 46, 48, 57, 58, 60, 61, 62, 63, 64, 66, 68, 69, 71, 72, 79, 55]
Это представляет:
- Порядок выполнения
- Конвейер сборщика
- Последовательность генерации сигналов
### Коды сигналов отпечатков и вывод ключей
Преобразует сигналы отпечатков в детерминированные ключи шифрования через многоступенчатый конвейер вывода
Каждое значение отпечатка:
- Нормализуется
- Кодируется в компактный код сигнала
- Преобразуется в числовой ключ шифрования
- Шифруется с использованием полученного ключа
Процесс является детерминированным, то есть одно и то же входное значение всегда генерирует одинаковый код сигнала и ключ шифрования```
Raw Value
↓
Signal Code Derivation
↓
Compact Signal Code
↓
Numeric Key Derivation
↓
Encryption Key
↓
Value Encryption
Входной сигнал``` "BUTTON,195a81c9"
***Шаг 1 - Получение кода сигнала***```javascript
deriveSignalCode("BUTTON,195a81c9")
// → "wg"
Сгенерированный сигнальный код представляет собой компактный детерминированный идентификатор исходного значения.
Шаг 2 - Получение числового ключа```javascript deriveKey("wg") // → 3792
Сигнальный код преобразуется в числовой ключ шифрования.
***Шаг 3 — Зашифровать значение***```javascript
encryptValueWithKey(3792, "wgia1z9pwq")
// → "bYVbh6BUsE_5pLA"
Следующее значение будет зашифровано с помощью производного ключа, следуя порядку сбора.
Все сгенерированные коды сигналов агрегируются в компактную сериализованную структуру
Процесс``` value -> "BUTTON,195a81c9" seed code -> wg key -> 3792 value -> "wgia1z9pwq" seed code -> 21 key -> 1599 value -> 1 seed code -> p1 key -> 3521 value -> "8cc68d83" seed code -> ld key -> 3448 value -> "https://nextcaptcha.com/demo..." seed code -> 9p key -> 1879 value -> 4 seed code -> op key -> 3553 value -> false seed code -> 1r key -> 1633 value -> 7 seed code -> qf key -> 3605 value -> "jYAQSHAEAI" seed code -> 1z key -> 1641 value -> "" seed code -> 80 key -> 1784 value -> 2 seed code -> jk key -> 3393 value -> "AAAAAAAAAA" seed code -> 1z key -> 1641 value -> "h3" seed code -> 9p key -> 1879 value -> "0,BUTTON,195a81c9" seed code -> ia key -> 3352 value -> -1 seed code -> 1u key -> 1636 value -> 0 seed code -> wq key -> 3802 value -> "74,fbfbc5b3" seed code -> 6z key -> 1796 value -> 0 seed code -> wq key -> 3802 value -> 0 seed code -> wq key -> 3802 value -> 2 seed code -> jk key -> 3393 value -> 0 seed code -> wq key -> 3802 value -> "-1,-1" seed code -> 1m key -> 1628 value -> "static.cloudflareinsights.co,..." seed code -> 1g key -> 1622 value -> "" seed code -> 80 key -> 1784 value -> "https://nextcaptcha.com,https...." seed code -> pd key -> 3572 value -> "[4,"CABIBQAggA","BAgAAgAAgA"]" seed code -> 1n key -> 1629 value -> "Demostración empresarial de reCAPTCHA v3..." seed code -> 1k key -> 1626 value -> "[2,76852,77850,77351]" seed code -> 9b key -> 1865 value -> "sha384-TNm" seed code -> 23 key -> 1601 value -> "[1680,1050,876,1166,844,876]" seed code -> 13 key -> 1570 value -> "[300,null,1778502450481]" seed code -> ex key -> 3251 value -> "[null,null,"",""]" seed code -> rx key -> 3654 value -> "[2147483648,70806416,65303240]" seed code -> 2r key -> 1664 value -> "GA1.1.354395113.1778502448" seed code -> 1g key -> 1622 value -> false seed code -> 1r key -> 1633 value -> "[[[1,"wg"],..." seed code -> 85 key -> 1789
**Финал**```json
[
[
[1, "wg"], [1, "21"], [1, "p1"],
[1, "ld"], [1, "9p"], [1, "op"],
[1, "1r"], [1, "qf"], [1, "1z"],
[1, "80"], [1, "jk"], [1, "1z"],
[1, "9p"], [1, "ia"], [1, "1u"],
[1, "wq"], [1, "6z"], [1, "wq"],
[1, "wq"], [1, "jk"], [1, "wq"],
[1, "1m"], [1, "1g"], [1, "80"],
[1, "pd"], [1, "1n"], [1, "1k"],
[1, "9b"], [1, "23"], [1, "13"],
[1, "ex"], [1, "rx"], [1, "2r"],
[1, "1g"], [1, "1r"]
],
"54"
]
Idx 4 (string)
Значение: 3ccb
Хэшировано: true
Описание: Генерирует HMAC-ключ из window.localStorage.getItem("rc::a") + "6d", преобразованного в байты, вычисляет HMAC-SHA256(siteKey) с этим ключом и возвращает первые 4 шестнадцатеричных символа результата.
Значение rc::a — это случайно сгенерированная строка в base64.
Idx 5 (integer)
window.localStorage.length * 2falsewindow.localStorage умноженная на 2Idx 16 (string)
Значение: yM5Us/j/fn6EDgtmPlP4Pxj605nMJN9dRYHyy5Mn
Хэшировано: true
Описание: 40-символьный отпечаток Bloom-фильтра в base64 для всех узлов <HEAD>. Обходит каждый дочерний элемент, собирая имена тегов, атрибуты и текстовое содержимое, которые сериализуются и пропускаются через хэш djb2.
Каждый дайджест подаётся в BitHash(240 бит, 7 раундов, макс. 25 узлов), который устанавливает биты в сетке 40×6, а затем кодирует каждый 6-битный сегмент как символ base64. Собственные скрипты reCAPTCHA исключаются с помощью динамического регулярного выражения по src.
Idx 17 (integer)
2falseIdx 18 (string)
a2hyNTFuNDI4NnA3falseIdx 27 (string)
location.originfalseIdx 28 (boolean)
falsefalsewindow.parent != window ? true : window.frameElement != null ? true : falseIdx 29 (string)
e2a3cd70truegrecaptcha.execute с помощью SHA-256, ПримерIdx 30 (integer)
0falsehttps://www.gstatic.com/recaptcha/releases/<version>/, ПримерIdx 31 (string)
AAgkAQI0SBtruedocument.cookie, ПримерIdx 32 (string)
""falsedocument.referrer.Idx 33 (integer)
Значение: 2
Хэшировано: false
Описание: Элемент блока reCAPTCHA, в котором происходит рендеринг.
// например, grecaptcha.execute('.grecaptcha') -> этот div
// По сути, возвращает число или глубину/уровень этого div в документе.
// Пример: html > body > main > form > fieldset > div.recaptcha.form-field
// 5 4 3 2 1 0
// out = 5
nM = document.getElementsByClassName('grecaptcha-badge')[0] // или document.getElementsByClassName('g-recaptcha')[0] v2
let count = 0;
for (; nM = nM.parentElement || null;) {
count++;
}
const result = count;
Idx 34 (string)
AAAAAAAAAAtrue<INPUT>, ПримерIdx 35 (string)
0,DIV,f0c7414etruedocument.activeElement - анализирует, является ли он элементом покупки, используя регулярное выражение /buy|pay|place|order|donate|purchase/i с textContent, classNames и id, ПримерIdx 36 (string)
h3, h2 or http/1.1falsenextHopProtocol из навигационной производительности performance.getEntriesByType("navigation")[0].nextHopProtocolIdx 37 (integer)
-1falseperformance.timing.unloadEventStartIdx 38 (integer)
96falseperformance.timing.domainLookupStart - performance.timing.domainLookupEndIdx 39 (integer)
0falseperformance.navigation.typeIdx 40 (integer)
0falsewindow.scrollY или document.defaultView.pageYOffsetIdx 41 (string)
DIV,a08cd360true:hover, над которым находилась мышь в тот же момент; берётся последний элемент и хэшируются tagName, classNames и id, ПримерIdx 42 (string)
9,e3b0c442trueIdx 44 (string)
2v591z63wqtrue:hover, document.activeElement, все элементы <INPUT> хэшированы, location.url, window.scrollY. Пример:const element = document.querySelectorAll(":hover");
const current = element[element.length - 1];
const result = [
deriveSignalCode(hash(current)),
deriveSignalCode(hash(document.activeElement)),
deriveSignalCode(hash(document.querySelectorAll('input'))),
deriveSignalCode(location.url),
deriveSignalCode(String(window.scrollY))
].join('')
Idx 45 (integer)
4falsewindow.history.Idx 46 (string)
https://static.kogstatic.com/0000/34c...falseline:column, где это произошло.Idx 47 (integer)
0falsewindow.getSelection().toString().length || 0Idx 49 (string)
h2-0falsenextHopProtocol и определить, равна ли его duration нулю. Вернуть результат в формате protocol-isZero, ПримерIdx 50 (array)
Значение: [1,0,null,1,1,["805b1z63wq"],"MWc2MzZwMnR0ZHNkMw==",0,null,[[1]]]
Хэшировано: true
Описание: Содержит поведение пользователя, хэшированные элементы :hover и таймаут, увеличивающийся каждый раз, когда собирается ещё один элемент :hover. Анализирует активацию пользователя для накопления количества раз, когда пользователь взаимодействовал или всё ещё присутствует на странице, вычисляя (10 * IsActive + IsBeenActive)
Базовый формат:
[
count,
timeout,
null,
timeoutMultiplier,
transitionCount,
[ elementsHoverHashed ],
sessionId,
userActivationScore,
null,
[ [ flags ] ]
]
Idx 51 (array)
[0]falsedocument.body.innerText совпадения со словами "try again", "incorrect", "invalid" или "declined" и возвращает общее количество найденных совпадений. В противном случае возвращает 0, ПримерIdx 52 (integer)
11false10 * navigator.userActivation.isActive + navigator.userActivation.hasBeenActiveIdx 53 (integer)
Значение: 4
Хэшировано: false
Описание: Берёт текущий URL веб-сайта, преобразует его в массив символов, обрезает до 100 символов и затем преобразует обратно в строку, Пример
Затем, если длина этой обрезанной строки чётная, возвращается 5, если нечётная — 4.
Idx 54 (boolean)
falsefalsewindow.document.hiddenIdx 55 (array)
[[[1,"2v"],[1,"9z"],[1,"z8"],[1,"us"],[1,..falseIdx 56 (string)
-1,-1falsewindow.opener, возвращает "-1,-1", если null, ПримерIdx 57 (string)
Значение: www.gstatic.com,_,static.kogstatic.com,www.google.com,www.googletagmanager.,...
Хэшировано: false
Описание: Собирает хосты из src всех элементов <SCRIPT>, представленных в document.scripts, Пример
Для каждого <SCRIPT>:
Если элемент имеет атрибут src (hasAttribute("src")), берётся значение src и разбирается с помощью URI-подобного регулярного выражения (nd), извлекая только компонент хоста (группа захвата 3 регулярного выражения).
Если <SCRIPT> не имеет атрибута src (является встроенным скриптом), используется плейсхолдер "_".
Idx 58 (string)
mapslitepromosdismissed1falsewindow.localStorage (образец).Idx 59 (string)
""falsewindow.nameIdx 60 (string)
https://www.kogama.com,https://www.google.com,https://js.stripe.comfalseIdx 61 (array)
[0,"AAAAAAAAAA","AAAAAAAAAA"]trueMutationObserver, ПримерIdx 62 (string)
document.titlefalseIdx 63 (array)
Значение: []
Хэшировано: false
Описание: Извлекает временные метки (12-13-значные числа, начинающиеся с 1[2-9], характерные для эпох миллисекунд ~2008-2099) из значений куки, Пример
Суммирует следующие значения по всем найденным ID: количество, минимум, максимум и среднее.
Конечным результатом является массив [count, min, max, avg]
Idx 64 (integer)
55557falseIdx 65 (string)
sha384-3qctruehttps://www.google.com/recaptcha/api.js?render= (обрезано до 10 символов)Idx 67 (array)
[screen.width, screen.height, screen.availHeight, window.innerWidth, window.innerHeight, window.outerHeight]falseIdx 68 (array)
[300,null,1777685870223]false[new Date().getTimezoneOffset(), null, Date.now()]Idx 69 (string)
09AKhCRwjrDFiHsrRA--o23-HB-eWqOtk_XQ-rLE1...falseIdx 70 (array)
Значение: [null,null,"","",null,"1l0fl"]
Хэшировано: false
Описание: Извлекает общие денежные суммы (например, Total: $50.00 или Total 150.00 EUR), Пример
из document.innerText путём поиска символа или кода валюты вместе с числовым значением с помощью регулярного выражения:
total[\S\s]{0,20}?(((R\$|\$|€|£|USD|EUR)\s*[\d\.,]+)|[\d\.,]+\s*(€|USD|EUR))`
total → Ищет точное слово "total"
[\S\s]{0,20}? → Ищет от (0) до (20) промежуточных символов (таких как пробелы, двоеточия или переносы строк) перед числом
(((R\$|\$|€|£|USD|EUR)\s*[\d\.,]+)|[\d\.,]+\s*(€|USD|EUR)) → Основной блок, который захватывает деньги и разделён на две отдельные опции
Idx 71 (array)
[jsHeapSizeLimit,usedJSHeapSize,totalJSHeapSize]falseIdx 72 (array)
Значение: [[["Google Chrome","147"],["Not.A/Brand",...
Хэшировано: false
Описание: Массив данных userAgent navigator.userAgentData, флаг, указывающий, является ли устройство мобильным, и платформа, Пример:
[
navigator.userAgentData.brands.map(b => [b.brand, b.version]),
navigator.userAgentData.mobile ? 1 : 0,
navigator.userAgentData.platform
]
Idx 73 (array)
[[null,2,0,"Mozilla/5.0 (Windows NT 10.0;...falseIdx 77 (integer)
nullfalseIdx 78 (string)
""falseНекоторые сигналы reCAPTCHA могут быть недоступны или не собраны (из-за тайм-аутов, связанных с отладкой). В таких случаях отсутствующий сигнал заполняется случайно сгенерированным значением, закодированным в base64. Например:``` ["CMXZyamk3ZHNjdGsyaQ==", 3249, 591]
После завершения построения отпечатка (fingerprint) он сериализуется в строку и шифруется с помощью ключа шифрования, загруженного из поля «anchor» (индекс 18). Затем зашифрованный результат сохраняется в поле с индексом `16` полезной нагрузки `/reload`.
## Сигналы VM
Ниже приведены значения браузера, полученные внутренней VM reCAPTCHA, вместе с ключом сигнала. Некоторые значения не отображаются в примере отпечатка, так как VM извлекает только определённые значения из конфигурационного байт-кода. Поэтому будут показаны другие значения. Подробнее см. [здесь](https://github.com/elyelysiox/recaptcha-vm)
Формат базового сигнала:```
[
null,
collectorElapsed,
encryptElapsed,
value
]
Где collectorElapsed означает, сколько времени (мс) потребовалось сборщику для получения значения
А encryptElapsed означает, сколько времени (мс) потребовалось для шифрования значения.
Эти времена измеряются во время выполнения VM.
[ null, 7, 0, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36" ], null, null, [ null, 0, 0, "[]" ], null, null, [ null, 4, 0, "["Join And Get Free Trial!","","Captcha Solving Service","Products","Docs"]" ], [ null, 0, 0, "false" ], null, [ null, 0, 0, "0" ], [ null, 1, 0, "[null,null,null,null,"false"]" ], [ null, 3, 4, "[[[0,921],[1,2053]],[[235054,1,1,1],[164277,3,4,1]],2,39]" ], [ null, 1, 4, "[[[0,0,979]],1,0]" ], [ null, 1, 1, "[[[1,0]]]" ], [ null, 4, 1, "[886,2300,262.2099999997952,392.7600000009595,2050.2399999993577]" ], [ null, 1, 1, "[[[1123,0,0]]]" ], [ null, 1, 2, "[[[1123,0]]]" ], [ null, 1, 3, "[0,null,null,null,null,null,null]" ], [ null, 5, 9, "[1,[[2050,125,633,266,[609,266,105,44],3,1,50,1,1]],[3,3,318],[28,777,858,28],0,0,25]" ], [ null, 0, 0, "" ], [ null, 0, 0, "229.6" ], [ null, 0, 0, "false" ], [ null, 1, 0, "[1,0,0,Infinity]" ], [ null, 1, 1, "["0",-1,-1,999]" ], [ null, 1, 0, "["undefined",3]" ], [ null, 1, 6, "[2,4,53324367238]" ], [ null, 12, 0, "["Google Inc. (Intel Inc.)","ANGLE (Intel Inc., Intel(R) UHD Graphics 630, OpenGL 4.1)",30]" ], [ null, 28, 2, "[1542343452,0,105002991,3556653,96784904,-731949068,-902263026,-1914850612,571474391,-1588406278,3443508,1967713171,-1249474914,-1588406278,0,918504841,-807058197]" ]
- **Key 417** (`string`)
- Значение: `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...`
- Описание: Полный User-Agent браузера
- **Key 727** (`array`)
- Значение: `[]`
- Описание: Блоки цен на товары, содержит цены на коммерческие продукты на веб-сайте; они могут появляться на торговых площадках, таких как Amazon, eBay, AliExpress и т.д. [Пример](https://github.com/elyelysiox/recaptcha/blob/main/findProductPricesBlock.js)
- **Key 150** (`array`)
- Значение: `[\"Join And Get Free Trial!\",\"\",\"Captcha Solving Service\",\"Prod...`
- Описание: Ссылки, извлечённые из элементов `<a>` на веб-сайте; VM редко извлекает эти значения
- **Key 545** (`boolean`)
- Значение: `false`
- Описание: `navigator.webdriver`
- **Key 370** (`integer`)
- Значение: `0`
- Описание: Максимальное количество точек касания устройства `navigator.maxTouchPoints`
- **Key 779** (`array`)
- Значение: `[null,null,null,null,"false"]`
- Описание: Извлекает данные из полей доставки/оплаты; [Пример](https://github.com/elyelysiox/recaptcha/blob/main/formFieldScrape.js)
`postal|postalcode|postalcode|postal_code|zip` → поиск поля почтового индекса
`ship|deliver` → раздел доставки
`billing|payment|credit` → раздел оплаты
`country` → выбор страны
`addr.*(match|equal)|(match|equal).*address` → флажок «совпадает с адресом»
Считывает name, id, autocomplete, className каждого элемента
Три фазы: текстовые поля, SELECT-выражения, флажки
- **Key 659** (`array`)
- Значение: `[[[0,921],[1,2053]],[[235054,1,1,1],[164277,3,4,1]],2,39]`
- Описание: Содержит данные о нажатых элементах на странице в формате `[[[count,timestamp]],[[hashedElem,tagType,type,autoComplete]],pressedCount,behaviorChecksum]`
### Тип элемента
| Тип элемента | ID |
|-------------|----|
| `""` | 1 |
| `text` | 2 |
| `button` | 3 |
| `submit` | 4 |
| `checkbox` | 5 |
| `email` | 6 |
| `password` | 7 |
| `select-one` | 8 |
| `select-multiple` | 9 |
| `radio` | 10 |
| `textarea` | 11 |
| `file` | 12 |
| `tel` | 13 |
| `number` | 14 |
| `date` | 15 |
| `reset` | 16 |
| `range` | 17 |
### Алгоритм контрольной суммы поведения (Behavior CheckSum)
`behaviorChecksum` — компактная оценка поведения, полученная на основе собранных данных о взаимодействиях пользователя.
Для каждого взаимодействия:
- Индекс взаимодействия увеличивается на единицу
- Временная метка комбинируется со скорректированным индексом взаимодействия
- Хеш элемента сокращается с помощью операции взятия остатка от деления
- Результат накапливается в текущее значение
---
```javascript
function computePressedElementsChecksum(pressedElementsArray) {
let accumulator = 0;
for (const [countTimestampPair, elementInfo] of zip(
pressedElementsArray[0],
pressedElementsArray[1],
)) {
const shiftedTimestamp = countTimestampPair[0] + 1;
const baseValue = countTimestampPair[1] + shiftedTimestamp;
const remainder = elementInfo[0] % baseValue;
accumulator = remainder + accumulator;
}
const logTerm = Math.log(accumulator + 1) * 4;
const modTerm = accumulator % 10;
const behaviorChecksum = Math.floor(logTerm + modTerm);
return JSON.stringify(pressedElementsArray);
}
function zip(a, b) {
const length = Math.min(a.length, b.length);
const result = [];
for (let i = 0; i < length; i++) {
result.push([a[i], b[i]]);
}
return result;
}
```
### Пример
```javascript
const checksum = computePressedElementsChecksum([[[0,921],[1,2053]],[[235054,1,1,1],[164277,3,4,1]],2,39]);
console.log(checksum); // → 39
```
- **Key 959** (`array`)
- Значение: `[[[0,0,979]],1,0]`
- Описание: Содержит данные о перемещениях прокрутки, собранные во время сессии на странице
### Базовый формат
```
[ [ [ scrollX, scrollY, timestamp ] ], count, behaviorChecksum ]
```
- **Key 895** (`array`)
- Значение: `[[[1,0]]]`
- Описание:`VisibilityStateEntry` отслеживает состояние видимости страницы, указывая, просматривает ли пользователь страницу в данный момент или ушёл с неё, в формате `[[[isVisible, startTime]]]`
- **Key 1092** (`array`)
- Значение: `[886,2300,262.2099999997952,392.7600000009595,2050.2399999993577]`
- Описание: Производительность reCAPTCHA, PerformanceNavigationTiming и PerformanceEventTiming, в формате `[vmStart,vmEnd,domContentLoadedEventEnd,loadEventEnd,firstUserInteractionTime]`
- **Key 41** (`array`)
- Значение: `[[[1123,0,0]]]`
- Описание: ClearTimeout
- **Key 43** (`array`)
- Значение: `[[[1123,0]]]`
- Описание: ClearTimeout
- **Key 549** (`array`)
- Значение: `[0,null,null,null,null,null,null]`
- Описание: KeyboardEvent, вычисленные клавиши + временные метки
- **Key 352** (`array`)
- Значение: `1,[[2050,125,633,266,[609,266,105,44],3,1,50,1,1]],[3,3,318],...`
- Описание:
Структура содержит:
- Хронометраж движения мыши
- Координаты курсора
- Продолжительность нажатий
- Размеры нажатого элемента
- Информацию о типе указателя
- Показатели давления
- Расстояние перемещения курсора
- Статистику временных характеристик поведения
### Базовый формат:
```
[
eventsCount,
mouseEvents,
[
coalescedEventsCount,
averageInterEventDelay,
timingVarianceScore
],
[
totalPoints,
clickSequenceScore,
totalCursorDistance,
totalPoints
],
0,
0,
behaviorChecksum
]
```
### Структура события мыши:
Каждая запись внутри mouseEvents имеет следующую структуру:
```
[
timestamp,
clickDuration,
clientX,
clientY,
[
elementLeft,
elementTop,
elementWidth,
elementHeight
],
elementTagType,
trackedElementIndex,
pressure,
pointerTravelDistance,
pointerArea
]
```
| Имя | Описание |
|---|---|
| `timestamp` | Временная метка события относительно начала события |
| `clickDuration` | Продолжительность между `pointerdown` и `pointerup` |
| `clientX` | ... |
| `clientY` | ... |
| `elementBounds` | Ограничивающий прямоугольник взаимодействующего элемента |
| `elementTagType` | Закодированный идентификатор тега DOM взаимодействующего элемента |
| `trackedElementIndex` | Внутренний идентификатор взаимодействующего элемента DOM из карты взаимодействий во время выполнения |
| `pressure` | Нормализованное значение давления указателя, умноженное на `100` |
| `pointerTravelDistance` | Расстояние между `pointerdown` и `pointerup` |
| `pointerArea` | Площадь контакта, вычисленная как `width * height` |
### Тип тега элемента
Инициализируется во время выполнения VM reCAPTCHA
| Тип | ID |
|-----------|----|
| `BODY` | 1 |
| `INPUT` | 2 |
| `BUTTON` | 3 |
| `SELECT` | 4 |
| `TEXTAREA`| 5 |
| `FORM` | 6 |
| `A` | 7 |
| `AREA` | 8 |
| `AUDIO` | 9 |
| `VIDEO` | 10 |
| `IFRAME` | 11 |
| `DIV` | 12 |
| `SPAN` | 13 |
| `LABEL` | 14 |
### Пример
```
[
18342,
91,
742,
381,
[700, 320, 120, 38],
3,
1,
50,
1.4,
16
]
```
Представляет:
- событие через `18,342 с`
- длительность нажатия `91 мс`
- нажатие в точке `(742, 381)`
- взаимодействие с кнопкой (`BUTTON`)
- давление указателя `0,5` (50)
- небольшое движение курсора во время нажатия
- площадь контакта указателя `16`
### Алгоритм контрольной суммы поведения (Behavior CheckSum)
Для каждого события мыши:
- Продолжительность нажатия усиливается
- Результат комбинируется с временной меткой события
- Координаты курсора и размеры элемента смешиваются
- Вычисляется взвешенное отношение
- Отношение округляется и накапливается
После обработки всех событий:
- Накопленная оценка комбинируется с общим расстоянием курсора
- Результат нормализуется по количеству собранных точек
- Применяется логарифмическое преобразование
- Добавляются дополнительные статистические показатели движения
- Итоговое значение сокращается с помощью операции взятия остатка от деления
- Результат округляется до небольшого целочисленного значения
---
```javascript
function computeMouseBehaviorChecksum(mouseDataArray) {
let totalSum = 0;
for (let i = 0; i < mouseDataArray[1].length; i++) {
const event = mouseDataArray[1][i];
const scaledClickDuration = event[1] * 4;
const xPlusScaledDuration = event[0] + scaledClickDuration;
const combinedElementDims = event[2] + event[4][2] + event[4][1]; // clientY + elementWidth + elementTop
const product = xPlusScaledDuration * combinedElementDims;
const divisor = event[3] + event[4][3] + event[4][0] + 1; // elementTagType + elementHeight + elementLeft + 1
const roundedValue = Math.round(product / divisor);
console.log(roundedValue);
totalSum += roundedValue;
}
const summaryArray = mouseDataArray[3]; // [totalPoints, clickSequenceScore, totalCursorDistance, totalPoints]
const sumPlusCursorDistance = totalSum + summaryArray[2];
const totalPointsPlusOne = summaryArray[0] + 1;
const ratio = sumPlusCursorDistance / totalPointsPlusOne;
const ratioPlusOne = ratio + 1;
const logRatio = Math.log(ratioPlusOne);
const scaledLog = logRatio * 4;
const ratioPlusTotalPoints = ratio + summaryArray[0];
const ratioPlusTotalPointsPlusScore = ratioPlusTotalPoints + summaryArray[1];
const modResult = ratioPlusTotalPointsPlusScore % 11;
const behaviorChecksum = Math.round(scaledLog + modResult);
return JSON.stringify(mouseDataArray);
}
```
### Пример
```javascript
const checksum = computeMouseChecksum([1,[[2050,125,633,266,[609,266,105,44],3,1,50,1,1]],[3,3,318],[28,777,858,28],0,0,25]);
console.log(checksum); // → 25
```
- **Key 360** (`string`)
- Значение: `""`
- Описание: Собранные общие цены на веб-сайте, извлечённые с помощью регулярного выражения `(?i)total[\S\s]{0,20}?(?:(?:(?:USD|\$)\s*)?[\d\.,]+\s*(?:USD|\$)|(?:USD|\$)\s*[\d\.,]+)`, [Пример](https://github.com/elyelysiox/recaptcha/blob/main/websitePricesTotal.js)
- **Key 1278** (`integer`)
- Значение: `229.6`
- Описание: Деление на 10 текущего времени выполнения `Math.trunc(performance.now()) / 10`
- **Key 1422** (`boolean`)
- Значение: `false`
- Описание: Определяет, есть ли на веб-сайте поля для OTP/2FA, [Пример](https://github.com/elyelysiox/recaptcha/blob/main/parseOTPFields.js)
- **Key 614** (`array`)
- Значение: `[1,0,0,Infinity]`
- Описание: Информация о батарее устройства, в формате `[level,isCharging,chargingTime,dischargingTime]`
- **Key 2033** (`array`)
- Значение: `["0",-1,-1,999]`
- Описание: Некоторые константные значения VM и время выполнения VM
- **Key 1313** (`array`)
- Значение: `["undefined",3]`
- Описание: Проверки Android, Chrome, `[typeof window.android, Object.keys(window.chrome).length]`
- **Key 1994** (`array`)
- Значение: `[2,4,53324367238]`
- Описание: `[navigator.hardwareConcurrency, navigator.deviceMemory, navigator.storage.estimate().qouta]`
- **Key 1310** (`array`)
- Значение: `["Google Inc. (Intel Inc.)","ANGLE (Intel Inc.,...`
- Описание: Рендерер WebGL, производитель и общее количество расширений, [Пример](https://github.com/elyelysiox/recaptcha/blob/main/webglSignals.js)
- **Key 291** (`array`)
- Значение: `[1542343452,0,105002991,3556653,96784904,-731949068,-902263026,...`
- Описание: Хешированные ключи определённых прототипов объектов браузера, таких как `SpeechSynthesisEvent`, `NetworkInformation`, `HTMLElement` и т.д., [Пример](https://github.com/elyelysiox/recaptcha/blob/main/hashBrowserProtos.js)
## Полезная нагрузка запроса на повторную загрузку (Reload Request Payload)
Эти данные сериализуются с помощью protobuf (Protocol Buffers) — эффективного, нейтрального метода сериализации структурированных данных, разработанного Google.```javascript
[
"rreq",
"U5VsmTDhJM1iOJUyw4DEUTYv",
"03AFcWeA5Els3SIQviW2OYCHYD4uDhKlFPG6A48z2nk74BC2vrXSRvtpP9uVkWW96kUKm7sHIr...",
null,
null,
"-383788762",
"q",
"05APQrAobTCFGtbKVY3NwExv-K_G7oqmzGyUcTXi532CBKvSJUiT49tkRSgGD__OdfVRZtrI3i...",
"submit",
null,
null,
null,
null,
null,
"6LcAbwIqAAAAAJvVAhSSJ8qzYsujc7kn1knmSgQX",
null,
"0XA4cHhALxgMF9_Kt6uze2ZTR08XAe2x8o3BrU1hbTouNf3o1cnRmYRxZW01IA0BCNC_qJykb...",
null,
null,
null,
"tbMywxNTQsODQ0XSxbMSwyOTksMTA4Nl1dLFtbMiw2MSwxMzIuNzM5OTk5OTk5ODQ2MzNdLF...",
"0aAPQrAobqMEazjp9PGLYWTgxWGGcq9k5MJNI6LFBWfTh0UkDaVobdH-InwAYsTXtxs3rH_fI",
"BDAAYAIAGEcAAUoYI4lDCKAsCJlcgB6AAJtgggoIEEOALSoQRZxSZyAJBHANCkAJQYhFJES...",
null,
null,
"W1tbNTAwNiw0M10sWzY0NjA3LDFdLFs0NTQ2NCwxXSxbMzU4MzcsMV1dXQ==",
null,
null,
]
| Индекс | Значение | Описание |
|---|---|---|
0 | rreq | Идентификатор типа запроса |
1 | U5VsmTDhJM1iO... | Версия reCAPTCHA |
2 | 03AFcWeA5Els3... | Токен проверки, полученный из конечной точки /anchor |
5 | -383788762 | Хэш всего сериализованного полезного отпечатка, Пример |
6 | q | Указывает на выполняемый вызов или режим запроса |
7 | 05APQrAobTCFG... | Шаблоны использования, содержащие историю использования и ранее решённые шаблоны вызовов. Могут значительно повысить оценку, если действительны |
8 | submit | Конкретное действие сайта, которое пользователь выполняет на странице во время проверки (например signin, register) |
14 | 6LcAbwIqAAAA... | Ключ веб-сайта |
16 | 0XA4cHhALxgM... | Зашифрованный весь сериализованный полезный отпечаток с помощью ключа шифрования из anchor (индекс 18) 1777669303203 |
20 | tbMywxNTQsOD... | Структура телеметрии в кодировке Base64, содержащая дельты времени виртуальной машины, временные данные ресурсов, собранную статистику объектов браузера и URL-адреса скриптов веб-сайта |
21 | 0aAPQrAobqME... | Токен, связанный с ранее решённой CAPTCHA. Используется для непрерывности сеанса и корреляции доверия |
22 | BDAAYAIAGEcA... | Хэш всех ключей объектов браузера |
25 |
| Значение | Описание |
|---|---|
q | Вызов reCAPTCHA V3 |
fi | Вызов reCAPTCHA V2/Invisible |
a | Аудио-вызов |
qr | Быстрый ответ (новый) |
Idx 20)Для декодирования:```javascript atob("W1tbMywwLDgyOV0sWzEs...") // → [[[3,0,829],[1,42,863]],[[2,727,1341.300000011921]...
Полезная нагрузка содержит:
- Информацию о времени загрузки ресурсов
- Метрики производительности долгих задач
- Дельта выполнения VM reCAPTCHA
- URL-адреса скриптов веб-сайта
- Статистику коллекций объектов браузера
### Базовая структура```js
[
[recaptchaResources],
[tasksTiming],
[
null,
null,
null,
idleTimeoutDelta,
deltaTiming,
0,
0,
0,
],
websiteScriptsUrls,
[collectionElapsed, browserObjectsLength]
]
[ PerformanceLongTaskTiming.name == "self" ? 2 : 4, PerformanceLongTaskTiming.duration, PerformanceLongTaskTiming.startTime ]
### Формат тайминга ресурсов reCAPTCHA```js
[
resourceType,
duration,
startTime
]
| Ресурс | Тип |
|---|---|
recaptcha/api.js | 3 |
recaptcha/releases/<version> | 1 |
https://www.google.com/recaptcha/api2/anchor | 2 |
https://www.google.com/recaptcha/api2/bframe | 4 |
Idx 25)Это поле содержит счетчики взаимодействий с браузером, закодированные в Base64, собранные во время выполнения.
Пример декодированного значения:```js [ [ [5006, 43], [64607, 1], [45464, 1], [35837, 1] ] ]
### Структура счетчика событий```js
[
[
[eventType, count]
]
]
| Хеш | Тип события |
|---|---|
5006 | pointermove |
64607 | pointerdown |
45464 | pointerup |
31617 | keydown |
37178 | keyup |
35837 | focusin |
[ "rresp", "", null, 120, [ "pmeta", [ "/m/013xlm", null, 3, 3, 3, null, "Tractor" ], null, [1] ], "", null, null, "", "", null, null, "", null, "", "", 0 ]
## Описание полей
| Индекс | Имя | Описание |
|---|---|---|
| `0` | `rresp` | Идентификатор типа ответа, возвращаемый конечной точкой reload |
| `1` | `responseToken` | Токен ответа reCAPTCHA |
| `3` | `tokenExpiration` | Время жизни токена в секундах до истечения срока |
| `4` | `pmeta` | Блок метаданных задачи, содержащий информацию о типе CAPTCHA, категории и расположении сетки |
| `5` | `challengeType` | Тип задачи CAPTCHA |
| `8` | `usagePatternToken` | Шаблоны использования, хранящиеся в `window.sessionStorage` и повторно используемые в будущих запросах `/reload` |
| `9` | `challengeImageToken` | Токен, используемый для получения полезной нагрузки изображения CAPTCHA из конечной точки `/payload` |
| `12` | `humanVerificationToken` | Токен идентификации человека, связанный с состоянием Google reCAPTCHA и файлами cookie браузера |
| `14` | `captchaSessionToken` | Внутренний сессионный токен |
| `15` | `bftToken` | ... |
---
### Структура `pmeta`
Пример:```js
[
"pmeta",
[
"/m/013xlm",
null,
3,
3,
3,
null,
"Tractor"
],
null,
[1]
]
pmeta[1] Значения| Позиция | Значение | Описание |
|---|---|---|
0 | /m/013xlm | Внутренний идентификатор категории для целевого класса объекта |
2 | 3 | Количество столбцов сетки |
3 | 3 | Количество строк сетки |
5 | null | Зарезервированное или опциональное поле метаданных |
6 | "Tractor" | Текстовая метка |
| Тип | Описание |
|---|---|
imageselect | Стандартная CAPTCHA с выбором изображения |
tileselect | Динамическое задание на основе плиток |
dynamic | Динамическое задание, где плитки перезагружаются после выбора |
multicaptcha | Многошаговое задание CAPTCHA |
audio | Аудио-задание для доступности |
nocaptcha | Проверка только с помощью флажка |
doscaptcha | Отказ от CAPTCHA при злоупотреблениях |
Значение в Idx 9 (challengeImageToken) используется для получения изображения CAPTCHA:```txt
https://www.google.com/recaptcha//payload
?p=
&k=
### Идентификаторы категорий
| ID | Категория |
|---|---|
| `/m/0pg52` | Такси |
| `/m/01bjv` | Автобус |
| `/m/02yvhj` | Школьный автобус |
| `/m/04_sv` | Мотоцикл |
| `/m/013xlm` | Трактор |
| `/m/01jk_4` | Дымоход |
| `/m/014xcs` | Пешеходный переход |
| `/m/015qff` | Светофор |
| `/m/0199g` | Велосипед |
| `/m/015qbp` | Паркомат |
| `/m/0k4j` | Автомобиль |
| `/m/015kr` | Мост |
| `/m/019jd` | Лодка |
| `/m/0cdl1` | Пальма |
| `/m/09d_r` | Гора / холм |
| `/m/01pns0` | Пожарный гидрант |
| `/m/01lynh` | Лестница |
## Полезная нагрузка запроса UserVerify
Полезная нагрузка `/userverify` отправляется после того, как пользователь пройдет задачу с изображениями reCAPTCHA v2
# Структура полезной нагрузки```
v: hsFBb1u5wWWWkWP4in1ua2cQ
c: 03AFcWeA5RIHz5mI2COQqTcLjdrC7FiPpdfkX-hB-B2iFenI...
response: eyJyZXNwb25zZSI6WzMsMSw0LDksMCwxMSwxMCwxMywxNF0....
t: 24521
ct: 24521
ch: -4007845
oc: 0ZpqzzPgYKEEaN0xpfo3v_voeVUySZK3Ktf_fLVRdhnu3yeTy4BhO...
pm: 51bGwsbnVsbCxbbnVsbCxudWxsLG51bGwsWzEwLDQuMjcwMDAwMD....
| Поле | Имя | Описание |
|---|---|---|
v | recaptchaVersion | Версия reCAPTCHA |
c | challengeToken | Токен состояния вызова, полученный от /replaceimage или предыдущего запроса /userverify |
response | Закодированная полезная нагрузка, содержащая индексы выбранных плиток и дополнительные метаданные взаимодействия | |
t | challengeElapsed | Прошедшее время с момента начальной загрузки вызова |
ct | completionTime | Общее время, затраченное пользователем на решение и отправку CAPTCHA |
ch | ocHash | Хеш всей сериализованной полезной нагрузки oc, Пример |
oc | encryptedVmSignals | Зашифрованные сигналы, собранные из виртуальной машины reCAPTCHA, см. здесь. |
pm | telemetryPayload | Закодированная в Base64 структура телеметрии, содержащая разницу времени выполнения ВМ, временные метки ресурсов, собранную статистику объектов браузера и метаданные скриптов веб-сайта |
responseПредставляет индексы/позиции квадратов, выбранных пользователем в сетке
Полезная нагрузка позже кодируется как JWT перед отправкой
```txt
0 1 2
3 4 5
6 7 8
Пример полезной нагрузки:```json
{
"response": [2, 3, 6]
}
```txt
0 1 2 3
4 5 6 7
8 9 10 11
12 13 14 15
Пример полезной нагрузки:```json
{
"response": [2, 3, 6, 14]
}
eПолезная нагрузка response может также включать поле e:```json
{
"response": [2, 3, 6, 14],
"e": "bxCoiXVt49I7CTS_tNlVbGg"
}
Это поле содержит зашифрованные метаданные взаимодействия, связанные с действиями кнопок CAPTCHA, такими как:
- `Verify`
- `Next`
- `Skip`
### Внутренняя структура `e`
После расшифровки структура выглядит следующим образом:
``````txt
[[["9ffd9672"]]]
Каждое значение представляет собой хешированный идентификатор для класса/действия кнопки rc-button-default
Хеш:
Более длинные последовательности взаимодействия могут выглядеть так:```txt [ [ ["9ffd9672"], ["9ffd9672"], ["9ffd9672"], ["9ffd9672"] ] ]
### `oc` — Зашифрованные сигналы VM
Использует тот же алгоритм шифрования, что и поле `idx 16` в полезной нагрузке запроса `/reload`, с ключом шифрования, загруженным в якорь (индекс 31) `1777752103288`
### `pm` — Полезная нагрузка телеметрии
Поле `pm` содержит структуру телеметрии, закодированную в Base64, аналогичную полезной нагрузке телеметрии, используемой в `/reload`.
## Антиотладка/Защита от взлома
- ***Метка целостности на основе Symbol***
reCAPTCHA устанавливает ключ Symbol `Symbol(jas)` для каждого массива и объекта с числовым значением, используемым в качестве проверки целостности. Поскольку Symbols не перечисляемы и невидимы для стандартных операций клонирования (таких как JSON.stringify), любая попытка заменить или клонировать эти структуры приводит к потере метки, что reCAPTCHA обнаруживает как подделку

- ***Захват переменных замыкания***
Некоторые значения намеренно ограничены областью внешней функции и используются только внутри вложенных обратных вызовов или замыканий; переменная никогда не существует в той же области, где используется
- ***Проверки времени выполнения***
reCAPTCHA включает механизмы обнаружения отладки во время выполнения. Если обнаруживается, что код анализируется отладчиком, он перезапускает сеанс отладки, что приводит к потере состояния выполнения и контекста
- ***Вкладка сбоя источника***
https://github.com/user-attachments/assets/4ae3bf68-5ae7-4adc-a0b9-36cc11b64998
Эта ошибка возникает в браузерах, использующих Chromium. Существует несколько ошибок, которые приводят к неожиданному закрытию браузера или инструментов разработчика, например, показанная здесь:
https://blog.castle.io/detect-and-crash-chromium-bots-with-one-weird-trick-bots-hate-it/
https://issues.chromium.org/issues/340836884?ref=blog.castle.io
Поэтому одна из этих ошибок, вероятно, срабатывает при наступлении тайм-аута, так как это, скорее всего, не произошло бы в ходе реального пользовательского сеанса
## reCAPTCHA Botguard
Хотя BotGuard был удалён Google, я кратко объясню, как он работает внутри, для тех, кто интересуется его устройством и тем, чем он отличается от других типов ботгарда.
Сначала процесс генерации токена: VM генерирует токены для постоянных ошибок во время выполнения. Эти ошибки могут быть вызваны отклонениями смещения байт-кода, отсутствующими регистрами VM, неверным состоянием VM и нарушением целостности потока управления. Эти ошибки перехватываются диспетчером, который вызывает функцию, записывающую байты для токена.
VM использует ARX Cipher — алгоритм, созданный АНБ в 2013 году, похожий на Speck для шифрования данных. Он также используется во всех версиях Google BotGuard. Алгоритм меняется с каждой версией, как и константа (3990), порядок операций и раунды. Выглядит это так:```javascript
/*
D -> cipher state array
P -> seed
E -> byte index
*/
Vl = function(D, P, E, F, v) {
F = D[3] | 0;
D = D[2] | 0;
for (v = 0; v < 14; v++) {
E = E >>> 8 | E << 24;
E += P | 0;
E ^= D + 3990;
P = P << 3 | P >>> 29;
P ^= E;
F = F >>> 8 | F << 24;
F += D | 0;
F ^= v + 3990;
D = D << 3 | D >>> 29;
D ^= F;
}
return [P >>> 24 & 255, P >>> 16 & 255, P >>> 8 & 255, P >>> 0 & 255, E >>> 24 & 255, E >>> 16 & 255, E >>> 8 & 255, E >>> 0 & 255];
}
Основная функция шифрования имеет два режима: с шифрованием и без шифрования. Режим с шифрованием использует ARX Cipher, Bitwise + XOR для шифрования данных и помещения их в буфер. Существует 4 типа буферов:
180353304243 (не используется в этом типе botguard).Режим без шифрования просто помещает уже зашифрованные байты в буфер```javascript // (The sequence flatten messed up the statements a bit) /* Q -> cipher state array FQ(v, c) -> derive a seed c = (z << 3) - 4 -> index FQ(v, (c | 0) + 4) -> position */ t = function(D, P, E, F, v, Q) { if (E.O == E) { P = function(h, A, c, x, z) { A = v.length; // buffer length z = (A | 0) - 4 >> 3; if (v.Jw != z) { // vm.block_index != index v.Jw = z; // set new block index c = (z << 3) - 4; x = [0, 0, Q[1], Q[2]]; try { v.zB = Vl(x, FQ(v, c), FQ(v, (c | 0) + 4)); // call ARX Cipher to generate keyStream } catch (V) { throw V; } } v.push(v.zB[A & 7] ^ h); // sets A to bits 0-7, and obtains a byte from the keystream and applies xor with a byte of the data to be encrypted }; v = Z(P, E); // get buffer if (P == 180 || P == 243 || P == 353) { Q = Z(189, E); } else { // set encripted bytes to buffer P = function(h) { v.push(h); }; } F && P(F & 255); E = D.length; for (F = 0; F < E; F++) { P(D[F]); } } }
А это функция, которая обрабатывает ошибки и вызывает byte writer.```javascript
// (The sequence flatten messed up the statements a bit)
p = function(D, P, E, F, v, Q, h, A) {
D = E[1];
A = E[2];
Q = Z(465, P) >> 3;
h.push(D, Q >> 8 & 255, Q & 255); // error code/header bytes
E.message && (D += E.message);
A = void 0;
E && E[0] === U && (E = void 0);
h = Z(244, P);
h.length == 0 && (A != void 0 && h.push(A & 255));
D = "";
E && (E.stack && (D += ":" + E.stack));
E = Z(0, P); // buffer size limit (2048)
if (!P.TB && E[0] > 3) {
D = D.slice(0, (E[0] | 0) - 3);
E[0] -= (D.length | 0) + 3; // reduce size limit
D = Pv(D);
E = P.O;
P.O = P;
try {
v = (v = Z(304, P)) && v[v.length - 1] || 95;
if (P.n3) {
// this path is never executed
(F = Z(123, P)) && F[F.length - 1] == v || t([v & 255], 123, P);
} else {
t([95], 304, P); // set byte to error buffer
}
t(O(2, D.length).concat(D), 180, P, 9); // set bytes to main buffer
} finally {
P.O = E;
}
}
}
В конце выполнения VM вызывает главную функцию, которая подготавливает полезную нагрузку с этими буферами и генерирует токен, являющийся подписью. В отличие от других BotGuard VM, эта VM не имеет системы снятия отпечатков, Google Botguard, а также не выполняет эти операции выдачи ошибок и записи байт, и она намного проще и легче поддается обратному проектированию.
Этот репозиторий предназначен исключительно для образовательных и исследовательских целей. Код, техники и концепции, представленные здесь, предназначены для академического изучения и понимания рассматриваемых тем.
W1tbNTAwNiw...| Счётчики в кодировке Base64 для событий взаимодействия пользователя, таких как движение мыши, клики, клавиатура и изменения фокуса |