En el análisis de seguridad de aplicaciones web y móviles, la ingeniería inversa de parámetros críticos como el X-S de XiaoHongShu requiere técnicas dinámicas precisas. Este enfoque se centra en métodos de hooking que eviten detecciones anti-depuración, permitiendo observar la generación de parámetros en tiempo real sin compromisos legales o éticos.
- Configuración del entorno de depuración
Establecer un entorno discreto es esencial. Las aplicaciones modernas emplean múltiples estrategias anti-depuración, desde comprobaciones de temporizadores hasta monitoreo de rendimiento.
1.1 Preparación del navegador
Utilizando un navegador basado en Chromium, se pueden aplicar modificaciones para reducir la visibilidad del depurador:
// Sobreescribir detección de herramientas de desarrollo
Object.defineProperty(window, 'chrome', {
get() {
return undefined;
},
configurable: false,
enumerable: false
});
// Silenciar detección de debugger
const depuradorOriginal = window.debugger;
window.debugger = function() {
console.log('[Depuración segura] Llamada a debugger interceptada');
return undefined;
};
Nota: Estos ajustes deben adaptarse a mecanismos específicos de detección para evitar efectos secundarios.
1.2 Estrategias anti-anti-depuración
Las técnicas comunes incluyen detección por temporizadores, análisis de rendimiento y verificación de propiedades. Para mitigarlas:
// Interceptar temporizadores sospechosos
const setIntervalOriginal = window.setInterval;
window.setInterval = function(callback, retraso, ...args) {
const callbackStr = callback.toString();
if (callbackStr.includes('debugger') || callbackStr.includes('performance.now')) {
console.log('[Interceptado] Temporizador de detección bloqueado');
return 0;
}
return setIntervalOriginal.call(this, callback, retraso, ...args);
};
1.3 Herramientas recomendadas
| Categoría | Herramienta | Propósito |
|---|---|---|
| Depuración | Chrome DevTools | Inspección de código y red |
| Proxy | mitmproxy | Análisis de tráfico HTTP |
| Análisis de código | AST Explorer | Examinar estructuras de JavaScript |
| Automatización | Playwright | Simulación de interacciones |
- Localización de parámetros y seguimiento de pila
Identificar el parámetro X-S en solicitudes de red es el primer paso. Se requiere monitoreo de XMLHttpRequest y Fetch API.
2.1 Monitoreo de solicitudes
// Capturar solicitudes XMLHttpRequest
const xhrOpenOriginal = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(metodo, url, async, usuario, contrasena) {
console.log(`[XHR] Método: ${metodo}, URL: ${url}`);
this.addEventListener('readystatechange', function() {
if (this.readyState === 4) {
console.log(`[XHR] Respuesta de: ${url}, Estado: ${this.status}`);
}
});
return xhrOpenOriginal.apply(this, arguments);
};
// Monitorear Fetch API
const fetchOriginal = window.fetch;
window.fetch = function(entrada, init) {
const inicio = performance.now();
return fetchOriginal.call(this, entrada, init).then(respuesta => {
const fin = performance.now();
console.log(`[Fetch] Solicitud completada en ${(fin - inicio).toFixed(2)}ms`);
return respuesta;
});
};
2.2 Establecimiento de puntos de ruptura
Para interceptar solicitudes específicas como las que contienen '/api/sns/web/v1/homefeed', se pueden usar proxies:
// Proxy para control granular sobre XMLHttpRequest
const proxyXHR = new Proxy(XMLHttpRequest, {
construir(destino, args) {
const instancia = new destino(...args);
return new Proxy(instancia, {
obtener(obj, prop) {
if (prop === 'send') {
return function(cuerpo) {
if (obj._url && obj._url.includes('homefeed')) {
console.log('[Proxy] Solicitud objetivo detectada:', cuerpo);
}
return obj[prop].call(this, cuerpo);
};
}
return obj[prop];
}
});
}
});
2.3 Análisis de pila asíncrono
// Rastreador de pila personalizado
class RastreadorPila {
constructor() {
this.maxProfundidad = 15;
this.patronesIgnorados = [/node_modules/, /\(native\)/];
}
capturar(profundidad = 5) {
const pila = new Error().stack;
if (!pila) return [];
const marcos = pila.split('\n').slice(2);
const filtrados = marcos.filter(marco =>
!this.patronesIgnorados.some(patron => patron.test(marco))
);
return filtrados.slice(0, profundidad);
}
registrar(etiqueta = 'Origen de llamada') {
const pila = this.capturar(this.maxProfundidad);
console.group(`🔍 ${etiqueta}`);
pila.forEach((marco, idx) => {
console.log(`${idx + 1}. ${marco.trim()}`);
});
console.groupEnd();
return pila;
}
}
- Técnicas seguras de hooking y captura de estado
El hooking debe ser discreto para evitar activar contramedidas. Se priorizan métodos como la redefinición de propiedades.
3.1 Hooking mediante Object.defineProperty
// Función genérica para hook seguro de propiedades
function hookPropiedadSeguro(objetivo, propiedad, opciones = {}) {
if (!objetivo || typeof objetivo !== 'object') {
console.warn('[Hook] Objetivo inválido');
return false;
}
const descriptor = Object.getOwnPropertyDescriptor(objetivo, propiedad);
if (!descriptor || !descriptor.configurable) {
console.warn(`[Hook] Propiedad ${propiedad} no configurable`);
return false;
}
const {
alObtener = null,
alEstablecer = null,
valorOriginal = objetivo[propiedad],
depurar = false
} = opciones;
let valorActual = valorOriginal;
try {
Object.defineProperty(objetivo, propiedad, {
configurable: true,
enumerable: descriptor.enumerable,
get() {
if (alObtener) {
const resultado = alObtener(valorActual, this);
if (resultado !== undefined) return resultado;
}
if (depurar) {
setTimeout(() => {
console.log(`[Acceso] ${propiedad} =`, valorActual);
}, 0);
}
return valorActual;
},
set(nuevoValor) {
const valorAnterior = valorActual;
if (alEstablecer) {
const valorProcesado = alEstablecer(nuevoValor, valorAnterior, this);
if (valorProcesado !== undefined) {
valorActual = valorProcesado;
return true;
}
}
console.log(`[Cambio] ${propiedad}: ${valorAnterior} -> ${nuevoValor}`);
valorActual = nuevoValor;
return true;
}
});
console.log(`[Hook] Propiedad ${propiedad} interceptada`);
return true;
} catch (error) {
console.error(`[Hook] Error en ${propiedad}:`, error);
return false;
}
}
// Ejemplo: Hook de encabezados de solicitud
function hookEncabezadosSolicitud(instanciaSolicitud) {
if (!instanciaSolicitud.headers) return;
hookPropiedadSeguro(instanciaSolicitud, 'headers', {
alObtener(valor) {
console.log('[Encabezados] Acceso:', valor);
return valor;
},
alEstablecer(nuevoValor) {
if (nuevoValor['X-S'] || nuevoValor['x-s']) {
console.log('[X-S] Parámetro establecido:', nuevoValor['X-S'] || nuevoValor['x-s']);
setTimeout(() => {
const pila = new RastreadorPila().capturar(10);
console.log('[X-S] Origen de la pila:', pila);
}, 0);
}
return nuevoValor;
}
});
}
3.2 Hooking de funciones
// Utilidad para hook de funciones con monitoreo
function hookFuncion(objetivo, nombreFuncion, envoltura) {
if (!objetivo || typeof objetivo[nombreFuncion] !== 'function') {
console.warn(`[Hook] Función ${nombreFuncion} no encontrada`);
return null;
}
const original = objetivo[nombreFuncion];
let conteoLlamadas = 0;
const funcionHooked = function(...args) {
conteoLlamadas++;
const idLlamada = `${nombreFuncion}_${conteoLlamadas}_${Date.now()}`;
console.group(`[Llamada] ${idLlamada}`);
console.log('Argumentos:', args);
try {
const resultado = original.apply(this, args);
console.log('Resultado:', resultado);
console.groupEnd();
if (envoltura && envoltura.despues) {
envoltura.despues.call(this, resultado, args, idLlamada);
}
return resultado;
} catch (error) {
console.error(`[Error] ${nombreFuncion}:`, error);
console.groupEnd();
throw error;
}
};
Object.keys(original).forEach(clave => {
funcionHooked[clave] = original[clave];
});
objetivo[nombreFuncion] = funcionHooked;
console.log(`[Hook] Función ${nombreFuncion} modificada`);
return funcionHooked;
}
Al aplicar estas técnicas, es crucial respetar los términos de servicio y las leyes aplicables, limitando el análisis a fines legítimos de investigación en seguridad.