rendered paste body; Catching wow64 exceptions in 32bit SEH - world first ;]
; Copyleft (c) Omega Red 2007
; fasm source
; 32-bit executable
format PE GUI
entry start
include '%fasminc%\win32a.inc'
include '%fasminc%\macro\proc32.inc'
;------------------------------------------------
include 'context.inc'
include 'context64.inc'
;------------------------------------------------
use32
section 'code' code readable executable
; values from 64bit ntdll - hardcoded for now ;P
KiUED64 = 0x77ef31b0 ; ntdll!KiUserExceptionDispatcher
NtSCT64 = 0x77ef1970 ; ntdll!NtSetContextThread
test64 = 0x78b83c8d ; wow64cpu!GetCurrentProcessorNumber
start:
; install SEH
push dword seh_handler
push dword [fs:0]
mov dword [fs:0], esp
; unprotect 64bit ntdll ;]
invoke vprotect, KiUED64, 0x10, PAGE_EXECUTE_READWRITE, dupa
; detour it to our proc ;]
mov byte [KiUED64], 0xe9 ; jmp
mov dword [KiUED64+1], (hookproc-KiUED64-5) ; relative offset
; messagebox
push 0
push caption3
push caption3
push 0 ; ^ msgbox parameters
push continue ; return address
; cause exception which will set up hardware memory breakpoint on return address
ud2
nop
; EXPEXTED RESULT: break on return from msgbox (which will read return address)
jmp [msgbox] ; call messagebox
continue:
nop
; install BPX on 64it RtlRandom
cli
; call 32bit function, which will thunk into 64bit version and cause breakpoint in 64bit code
call [test32]
; disable BP
ud2
; delete SEH
pop dword [fs:0]
add esp, 4
invoke msgbox, 0, caption4, caption4, 0
invoke exit, 0
;-------------------------------------------------------------------------
; 31 29 27 25 23 21 19 17 15 13 12 9 8 7 6 5 4 3 2 1 0
; +---------------+---------------+---------------+---------------+
; |LEN|R/W|LEN|R/W|LEN|R/W|LEN|R/W| |G| |G|L|G|L|G|L|G|L|G|L|
; | | | | | | | | |0 0|D|0 0 0| | | | | | | | | | | DR7
; | 3 | 3 | 2 | 2 | 1 | 1 | 0 | 0 | | | |E|E|3|3|2|2|1|1|0|0|
; +-------------------------------+---+-+-----------------+-+-+-+-+
;-------------------------------------------------------------------------
proc seh_handler ExceptionRecord, EstablisherFrame, ContextRecord, DispatcherContext
push ebx esi edi ; save win32 callback registers
mov edx, [ExceptionRecord]
mov esi, [ContextRecord]
mov eax, [edx+EXCEPTION_RECORD.ExceptionCode]
cmp eax, STATUS_ILLEGAL_INSTRUCTION
jne @f
; install BP
add [esi+CONTEXT.Eip], 2
inc dword [count]
cmp dword [count], 2
jne seh_install
; delete BP
mov [esi+CONTEXT.Dr0], 0
and [esi+CONTEXT.Dr7], not 0x000f0001
invoke msgbox, 0, caption7, caption7, 0
jmp seh_end
seh_install:
mov eax, [esi+CONTEXT.Esp] ; [esp] = return address for MessageBox call
mov [esi+CONTEXT.Dr0], eax ; address of breakpoint
or [esi+CONTEXT.Dr7], 0x000f0001 ; enable local BPMD (4B) on DR0
invoke msgbox, 0, caption1, caption1, 0
jmp seh_end
@@:
cmp eax, STATUS_PRIVILEGED_INSTRUCTION
jne @f
; install BPX
add [esi+CONTEXT.Eip], 1
mov [esi+CONTEXT.Dr1], test64 ; address of breakpoint
or [esi+CONTEXT.Dr7], 0x00000004 ; enable local BPX on DR1
invoke msgbox, 0, caption6, caption6, 0
jmp seh_end
@@:
; breakpoint - display DRs
cinvoke printf, buf, format1, eax, [edx+EXCEPTION_RECORD.ExceptionAddress], [esi+CONTEXT.Dr0], [esi+CONTEXT.Dr6], [esi+CONTEXT.Dr7]
invoke msgbox, 0, buf, caption5, 0
test [esi+CONTEXT.Dr6], 2 ; bp1?
je @f
and [esi+CONTEXT.Dr7], not 4 ; disable bp1 if hit
@@:
and [esi+CONTEXT.Dr6], not 0x0000000f ; clear status bits
; or [esi+CONTEXT.EFlags], 1 shl 16 ; set resume flag - ONLY WORKS ON 64BIT
seh_end:
xor eax, eax ; continue execution
pop edi esi ebx
ret ; proc macro takes care of stack balance
endp
;------------------------------------------------
; ntdll!KiUserExceptionDispatcher hook
; 64-bit code here! ;]
use64
hookproc:
cmp dword [rsp+0x4d0], 0x4000001e ; wow64 single step?
jne hook_end
; We've got debug exception caused by 64bit code.
; Now we need to disable HW breakpoints - if we don't do that,
; further wow64 exception processing (which will touch our stack)
; will cause another nested debug break which would be fatal.
; Also, we CAN'T use SEH to clear DRs for obvious reasons
; (more nested exceptions, and wow64 exc. won't be passed to 32bit SEH).
; save regs
mov [_rcx], rcx
mov [_rdx], rdx
mov [_r8], r8
mov [_r9], r9
; use NtSetContextThread to disable Dr7
mov qword [ctx64+CONTEXT64.Dr7], 0
mov rcx, -2 ; current thread
mov rdx, ctx64
call NtSCT64
; after this debug registers should be clear, we can continue
mov rcx, [_rcx]
mov rdx, [_rdx]
mov r8, [_r8]
mov r9, [_r9]
hook_end:
mov rax, qword [0x0000000077fa6720] ; first line from original KiUserExceptionDispatcher
jmp KiUED64 +7
;ntdll!KiUserExceptionDispatch:
;00000000`77ef31b0 488b0569350b00 mov rax,qword ptr [ntdll!Wow64PrepareForException (00000000`77fa6720)]
;00000000`77ef31b7 4885c0 test rax,rax
;00000000`77ef31ba 740f je ntdll!KiUserExceptionDispatcher+0x1b (00000000`77ef31cb)
;00000000`77ef31bc 488bcc mov rcx,rsp
;00000000`77ef31bf 4881c1d0040000 add rcx,4D0h ; EXCEPTION_RECORD
;00000000`77ef31c6 488bd4 mov rdx,rsp ; CONTEXT
;00000000`77ef31c9 ffd0 call rax {wow64!Wow64PrepareForException (00000000`6b0299e0)}
;------------------------------------------------
use32
section 'data' data readable writable
count dd 0
exc_rec EXCEPTION_RECORD 0xc000001d,0,0,0,0
format1 db 'Code: %-8x, Address: %08x, DR0: %08x, DR1: %08x, DR6: %08x, DR7: %08x',0
caption1 db 'BPM0 installed',0
caption3 db 'Test',0
caption4 db 'END',0
caption5 db 'Exception',0
caption6 db 'BPX1 installed',0
caption7 db 'BPM0 deleted',0
buf db 100 dup (0)
dupa dd 0
; 64bit hook data
align 64
ctx64 CONTEXT64 0,0,0,0,0,0,CONTEXT_DEBUG_REGISTERS64
_rcx dq 0
_rdx dq 0
_r8 dq 0
_r9 dq 0
;------------------------------------------------
data import
library\
kernel, 'kernel32.dll',\
ntdll, 'ntdll.dll',\
user, 'user32.dll'
import kernel,\
exit, 'ExitProcess',\
vprotect,'VirtualProtect'
import ntdll,\
test32,'NtGetCurrentProcessorNumber'
import user,\
msgbox, 'MessageBoxA',\
printf, 'wsprintfA'
end data
;------------------------------------------------