All pastes #475547 Raw Edit

omeg

public text v1 · immutable
·published 2007-05-07 14:54 UTC
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
;------------------------------------------------