segment .text

;%define masm

%ifdef masm
	%define __try   ___try@0
	%define __catch ___catch@12
	%define __leave ___leave@0
	%define __throw ___throw@4
%endif

global __try
global __catch
global __leave
global __throw
global get_exception_stringA

%macro assume 2
	%define %2(x) [%1 + %2. %+ x]
%endm

%macro invoke 1-*
	%if %0>3
		%assign args %0-1
		%error "invoke does not handle args arguments"
	%endif
	%if %0>2
		push dword %3
	%endif
	%if %0>1
		push dword %2
	%endif
	extern %1
	call   %1
%endm

%define sizeof(x) x %+ _size

struc CONTEXT
.ctxflg resd 1+6+28+4
;Dr0 resd 1,Dr1 resd 1,Dr2 resd 1,Dr3 resd 1,Dr6 resd 1,Dr7 resd 1;fltsav resd 28,gs resd 1,fs resd 1,es resd 1,ds resd 1
.edi    resd 1
.esi    resd 1
.ebx    resd 1
.edx    resd 1
.ecx    resd 1
.eax    resd 1
.ebp    resd 1
.eip    resd 1+2;cs resd 1;eFlags resd 1
.esp    resd 1+2;ss resd 1;ExtendedRegisters resd 1
endstruc


struc EXCEPTION_RECORD
.ExceptionCode			resd 1
.ExceptionFlags			resd 1
.pExceptionRecord		resd 1
.ExceptionAddress		resd 1
.NumberParameters		resd 1
.ExceptionInformation	resd 1
endstruc

%define EXCEPTION_USER_THROW -2

struc EXCEPTION_REGISTRATION
.next     resd 1
.handler  resd 1
; user defined
.resume   resd 1 ; if __try() {} <- here, outside IF
.ebp      resd 1 ; saved ebp
.esp      resd 1 ; saved esp
.code     resd 1 ; ExceptionCode
.addr     resd 1 ; ExceptionAddress
.usermsg  resd 1 ; used by __throw
endstruc

;-----------------------------------------------------------
align 4
__try:
	pop    eax                  ; address of 'test eax, eax'
	cmp    dword[eax],0x840FC085; validate "test eax,eax; jz near *"
	jne   .q                    ; return if code is not matching
	lea    ecx,[eax+8]          ; code address inside 'if' - we jump here at end
	add    eax,2                ; address of jz near
	add    eax,[eax+2]          ; add the offset from jz near
	add    eax,6                ; skip the jz near
	xor    edx,edx
	push   edx                  ; EXCEPTION_REGISTRATION.usermsg = NULL
	push   edx                  ; EXCEPTION_REGISTRATION.addr
	push   edx                  ; EXCEPTION_REGISTRATION.code
	push   esp                  ; EXCEPTION_REGISTRATION.esp   ; invalid!
	push   ebp                  ; EXCEPTION_REGISTRATION.ebp
	push   eax                  ; EXCEPTION_REGISTRATION.resume
	push   _exception_handler   ; EXCEPTION_REGISTRATION.handler
	push   dword[fs:0]          ; EXCEPTION_REGISTRATION.next
	mov    [fs:0],esp           ; add new node to current thread SEH
	assume esp,EXCEPTION_REGISTRATION
	mov    EXCEPTION_REGISTRATION(esp),esp ; fix the invalid esp
	jmp    ecx                  ; jump (return) inside IF
.q:
	xor    ecx,ecx
	xchg   eax,ecx
	jmp    ecx

;-----------------------------------------------------------

align 4  ; WARING!! do not alter EAX
__leave:
	pop    ecx          ; pop return address
	xchg   edx,esi      ; save esi in edx
	mov    esi,[fs:0]
	assume esi,EXCEPTION_REGISTRATION

	and    esi,esi
	jz    .q            ; cancel if [fs:0] is NULL
	inc    esi
	jz    .q            ; or -1
	dec    esi
	cmp    dword EXCEPTION_REGISTRATION(handler),_exception_handler
	jne   .q


	pop    dword[fs:0] ; restore previous handler
	add    esp,sizeof(EXCEPTION_REGISTRATION)-4
.q:
	xchg   edx,esi      ; restore esi
	jmp    ecx          ; and return

;-----------------------------------------------------------

align 4
__catch:                ; code*, addr*, string*
	enter  0,0
	%define arg(x) [ebp+8+(x*4)]
	push   edi
	push   esi
	mov    esi,[fs:0]
	assume esi,EXCEPTION_REGISTRATION

	xor    eax,eax      ; default return value is FALSE

	and    esi,esi
	jz    .q            ; return FALSE if [fs:0] is NULL
	inc    esi
	jz    .q            ; or -1
	dec    esi
	cmp    dword EXCEPTION_REGISTRATION(handler),_exception_handler
	jne   .q            ; or the handler is not equal to _exception_handler()

	; ensure that EAX = FALSE !
	mov    ecx,EXCEPTION_REGISTRATION(code)
	mov    eax,EXCEPTION_REGISTRATION(addr)
	and    ecx,ecx
	jz    .q                ; here was no exception
	; now eax can be altered

	mov    edi,arg(0)       ; copy exception code ?
	and    edi,edi
	jz    .sk1
	mov    [edi],ecx
	.sk1:

	mov    edi,arg(1)       ; copy exception addr ?
	and    edi,edi
	jz    .sk2
	mov    [edi],eax
	.sk2:

	mov    edi,arg(2)
	mov    eax,EXCEPTION_REGISTRATION(usermsg)
	cmp    ecx,EXCEPTION_USER_THROW  ; throwed exception?
	je    .sk3                       ; yes

	; search for exception string (code in ecx)
.sk:
	call   _get_exception_string
.sk3:
	and    edi,edi      ; copy exception string* ?
	jz    .sk4
	mov    [edi],eax
	.sk4:

	mov    al,1
.q:
	pop    esi
	pop    edi
	leave
;	ret    12           <-- we do it indirectly
	pop    edx          ; return address
	add    esp,12       ; clear the stack
	; we are now in the caller stack frame. Removing exception handler...
	push   edx
	jmp    __leave      ; don't worry about eax

%undef arg
;-----------------------------------------------------------
align 4
_exception_handler: ; EXCEPTION_RECORD*, EXCEPTION_REGISTRATION*, CONTEXT*, void *DispatcherContext)

	enter  0,0
	%define arg(x) [ebp+8+(x*4)]
	push   esi
	push   edi
	mov    edi,arg(1) ; EXCEPTION_REGISTRATION
	mov    esi,arg(2) ; CONTEXT *

	assume edi,EXCEPTION_REGISTRATION
	assume esi,CONTEXT

	mov    eax,EXCEPTION_REGISTRATION(esp)
	mov    ecx,EXCEPTION_REGISTRATION(ebp)
	mov    edx,EXCEPTION_REGISTRATION(resume)

	mov    CONTEXT(esp),eax
	mov    CONTEXT(ebp),ecx
	mov    CONTEXT(eip),edx


	mov    esi,arg(0)
	assume esi,EXCEPTION_RECORD
	mov    eax,EXCEPTION_RECORD(ExceptionCode)
	mov    ecx,EXCEPTION_RECORD(ExceptionAddress)

	; exception generated in __throw.allowed ?
	cmp    ecx, __throw.allowed
	je    .skip ; yes! do not change exception code or address

	mov    EXCEPTION_REGISTRATION(code),eax
	mov    EXCEPTION_REGISTRATION(addr),ecx
.skip:

	pop    edi
	pop    esi
	leave
	xor    eax,eax
	ret


;-----------------------------------------------------------
align 4
get_exception_stringA:
	mov ecx,[esp+4]
	call _get_exception_string
	ret 4

align 4
_get_exception_string: ; ecx = exception code, but never equal to EXCEPTION_USER_THROW
	push  esi
	mov   esi,_e_codes
.f1:
	lodsd           ; after lodsd, esi points to a string, eax = exception string code.
	and    eax,eax  ; more items in the list ?
	jz    .fq       ; no (esi = &"")
	cmp    eax,ecx  ; current code == ecx ?
	je    .fq       ; yes
.f2:
	lodsb           ; skip string
	and    al,al
	jnz    .f2

	mov    eax,4    ; align esi to next dword
	sub    eax,esi
	and    eax,3
	add    esi,eax
	jmp   .f1       ; and search again
.fq:
	mov    eax,esi  ; return string pointer
	pop    esi
	ret

;-----------------------------------------------------------
align 4
__throw:   ; string *
	push   esi
	mov    esi,[fs:0]
	assume esi,EXCEPTION_REGISTRATION

	inc    esi
	jz    .q            ; ignore if [fs:0] is -1
	dec    esi
	jz    .q            ; or NULL
	cmp    dword EXCEPTION_REGISTRATION(handler),_exception_handler
	jne   .q            ; or the handler is not equal to _exception_handler()
	

	mov    dword EXCEPTION_REGISTRATION(code),EXCEPTION_USER_THROW

	mov    eax,[esp+4]  ; save return address in SEH frame - next instruction after call to __throw
	mov    EXCEPTION_REGISTRATION(addr),eax


	mov    eax,[esp+8]
	mov    EXCEPTION_REGISTRATION(usermsg),eax

	pop    esi ; esi
	pop    eax ; return address
	pop    eax ; string *
.allowed
	int3       ; generate exception
.q:
	pop    esi
	ret    4

;-----------------------------------------------------------

%define DBG_EXCEPTION_HANDLED            (0x00010001)
%define DBG_CONTINUE                     (0x00010002)
%define DBG_TERMINATE_THREAD             (0x40010003)
%define DBG_TERMINATE_PROCESS            (0x40010004)
%define DBG_COMMAND_EXCEPTION            (0x40010009)
%define DBG_EXCEPTION_NOT_HANDLED        (0x80010001)
%define STATUS_SEGMENT_NOTIFICATION      (0x40000005)
%define STATUS_REG_NAT_CONSUMPTION       (0xC00002C9)
%define STATUS_SXS_EARLY_DEACTIVATION    (0xC015000F)
%define STATUS_SXS_INVALID_DEACTIVATION  (0xC0150010)

%macro EXCEPTION 3
	dd %2
	db %3,0
	align 4
%endm

;-----------------------------------------------------------
segment .data
_e_codes:
EXCEPTION DBG_CONTROL_BREAK               , (0x40010008),"Control-Break"
EXCEPTION DBG_CONTROL_C                   , (0x40010005),"Control-C"
EXCEPTION STATUS_GUARD_PAGE_VIOLATION     , (0x80000001), "Guard Page Violation"
EXCEPTION STATUS_DATATYPE_MISALIGNMENT    , (0x80000002), "Datatype Misalignment"
EXCEPTION STATUS_BREAKPOINT               , (0x80000003), "Breakpoint"
EXCEPTION STATUS_SINGLE_STEP              , (0x80000004), "Single Step"
EXCEPTION STATUS_ACCESS_VIOLATION         , (0xC0000005), "Access Violation"
EXCEPTION STATUS_IN_PAGE_ERROR            , (0xC0000006), "In Page Error"
EXCEPTION STATUS_INVALID_HANDLE           , (0xC0000008), "Invalid Handle"
EXCEPTION STATUS_NO_MEMORY                , (0xC0000017), "No Memory"
EXCEPTION STATUS_ILLEGAL_INSTRUCTION      , (0xC000001D), "Illegal Instruction"
EXCEPTION STATUS_NONCONTINUABLE_EXCEPTION , (0xC0000025), "Noncontinuable Exception"
EXCEPTION STATUS_INVALID_DISPOSITION      , (0xC0000026), "Invalid Disposition"
EXCEPTION STATUS_ARRAY_BOUNDS_EXCEEDED    , (0xC000008C), "Array Bounds Exceeded"
EXCEPTION STATUS_FLOAT_DENORMAL_OPERAND   , (0xC000008D), "Float Denormal Operand"
EXCEPTION STATUS_FLOAT_DIVIDE_BY_ZERO     , (0xC000008E), "Float Divide by Zero"
EXCEPTION STATUS_FLOAT_INEXACT_RESULT     , (0xC000008F), "Float Inexact Result"
EXCEPTION STATUS_FLOAT_INVALID_OPERATION  , (0xC0000090), "Float Invalid Operation"
EXCEPTION STATUS_FLOAT_OVERFLOW           , (0xC0000091), "Float Overflow"
EXCEPTION STATUS_FLOAT_STACK_CHECK        , (0xC0000092), "Float Stack Check"
EXCEPTION STATUS_FLOAT_UNDERFLOW          , (0xC0000093), "Float Underflow"
EXCEPTION STATUS_INTEGER_DIVIDE_BY_ZERO   , (0xC0000094), "Integer Divide by Zero"
EXCEPTION STATUS_INTEGER_OVERFLOW         , (0xC0000095), "Integer Overflow"
EXCEPTION STATUS_PRIVILEGED_INSTRUCTION   , (0xC0000096), "Privileged Instruction"
EXCEPTION STATUS_STACK_OVERFLOW           , (0xC00000FD), "Stack Overflow"
EXCEPTION STATUS_CONTROL_C_EXIT           , (0xC000013A), "Control-C Exit"
EXCEPTION STATUS_FLOAT_MULTIPLE_FAULTS    , (0xC00002B4), "Float Multiple Faults"
EXCEPTION STATUS_FLOAT_MULTIPLE_TRAPS     , (0xC00002B5), "Float Multiple Traps"
EXCEPTION 0,0,"Unknown Exception"



