首页 电脑 电脑学堂 查看内容

SEH溢出利用检测思路

2009-2-12 05:29 805 0

摘要: 看到安焦上的一篇《基于栈指纹检测缓冲区溢出的一点思路》,这是在ShellCode已经运行时在它的调用堆栈(被Hook的下级调用函数LoadLibrary)里进行检测,有...
关键词: nbsp ExceptionRecord EXCEPTION RegistrationFram Handler Context ExceptionFlags RECORD NestedExceptionR NONCONTINUABLE

看到安焦上的一篇《基于栈指纹检测缓冲区溢出的一点思路》,这是在ShellCode已经运行时在它的调用堆栈(被Hook的下级调用函数LoadLibrary)里进行检测,有些利用溢出覆盖SEH Handler,然后任程序运行,因为溢出破坏了堆或栈,肯定会出现异常,这时指向ShellCode的Handler被运行,我在想这一类的溢出利用,既然它想运行,那首先要过操作系统的异常派遣这一关,如果在分派异常时我们就对SEH Handler进行一下检测,或许能在ShellCode运行前就发现它。     我简单看了一下SEH处理流程,一直跟到这两个函数,因为wrk代码不全,所以我选取ReactOS的代码,但并不影响理解。 以下代码来自ReactOS,版权归原作者 VOIDNTAPIKiUserExceptionDispatcher(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);} BOOLEANNTAPIRtlDispatchException(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;} VOIDWINAPIHookedUserExceptionDispatcher(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把可能出现危险的线程给退掉,如果是主线程则进程会退出,需要的话同时记录一下日志,以供管理员分析受攻击情况。  
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系 [邮箱地址] 删除

路过

雷人

握手

鲜花

鸡蛋

最新评论

返回顶部