Flag: Tornado! Hurricane!

Blogs >> omeg's Blog

Created: Saturday, March 15 2008 11:20.19 CDT Modified: Saturday, March 15 2008 11:26.07 CDT
Printer Friendly ...
Non-continuable exception trick
Author: omeg # Views: 23818

I haven't seen it before in public but it's possible I'm not the first one who researched this subject. I implemented similar code about year ago in my "ever unfinished" crackme, but since I doubt I'll finish the crackme, here it goes.

The idea revolves about non-continuable exceptions, that is exceptions with EXCEPTION_NONCONTINUABLE flag set in exception record. Normally, if your SEH procedure gets such an exception, you're basically screwed: you can't return 'continue execution' status, and your process is going to be mercilessly killed. If you try to continue, you will get STATUS_NONCONTINUABLE_EXCEPTION thrown by Windows exception dispatcher - there is no way out. Or is there? ;)

What if we patch or hook windows exception dispatcher (in our process only) and just clear the noncontinuable bit if it's present before dispatching the exception down to SEH? It turns out that it works as expected - we can now escape and continue even after originally non-continuable exception. Furthermore, debuggers seem to not really like it. Olly simply refuses to continue even if we clear the noncontinuable flag (but olly can't even properly handle haedware BPs set in the code so who cares ;). Windbg fares a bit better, but still falls in an infinite loop (maybe more experienced users could overcome that). IDA seems to not handle the "rethrow" of division by zero exception at the end properly (but I hardly use IDA's debugger, so others may have more luck). Also, it doesn't properly run on WINE I heard, but more tests would be nice. :)

Anyway, it's quite fun code, maybe it will be useful to someone. Below is the FASM source, and here is the source+exe:
http://omeg.pl/code/noncontinuable_exc.zip

;------------------------------------------------
; Invalid Disposition exception trick
; or making noncontinuable exception continuable ;)
; Copyleft (c) Omega Red 2007, 2008
; fasm source
;------------------------------------------------
; 32-bit executable
format PE GUI
entry start

include '%fasminc%\win32a.inc'
include '%fasminc%\macro\proc32.inc'
;------------------------------------------------
struct EXCEPTION_POINTERS
    ExceptionRecord     dd  ?   ; ptr
    ContextRecord       dd  ?   ; ptr
ends

struct EXCEPTION_RECORD
    ExceptionCode           dd  ?
    ExceptionFlags          dd  ?
    NestedExceptionRecord   dd  ?
    ExceptionAddress        dd  ?
    NumberParameters        dd  ?
    ExceptionInformation    dd  15    dup (?)
ends

SIZE_OF_80387_REGISTERS         =   80
MAXIMUM_SUPPORTED_EXTENSION     =   512

struct FLOATING_SAVE_AREA
    ControlWord         dd  ?
    StatusWord          dd  ?
    TagWord             dd  ?
    ErrorOffset         dd  ?
    ErrorSelector       dd  ?
    DataOffset          dd  ?
    DataSelector        dd  ?
    RegisterArea        db  SIZE_OF_80387_REGISTERS dup (?)
    Cr0NpxState         dd  ?
ends

struct CONTEXT
    ContextFlags    dd  ?
    Dr0             dd  ?
    Dr1             dd  ?
    Dr2             dd  ?
    Dr3             dd  ?
    Dr6             dd  ?
    Dr7             dd  ?
    FloatSave       FLOATING_SAVE_AREA
    SegGs           dd  ?
    SegFs           dd  ?
    SegEs           dd  ?
    SegDs           dd  ?
    Edi             dd  ?
    Esi             dd  ?
    Ebx             dd  ?
    Edx             dd  ?
    Ecx             dd  ?
    Eax             dd  ?
    Ebp             dd  ?
    Eip             dd  ?
    SegCs           dd  ?
    EFlags          dd  ?
    Esp             dd  ?
    SegSs           dd  ?
    ExtendedRegisters   db MAXIMUM_SUPPORTED_EXTENSION dup (?)
ends
;------------------------------------------------
section 'code' code readable executable

start:
;    int3
; install UEF
    invoke      SetUnhandledExceptionFilter, UnhandledExceptionFilter
; install SEH
    push        dword seh_handler
    push        dword [fs:0]
    mov         dword [fs:0], esp

    invoke      GetModuleHandle, _ntdll
    invoke      GetProcAddress, eax, _rre
; cause #UD exception which will set up hardware BPX on RtlRaiseException
    ud2
    nop

; cause divide error -> invalid disposition exception (normally noncontinuable )
    xor eax,eax
    div eax

_end:
; delete SEH
    pop         dword [fs:0]
    add         esp, 4
    
    invoke      msgbox, 0, t9, t9, 0
    invoke      exit, 0
;------------------------------------------------
proc seh_handler    ExceptionRecord, EstablisherFrame, ContextRecord, DispatcherContext
    push        ebx esi edi                         ; save win32 callback registers

    mov         ebx, [ExceptionRecord]
    mov         esi, [ContextRecord]
    mov         eax, [ebx+EXCEPTION_RECORD.ExceptionCode]

    cmp         eax, STATUS_ILLEGAL_INSTRUCTION     ; c000001d
    jne         seh_ss

    invoke      msgbox, 0, t2, t1, 0
    ; #UD: let's install BPX on RtlRaiseException
    push        [esi+CONTEXT.Eax]                   ; address here
    pop         [esi+CONTEXT.Dr0]
    mov         [esi+CONTEXT.Dr7], 0x00000101       ; enable local BPX on DR0
    add         [esi+CONTEXT.Eip], 2                ; skip UD2
    jmp         seh_end

seh_ss:
    cmp         eax, STATUS_SINGLE_STEP             ; 80000004 - BPM or single step
    jne         seh_dz
    invoke      msgbox, 0, t3, t1, 0

    ; RtlRaiseException hook bpx ;]
    ; check if exception is noncontinuable - if it is, make it continuable
    mov         eax, [esi+CONTEXT.Esp]              ; get esp on RtlRaiseException entry
    mov         eax, [eax+4]                        ; get parameter - ptr to EXCEPTION_RECORD
    test        [eax+EXCEPTION_RECORD.ExceptionFlags], EXCEPTION_NONCONTINUABLE
    jz          seh_ss2                             ; continuable exception, ignore
    ; clear noncontinuable flag
    and         [eax+EXCEPTION_RECORD.ExceptionFlags], not EXCEPTION_NONCONTINUABLE
seh_ss2:
    mov         [esi+CONTEXT.Dr7], 0                ; disable bpx so we don't loop forever
    jmp         seh_end
    
seh_dz:
    cmp         eax, STATUS_INTEGER_DIVIDE_BY_ZERO
    jne         seh_id
    invoke      msgbox, 0, t4, t1, 0
    ; divide error, let's trigger noncontinuable condition
    mov         eax, 0xdeadc0de                     ; invalid disposition
    jmp         seh_end2

seh_id:         ; invalid disposition handler (which should be made continuable by our hook ;)
    cmp         eax, STATUS_INVALID_DISPOSITION     ; c0000026
    jz          @f
    ; unknown exception, commit suicide ;]
    invoke      msgbox, 0, t8, t1, 0
    mov         eax, 1                              ; continue search (= effectively kill process)
    jmp         seh_end2
    
@@: ; fix the condition that leaded to noncontinuable exception - divide error in this case
    invoke      msgbox, 0, t5, t1, 0
    mov         eax, [ebx+EXCEPTION_RECORD.NestedExceptionRecord]
    mov         eax, [eax-0x0c]    ; context pointer - ONLY IF EXCEPTION PARAMETERS ARE DIRECTLY BEFORE SEH FRAME!
    mov         [eax+CONTEXT.Eax], 0xdeadbeef   ; correct div ;)
    ; this exception is now continuable so we can go ahead

seh_end:
    xor         eax, eax                            ; status: continue execution
seh_end2:
    pop         edi esi ebx
    ret                                             ; proc macro takes care of stack balance
endp
;------------------------------------------------
UnhandledExceptionFilter:
    push        esi edi ebx

    ExceptionPointers   equ esp+0x10    ; EXCEPTION_POINTERS

    mov         eax, [ExceptionPointers]
    mov         ebx, [eax+EXCEPTION_POINTERS.ExceptionRecord]   ; exception info
    mov         esi, [eax+EXCEPTION_POINTERS.ContextRecord]     ; CPU state when exception occured
    mov         eax, [ebx+EXCEPTION_RECORD.ExceptionCode]       ; code

    cmp         eax, STATUS_INTEGER_DIVIDE_BY_ZERO
    jne         @f
    ; nothing for now - seh fixes context 2 frames back *o*
    ; well, we NEED to handle it here - I still don't know exactly why ZwRaiseException ignores SEH after nested exception and throws us here
    ; seems like some unwind is going on, since DRs are reverted to the state at previous (first) DIV error
    ; (that's actually good, because we don't need to reenable bpx manually)
    invoke      msgbox, 0, t7, t6, 0

    jmp         uef_end
;------------
@@:
    ; unknown exception
    invoke      msgbox, 0, t8, t6, 0
;------------
uef_end:
    mov         eax, -1 ; continue

    pop         ebx edi esi
    ret         4
;------------------------------------------------
section 'data' data readable writable

_ntdll      db  'ntdll.dll',0
_rre        db  'RtlRaiseException',0
t1          db  'SEH',0
t2          db  'STATUS_ILLEGAL_INSTRUCTION',10,'Installing RtlRaiseException hook',0
t3          db  'STATUS_SINGLE_STEP',10,'RtlRaiseException called',0
t4          db  'STATUS_INTEGER_DIVIDE_BY_ZERO',10,'Returning invalid disposition',0
t5          db  'STATUS_INVALID_DISPOSITION',10,'Fixing div and continuing',0
t6          db  'UEF',0
t7          db  'STATUS_INTEGER_DIVIDE_BY_ZERO',0
t8          db  'Unknown exception',0
t9          db  'Exiting properly, all is ok',0
;------------------------------------------------
data import

library user,   'user32.dll',\
        kernel, 'kernel32.dll'

import  user,\
        msgbox, 'MessageBoxA'

import  kernel,\
        GetProcAddress, 'GetProcAddress',\
        GetModuleHandle, 'GetModuleHandleA',\
        SetUnhandledExceptionFilter, 'SetUnhandledExceptionFilter',\
        exit, 'ExitProcess'
end data
;------------------------------------------------


Blog Comments
wtbw Posted: Wednesday, March 19 2008 16:17.07 CDT
interesting find!



Add New Comment
Comment:









There are 31,320 total registered users.


Recently Created Topics
[help] Unpacking VMP...
Mar/12
Reverse Engineering ...
Jul/06
hi!
Jul/01
let 'IDAPython' impo...
Sep/24
set 'IDAPython' as t...
Sep/24
GuessType return une...
Sep/20
About retrieving the...
Sep/07
How to find specific...
Aug/15
How to get data depe...
Jul/07
Identify RVA data in...
May/06


Recent Forum Posts
Finding the procedur...
rolEYder
Question about debbu...
rolEYder
Identify RVA data in...
sohlow
let 'IDAPython' impo...
sohlow
How to find specific...
hackgreti
Problem with ollydbg
sh3dow
How can I write olly...
sh3dow
New LoadMAP plugin v...
mefisto...
Intel pin in loaded ...
djnemo
OOP_RE tool available?
Bl4ckm4n


Recent Blog Entries
halsten
Mar/14
Breaking IonCUBE VM

oleavr
Oct/24
Anatomy of a code tracer

hasherezade
Sep/24
IAT Patcher - new tool for ...

oleavr
Aug/27
CryptoShark: code tracer ba...

oleavr
Jun/25
Build a debugger in 5 minutes

More ...


Recent Blog Comments
nieo on:
Mar/22
IAT Patcher - new tool for ...

djnemo on:
Nov/17
Kernel debugger vs user mod...

acel on:
Nov/14
Kernel debugger vs user mod...

pedram on:
Dec/21
frida.github.io: scriptable...

capadleman on:
Jun/19
Using NtCreateThreadEx for ...

More ...


Imagery
SoySauce Blueprint
Jun 6, 2008

[+] expand

View Gallery (11) / Submit