当前位置: 首页 > news >正文

利用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    $
    编译使用:
    \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

 

相关文章:

  • 用vue开发单页应用的一些心得
  • leetcode笔记:Search in Rotated Sorted Array
  • 图片文件重命名
  • Codeforces Round #424 (Div. 2, rated, based on VK Cup Finals)
  • 产品和团队
  • MySQL慎用 ENUM 字段
  • mysql取差集、交集、并集
  • Tex: The top-level auxiliary file: *.aux I couldn't open style file IEEEtran.bst 解决方法
  • 【java项目实战】一步步教你使用MyEclipse搭建java Web项目开发环境(一)
  • 嵌入式开发之hisilicon---hi3536 处理器简介
  • 分布式开放消息系统(RocketMQ)的原理与实践
  • 【翻译】Mashape是如何管理15000个API和微服务的(三)
  • nodeJS中的包
  • oracle学习3
  • 即将到来的Android N,将具备这些新特性
  • Fabric架构演变之路
  • Java 最常见的 200+ 面试题:面试必备
  • js数组之filter
  • VUE es6技巧写法(持续更新中~~~)
  • 码农张的Bug人生 - 见面之礼
  • 人脸识别最新开发经验demo
  • 手机端车牌号码键盘的vue组件
  • 小李飞刀:SQL题目刷起来!
  • 正则学习笔记
  • 深度学习之轻量级神经网络在TWS蓝牙音频处理器上的部署
  • 你对linux中grep命令知道多少?
  • NLPIR智能语义技术让大数据挖掘更简单
  • 关于Android全面屏虚拟导航栏的适配总结
  • 如何用纯 CSS 创作一个菱形 loader 动画
  • 移动端高清、多屏适配方案
  • ​LeetCode解法汇总2304. 网格中的最小路径代价
  • #、%和$符号在OGNL表达式中经常出现
  • #14vue3生成表单并跳转到外部地址的方式
  • #stm32整理(一)flash读写
  • #我与虚拟机的故事#连载20:周志明虚拟机第 3 版:到底值不值得买?
  • (附源码)springboot优课在线教学系统 毕业设计 081251
  • (论文阅读11/100)Fast R-CNN
  • (十三)Flask之特殊装饰器详解
  • (转)eclipse内存溢出设置 -Xms212m -Xmx804m -XX:PermSize=250M -XX:MaxPermSize=356m
  • *(长期更新)软考网络工程师学习笔记——Section 22 无线局域网
  • .a文件和.so文件
  • .mat 文件的加载与创建 矩阵变图像? ∈ Matlab 使用笔记
  • .Net - 类的介绍
  • .NET Core SkiaSharp 替代 System.Drawing.Common 的一些用法
  • .NET 设计模式—简单工厂(Simple Factory Pattern)
  • .NET 中的轻量级线程安全
  • .NET/C# 将一个命令行参数字符串转换为命令行参数数组 args
  • .NET中winform传递参数至Url并获得返回值或文件
  • /etc/X11/xorg.conf 文件被误改后进不了图形化界面
  • ?php echo $logosrc[0];?,如何在一行中显示logo和标题?
  • [ vulhub漏洞复现篇 ] ThinkPHP 5.0.23-Rce
  • [1204 寻找子串位置] 解题报告
  • [22]. 括号生成
  • [Android] 240204批量生成联系人,短信,通话记录的APK
  • [android]-如何在向服务器发送request时附加已保存的cookie数据