;------------------------------------------------
; 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
;------------------------------------------------
Leave a Reply