会员登陆:
载入中…
我要发布
联系我们
广告合作
推荐:系统管理 信息安全 企业管理 大众消费 多媒体 存储备份 商业智能 系统软件 PK 企业库 软件商城 威客 在线演示 我要发布
病毒防护 | 防火墙 | 入侵检测 | 漏洞扫描 | 入侵防御 | VPN | 身份认证 | 密码保护 | 访问控制 |
安全网关 | 反黄 | 防伪 | 软件加密 | 信息加密 | 防垃圾邮件 | 网络流量分析 | 负载均衡
    ISS  |  金山  |  三星   |  AdventNet   |  其它
您现在的位置: 中华软件网|中国软件导购网 >> 信息安全 >> 漏洞扫描 >> 解决方案 >> 信息安全正文
企业新闻
普通信息安全金山毒霸荣获2006年《数字时
普通信息安全三星漏洞扫描产品绑入冠群金
普通信息安全前沿动力文档安全管理 防止企
普通信息安全微软信息安全公告 ActiveX漏
普通信息安全微软漏洞养活了黑客及安全公
普通信息安全忘记了密码照样能进入Window
普通信息安全暴风影音播放器存在多个缓冲
普通信息安全“假补丁”病毒利用微软漏洞
导购论坛
·常用软件导购
·报表工具软件导购
·数据备份软件导购
·系统管理软件导购
·信息安全软件导购
·数据库软件导购
·基础软件导购
·行业软件导购
产品评测
普通信息安全 内网信息安全/信息防泄漏产品
普通信息安全 榕基RJ-iTop漏洞扫描系统护航
普通信息安全 超半数行业用户担忧自身信息
相关软件下载
相关技术白皮书
在线体验
服务点评
  • 此栏目下没有信息安全
  • 专家点评
    普通信息安全 黑客兵器 如何选择好的漏洞扫
    客户评价
    普通信息安全 三星漏洞扫描获殊荣
    负面报道
  • 此栏目下没有信息安全
  • 解决方案
    普通信息安全 如何快速排除网络故障
    普通信息安全 实用级反主动防御rootkit设计思路
    普通信息安全 覆盖SEH的溢出利用检测思路
    普通信息安全 php代码不开源下的一种漏洞检测思
    普通信息安全 使用漏洞扫描工具有效防范蠕虫病
    推荐信息安全 SecureCentral™ ScanFi 4方
    普通信息安全 三星中小企业网络安全解决方案
    普通信息安全 ISS漏洞扫描解决方案
    Google
    论坛
    覆盖SEH的溢出利用检测思路
    作者:佚名    信息安全来源:本站原创    点击数:    更新时间:2007-9-20    

      看到安焦上的一篇《基于栈指纹检测缓冲区溢出的一点思路》,这是在ShellCode已经运行时在它的调用堆栈(被Hook的下级调用函数LoadLibrary)里进行检测,有些利用溢出覆盖SEH Handler,然后任程序运行,因为溢出破坏了堆或栈,肯定会出现异常,这时指向ShellCode的Handler被运行,我在想这一类的溢出利用,既然它想运行,那首先要过操作系统的异常派遣这一关,如果在分派异常时我们就对SEH Handler进行一下检测,或许能在ShellCode运行前就发现它。

      我简单看了一下SEH处理流程,一直跟到这两个函数,因为wrk代码不全,所以我选取ReactOS的代码,但并不影响理解。

      以下代码来自ReactOS,版权归原作者

    VOID
    NTAPI
    KiUserExceptionDispatcher(PEXCEPTION_RECORD ExceptionRecord,
                                       PCONTEXT Context)
    {
                 EXCEPTION_RECORD NestedExceptionRecord;
                 NTSTATUS Status;

                 /* call the vectored exception handlers */
                 if(RtlpExecuteVectoredExceptionHandlers(ExceptionRecord,
                                                         Context) != ExceptionContinueExecution)
                 {//VEH??? ReactOS也太强了吧,实现了XP的VEH,兼容度很高啊!
                     goto ContinueExecution;
                 }
                 else
                 {
                     /* Dispatch the exception and check the result */
                     if(RtlDispatchException(ExceptionRecord, Context))
                     {
    ContinueExecution:
                         /* Continue executing */
                         Status = NtContinue(Context, FALSE);
                     }
                     else
                     {
                         /* Raise an exception */
                         Status = NtRaiseException(ExceptionRecord, Context, FALSE);
                     }
                 }

                 /* Setup the Exception record */
                 NestedExceptionRecord.ExceptionCode = Status;
                 NestedExceptionRecord.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
                 NestedExceptionRecord.ExceptionRecord = ExceptionRecord;
                 NestedExceptionRecord.NumberParameters = Status;

                 /* Raise the exception */
                 RtlRaiseException(&NestedExceptionRecord);
    }

    BOOLEAN
    NTAPI
    RtlDispatchException(IN PEXCEPTION_RECORD ExceptionRecord,
                                  IN PCONTEXT Context)
    {
                 PEXCEPTION_REGISTRATION_RECORD RegistrationFrame, NestedFrame = NULL;
                 DISPATCHER_CONTEXT DispatcherContext;
                 EXCEPTION_RECORD ExceptionRecord2;
                 EXCEPTION_DISPOSITION Disposition;
                 ULONG_PTR StackLow, StackHigh;
                 ULONG_PTR RegistrationFrameEnd;

                 /* Get the current stack limits and registration frame */
                 RtlpGetStackLimits(&StackLow, &StackHigh);
                 RegistrationFrame = RtlpGetExceptionList();

                 /* Now loop every frame */
                 while (RegistrationFrame != EXCEPTION_CHAIN_END)
                 {
                     /* Find out where it ends */
                     RegistrationFrameEnd = (ULONG_PTR)RegistrationFrame +
                                             sizeof(EXCEPTION_REGISTRATION_RECORD);

                     /* Make sure the registration frame is located within the stack */
                     if ((RegistrationFrameEnd > StackHigh) ||
                         ((ULONG_PTR)RegistrationFrame < StackLow) ||
                         ((ULONG_PTR)RegistrationFrame & 0x3))
                     {
                         /* Check if this happened in the DPC Stack */
                         if (RtlpHandleDpcStackException(RegistrationFrame,
                                                         RegistrationFrameEnd,
                                                         &StackLow,
                                                         &StackHigh))
                         {
                             /* Use DPC Stack Limits and restart */
                             continue;
                         }

                         /* Set invalid stack and return false */
                         ExceptionRecord->ExceptionFlags |= EXCEPTION_STACK_INVALID;
                         return FALSE;
                     }

                     /* Check if logging is enabled */
                     RtlpCheckLogException(ExceptionRecord,
                                           Context,
                                           RegistrationFrame,
                                           sizeof(*RegistrationFrame));

                     /* Call the handler */
                     Disposition = RtlpExecuteHandlerForException(ExceptionRecord,
                                                                  RegistrationFrame,
                                                                  Context,
                                                                  &DispatcherContext,
                                                                  RegistrationFrame->
                                                                  Handler);

                     /* Check if this is a nested frame */
                     if (RegistrationFrame == NestedFrame)
                     {
                         /* Mask out the flag and the nested frame */
                         ExceptionRecord->ExceptionFlags &= ~EXCEPTION_NESTED_CALL;
                         NestedFrame = NULL;
                     }

                     /* Handle the dispositions */
                     switch (Disposition)
                     {
                         /* Continue searching */
                         case ExceptionContinueExecution:

                             /* Check if it was non-continuable */
                             if (ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
                             {
                                 /* Set up the exception record */
                                 ExceptionRecord2.ExceptionRecord = ExceptionRecord;
                                 ExceptionRecord2.ExceptionCode =
                                     STATUS_NONCONTINUABLE_EXCEPTION;
                                 ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
                                 ExceptionRecord2.NumberParameters = 0;

                                 /* Raise the exception */
                                 RtlRaiseException(&ExceptionRecord2);
                             }
                             else
                             {
                                 /* Return to caller */
                                 return TRUE;
                             }

                         /* Continue searching */
                         case ExceptionContinueSearch:
                             break;

                         /* Nested exception */
                         case ExceptionNestedException:

                             /* Turn the nested flag on */
                             ExceptionRecord->ExceptionFlags |= EXCEPTION_NESTED_CALL;

                             /* Update the current nested frame */
                             if (DispatcherContext.RegistrationPointer > NestedFrame)
                             {
                                 /* Get the frame from the dispatcher context */
                                 NestedFrame = DispatcherContext.RegistrationPointer;
                             }
                             break;

                         /* Anything else */
                         default:

                             /* Set up the exception record */
                             ExceptionRecord2.ExceptionRecord = ExceptionRecord;
                             ExceptionRecord2.ExceptionCode = STATUS_INVALID_DISPOSITION;
                             ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
                             ExceptionRecord2.NumberParameters = 0;

                             /* Raise the exception */
                             RtlRaiseException(&ExceptionRecord2);
                             break;
                     }

                     /* Go to the next frame */
                     RegistrationFrame = RegistrationFrame->Next;
                 }

                 /* Unhandled, return false */
                 return FALSE;
    }

      然后我们可以为需要保护的进程Hook KiUserExceptionDispatcher,在这里面检测Handler是否安全,我能想到的可能不太安全的Handler有四种情况,也许有更多,我只简单的实现了第一个策略(就是遍历一下SEH链),下面是相关的代码片段。

    //SEHChecker.cpp

    inline DWORD __fastcall GetFsDword(DWORD dwOffset)
    {
    __asm mov eax,DWORD PTR fs:[ecx]
    }

    //策略:
    //1. Handler在栈区域
    //2. Handler在堆区域
    //3. Handler在全局数据区
    //4. Handler在正常的代码页中,但第一条指令是jmp xxx,或者Handler前一段是不影响ShellCode的指令,后面带有一个jmp xxx,跳到Handler中,怎么检测?

    BOOL AnyUnsafeHandler(void)
    {
    struct SEHChain{
              SEHChain *pNext;
              void *pHandler;
    };

    SEHChain* pChain=(SEHChain*)GetFsDword(0);
    DWORD dwStackBase=GetFsDword(4);
    DWORD dwStackLimit=GetFsDword(8);

    BOOL bRet=FALSE;

    do
    {
              bRet=((DWORD)(pChain->pHandler)>=dwStackLimit)&&((DWORD)(pChain->pHandler)<=dwStackBase);
              pChain=pChain->pNext;
    }while(!bRet&&(pChain!=(SEHChain*)-1));

    return bRet;
    }

    VOID
    WINAPI
    HookedUserExceptionDispatcher(PEXCEPTION_RECORD ExceptionRecord,
                                      PCONTEXT Context)
    {
    if(AnyUnsafeHandler())
    {
              ExitThread(0);
    }

    TrampolineUserExceptionDispatcher(ExceptionRecord,Context);
    }

      在检测到非安全的Handler时我为什么要用ExitThread呢,因为基于TIB的Seh Chain是线程相关的,它不是Final型的SEH Handler(不懂的参考一下Hume大侠的经典文章<<SEH in ASM>>),所以直接用ExitThread把可能出现危险的线程给退掉,如果是主线程则

    Copyright@ 2003-2008 www.soft2008.com.cn All Right Reserved
    京ICP备06062655号
    分类导航