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.