zoukankan      html  css  js  c++  java
  • 利用SEH防范BP(int 3)断点

    利用SEH技术实现反跟踪,这个方法比单纯用判断API函数第一个字节是否为断点更加有效,可以防止在API函数内部的多处地址设置断点

    通过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)即可

    ;******************************************
    ;coded by 
    ;******************************************
    ;演示检测BPX断点
    ;******************************************
    ;---------------------------------------asm------------------------------------------------
    COMMENT    $
        编译使用:
        masm32inml /c /coff antibpx1.asm
        masm32inLink /SECTION:.text,ERW /SUBSYSTEM:WINDOWS antibpx1.obj
    $
    
    .586P
    .MODEL FLAT,STDCALL
    OPTION CASEMAP:NONE
    
    include        e:masm32includewindows.inc
    include        e:masm32includekernel32.inc
    include        e:masm32includeuser32.inc
    include        e:masm32includecomctl32.inc
    
    includelib    e:masm32libkernel32.lib
    includelib    e:masm32libuser32.lib
    includelib    e:masm32libcomctl32.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
  • 相关阅读:
    33.数组声明方式(var构造函数) 、检测数组类型、数组的属性(封装好的就一个length)、数组的方法
    31.this指向(写出调用链,找最近对象) this的默认绑定 隐式绑定 显示绑定(call(绑定对象) apply(绑定对象) 当括号内没放绑定对象的时候恢复默认绑定) bind
    31.
    30.函数作用域链 (GO AO 也叫词法作用域链)、 调用栈、调用栈涉及this绑定
    29.包装类(构造函数) 包装类作用及调用栈
    916. Word Subsets
    246. Strobogrammatic Number
    445. Add Two Numbers II
    2. Add Two Numbers
    341. Flatten Nested List Iterator
  • 原文地址:https://www.cnblogs.com/qintangtao/p/7156875.html
Copyright © 2011-2022 走看看