News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

my SEH doesn't work

Started by x64Core, December 02, 2012, 05:55:59 PM

Previous topic - Next topic

x64Core

Hi guys, could anyone tell me why my code doesn't work:

.386
.model flat, stdcall
option casemap :none

.data

.CODE
ASSUME FS:NOTHING

handler:
      invoke MessageBox,0,0,0,0
           ret
start:
        push    DWORD ptr handler
        push    DWORD ptr fs:[0]
        mov     DWORD ptr fs:[0],esp
        xor     eax,eax
        mov     eax,DWORD ptr [eax]
        pop     DWORD ptr fs:[0]
        add     esp,4
        ret
end start

my messagebox never is executed and the program crashes  :P


Magnum

Here is some example code written by Jeremy Gordon and translated to Masm by Dave.



;Jeremy Gordon's "Except1" SEH program, adapted for MASM by DednDave, 11-2012

;Copyright Jeremy Gordon 2002

;build with \masm32\bin\build.bat


        INCLUDE    \masm32\include\masm32rt.inc

        .DATA

szFatalMess db 'I thoroughly enjoyed it and I have already tidied everything up - '
            db 'you know, completed records, closed filehandles, '
            db 'released memory, that sort of thing ..'
            db 'Glad this was by design - bye, bye ..',0Dh,0Ah
            db '.. but first, I expect the system will do an unwind ..',0
szExcept1a  db 'Except1',0
szExcept1b  db "Except1 - well it's all over for now.",0
szHappy     db 'This is a very happy ending',0
szException db 'There was an exception - do you want me to swallow it?',0
szUnWind    db 'The system calling the handler again for more clearing up (unwinding)',0

        .CODE
        ASSUME  FS:Nothing

CleanUp PROC

;all clean up would be done here

        ret

CleanUp ENDP

FinalHandler PROC

;system passes EXCEPTION_POINTERS

        push    ebx
        push    esi
        push    edi
        call    CleanUp
        INVOKE  MessageBox,NULL,offset szFatalMess,offset szExcept1b,MB_OK or MB_ICONEXCLAMATION
        mov     eax,TRUE            ;terminate process without showing system message box
        pop     edi
        pop     esi
        pop     ebx
        ret

FinalHandler ENDP

_main   PROC

        INVOKE  SetUnhandledExceptionFilter,FinalHandler
        call    ProtectedArea
        call    CleanUp
        INVOKE  MessageBox,NULL,offset szHappy,offset szExcept1a,MB_OK or MB_ICONEXCLAMATION
        INVOKE  ExitProcess,0

_main   ENDP

ProtectedArea PROC

        push    ebp
        push    0             ; )create the
        push    0             ; )ERR structure
        push    SafePlace     ; )on the
        push    Handler       ; )stack
        push dword ptr fs:[0]
        mov     fs:[0],esp    ;point to structure just established on the stack

;*********************** and now lets cause the exception

        xor     ecx,ecx       ;set ecx to zero
        div     ecx           ;divide by zero, causing exception

;*********************** because of the exception the code never gets to here

;but the handler will jump to here ..

SafePlace::
        pop     fs:[0]        ;restore original exception handler from stack
        add     esp,14h       ;throw away remainder of ERR structure made earlier
        ret

ProtectedArea ENDP

Handler PROC

;This simple handler is called by the system when the divide by zero
;occurs.  In this handler the user is given a choice of swallowing the
;exception by jumping to the safe-place, or not dealing with it at all,
;in which case the system will send the exception to the FINAL_HANDLER

        push    ebx
        push    esi
        push    edi

        mov     ebx,[ebp+8]         ;get exception record in ebx
        mov     eax,[ebx+4]         ;get flag sent by the system
        test    al,1                ;see if its a non-continuable exception
        jnz     NoDeal              ;yes, so not allowed by system to touch it

        test    al,2                ;see if its the system unwinding
        jnz     UnWind              ;yes

        INVOKE  MessageBox,NULL,offset szException,offset szExcept1a,MB_YESNO or MB_ICONQUESTION
        cmp     eax,6               ;see if yes clicked
        jnz     NoDeal              ;no

;***************************** go to SAFE_PLACE

        mov     esi,[ebp+10h]       ;get register context record in esi
        mov     edi,[ebp+0Ch]       ;get pointer to ERR structure in edi
        mov     [esi+0C4h],edi      ;insert new esp (happens to be pointer to ERR)
        mov     eax,[edi+8]         ;get address of SAFE_PLACE given in ERR structure
        mov     [esi+0B8h],eax      ;insert that as new eip in register context
        mov     eax,[edi+14h]       ;get ebp at safe place given in ERR structure
        mov     [esi+0B4h],eax      ;insert that as new ebp in register context
        xor     eax,eax             ;FALSE = reload context and return to system
        jmp short Fin

UnWind: INVOKE  MessageBox,NULL,offset szUnWind,offset szExcept1a,MB_OK or MB_ICONEXCLAMATION

NoDeal: mov     eax,TRUE            ;TRUE = system to go to next handler

Fin:    pop     edi
        pop     esi
        pop     ebx
        ret

Handler ENDP

END     _main


Take care,
                   Andy

Ubuntu-mate-18.04-desktop-amd64

http://www.goodnewsnetwork.org

dedndave

yah - Jeremy calls it an "ERR" structure - there is probably a better way to label those offsets   :P
with a quick search, i didn't find the name for that structure

MichaelW

The name of the structure is CONTEXT and it's defined in WINDOWS.INC.
Well Microsoft, here's another nice mess you've gotten us into.

dedndave

thanks, Michael   :t

must be a reduced version of it ?

MichaelW

The CONTEXT structure in WINDOWS.INC matches the x86 structure in WinNT.h from my 2003 PSDK. It's a reduced version relative to the most recent, but within limits it does do the job.

And here is some well-known example code for a per-thread exception handler:

//==================================================
// MYSEH - Matt Pietrek 1997
// Microsoft Systems Journal, January 1997
// FILE: MYSEH.CPP
// To compile: CL MYSEH.CPP
//==================================================
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>

DWORD  scratch;

EXCEPTION_DISPOSITION
__cdecl
_except_handler(
    struct _EXCEPTION_RECORD *ExceptionRecord,
    void * EstablisherFrame,
    struct _CONTEXT *ContextRecord,
    void * DispatcherContext )
{
    unsigned i;

    // Indicate that we made it to our exception handler
    printf( "Hello from an exception handler\n" );

    // Change EAX in the context record so that it points to someplace
    // where we can successfully write
    ContextRecord->Eax = (DWORD)&scratch;

    // Tell the OS to restart the faulting instruction
    return ExceptionContinueExecution;
}

int main()
{
    DWORD handler = (DWORD)_except_handler;

    __asm
    {                           // Build EXCEPTION_REGISTRATION record:
        push    handler         // Address of handler function
        push    FS:[0]          // Address of previous handler
        mov     FS:[0],ESP      // Install new EXECEPTION_REGISTRATION
    }

    __asm
    {
        mov     eax,0           // Zero out EAX
        mov     [eax], 1        // Write to EAX to deliberately cause a fault
    }

    printf( "After writing!\n" );

    __asm
    {                           // Remove our EXECEPTION_REGISTRATION record
        mov     eax,[ESP]       // Get pointer to previous record
        mov     FS:[0], EAX     // Install previous record
        add     esp, 8          // Clean our EXECEPTION_REGISTRATION off stack
    }

    return 0;
}


In this case you would be changing the regEip member to a safe place to continue execution and returning ExceptionContinueExecution.

http://www.microsoft.com/msj/0197/Exception/Exception.aspx

Well Microsoft, here's another nice mess you've gotten us into.

TouEnMasm


The use of FS register is an old code who had been changed by XP.
Use:
AddVectoredExceptionHandler
RemoveVectoredExceptionHandler

Fa is a musical note to play with CL

MichaelW

The need to use FS directly was removed long before XP.
Well Microsoft, here's another nice mess you've gotten us into.

Magnum

Michael,

Is that code more efficient, smaller, or work better with different windows versions ?

If so, I can convert the c code.

Take care,
                   Andy

Ubuntu-mate-18.04-desktop-amd64

http://www.goodnewsnetwork.org

dedndave

EXECEPTION_REGISTRATION

that's the name i was looking for, Michael   :t

found this article by Matt...

http://msdn.microsoft.com/en-us/magazine/cc301714.aspx

Magnum

Here is some more SEH code.


; Code from samael at http://www.winasm.net/
;
;
.386
.model flat,stdcall
option casemap:none


include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\comctl32.inc
include \masm32\include\gdi32.inc
include \masm32\include\masm32.inc


includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\comctl32.lib
includelib \masm32\lib\gdi32.lib
includelib \masm32\lib\masm32.lib


FinalExceptionHandler PROTO :DWORD


.DATA

szPTEHMessage DB "I'm displayed from SafeOffset, because an exception occured inside the area guarded by the Per-Thread Exception Handler...",0
szPTEHCaption DB "Per-thread EH",0

szFEHMessage DB "I'm displayed against all odds, tearing my way through bad code... ;)",0
szFEHCaption DB "Y-e-e-e-e-h-a !!!",0

.CODE

EntryPoint:
;int 3
;-------------------------------------------------------------------------------------
;Install Universal (Final) EH.
;-------------------------------------------------------------------------------------
INVOKE SetUnhandledExceptionFilter, OFFSET FinalExceptionHandler

;-------------------------------------------------------------------------------------
;Install per-thread EH
;-------------------------------------------------------------------------------------
ASSUME FS:NOTHING ; We use the ASSUME FS:NOTHING directive because MASM by default assumes the use of FS register to ERROR
PUSH OFFSET PTExceptionHandler
PUSH FS:[0]
    MOV FS:[0], ESP
   
   
;-------------------------------------------------------------------------------------   
    ; The code between installation and de-installation of the Per-Thread Exception
    ; Handler, is guarded by the handler. If exception occurs anywhere inside the guarded area,
    ; we will go to the SafeOffset.
;-------------------------------------------------------------------------------------


; Cause an exception by writting to a forbidden address in order to activate the per-thread EH...
; The program flow will be redirected to SafeOffset
XOR EAX,EAX
MOV DWORD PTR [EAX], EAX ;Exceptio Access Violation

@UninstallPerThreadSEH:

;-------------------------------------------------------------------------------------
;Uninstall per-thread EH
;-------------------------------------------------------------------------------------
POP FS:[0]
ADD ESP,4

;-------------------------------------------------------------------------------------   
    ; Having uninstalled the Per-Thread Exception Handler, the code below is guarded by the
    ; "final" handler.
    ; Note that we could setup the "final" handler, to do an attempt to continue executing
    ; the program, rather than just terminating it...
;-------------------------------------------------------------------------------------


;Cause more exceptions in order to activate the  Universal (Final) EH.

CLI ;Lets execute a privilaged instruction... ; Instruction length = 1 [This instruction is going ot be patched to NOP by the Final Exception Handler]
INT 3 ;Lets cause a breakpoint exception... ; Instruction length = 1 [This instruction is going ot be patched to NOP by the Final Exception Handler]
CLI ;Lets execute a privilaged instruction... ; Instruction length = 1 [This instruction is going ot be patched to NOP by the Final Exception Handler]
INT 3 ;Lets cause a breakpoint exception... ; Instruction length = 1 [This instruction is going ot be patched to NOP by the Final Exception Handler]

;Will this part ever be executed ?
INVOKE MessageBox, NULL, ADDR szFEHMessage, ADDR szFEHCaption,MB_OK
INVOKE ExitProcess,NULL



PTExceptionHandler proc C pExcept:DWORD, pFrame:DWORD, pContext:DWORD, pDispatch:DWORD
MOV EAX, pContext
MOV [EAX].CONTEXT.regEip, OFFSET SafeOffset
    MOV EAX,ExceptionContinueExecution
    RET
PTExceptionHandler endp


FinalExceptionHandler proc lpExceptionInfo:DWORD

LOCAL dwExceptionAddress : DWORD
LOCAL dwExceptionCode : DWORD

.CONST
LINE_BREAK EQU 0Dh,0Ah
DEFAULT_BUFFER_SIZE EQU 1024
.DATA
szErrorCaption DB "Universal (Final) EH",0
szErrorMessage DB "Cannot continue the normal execution of this program.",LINE_BREAK,\
"An exception was generated at address 0x%0.8lX.",LINE_BREAK,\
  "Exception type: %s.",LINE_BREAK,\
"This application will now terminate.",LINE_BREAK,LINE_BREAK,\
"Or perhaps not... ;)",0

szACCESS_VIOLATION DB "EXCEPTION_ACCESS_VIOLATION",0
szARRAY_BOUNDS_EXCEEDED DB "EXCEPTION_ARRAY_BOUNDS_EXCEEDED",0
szBREAKPOINT DB "EXCEPTION_BREAKPOINT",0
szDATATYPE_MISALIGNMENT DB "EXCEPTION_DATATYPE_MISALIGNMENT",0
szFLT_DENORMAL_OPERAND DB "EXCEPTION_FLT_DENORMAL_OPERAND",0
szFLT_DIVIDE_BY_ZERO  DB "EXCEPTION_FLT_DIVIDE_BY_ZERO",0
szFLT_INEXACT_RESULT  DB "EXCEPTION_FLT_INEXACT_RESULT",0
szFLT_INVALID_OPERATION DB "EXCEPTION_FLT_INVALID_OPERATION",0
szFLT_OVERFLOW  DB "EXCEPTION_FLT_OVERFLOW",0
szFLT_STACK_CHECK  DB "EXCEPTION_FLT_STACK_CHECK",0
szFLT_UNDERFLOW DB "EXCEPTION_FLT_UNDERFLOW",0
szILLEGAL_INSTRUCTION  DB "EXCEPTION_ILLEGAL_INSTRUCTION",0
szIN_PAGE_ERROR DB "EXCEPTION_IN_PAGE_ERROR",0
szINT_DIVIDE_BY_ZERO  DB "EXCEPTION_INT_DIVIDE_BY_ZERO",0
szINT_OVERFLOW  DB "EXCEPTION_INT_OVERFLOW",0
szINVALID_DISPOSITION DB "EXCEPTION_INVALID_DISPOSITION",0
szNONCONTINUABLE_EXCEPTION DB "EXCEPTION_NONCONTINUABLE_EXCEPTION",0
szPRIV_INSTRUCTION  DB "EXCEPTION_PRIV_INSTRUCTION",0
szSINGLE_STEP  DB "EXCEPTION_SINGLE_STEP",0
szSTACK_OVERFLOW  DB "EXCEPTION_STACK_OVERFLOW",0
szUNKNOWN_EXCEPTION DB "EXCEPTION_UNKNOWN_EXCEPTION",0

.DATA?

hHeap HANDLE ?
pBuffer LPVOID ?
dwPreviousProtect DWORD ?

.CODE
PUSHAD
INVOKE GetProcessHeap
MOV hHeap, EAX
INVOKE HeapAlloc, hHeap , HEAP_ZERO_MEMORY, DEFAULT_BUFFER_SIZE
MOV pBuffer,EAX
MOV EAX, [lpExceptionInfo]
MOV EAX, [EAX] ;lpEXCEPTION_RECORD
MOV edi, [EAX+12] ;ExceptionAddress;
MOV dwExceptionAddress,edi
MOV edi, [EAX] ;ExceptionCode
MOV dwExceptionCode,edi

.if dwExceptionCode == EXCEPTION_ACCESS_VIOLATION
MOV ESI, OFFSET szACCESS_VIOLATION
.ELSEIF dwExceptionCode == EXCEPTION_ARRAY_BOUNDS_EXCEEDED
MOV ESI, OFFSET szARRAY_BOUNDS_EXCEEDED
.ELSEIF dwExceptionCode == EXCEPTION_BREAKPOINT
MOV ESI, OFFSET szBREAKPOINT
.ELSEIF dwExceptionCode == EXCEPTION_DATATYPE_MISALIGNMENT
MOV ESI, OFFSET szDATATYPE_MISALIGNMENT
.ELSEIF dwExceptionCode == EXCEPTION_FLT_DENORMAL_OPERAND
MOV ESI, OFFSET szFLT_DENORMAL_OPERAND
.ELSEIF dwExceptionCode == EXCEPTION_FLT_DIVIDE_BY_ZERO
MOV ESI, OFFSET szFLT_DIVIDE_BY_ZERO
.ELSEIF dwExceptionCode == EXCEPTION_FLT_INEXACT_RESULT
MOV ESI, OFFSET szFLT_INEXACT_RESULT
.ELSEIF dwExceptionCode == EXCEPTION_FLT_INVALID_OPERATION
MOV ESI, OFFSET szFLT_INVALID_OPERATION
.ELSEIF dwExceptionCode == EXCEPTION_FLT_OVERFLOW
MOV ESI, OFFSET szFLT_OVERFLOW
.ELSEIF dwExceptionCode == EXCEPTION_FLT_STACK_CHECK
MOV ESI, OFFSET szFLT_STACK_CHECK
.ELSEIF dwExceptionCode == EXCEPTION_FLT_UNDERFLOW
MOV ESI, OFFSET szFLT_UNDERFLOW
.ELSEIF dwExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION
MOV ESI, OFFSET szILLEGAL_INSTRUCTION
.ELSEIF dwExceptionCode == EXCEPTION_IN_PAGE_ERROR
MOV ESI, OFFSET szIN_PAGE_ERROR
.ELSEIF dwExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO
MOV ESI, OFFSET szINT_DIVIDE_BY_ZERO
.ELSEIF dwExceptionCode == EXCEPTION_INT_OVERFLOW
MOV ESI, OFFSET szINT_OVERFLOW
.ELSEIF dwExceptionCode == EXCEPTION_PRIV_INSTRUCTION
MOV ESI, OFFSET szPRIV_INSTRUCTION
.ELSEIF dwExceptionCode == EXCEPTION_SINGLE_STEP
MOV ESI, OFFSET szSINGLE_STEP
.ELSE
MOV ESI, OFFSET szUNKNOWN_EXCEPTION
.ENDIF

INVOKE wsprintf, ADDR pBuffer, ADDR szErrorMessage, dwExceptionAddress, ESI
INVOKE MessageBox, NULL, ADDR pBuffer, ADDR szErrorCaption, MB_ICONERROR OR MB_OK OR MB_APPLMODAL
INVOKE HeapFree,hHeap,NULL,pBuffer

;Try to patch our way through the bad code ;)

MOV EAX, [lpExceptionInfo]
MOV EAX, [EAX].EXCEPTION_POINTERS.ContextRecord
MOV esi, [EAX].CONTEXT.regEip

; A length disassembler could be used to determine the size of code to be patched...
; Now i set the instruction length to 1 (constant) because i know it will work
; with the "bad" opcodes in this program... (They all have one-byte instructions)

INVOKE VirtualProtect,ESI,1,PAGE_EXECUTE_WRITECOPY,ADDR dwPreviousProtect ;Override the READONLY atrribute of the Code segment
MOV byte ptr [ESI], 090h ;Patch the bad instruction with the NOP opcode
;INC ESI ;INC EIP (NOT REALLY NECESSARY, SINCE WE PATCH THE BAD INSTRUCTIONS...)
MOV EAX, [lpExceptionInfo]
MOV EAX, [EAX].EXCEPTION_POINTERS.ContextRecord
MOV [EAX].CONTEXT.regEip,ESI
POPAD
MOV EAX,-1 ; Reload the context record into the processor and continue execution from the eip given in the context. 
    RET


;INVOKE ExitProcess, NULL
FinalExceptionHandler ENDP

;---------------------------------------------------------------------------------------------------------------------
; Safe Offset for PTEH
;---------------------------------------------------------------------------------------------------------------------
SafeOffset:
INVOKE MessageBox, NULL, ADDR szPTEHMessage, ADDR szPTEHCaption,MB_OK
jmp @UninstallPerThreadSEH

end EntryPoint
Take care,
                   Andy

Ubuntu-mate-18.04-desktop-amd64

http://www.goodnewsnetwork.org

x64Core

Thanks guys, I see... so the use of FS register doesn't work on Windows Vista/7?
I have windows 7 and the MichaelW's code doesn't work for me...

dave masm code works fine for me btw  :bgrin:
guys, so the better way is to use the FS register + SetUnhandledExceptionFilter ? :)

dedndave

the new way is probably the better way
we just have to figure out how to use it - lol
it may that something changed in win 7 and the code needs a little update

Antariy

Forum search does not seem to work properly - for the SEH it founds stuff that is not related, for AxEH it founds nothing.

Have a look on this (and try it under Win7), maybe it will be useful:
http://masm32.com/board/index.php?topic=350.msg2206#msg2206



Quote from: RHL on December 02, 2012, 05:55:59 PM
Hi guys, could anyone tell me why my code doesn't work:

        push    DWORD ptr handler

This code pushes first bytes of the handler, but it should push the offset of the handler:


push offset handler


will do the job.

x64Core

thank you, alex, I have used your code on W$ xp,7,8 and it's working  :biggrin: