Marco de Hook en Python para Inyección de Procesos y Registro de Logs de WeChat en Windows

Implementación de un Framework de Hook para Aplicaciones de 32 y 64 bits

Este artículo describe la integración de un framework de hook en Python, diseñado para inyectar código en procesos de Windows y monitorear logs de WeChat. El enfoque principal es proporcionar una solución que soporte tanto arquitecturas de 32 como de 64 bits, permitiendo interceptar funciones y acceder a estructuras de memoria de manera controlada.

Hook para Programas de 32 bits

El hook para aplicaciones de 32 bits requiere dos parámetros: la dirección de memoria a interceptar y una función de callback. Esta función de callback recibe un puntero a una estructura que contiene el estado de los registros del procesador x86, sin valor de retorno. La estructura se define como sigue:

class EstadoRegistros(Structure):
    _fields_ = [
        ('BanderasEFLAGS', DWORD),
        ('RegistroEDI', DWORD),
        ('RegistroESI', DWORD),
        ('RegistroEBP', DWORD),
        ('RegistroESP', DWORD),
        ('RegistroEBX', DWORD),
        ('RegistroEDX', DWORD),
        ('RegistroECX', DWORD),
        ('RegistroEAX', DWORD),
    ]

Un ejemplo básico de hook muestra cómo acceder a los valores de los registros durante la ejecución:

def mi_funcion_hook(puntero_contexto):
    # Obtener el contenido del puntero como una instancia de EstadoRegistros
    contexto: EstadoRegistros = puntero_contexto.contents
    # Leer el valor del registro EAX
    valor_eax = contexto.RegistroEAX
    print("Valor actual del registro EAX:", valor_eax)

direccion_objetivo = 0x100000
instancia_hook = Hook()
instancia_hook.hook(direccion_objetivo, mi_funcion_hook)

El contexto proporciona acceso a los registros en el momento de la intercepción, similar a lo que se observaría en depuradores como x32dbg. Los valores son de tipo DWORD; para datos más complejos como cadenas o estructuras, se requiere conversión adicional en Python, como se demuestra en el ejemplo de registro de logs.

Además, es posible modificar los valores de los registros dentro de la función de callback, lo que se reflejará en el flujo de ejecución real. Esto se ilustrará en artículos posteriores sobre prevención de revocación de mensajes.

Hook para Programas de 64 bits

Para aplicaciones de 64 bits, el hook utiliza la biblioteca Detours, lo que requiere definir un puntero a función adicional y solo puede interceptar funciones específicas. Antes de aplicar el hook, es necesario conocer el número y tipo de parámetros de la función objetivo; en caso de desconocerlos, pueden definirse como c_uint64. La función de callback debe coincidir en firma con la función original; si hay muchos parámetros, se puede usar *args para simplificar. Ejemplo:

def callback_hook_registro(*args, **kwargs):
    print(args)
    print(kwargs)
        
instancia_hook = Hook()
direccion_log = 0x100000
puntero_direccion = c_uint64(direccion_log)
tipo_funcion_log = CFUNCTYPE(c_uint64, c_uint64, c_uint64, c_uint64, c_uint64, c_uint64, c_uint64, c_uint64, c_uint64, c_uint64, c_uint64, c_uint64, c_uint64)
instancia_hook.hook(puntero_direccion, tipo_funcion_log, callback_hook_registro)

El valor de retorno de la función de callback debe coincidir con el de la función original; generalmente, se invoca primero a la función interceptada para obtener el resultado antes de retornarlo. Un tipo de retorno incorrecto puede causar un fallo en el proceso.

Caso Práctico: Interceptación de Logs de WeChat

Se elige el registro de logs como ejemplo debido a su naturaleza multihilo; si el hook funciona correctamente en este escenario, es indictaivo de robustez para otras intercepciones.

Implementación para 32 bits

El código siguiente demuestra cómo interceptar la función de logs en WeChatWin.dll. Se necesitan dos hooks: uno al inicio para capturar parámetros y otro al final para el valor de retorno, ya que se requiere acceso tanto a argumentos como al resultado.

from modulo_hook import Hook
from modulo_hook.api_windows import *

base_modulo = GetModuleHandleW("WeChatWin.dll")

def callback_inicio_log(puntero_contexto):
    contexto = puntero_contexto.contents
    puntero_esp = contexto.RegistroESP
    # Calcular el desplazamiento de la dirección de retorno
    direccion_retorno = c_ulong.from_address(puntero_esp).value - base_modulo
    # Obtener la ruta del archivo de código desde EDX
    puntero_edx = contexto.RegistroEDX
    # Definir un buffer para la cadena de caracteres
    buffer_ruta = (c_char * MAX_PATH).from_address(puntero_edx)
    ruta_codigo = buffer_ruta.value.decode()
    print(f"Dirección de llamada: WeChatWin.dll+{hex(direccion_retorno)}, Ruta de archivo: {ruta_codigo}, ", end=" ")

def callback_fin_log(puntero_contexto):
    contexto = puntero_contexto.contents
    valor_eax = contexto.RegistroEAX
    # Leer la información de log desde la dirección en EAX
    buffer_log = (c_char * 1000).from_address(valor_eax)
    mensaje_log = buffer_log.value.decode()
    print("Información de log:", mensaje_log)

instancia_hook = Hook()
direccion_inicio = base_modulo + 0x102C250
instancia_hook.hook(direccion_inicio, callback_inicio_log)

direccion_fin = base_modulo + 0x102C584
instancia_hook.hook(direccion_fin, callback_fin_log)

Para habilitar la recarga en caliente, se recomienda llamar a unhook antes de aplicar el hook, asegurando que las modificaciones en el código surtan efecto.

Implementación para 64 bits

En sistemas de 64 bits, la interceptación se realiza mediante Detours, lo que requiere definir un puntero a función explícito. La función de logs tiene múltiples parámetros; en este ejemplo, se usa *args para manejarlos.

from modulo_hook import Hook
from modulo_hook.api_windows import *

def callback_hook_log(*args):
    # Leer la ruta del archivo de código desde el segundo parámetro (RDX)
    buffer_ruta = (c_char * MAX_PATH).from_address(args[1])
    ruta_codigo = buffer_ruta.value.decode()
    # Invocar la función original para obtener el valor de retorno
    resultado = tipo_funcion_log(puntero_direccion.value)(*args)
    # Extraer el mensaje de log desde el resultado
    buffer_log = (c_char * 1000).from_address(resultado)
    mensaje_log = buffer_log.value.decode()
    print(f"Ruta de archivo: {ruta_codigo}, Mensaje de log: {mensaje_log}")
    return resultado

# Configuración del hook
direccion_base = GetModuleHandleW("WeChatWin.dll")
direccion_log = direccion_base + 0x13D6380
puntero_direccion = c_uint64(direccion_log)
tipo_funcion_log = CFUNCTYPE(c_uint64, c_uint64, c_uint64, c_uint64, c_uint64, c_uint64, c_uint64, c_uint64, c_uint64, c_uint64, c_uint64, c_uint64, c_uint64)

instancia_hook = Hook()
instancia_hook.hook(puntero_direccion, tipo_funcion_log, callback_hook_log)

Es crucial mantener la referencai a puntero_direccion para evitar que el recolector de basura lo elimine durante la ejecución.

Notas de Implementación

Este framwork de hook está disponible como una biblioteca reutilizable, diseñada para simplificar la inyección de procesos y la interceptación de funciones en entornos Windows. Los desarrolladores pueden extenderlo para aplicaciones específicas, como el monitoreo de aplicaciones de mensajería, adaptando los ejemplos proporcionados según sus necesidades.

Etiquetas: Python ctypes Windows API WeChat hooking

Publicado el 6-13 17:06