利用SEH防範BP(int 3)斷點

利用SEH技術實現反跟蹤,這個方法比單純用判斷API函數第一個字節是否爲斷點更加有效,能夠防止在API函數內部的多處地址設置斷點windows

經過int 3指令故意產生一個異常,從而讓系統轉入本身的異常處理函數,修改CONTEXT結構的regFlag中的TF位設置爲1,而且將ContextFlags修改成CONTEXT_NULL,那麼當程序恢復執行時,標誌寄存器TF位也將爲1,這樣當程序執行一條指令後,將產生EXCEPTION_SINGLE_STEP異常,系統將再次轉出咱們的異常處理函數中函數

若是每次對EXCEPTION_SINGLE_STEP異常的處理中把CONTEXT結構的regFlag中的TF位設置爲1,那麼程序就如同咱們單步執行程序的效果同樣測試

此時能夠在EXCEPTION_SINGLE_STEP異常處理中判斷當前IP處的本身是否爲CC(INT 3)便可this

;******************************************
;coded by 
;******************************************
;演示檢測BPX斷點
;******************************************
;---------------------------------------asm------------------------------------------------
COMMENT    $
    編譯使用:
    \masm32\bin\ml /c /coff antibpx1.asm
    \masm32\bin\Link /SECTION:.text,ERW /SUBSYSTEM:WINDOWS antibpx1.obj
$

.586P
.MODEL FLAT,STDCALL
OPTION CASEMAP:NONE

include        e:\masm32\include\windows.inc
include        e:\masm32\include\kernel32.inc
include        e:\masm32\include\user32.inc
include        e:\masm32\include\comctl32.inc

includelib    e:\masm32\lib\kernel32.lib
includelib    e:\masm32\lib\user32.lib
includelib    e:\masm32\lib\comctl32.lib

.Data                                        
szDebugMsg            db    0Dh,0Ah,0Dh,0Ah
                    db    '你能夠經過在如下API函數中設置斷點來進行測試:'
                    db    0Dh,0Ah,0Dh,0Ah
                    db    'MessageBoxA',0Dh,0Ah,'MessageBeep',0Dh,0Ah
                    db    0Dh,0Ah,0Dh,0Ah,0
szNoFoundTracerMsg    db    '我沒有發現被跟蹤...:)',0
szFoundTracerMsg    db    '我發現你了!....你在跟蹤我....哈哈...',0
szTitle                db    '樣例:利用SEH技術進行反跟蹤',0

SafeEsp                dd    0
CallLevel            dd    0
ReturnAddrEsp        dd    255 dup(0)

hKernel32            dd    0
szKernel32Dll        db    'KERNEL32.DLL',0
szSleepEx            db    'SleepEx',0
szFormat            db    'KERNEL32.SleepEx : %08lX',0
szText                db    255 dup(0)


.Code
            assume    fs:nothing
;---------------------------------------------主程序開始------------------------------------------------
Main:
            ; 創建異常處理機制:結構化異常處理
            push    xMyHandler
            push    fs:[0]
            mov        fs:[0],esp            ; 
            mov        [SafeEsp],esp

            invoke    MessageBoxA,NULL,addr szDebugMsg,addr szTitle,MB_OK

            ; 故意產生一個異常
            int        3h                    ; 異常!!將被系統捕獲,系統將調用咱們的異常處理過程 xApiHandler
            nop
            ; 運行在 start_anti_trace 到 stop_anti_trace 之間的代碼時,都處在
            ; 程序經過SEH機制創建的單步調試狀態,程序將對每一條指令進行識別(包括API函數中的指令),
            ; 若是這些指令中存在斷點,都將被程序發現。
start_anti_trace:    ;===反跟蹤開始===
            invoke    MessageBeep,100
            invoke    LoadLibraryA,addr szKernel32Dll
            mov        [hKernel32],eax
            invoke    GetProcAddress,[hKernel32],addr szSleepEx
            invoke    wsprintf,addr szText,addr szFormat,eax
            invoke    MessageBoxA,NULL,addr szText,addr szTitle,MB_OK
stop_anti_trace:    ;===反跟蹤結束===

            ; 若是在以上進行反跟蹤的代碼執行中沒有設置斷點,程序將執行到此處,
            ; 而且顯示"沒有發現跟蹤者...:)"的提示信息
            invoke    MessageBoxA,NULL,addr szNoFoundTracerMsg,addr szTitle,MB_OK
            jmp        stop_self_trace_addr
found_tracer:
            ; 若是在以上進行反跟蹤的代碼執行中發現設置斷點,程序將執行到此處,
            ; 而且顯示"我發現你了....!"等提示信息(可能僅在此測試樣例中我會這樣作 :)
            invoke    MessageBoxA,NULL,addr szFoundTracerMsg,addr szTitle,MB_OK

stop_self_trace_addr:
            ; 解除本身創建的SEH結構化異常處理,而後結束進程
            mov        esp,[SafeEsp]
            pop        fs:[0]
            add        esp,4
            invoke    ExitProcess,0
;---------------------------------------------主程序結束------------------------------------------------

;--------------------------------------------異常處理函數------------------------------------------------
xMyHandler    proc    C uses ebx esi edi pExcept:DWORD,pFrame:DWORD,pContext:DWORD,pDispatch:DWORD
            mov        esi,pExcept
            assume    esi:ptr EXCEPTION_RECORD
            mov        edi,pContext
            assume    edi:ptr CONTEXT

            ; 若是發生嚴重錯誤,則不進行處理直接返回,從而轉向下一級異常處理程序
            test    [esi].ExceptionFlags,1
            jnz        lm0_continue_search

            ; 若是異常進行展開,則不進行處理直接返回。
            test    [esi].ExceptionFlags,6
            jnz        lm0_unwind

            ; 對"軟斷點異常"進行處理
            cmp        [esi].ExceptionCode,EXCEPTION_BREAKPOINT
            jz        lm0_start_self_trace

            ; 對"單步異常"進行處理
            cmp        [esi].ExceptionCode,EXCEPTION_SINGLE_STEP
            jz        lm0_self_trace

            ; 其餘狀況,都直接返回,轉向下一級異常處理程序
lm0_continue_search:
lm0_unwind:    mov        eax,ExceptionContinueSearch
            jmp        lm0_ret

lm0_start_self_trace:
            ; 開始啓動單步自跟蹤,即初始化變量、設置TF標誌等
            ;mov        [CallLevel],0                    ; 初始化 CALL層級  
            inc         [edi].regEip
lm0_trap_it:or        byte ptr [edi+1].regFlag,01h    ; 設置 TF 標誌
lm0_modify_drx_reg:
            mov        [edi].iDr0,0
            and        [edi].iDr6,0FFFF0FF0h
            mov        [edi].iDr7,0h                    ; 清除調試寄存器設置的信息
            mov        [edi].ContextFlags,CONTEXT_FULL OR CONTEXT_DEBUG_REGISTERS
            jmp        lm0_continue_exec

lm0_self_trace:
            ; 判斷是否自跟蹤到達結束地址,是,則中止單步自跟蹤
            mov        ebx,[edi].regEip
            cmp        ebx,stop_anti_trace
            jnz        @F                                ; 不是,則跳轉
            mov        [CallLevel],0                    ; 初始化CALL層級
            and        byte ptr [edi+1].regFlag,00h    ; 復位 TF 標誌,中止單步自跟蹤
            jmp        lm0_modify_drx_reg                ; 

@@:            lea        eax,[ebx+5]                        ; 獲取下下一條要執行的指令地址到 EAX 寄存器
            cmp        byte ptr [ebx],0E8h                ; 判斷下一條要執行的指令是否爲 CALL x 指令
            jz        lm0_do_call_instr
            inc        eax
            cmp        word ptr [ebx],015FFh            ; 判斷下一條要執行的指令是否爲 CALL [x] 指令
            jz        lm0_do_call_instr
            cmp        byte ptr [ebx],0C2h                ; 判斷下一條要執行的指令是否爲 ret n 指令
            jz        lm0_do_ret_instr
            cmp        byte ptr [ebx],0C3h                ; 判斷下一條要執行的指令是否爲 retn 指令
            jz        lm0_do_ret_instr
            cmp        byte ptr [ebx],0CCh                ; 判斷下一條要執行的指令是否爲 INT 3h 指令
            jz        lm0_do_int3_instr
            cmp        word ptr [ebx],08964h            ; 判斷下一條要執行的指令是否爲修改FS:[0]之類指令
            jz        lm0_do_fs_instr
            jmp        lm0_trap_it                        ; 都不是以上的指令,則繼續進行單步自跟蹤

lm0_do_fs_instr:                                    ; 
            mov        al,byte ptr [ebx+2]
            and        al,11100111b
            cmp        al,00000101b                    ; 判斷下一條要執行的指令是否爲 mov fs:[0],reg 指令
            jnz        lm0_do_other_fs_instr
            movzx    eax,byte ptr [ebx+2]
            and        eax,00011000b
            shr        eax,3
            not        eax
            and        eax,00000011b
            mov        eax,[edi+eax*4].regEbx
            cmp        eax,xMyHandler
            jz        @F
            mov        eax,[eax+4]
            mov        [ApiHandler],eax
            jmp        lm0_skip_this_instr
@@:            mov        [ApiHandler],0
lm0_skip_this_instr:
            lea        eax,[ebx+7]
            mov        [edi].regEip,eax
            jmp        lm0_trip_it


lm0_do_other_fs_instr:                                ; 針對其餘修改FS:[x]的指令,改用調試寄存器斷點跟蹤
            mov        ebx,[CallLevel]
            mov        eax,[ReturnAddrEsp+ebx*4]
            mov        eax,[eax]                        ; 取得最後一個CALL調用的返回地址
            dec        [CallLevel]
lm0_bp_trace:
            mov        [edi].iDr0,eax                    ; 在CALL指令的返回地址處設置調試寄存器斷點
            and        [edi].iDr6,0FFFF0FF0h
            mov        [edi].iDr7,155h
            mov        [edi].ContextFlags,CONTEXT_FULL OR CONTEXT_DEBUG_REGISTERS
            jmp        lm0_continue_exec                ; 繼續

lm0_do_int3_instr:
            mov        [edi].regEip,found_tracer        ; 發現設置API函數斷點,則修改CONTEXT.EIP
            mov        eax,[SafeEsp]
            mov        [edi].regEsp,eax                ; 修改CONTEXT.ESP
            jmp        lm0_modify_drx_reg                ; 

lm0_do_ret_instr:
            dec        [CallLevel]                        ; 下一步將執行 ret 指令, CALL層級 減一
            jmp        lm0_trap_it                        ; 繼續進行單步自跟蹤

lm0_do_call_instr:
            ; 經過判斷CALL層級,能夠知道是否已經執行進入到API函數內
            cmp        [CallLevel],0                    ; 
            jnz        lm0_bp_trace                    ; 對API函數內部的CALL調用再也不進行單步自跟蹤,
                                                    ; 而改用調試寄存器斷點自跟蹤方式

            inc        [CallLevel]                        ; 下一步將執行 CALL 指令, CALL 層級 加一
            mov        ebx,[CallLevel]
            mov        eax,[edi].regEsp
            sub        eax,4                            ; 計算 CALL 返回地址的CONTEXT.ESP堆棧指針
            mov        [ReturnAddrEsp+ebx*4],eax        ; 保存
            jmp        lm0_trap_it                        ; 繼續進行單步自跟蹤

lm0_continue_exec:
            mov        eax,ExceptionContinueExecution
lm0_ret:    ret
xMyHandler    endp

End Main                        ;End of code, Main is the entrypoint
相關文章
相關標籤/搜索