Hooking y depuración segura del parámetro X-S de XiaoHongShu en análisis inverso

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.

  1. 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
  1. 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;
  }
}
  1. 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.

Etiquetas: JavaScript ingenieria-inversa hooking depuración seguridad-web

Publicado el 6-5 16:12