i dont use macro, just made it for this example.
include 'win32a.inc'
format PE console 4.0
macro __INSTALL_SEH {
pushfd
pushad
push __SEH ; our handler
push dword [fs:0] ; pointer to previous seh
mov dword [fs:0],esp ; install it
}
macro __SEH_ROUTINE {
jmp __NO_EXCEPTION
__SEH:
mov eax,[ebp+8] ; pointer to exception code
mov eax,[eax] ; read exception code
mov [exception_code],eax
cinvoke itoa,eax,_exception,10h
mov esp,[ebp-4] ; restore previous stack
pop dword [fs:0] ; restore old handler
add esp,4 ; get rid of our handler
popad
popfd
}
macro __END_SEH {
jmp __EXCEPTION_HANDLED
__NO_EXCEPTION:
pop dword [fs:0] ; no exceptions? restore old handler
add esp,28h ; discard all PUSHes we made
__EXCEPTION_HANDLED:
}
section '.text' code readable executable
__INSTALL_SEH
int3 ; generate breakpoint exception
invoke MessageBox,0,_debugger,_seh,0
__SEH_ROUTINE
invoke MessageBox,0,_exception,_seh,0
__END_SEH
xor eax,eax
ret
section '.data' data readable writable
exception_code dd 0
_seh db 'seh',0
_debugger db 'stop debugging or pass int3 exception to the program',0
_exception rb 9
section '.idata' import data readable
library ntdll,'NTDLL.DLL',\
user32,'USER32.DLL'
import ntdll,\
itoa,'_itoa'
import user32,\
MessageBox,'MessageBoxA'
section '.reloc' fixups data readable discardable
another example, skip over int3 instruction:
include 'win32a.inc'
format PE console 4.0
STATUS_BREAKPOINT = 80000003h
EXCEPTION_CONTINUE_SEARCH = 0
virtual at eax
context:
.ContextFlags dd ?
.Dr0 dd ?
.Dr1 dd ?
.Dr2 dd ?
.Dr3 dd ?
.Dr6 dd ?
.Dr7 dd ?
.ControlWord dd ?
.StatusWord dd ?
.TagWord dd ?
.ErrorOffset dd ?
.ErrorSelector dd ?
.DataOffset dd ?
.DataSelector dd ?
.St0 dt ?
.St1 dt ?
.St2 dt ?
.St3 dt ?
.St4 dt ?
.St5 dt ?
.St6 dt ?
.St7 dt ?
.Cr0NpxState dd ?
.Gs dd ?
.Fs dd ?
.Es dd ?
.Ds dd ?
.Edi dd ?
.Esi dd ?
.Ebx dd ?
.Edx dd ?
.Ecx dd ?
.Eax dd ?
.Ebp dd ?
.Eip dd ?
.Cs dd ?
.EFlags dd ?
.Esp dd ?
.Ss dd ?
end virtual
macro __INSTALL_SEH {
pushfd
pushad
push __SEH
push dword [fs:0]
mov dword [fs:0],esp
}
macro __SEH_ROUTINE {
jmp __NO_EXCEPTION
__SEH:
mov eax,[ebp+8]
mov eax,[eax]
mov [exception_code],eax
cmp eax,STATUS_BREAKPOINT
jnz @F
mov eax,[ebp+10h] ; pointer to context record
inc [context.Eip] ; increase eip by one to skip int3
mov eax,EXCEPTION_CONTINUE_SEARCH
ret
@@:
cinvoke itoa,eax,_exception,10h
mov esp,[ebp-4]
pop dword [fs:0]
add esp,4
popad
popfd
}
macro __END_SEH {
jmp __EXCEPTION_HANDLED
__NO_EXCEPTION:
pop dword [fs:0]
add esp,28h
__EXCEPTION_HANDLED:
}
section '.text' code readable executable
__INSTALL_SEH
int3 ; generate breakpoint exception
xor eax,eax
mov [eax],eax ; generate access violation exception
invoke MessageBox,0,_debugger,_seh,0
__SEH_ROUTINE
invoke MessageBox,0,_exception,_seh,0
__END_SEH
xor eax,eax
ret
section '.data' data readable writable
exception_code dd 0
_seh db 'seh',0
_debugger db 'stop debugging or pass int3/access violation exceptions to the program',0
_exception rb 9
section '.idata' import data readable
library ntdll,'NTDLL.DLL',\
user32,'USER32.DLL'
import ntdll,\
itoa,'_itoa'
import user32,\
MessageBox,'MessageBoxA'
section '.reloc' fixups data readable discardable
side notes:
- size of context record is increased on windows version => XP, to include a snapshot of xmm registers and other interesting stuff. (i did not include them here)
- ignore microsoft's description about EXCEPTION_CONTINUE_SEARCH, the meaning of EXCEPTION_CONTINUE_SEARCH changes based on the context (top-leve-filter-function vs SEH vs VEH).