
Documentación e ingeniería inversa de reCAPTCHA
Este repositorio contiene un análisis técnico del antibot de Google (reCAPTCHA) centrado en:
@g_recaptcha@lyxlobyxreCAPTCHA es uno de los sistemas antibot con las técnicas de ofuscación más sofisticadas, ya que emplea una serie de transformaciones que hacen que el código sea menos legible y más difícil de aplicar ingeniería inversa. La mayoría de las ofuscaciones pueden manipularse fácilmente utilizando el Árbol de Sintaxis Abstracta (AST), pero algunas se procesan en tiempo de ejecución, lo que hace que el AST sea inútil en ese caso. También se aplica polimorfismo al código para cambiar su estructura en cada versión del script. Por ejemplo, el código no realiza la acción directamente. En su lugar, utiliza objetos o funciones que cambian de forma
Expresiones de secuencia
El código se aplana convirtiendo cada sentencia de bloque en una expresión continua separada por comas; puede aparecer en sentencias if, argumentos de funciones e incluso dentro de objetos

Aritmética mixta booleana Entrelaza operaciones aritméticas (suma, resta, multiplicación) con operaciones bit a bit (AND, OR, XOR, NOT) para ocultar la lógica original, por ejemplo
-2 * ~(h & H) + -2 + (h ^ H)Tabla indirecta de funciones
Cada función se construye dentro de una tabla y se llama mediante su índice, como functions[index](https://github.com/elyelysiox/recaptcha/blob/main/args)
Array constante en línea Se asigna un literal de array local en línea, en mitad de una expresión; el array agrupa constantes (números, cadenas) que se reutilizan en todo el cuerpo de la función mediante acceso por índice
// 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
}
Multiplexación de funciones Múltiples funciones lógicamente distintas se fusionan en una sola, utilizando un parámetro numérico como selector de bloque. El bloque activo se determina evaluando el parámetro con condiciones bit a bit. Los llamadores pasan un literal numérico como selector
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;
}
Ramificación con operadores lógicos Reemplaza las sentencias if y los bloques if/else con la evaluación de cortocircuito de operadores lógicos, convirtiendo el flujo de control en expresiones. Combinado con expresiones de secuencia, múltiples ramas aparecen como una única expresión continua separada por comas
// 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), ...)),
Constantes de métodos nativos vinculados Vincula métodos nativos del navegador a sus receptores originales, almacenándolos como constantes para evitar su manipulación
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)
Código muerto Se inyectan bloques de código inaccesibles o sin uso a lo largo del archivo, aumentando su tamaño a más de 60 000 líneas; esto dificulta el análisis estático y la ingeniería inversa basada en LLM
Aplanamiento del flujo de control Transforma cada parte del código (declaraciones y bucles) en una máquina de estados plana. Oculta la lógica de ejecución original enrutando todos los bloques de código a través de un bloque central «dispatcher»
Los dispatchers pueden cambiar de forma; algunos tienen 2 o 3 variables de estado y el tipo de bucle/condición cambia. Tienen este aspecto

Este es un CFF con 2 variables de estado: una gestiona el bloque catch y la otra el bloque try.
Grupo de cadenas cifradas Todos los literales de cadena (APIs DOM, propiedades del navegador, valores CSS, mensajes de error, etc.) se cifran en un único grupo de cadenas masivo. Una función de descifrado utiliza una semilla y un cifrado XOR basado en LCG para extraer cada cadena en tiempo de ejecución
Hay más de 1990 puntos de llamada repartidos por el código; la función de descifrado utiliza una clave continua que acumula los puntos de código descifrados, haciendo que cada carácter dependa de todos los anteriores
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"

Iterador de valores con estado reCAPTCHA utiliza una función con estado que devuelve una secuencia de objetos y valores de tiempo de ejecución (como window, document.body, constantes numéricas) en un orden fijo; cada llamada avanza un cursor interno, y llamarla fuera de secuencia o demasiadas veces corrompe todas las lecturas posteriores. Un mecanismo de tiempo de espera invalida el estado tras un intervalo fijo, devolviendo null para cualquier lectura tardía
// 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
Tabla de funciones calculadas
Esto es similar a Indirect Function Table, pero aquí el índice de la función a obtener se calcula en tiempo de ejecución con una semilla, usando XOR y módulo
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

Cifrado de valores en tiempo de ejecución
Algunos valores (parámetros de configuración de captcha, parámetros anchor, etc.) nunca se almacenan en texto plano; se cifran inmediatamente después de su recopilación y solo se descifran en el momento de su uso, y tienen un prefijo B al principio

Ofuscación asíncrona del flujo de control La lógica síncrona se convierte en máquinas de estado basadas en generadores envueltas en cadenas recursivas de Promises; rastrear cualquier valor con el depurador obliga a pasar por múltiples manejadores asíncronos, perdiendo el contexto de ejecución original en cada límite .then()
Componentes para inicializar reCAPTCHA``` ar: <WidgetInit?> k: co: hl: v: size: sa: anchor-ms: execute-ms: cb:
### Respuesta
La respuesta contiene:
- Diseño de la ventana del iframe de CAPTCHA
- Token de anclaje utilizado para la validación del payload (/reload)
- La configuración principal para la inicialización, ejecutada en el método `recaptcha.anchor.Main.init` para recibirla en recaptcha_en.js
### Estructura:
Nota: Recaptcha BotGuard fue eliminado el `04/01/2026`, puedes ver algunos ejemplos [aquí](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
]
Los valores se basan en los valores descifrados del primer sample fingerprint; consulta aquí
Cada subcampo de los valores del fingerprint tiene un formato base:
[value, key, elapsed]
Donde key es la clave de cifrado y elapsed es el tiempo que tardó el collector en obtener y cifrar el valor. Esto permite que reCAPTCHA detecte:
El sistema utiliza un scheduler interno:``` [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]
Esto representa:
- Orden de Ejecución
- Pipeline de Recolección
- Secuencia de Generación de Señales
### Códigos de Señales de Huellas Dactilares y Derivación de Claves
Transforma las señales de huellas dactilares en claves de cifrado deterministas mediante un pipeline de derivación de múltiples etapas
Cada valor de huella dactilar es:
- Normalizado
- Codificado en un código de señal compacto
- Convertido en una clave de cifrado numérica
- Cifrado usando la clave derivada
El proceso es determinista, lo que significa que el mismo valor de entrada siempre genera el mismo código de señal y la misma clave de cifrado.```
Raw Value
↓
Signal Code Derivation
↓
Compact Signal Code
↓
Numeric Key Derivation
↓
Encryption Key
↓
Value Encryption
Señal de entrada``` "BUTTON,195a81c9"
***Paso 1 - Derivar el Código de Señal***```javascript
deriveSignalCode("BUTTON,195a81c9")
// → "wg"
El código de señal generado es un identificador determinista compacto para el valor original.
Paso 2 - Derivar clave numérica```javascript deriveKey("wg") // → 3792
El código de señal se transforma en una clave de cifrado numérica.
***Paso 3 — Cifrar valor***```javascript
encryptValueWithKey(3792, "wgia1z9pwq")
// → "bYVbh6BUsE_5pLA"
El siguiente valor será cifrado con la clave derivada, siguiendo el orden del recopilador.
Todos los códigos de señal generados se agregan en una estructura serializada compacta
Proceso``` 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
**Final**```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)
Valor: 3ccb
Hasheado: true
Descripción: Genera una clave HMAC a partir de window.localStorage.getItem("rc::a ") + "6d" convertido a bytes, calcula HMAC-SHA256(siteKey) con esa clave y devuelve los primeros 4 caracteres hexadecimales del resultado.
El valor de rc::a es un base64 generado aleatoriamente.
Idx 5 (integer)
window.localStorage.length * 2falsewindow.localStorage multiplicada por 2Idx 16 (string)
Valor: yM5Us/j/fn6EDgtmPlP4Pxj605nMJN9dRYHyy5Mn
Hasheado: true
Descripción: Huella de filtro Bloom base64 de 40 caracteres de todos los nodos <HEAD>. Recorre cada elemento hijo recopilando nombres de etiquetas, atributos y contenido de texto, serializado y procesado mediante un hash djb2.
Cada digest se introduce en un BitHash(240 bits, 7 rounds, max 25 nodes) que establece bits en una cuadrícula de 40×6 y luego codifica cada segmento de 6 bits como un carácter base64. Las propias etiquetas de script de reCAPTCHA se excluyen mediante una expresión regular dinámica sobre 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 con hash SHA-256, EjemploIdx 30 (integer)
0falsehttps://www.gstatic.com/recaptcha/releases/<version>/, EjemploIdx 31 (string)
AAgkAQI0SBtruedocument.cookie con hash, EjemploIdx 32 (string)
""falsedocument.referrer.Idx 33 (integer)
Valor: 2
Hasheado: false
Descripción: Elemento de la caja de reCAPTCHA donde se realiza el renderizado.
// like grecaptcha.execute('.grecaptcha') -> this div
// Basically, it returns a number or the depth or level of that div in the document.
// Example: html > body > main > form > fieldset > div.recaptcha.form-field
// 5 4 3 2 1 0
// out = 5
nM = document.getElementsByClassName('grecaptcha-badge')[0] // or document.getElementsByClassName('g-recaptcha')[0] v2
let count = 0;
for (; nM = nM.parentElement || null;) {
count++;
}
const result = count;
Idx 34 (string)
AAAAAAAAAAtrue<INPUT> con hash, EjemploIdx 35 (string)
0,DIV,f0c7414etruedocument.activeElement. Analiza si es un elemento de compra mediante una expresión regular /buy|pay|place|order|donate|purchase/i con textContent, classNames e id, EjemploIdx 36 (string)
h3, h2 o http/1.1falsenextHopProtocol del rendimiento de navegación 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 o document.defaultView.pageYOffsetIdx 41 (string)
DIV,a08cd360true:hover sobre el que estaba posicionado el ratón en ese mismo momento; obtiene el último elemento y aplica hash a tagName, classNames e id, EjemploIdx 42 (string)
9,e3b0c442trueIdx 44 (string)
2v591z63wqtrue:hover, document.activeElement, todos los elementos <INPUT> con hash, location.url, window.scrollY, Ejemplo: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 donde ocurrió.Idx 47 (integer)
0falsewindow.getSelection().toString().length || 0Idx 49 (string)
h2-0falsenextHopProtocol y determina si su duration es cero. Devuelve el resultado en el formato protocol-isZero, EjemploIdx 50 (array)
Valor: [1,0,null,1,1,["805b1z63wq"],"MWc2MzZwMnR0ZHNkMw==",0,null,[[1]]]
Hasheado: true
Descripción: Contiene comportamientos del usuario, elementos :hover con hash y un timeout que se incrementa cada vez que se recopila otro elemento :hover. Analiza las activaciones del usuario para acumular el número de veces que el usuario ha interactuado con la página o sigue presente en ella calculando (10 * IsActive + IsBeenActive)
Formato base:
[
count,
timeout,
null,
timeoutMultiplier,
transitionCount,
[ elementsHoverHashed ],
sessionId,
userActivationScore,
null,
[ [ flags ] ]
]
Idx 51 (array)
[0]falsedocument.body.innerText coincidencias con las palabras "try again", "incorrect", "invalid" o "declined" y devuelve el número total de coincidencias encontradas. De lo contrario, devuelve 0, EjemploIdx 52 (integer)
11false10 * navigator.userActivation.isActive + navigator.userActivation.hasBeenActiveIdx 53 (integer)
Valor: 4
Hasheado: false
Descripción: Toma la URL actual del sitio web, la convierte en una matriz de caracteres, la trunca a 100 caracteres y luego la vuelve a convertir en una cadena, Ejemplo
Luego es 5 si la longitud de esa cadena truncada es par, o 4 si es impar.
Idx 54 (boolean)
falsefalsewindow.document.hiddenIdx 55 (array)
[[[1,"2v"],[1,"9z"],[1,"z8"],[1,"us"],[1,..falseIdx 56 (string)
-1,-1falsewindow.opener; devuelve "-1,-1" si es null, EjemploIdx 57 (string)
Valor: www.gstatic.com,_,static.kogstatic.com,www.google.com,www.googletagmanager.,...
Hasheado: false
Descripción: Recopila los hosts del src de todos los elementos <SCRIPT> presentes en document.scripts, Ejemplo
Para cada <SCRIPT>
Si el elemento tiene el atributo src (hasAttribute("src")), se toma el valor de src y se analiza con una expresión regular de tipo URI (nd), extrayendo solo el componente host (grupo de captura 3 de la expresión regular).
Si el <SCRIPT> no tiene un atributo src (es un script en línea), se utiliza el marcador de posición "_".
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, EjemploIdx 62 (string)
document.titlefalseIdx 63 (array)
Valor: []
Hasheado: false
Descripción: Extrae marcas de tiempo (números de 12-13 dígitos que comienzan con 1[2-9], típicos de epoch millis ~2008-2099) de los valores de las cookies, Ejemplo
Acumula los siguientes valores sobre todos los IDs encontrados: count, min, max y promedio.
El valor resultante final es una matriz [count, min, max, avg]
Idx 64 (integer)
55557falseIdx 65 (string)
sha384-3qctruehttps://www.google.com/recaptcha/api.js?render= (truncado a 10 caracteres)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)
Valor: [null,null,"","",null,"1l0fl"]
Hasheado: false
Descripción: Extrae cantidades monetarias totales (como Total: $50.00 o Total 150.00 EUR), Ejemplo
de document.innerText buscando el símbolo o código de moneda junto con la cantidad numérica mediante la expresión regular:
total[\S\s]{0,20}?(((R\$|\$|€|£|USD|EUR)\s*[\d\.,]+)|[\d\.,]+\s*(€|USD|EUR))`
total → Busca la palabra exacta "total"
[\S\s]{0,20}? → Busca entre (0) y (20) caracteres intermedios (como espacios, dos puntos o saltos de línea) antes de encontrar el número
(((R\$|\$|€|£|USD|EUR)\s*[\d\.,]+)|[\d\.,]+\s*(€|USD|EUR)) → Este es el bloque principal que captura el dinero y está dividido en dos opciones separadas
Idx 71 (array)
[jsHeapSizeLimit,usedJSHeapSize,totalJSHeapSize]falseIdx 72 (array)
Valor: [[["Google Chrome","147"],["Not.A/Brand",...
Hasheado: false
Descripción: Matriz de datos userAgent navigator.userAgentData, una marca que indica si es móvil y la plataforma, Ejemplo:
[
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)
""falseCiertas señales de reCAPTCHA pueden no estar disponibles o no recopilarse (debido a timeouts relacionados con la depuración). En tales casos, la señal faltante se rellena con un valor codificado en base64 generado aleatoriamente. Por ejemplo:``` ["CMXZyamk3ZHNjdGsyaQ==", 3249, 591]
Una vez completada la construcción de la huella, se serializa en una cadena y se cifra utilizando la clave de cifrado cargada desde el campo anchor (índice 18). El resultado cifrado se almacena entonces en el campo de índice `16` de la carga útil `/reload`.
## Señales de la VM
Los siguientes son los valores del navegador obtenidos por la VM interna de reCAPTCHA, junto con la clave de señal. Algunos valores no se muestran en la huella de muestra porque la VM solo extrae ciertos valores del bytecode de configuración. Por lo tanto, se mostrarán otros valores. Para más detalles, consulte [aquí](https://github.com/elyelysiox/recaptcha-vm)
Formato de Señal Base:```
[
null,
collectorElapsed,
encryptElapsed,
value
]
Donde collectorElapsed significa cuánto tiempo (ms) le tomó al collector obtener el valor
Y encryptElapsed significa cuánto tiempo (ms) le tomó cifrar el valor.
Estos tiempos se miden durante la ejecución de la 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`)
- Value: `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...`
- Description: User-Agent completo del navegador
- **Key 727** (`array`)
- Value: `[]`
- Description: Bloques de precios de productos, contiene precios de productos comerciales en el sitio web; estos pueden aparecer en sitios de mercado como Amazon, eBay, AliExpress, etc., [Muestra](https://github.com/elyelysiox/recaptcha/blob/main/findProductPricesBlock.js)
- **Key 150** (`array`)
- Value: `[\"Join And Get Free Trial!\",\"\",\"Captcha Solving Service\",\"Prod...`
- Description: Enlaces extraídos de los elementos `<a>` del sitio web; la VM raramente extrae estos valores
- **Key 545** (`boolean`)
- Value: `false`
- Description: `navigator.webdriver`
- **Key 370** (`integer`)
- Value: `0`
- Description: Máximo de puntos táctiles del dispositivo `navigator.maxTouchPoints`
- **Key 779** (`array`)
- Value: `[null,null,null,null,"false"]`
- Description: Extrae datos de los campos de envío/facturación, [Muestra](https://github.com/elyelysiox/recaptcha/blob/main/formFieldScrape.js)
`postal|postalcode|postalcode|postal_code|zip` → busca el campo de código postal
`ship|deliver` → sección de envío
`billing|payment|credit` → sección de facturación
`country` → selector de país
`addr.*(match|equal)|(match|equal).*address` → casilla de verificación "misma dirección"
Lee el nombre, id, autocomplete, className de cada elemento
Tres fases: entradas de texto, sentencias SELECT, casillas de verificación
- **Key 659** (`array`)
- Value: `[[[0,921],[1,2053]],[[235054,1,1,1],[164277,3,4,1]],2,39]`
- Description: Contiene datos de los elementos presionados en la página en el formato `[[[count,timestamp]],[[hashedElem,tagType,type,autoComplete]],pressedCount,behaviorChecksum]`
### Tipo de Elemento
| Tipo de Elemento | 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 |
### Algoritmo de Checksum de Comportamiento
El behaviorChecksum es una puntuación de comportamiento compacta derivada de los datos de interacciones del usuario recopilados.
Para cada interacción:
- El índice de interacción se incrementa en uno
- La marca de tiempo se combina con el índice de interacción ajustado
- El hash del elemento se reduce mediante una operación de módulo
- El resultado se acumula en una puntuación continua
---
```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;
}
```
### Ejemplo
```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`)
- Value: `[[[0,0,979]],1,0]`
- Description: Contiene los movimientos de desplazamiento recopilados durante la sesión de la página
### Formato Base
```
[ [ [ scrollX, scrollY, timestamp ] ], count, behaviorChecksum ]
```
- **Key 895** (`array`)
- Value: `[[[1,0]]]`
- Description:` VisibilityStateEntry` rastrea el estado de visibilidad de la página, indicando si el usuario está viendo actualmente la página o ha navegado fuera de ella, en el formato `[[[isVisible, startTime]]]`
- **Key 1092** (`array`)
- Value: `[886,2300,262.2099999997952,392.7600000009595,2050.2399999993577]`
- Description: Rendimiento de reCAPTCHA, PerformanceNavigationTiming y PerformanceEventTiming, en el formato `[vmStart,vmEnd,domContentLoadedEventEnd,loadEventEnd,firstUserInteractionTime]`
- **Key 41** (`array`)
- Value: `[[[1123,0,0]]]`
- Description: ClearTimeout
- **Key 43** (`array`)
- Value: `[[[1123,0]]]`
- Description: ClearTimeout
- **Key 549** (`array`)
- Value: `[0,null,null,null,null,null,null]`
- Description: KeyboardEvent, teclas calculadas + marcas de tiempo
- **Key 352** (`array`)
- Value: `1,[[2050,125,633,266,[609,266,105,44],3,1,50,1,1]],[3,3,318],...`
- Description:
La estructura contiene:
- Tiempos de movimiento del ratón
- Coordenadas del cursor
- Duraciones de clic
- Dimensiones del elemento presionado
- Información del tipo de puntero
- Métricas de presión
- Distancia de recorrido del cursor
- Estadísticas de tiempo de comportamiento
### Formato Base:
```
[
eventsCount,
mouseEvents,
[
coalescedEventsCount,
averageInterEventDelay,
timingVarianceScore
],
[
totalPoints,
clickSequenceScore,
totalCursorDistance,
totalPoints
],
0,
0,
behaviorChecksum
]
```
### Estructura del Evento de Ratón:
Cada entrada dentro de mouseEvents sigue esta estructura:
```
[
timestamp,
clickDuration,
clientX,
clientY,
[
elementLeft,
elementTop,
elementWidth,
elementHeight
],
elementTagType,
trackedElementIndex,
pressure,
pointerTravelDistance,
pointerArea
]
```
| Nombre | Descripción |
|---|---|
| `timestamp` | Marca de tiempo del evento relativa al inicio del evento |
| `clickDuration` | Duración entre `pointerdown` y `pointerup` |
| `clientX` | ... |
| `clientY` | ... |
| `elementBounds` | Rectángulo delimitador del elemento interactuado |
| `elementTagType` | Identificador de etiqueta DOM codificado del elemento interactuado |
| `trackedElementIndex` | Identificador interno del elemento DOM interactuado del mapa de interacción en tiempo de ejecución |
| `pressure` | Valor de presión del puntero normalizado multiplicado por `100` |
| `pointerTravelDistance` | Distancia recorrida entre `pointerdown` y `pointerup` |
| `pointerArea` | Área de contacto calculada como `width * height` |
### Tipo de Etiqueta de Elemento
Esto se inicializa durante la ejecución de la VM de reCAPTCHA
| Tipo | 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 |
### Ejemplo
```
[
18342,
91,
742,
381,
[700, 320, 120, 38],
3,
1,
50,
1.4,
16
]
```
Representa:
- evento en `18.342s`
- duración de clic de `91ms`
- clic en `(742, 381)`
- interacción con un `BUTTON`
- presión del puntero `0.5` (50)
- ligero movimiento del cursor durante el clic
- área de contacto del puntero de `16`
### Algoritmo de Checksum de Comportamiento
Para cada evento de ratón:
- La duración del clic se amplifica
- El resultado se combina con la marca de tiempo del evento
- Las coordenadas del cursor y las dimensiones del elemento se mezclan
- Se calcula una proporción ponderada
- La proporción se redondea y se acumula
Después de procesar todos los eventos:
- La puntuación acumulada se combina con la distancia total del cursor
- El resultado se normaliza usando el número de puntos recopilados
- Se aplica una transformación logarítmica
- Se incorporan estadísticas de movimiento adicionales
- El valor final se reduce mediante una operación de módulo
- El resultado se redondea a una puntuación entera pequeña
---
```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);
}
```
### Ejemplo
```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`)
- Value: `""`
- Description: Total de precios del sitio web recopilado en $, analizado con una expresión regular ``(?i)total[\S\s]{0,20}?(?:(?:(?:USD|\$)\s*)?[\d\.,]+\s*(?:USD|\$)|(?:USD|\$)\s*[\d\.,]+)``, [Muestra](https://github.com/elyelysiox/recaptcha/blob/main/websitePricesTotal.js)
- **Key 1278** (`integer`)
- Value: `229.6`
- Description: División entre 10 del tiempo de ejecución actual `Math.trunc(performance.now()) / 10`
- **Key 1422** (`boolean`)
- Value: `false`
- Description: Analiza si hay campos OTP/2FA en el sitio web, [Muestra](https://github.com/elyelysiox/recaptcha/blob/main/parseOTPFields.js)
- **Key 614** (`array`)
- Value: `[1,0,0,Infinity]`
- Description: Información de la batería del dispositivo, en el formato `[level,isCharging,chargingTime,dischargingTime]`
- **Key 2033** (`array`)
- Value: `["0",-1,-1,999]`
- Description: Algunos valores constantes de la VM y tiempo de ejecución de la VM
- **Key 1313** (`array`)
- Value: `["undefined",3]`
- Description: Comprobaciones de Android y Chrome, `[typeof window.android, Object.keys(window.chrome).length]`
- **Key 1994** (`array`)
- Value: `[2,4,53324367238]`
- Description: `[navigator.hardwareConcurrency, navigator.deviceMemory, navigator.storage.estimate().qouta]`
- **Key 1310** (`array`)
- Value: `["Google Inc. (Intel Inc.)","ANGLE (Intel Inc.,...`
- Description: Renderizador WebGL, proveedor y extensiones totales, [Muestra](https://github.com/elyelysiox/recaptcha/blob/main/webglSignals.js)
- **Key 291** (`array`)
- Value: `[1542343452,0,105002991,3556653,96784904,-731949068,-902263026,...`
- Description: Claves hash de prototipos específicos de objetos del navegador, como `SpeechSynthesisEvent`, `NetworkInformation`, `HTMLElement`, etc., [Muestra](https://github.com/elyelysiox/recaptcha/blob/main/hashBrowserProtos.js)
## Carga útil de la solicitud de recarga
Estos datos se serializan mediante protobuf (Protocol Buffers), un método eficiente y neutral para serializar datos estructurados, desarrollado por 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,
]
| Índice | Valor | Descripción |
|---|---|---|
0 | rreq | Identificador del tipo de solicitud |
1 | U5VsmTDhJM1iO... | Versión de reCAPTCHA |
2 | 03AFcWeA5Els3... | Token de validación obtenido del endpoint /anchor |
5 | -383788762 | Hash de la carga útil completa del fingerprint serializado, Muestra |
6 | q | Indica el modo de desafío o solicitud que se está ejecutando |
7 | 05APQrAobTCFG... | Patrones de uso, que contienen historial de uso y patrones de desafíos resueltos anteriormente. Pueden aumentar significativamente la puntuación si son válidos |
8 | submit | Acción específica del sitio que el usuario está realizando en la página en el momento de la verificación (p. ej. signin, register) |
14 | 6LcAbwIqAAAA... | Clave del sitio web |
16 | 0XA4cHhALxgM... | Cifrado de la carga útil completa del fingerprint serializado con la clave de cifrado de anchor (índice 18) 1777669303203 |
20 | tbMywxNTQsOD... | Estructura de telemetría codificada en Base64 que contiene deltas de temporización de VM, tiempos de recursos, estadísticas de objetos del navegador recopiladas y URLs de scripts del sitio web |
21 | 0aAPQrAobqME... | Token asociado a un CAPTCHA resuelto anteriormente. Se utiliza para la continuidad de sesión y la correlación de confianza |
22 | BDAAYAIAGEcA... | Hash de todas las claves de los objetos del navegador |
| Valor | Descripción |
|---|---|
q | Desafío de reCAPTCHA V3 |
fi | Desafío de reCAPTCHA V2/Invisible |
a | Desafío de audio |
qr | Respuesta rápida (nuevo) |
Idx 20)Para decodificar:```javascript atob("W1tbMywwLDgyOV0sWzEs...") // → [[[3,0,829],[1,42,863]],[[2,727,1341.300000011921]...
El payload contiene:
- Información de temporización de recursos
- Temporización de rendimiento de tareas largas
- Deltas de ejecución de VM de reCAPTCHA
- URLs de scripts del sitio web
- Estadísticas de recopilación de objetos del navegador
### Estructura base```js
[
[recaptchaResources],
[tasksTiming],
[
null,
null,
null,
idleTimeoutDelta,
deltaTiming,
0,
0,
0,
],
websiteScriptsUrls,
[collectionElapsed, browserObjectsLength]
]
[ PerformanceLongTaskTiming.name == "self" ? 2 : 4, PerformanceLongTaskTiming.duration, PerformanceLongTaskTiming.startTime ]
### Formato de temporización de recursos de reCAPTCHA```js
[
resourceType,
duration,
startTime
]
| Recurso | Tipo |
|---|---|
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)Este campo contiene contadores codificados en Base64 para eventos de interacción del navegador recopilados durante la ejecución
Ejemplo decodificado:```js [ [ [5006, 43], [64607, 1], [45464, 1], [35837, 1] ] ]
### Estructura del Contador de Eventos```js
[
[
[eventType, count]
]
]
| Hash | Tipo de evento |
|---|---|
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 ]
## Descripciones de campos
| Índice | Nombre | Descripción |
|---|---|---|
| `0` | `rresp` | Identificador del tipo de respuesta devuelto por el endpoint de recarga |
| `1` | `responseToken` | Token de respuesta de reCAPTCHA |
| `3` | `tokenExpiration` | Vida útil del token en segundos antes de la expiración |
| `4` | `pmeta` | Bloque de metadatos del desafío que contiene información sobre el tipo de CAPTCHA, la categoría y la disposición de la cuadrícula |
| `5` | `challengeType` | Tipo de desafío CAPTCHA |
| `8` | `usagePatternToken` | Patrones de uso, almacenados en `window.sessionStorage` y reutilizados en futuras solicitudes `/reload` |
| `9` | `challengeImageToken` | Token utilizado para recuperar la carga útil de la imagen CAPTCHA del endpoint `/payload` |
| `12` | `humanVerificationToken` | Token de identificación humana asociado con el estado de Google reCAPTCHA y las cookies del navegador |
| `14` | `captchaSessionToken` | Token de sesión interno |
| `15` | `bftToken` | ... |
---
### Estructura de `pmeta`
Ejemplo:```js
[
"pmeta",
[
"/m/013xlm",
null,
3,
3,
3,
null,
"Tractor"
],
null,
[1]
]
pmeta[1]| Posición | Valor | Descripción |
|---|---|---|
0 | /m/013xlm | Identificador interno de categoría para la clase del objeto objetivo |
2 | 3 | Número de columnas de la cuadrícula |
3 | 3 | Número de filas de la cuadrícula |
5 | null | Campo de metadatos reservado u opcional |
6 | "Tractor" | Etiqueta de texto |
| Tipo | Descripción |
|---|---|
imageselect | CAPTCHA estándar de selección de imágenes |
tileselect | Desafío de imagen dinámica basado en mosaicos |
dynamic | Desafío dinámico donde los mosaicos se recargan tras la selección |
multicaptcha | Desafío CAPTCHA de varios pasos |
audio | Desafío de accesibilidad basado en audio |
nocaptcha | Flujo de verificación solo con casilla de verificación |
doscaptcha | Denegar el CAPTCHA si hay abuso |
El valor en Idx 9 (challengeImageToken) se utiliza para recuperar la imagen del CAPTCHA:```txt
https://www.google.com/recaptcha//payload
?p=
&k=
### Identificadores de categoría
| ID | Categoría |
|---|---|
| `/m/0pg52` | Taxi |
| `/m/01bjv` | Autobús |
| `/m/02yvhj` | Autobús escolar |
| `/m/04_sv` | Motocicleta |
| `/m/013xlm` | Tractor |
| `/m/01jk_4` | Chimenea |
| `/m/014xcs` | Paso de peatones |
| `/m/015qff` | Semáforo |
| `/m/0199g` | Bicicleta |
| `/m/015qbp` | Parquímetro |
| `/m/0k4j` | Coche |
| `/m/015kr` | Puente |
| `/m/019jd` | Barco |
| `/m/0cdl1` | Palmera |
| `/m/09d_r` | Montaña / colina |
| `/m/01pns0` | Hidrante |
| `/m/01lynh` | Escaleras |
## Payload de solicitud UserVerify
El payload `/userverify` se envía después de que el usuario completa un desafío de imagen de reCAPTCHA v2
# Estructura del Payload```
v: hsFBb1u5wWWWkWP4in1ua2cQ
c: 03AFcWeA5RIHz5mI2COQqTcLjdrC7FiPpdfkX-hB-B2iFenI...
response: eyJyZXNwb25zZSI6WzMsMSw0LDksMCwxMSwxMCwxMywxNF0....
t: 24521
ct: 24521
ch: -4007845
oc: 0ZpqzzPgYKEEaN0xpfo3v_voeVUySZK3Ktf_fLVRdhnu3yeTy4BhO...
pm: 51bGwsbnVsbCxbbnVsbCxudWxsLG51bGwsWzEwLDQuMjcwMDAwMD....
| Campo | Nombre | Descripción |
|---|---|---|
v | recaptchaVersion | Versión de reCAPTCHA |
c | challengeToken | Token de estado del desafío devuelto por /replaceimage o una solicitud /userverify anterior |
response | Carga útil codificada que contiene los índices de las celdas seleccionadas y metadatos adicionales de interacción | |
t | challengeElapsed | Tiempo transcurrido desde que el desafío se cargó inicialmente |
ct | completionTime | Tiempo total que tardó el usuario en resolver y enviar el CAPTCHA |
ch | ocHash | Hasheado de toda la carga útil oc serializada, Muestra |
oc | encryptedVmSignals | Señales cifradas recopiladas de la VM de reCAPTCHA; ver aquí. |
pm | telemetryPayload | Estructura de telemetría codificada en Base64 que contiene deltas de tiempo de la VM, temporizaciones de recursos, estadísticas de objetos del navegador recopiladas y metadatos de scripts del sitio web |
responseRepresenta los índices/posiciones de los cuadrados seleccionados por el usuario en la cuadrícula
La carga útil se codifica posteriormente como JWT antes del envío
```txt
0 1 2
3 4 5
6 7 8
Ejemplo de payload:```json
{
"response": [2, 3, 6]
}
```txt
0 1 2 3
4 5 6 7
8 9 10 11
12 13 14 15
Ejemplo de payload:```json
{
"response": [2, 3, 6, 14]
}
eEl payload response también puede incluir un campo e:```json
{
"response": [2, 3, 6, 14],
"e": "bxCoiXVt49I7CTS_tNlVbGg"
}
Este campo contiene metadatos de interacción cifrados relacionados con las acciones de los botones de CAPTCHA, tales como:
- `Verify`
- `Next`
- `Skip`
### Estructura interna de `e`
Después del descifrado, la estructura se asemeja a:```txt
[[["9ffd9672"]]]
Cada valor representa un identificador con hash para la clase/acción de botón rc-button-default
El hash es:
Las secuencias de interacción más largas pueden verse así:```txt [ [ ["9ffd9672"], ["9ffd9672"], ["9ffd9672"], ["9ffd9672"] ] ]
### `oc` — Señales de VM cifradas
Usa el mismo algoritmo de cifrado que el campo `idx 16` del payload de la solicitud `/reload`, con la clave de cifrado cargada en el ancla (índice 31) `1777752103288`
### `pm` — Payload de telemetría
El campo `pm` contiene una estructura de telemetría codificada en Base64 similar al payload de telemetría utilizado en `/reload`.
## Anti-depuración/manipulación
- ***Etiqueta de integridad basada en Symbol***
reCAPTCHA establece una clave Symbol `Symbol(jas)` en cada array y objeto con un valor numérico utilizado como comprobación de integridad. Dado que los Symbols no son enumerables e invisibles para las operaciones de clonación estándar (como JSON.stringify), cualquier intento de reemplazar o clonar estas estructuras pierde la etiqueta, lo que reCAPTCHA detecta como manipulación

- ***Captura de variables de cierre***
Algunos valores se delimitan intencionalmente en una función externa y se consumen solo dentro de callbacks o cierres anidados; la variable nunca existe en el mismo ámbito donde se utiliza
- ***Comprobaciones de tiempo***
reCAPTCHA incorpora mecanismos de detección de depuración en tiempo de ejecución. Si detecta que el código está siendo analizado por un depurador, reinicia la sesión de depuración, lo que provoca que se pierdan el estado y el contexto de ejecución
- ***Bloquear la pestaña de origen***
https://github.com/user-attachments/assets/4ae3bf68-5ae7-4adc-a0b9-36cc11b64998
Este error ocurre en navegadores que usan Chromium. Hay varios errores que provocan que el navegador o las herramientas de desarrollo se cierren inesperadamente, como el que se muestra aquí:
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
Por lo tanto, es muy probable que uno de estos errores se active cuando se produce el tiempo de espera, ya que probablemente no sea algo que ocurriría en el flujo de un usuario real
## reCAPTCHA Botguard
Aunque Google eliminó BotGuard, explicaré un poco cómo funciona internamente para aquellos interesados en cómo es y en lo diferente que resulta de otros tipos de botguard
Primero, el proceso de generación de tokens: la VM genera tokens para errores persistentes durante la ejecución. Estos errores pueden deberse a desviaciones en el offset del bytecode, registros de VM faltantes, estado incorrecto de la VM y una mala integridad del flujo de control. Estos errores son capturados por el dispatcher, que llama a una función que escribe los bytes del token.
La VM utiliza ARX Cipher, un algoritmo creado por la NSA en 2013, similar a Speck para cifrar datos. También se utiliza en todo Google BotGuard. El algoritmo cambia con cada versión, al igual que la constante (3990), el orden de las operaciones y las rondas. Tiene este aspecto:```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];
}
La función principal de cifrado tiene dos modos: cifrado y sin cifrado. El modo de cifrado utiliza ARX Cipher, Bitwise + XOR para cifrar los datos y colocarlos en un buffer. Hay 4 tipos de buffers:
180353304243 (no se usa en este tipo de botguard).El modo sin cifrado simplemente coloca bytes ya cifrados en el buffer```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]); } } }
Y esta es la función que maneja los errores y llama al escritor de bytes.```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;
}
}
}
Al final de la ejecución, la VM llama a la función principal que prepara el payload con estos buffers y genera el token, que es una firma. A diferencia de otras VM de BotGuard, esta VM no tiene un sistema de fingerprinting, ni Google Botguard, ni realiza la emisión de errores + escritura de bytes, y es mucho más simple y fácil de aplicar ingeniería inversa.
Este repositorio es exclusivamente para fines educativos y de investigación. El código, las técnicas y los conceptos presentados aquí están destinados al estudio académico y la comprensión de los temas tratados.
25 | W1tbNTAwNiw... | Contadores codificados en Base64 para eventos de interacción del usuario como movimiento del ratón, clics, teclado y cambios de enfoque |