利用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