Implementación de SEH y Técnicas Anti-Depuración en Ensamblador Win32

Manejo Estructurado de Excepciones (SEH)

El Manejo Estructurado de Excepciones (SEH) es un mecanismo de Windows que permite a las aplicaciones gestionar errores de ejecución de manera localizada. A diferencia de los filtros de excepciones globales, el SEH permite que cada función defina su propia lógica de recuperación mediante funciones de devolución de llamada (callbacks). Cuando ocurre una excepción, el sistema operativo recorre una lista vinculada de manejadores para determinar quién procesará el error.

En la arquitectura x86, el inicio de esta cadena de excepciones se encuentra en el Bloque de Información del Hilo (TIB), específicamente en el desplazamiento 0 del segmento fs (fs:[0]). Para registrar un nuevo manejador, debemos crear una estructura que contenga el puntero al siguiente registro y la dirección de nuestra función de manejo.

; Estructura básica de un registro SEH
REGISTRO_EXCEPCION struct
    Siguiente dd ?    ; Puntero al siguiente nodo en la cadena SEH
    Manejador dd ?    ; Dirección de la función callback
REGISTRO_EXCEPCION ends

Registro Manual mediante la Pila

Una forma eficiente de registrar un manejador SEH sin declarar estructuras complejas es utilizar la pila del sistema. Puesto que la pila crece hacia direcciones de memoria menores, podemos simplemente empujar la dirección de nuestro manejador y el valor actual de fs:[0], y luego actualizar fs:[0] para que apunte al tope de la pila.

.586
.model flat,stdcall
option casemap:none

include windows.inc
include user32.inc
include kernel32.inc
includelib user32.lib
includelib kernel32.lib

.data
    msg_error db "Excepción capturada en Nivel 1", 0
    msg_final db "Proceso finalizado correctamente", 0

.code
assume fs:nothing

; Callback del manejador de excepciones
ManejadorInterno proc pRecord:ptr EXCEPTION_RECORD, pFrame:dword, pContext:ptr CONTEXT, pDispatcher:dword
    invoke MessageBox, NULL, addr msg_error, NULL, MB_ICONWARNING
    
    ; Intentamos corregir el flujo saltando la instrucción problemática
    mov eax, pContext
    assume eax:ptr CONTEXT
    add [eax].regEip, 2 ; Asumiendo una instrucción de 2 bytes (ej. div esi)
    assume eax:nothing
    
    mov eax, ExceptionContinueExecution
    ret
ManejadorInterno endp

EjecutarTarea proc
    ; Registro del SEH en la pila
    push offset ManejadorInterno    ; Guardar dirección del manejador
    push fs:[0]                     ; Guardar puntero anterior
    mov fs:[0], esp                 ; Instalar el nuevo registro
    
    ; Provocar una excepción de división por cero de forma intencional
    xor ecx, ecx
    div ecx
    
    ; Desinstalación del SEH
    pop fs:[0]                      ; Restaurar el puntero anterior
    add esp, 4                      ; Limpiar la pila
    ret
EjecutarTarea endp

start:
    call EjecutarTarea
    invoke MessageBox, NULL, addr msg_final, NULL, MB_OK
    invoke ExitProcess, 0
end start

Técnicas Anti-Depuración con el Trap Flag

La depuración de software se basa a menudo en la inserción de puntos de interrupción (breakpoints). El método más común es reemplazar el primer byte de una instrucción con el código de operación 0xCC (INT 3). Una técnica eficaz para detectar la presencia de un depurador consiste en utilizar el bit de trampa (Trap Flag - TF) del registro EFLAGS.

Cuando el bit TF (bit 8) está activo, el procesador ejecuta una sola instrucción y genera una excepción de tipo SINGLE_STEP. Si un manejador SEH captura esta excepción, puede inspeccionar el código de la siguiente instrucción para buscar el byte 0xCC. Si se encuentra, significa que el software está siendo analizado activamente.

.586
.model flat,stdcall
option casemap:none

include windows.inc
include user32.inc
include kernel32.inc
includelib user32.lib
includelib kernel32.lib

.data
    alerta_dbg db "Depurador detectado: Interrupción ilegal encontrada.", 0
    titulo_warn db "Seguridad del Sistema", 0
    limite_codigo dd 0

.code
assume fs:nothing

; Manejador que verifica la integridad de las instrucciones
MonitorAntiDbg proc pRec:ptr EXCEPTION_RECORD, pFrame:dword, pCtx:ptr CONTEXT, pDisp:dword
    mov edx, pCtx
    assume edx:ptr CONTEXT
    
    ; Obtener la dirección donde ocurrió la excepción
    mov eax, [edx].regEip
    
    ; Verificar si el byte en EIP es un INT 3 (0xCC)
    movzx ecx, byte ptr [eax]
    .if ecx == 0CCh
        invoke MessageBox, NULL, addr alerta_dbg, addr titulo_warn, MB_ICONSTOP
        invoke ExitProcess, 0
    .endif
    
    ; Si no hemos llegado al final de la rutina, reactivar TF
    .if eax != limite_codigo
        or [edx].regFlag, 100h ; Activar bit 8 (TF)
    .endif
    
    mov eax, ExceptionContinueExecution
    assume edx:nothing
    ret
MonitorAntiDbg endp

RutinaProtegida proc
    mov limite_codigo, offset MarcaFinal
    
    ; Activar el Trap Flag manualmente
    pushfd
    or dword ptr [esp], 100h
    popfd
    
    ; Bloque de instrucciones a monitorizar
    nop
    xor eax, eax
    inc eax
    add eax, 50h
    
MarcaFinal:
    ret
RutinaProtegida endp

InicioSeguro proc
    ; Instalar monitor de integridad
    push offset MonitorAntiDbg
    push fs:[0]
    mov fs:[0], esp
    
    call RutinaProtegida
    
    ; Quitar monitor
    pop fs:[0]
    add esp, 4
    ret
InicioSeguro endp

main:
    call InicioSeguro
    invoke ExitProcess, 0
end main

El uso de SEH para anti-depuración es una técnica de defensa en profundidad. Los analistas avanzados suelen contrarrestar esto configurando el depurador para que ignore las excepciones SINGLE_STEP o utilizando herramientas de trazado que no modifiquen el código original. Sin embargo, la combinación de cifrado de código y validación mediante excepciones sigue siendo un pilar fundamental en la protección de software moderno.

Etiquetas: Win32 assembly SEH Anti-debugging Reverse-Engineering

Publicado el 7-16 14:51