首页 | 安全文章 | 安全工具 | Exploits | 本站原创 | 关于我们 | 网站地图 | 安全论坛
  当前位置:主页>安全文章>文章资料>网络安全>文章内容
覆盖SEH的溢出利用检测思路
来源:kruglinski_at_sohu.com 作者:kruglinski 发布时间:2007-08-22  
看到安焦上的一篇《基于栈指纹检测缓冲区溢出的一点思路》,这是在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把可能出现危险的线程给退掉,如果是主线程则进程会退出,需要的话同时记录一下日志,以供管理员分析受攻击情况。
 
[推荐] [评论(0条)] [返回顶部] [打印本页] [关闭窗口]  
匿名评论
评论内容:(不能超过250字,需审核后才会公布,请自觉遵守互联网相关政策法规。
 §最新评论:
  热点文章
·一句话木马
·samcrypt.lib简介
·教你轻松查看QQ空间加密后的好友
·web sniffer 在线嗅探/online ht
·SPIKE与Peach Fuzzer相关知识
·asp,php,aspx一句话集合
·Cisco PIX525 配置备忘
·用Iptables+Fedora做ADSL 路由器
·检查 Web 应用安全的几款开源免
·Md5(base64)加密与解密实战
·NT下动态切换进程分析笔记
·风险评估中的渗透测试
  相关文章
·反向进程注入及隐藏--动手做一个
·反病毒引擎设计之可控制引擎流
·常见php页面漏洞分析及相关问题
·企业无线局域网安全
·入侵检测系统逃避技术和对策的介
·BIOS Rootkit:Welcome home,my L
·基于Filter-Hook Driver(使用ip
·程序从DOS/bios驻留内存到WINNT
·代码逆向乱谈之导引
·对抗启发式代码仿真检测技术分析
·WEB漏洞挖掘技术
·设计安全的文件上传功能
  推荐广告
CopyRight © 2002-2022 VFocuS.Net All Rights Reserved