Author Topic: my SEH doesn't work  (Read 12955 times)

x64Core

  • Member
  • **
  • Posts: 80
my SEH doesn't work
« on: December 02, 2012, 05:55:59 PM »
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

  • Member
  • *****
  • Posts: 2314
Re: my SEH doesn't work
« Reply #1 on: December 03, 2012, 12:05:47 AM »
Here is some example code written by Jeremy Gordon and translated to Masm by Dave.

Code: [Select]

;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

  • Member
  • *****
  • Posts: 8825
  • Still using Abacus 2.0
    • DednDave
Re: my SEH doesn't work
« Reply #2 on: December 03, 2012, 12:30:44 AM »
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

  • Global Moderator
  • Member
  • *****
  • Posts: 1209
Re: my SEH doesn't work
« Reply #3 on: December 03, 2012, 02:24:43 AM »
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

  • Member
  • *****
  • Posts: 8825
  • Still using Abacus 2.0
    • DednDave
Re: my SEH doesn't work
« Reply #4 on: December 03, 2012, 03:08:44 AM »
thanks, Michael   :t

must be a reduced version of it ?

MichaelW

  • Global Moderator
  • Member
  • *****
  • Posts: 1209
Re: my SEH doesn't work
« Reply #5 on: December 03, 2012, 03:41:50 AM »
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:
Code: [Select]
//==================================================
// 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.

ToutEnMasm

  • Member
  • *****
  • Posts: 1189
    • EditMasm
Re: my SEH doesn't work
« Reply #6 on: December 03, 2012, 04:21:12 AM »

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

  • Global Moderator
  • Member
  • *****
  • Posts: 1209
Re: my SEH doesn't work
« Reply #7 on: December 03, 2012, 05:14:30 AM »
The need to use FS directly was removed long before XP.
Well Microsoft, here’s another nice mess you’ve gotten us into.

Magnum

  • Member
  • *****
  • Posts: 2314
Re: my SEH doesn't work
« Reply #8 on: December 03, 2012, 05:51:51 AM »
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

  • Member
  • *****
  • Posts: 8825
  • Still using Abacus 2.0
    • DednDave
Re: my SEH doesn't work
« Reply #9 on: December 03, 2012, 07:25:15 AM »
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

  • Member
  • *****
  • Posts: 2314
Re: my SEH doesn't work
« Reply #10 on: December 03, 2012, 08:13:29 AM »
Here is some more SEH code.

Code: [Select]
; 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

  • Member
  • **
  • Posts: 80
Re: my SEH doesn't work
« Reply #11 on: December 03, 2012, 09:46:06 AM »
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

  • Member
  • *****
  • Posts: 8825
  • Still using Abacus 2.0
    • DednDave
Re: my SEH doesn't work
« Reply #12 on: December 03, 2012, 10:22:29 AM »
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

  • Member
  • ****
  • Posts: 551
Re: my SEH doesn't work
« Reply #13 on: December 03, 2012, 11:32:33 AM »
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



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:

Code: [Select]
push offset handler

will do the job.

x64Core

  • Member
  • **
  • Posts: 80
Re: my SEH doesn't work
« Reply #14 on: December 03, 2012, 12:19:31 PM »
thank you, alex, I have used your code on W$ xp,7,8 and it's working  :biggrin: