/SafeSEH编译选项 : 原理及绕过技术浅析

2024-02-05 19:58

本文主要是介绍/SafeSEH编译选项 : 原理及绕过技术浅析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

/SafeSEH编译选项

x86 : XP支持/Win7支持

x64 : 不支持


摘要:主要介绍SafeSEH的基本原理和SafeSEH的绕过技术,重点在原理介绍。

关键词:SafeSEH;绕过技术;异常处理

 

目录

前言

SafeSEH的保护原理

(1)      二进制层面

(2)      系统层面

怎么关掉编译器的SafeSEH支持

怎样检测一个PE文件是否启用了SafeSEH

绕过方法简介

参考文献

 

前言

设计SafeSEH保护机制的目的,以为了防止那种攻击者通过覆盖堆栈上的异常处理函数句柄,从而控制程序执行流程的攻击。

Windwos XP SP2之后,微软就已经引入了SafeSEH技术。不过由于SafeSEH需要编译器在编译PE文件时进行特殊支持才能发挥作用,而xpsp2下的系统文件基本都是不支持SafeSEH的编译器编译的,因此在xpsp2下,SafeSEH还没有发挥作用(VS2003及更高版本的编译器中已经开始支持)。

Vista开始,由于系统PE文件基本都是由支持SafeSEH的编译器编译的,因此从Vista开始,SafeSEH开始发挥他强大的作用,对于以前那种简单的通过覆盖异常处理句柄的漏洞利用技术,也就基本失效了。

 

SafeSEH的保护原理

SafeSEH的基本原理很简单,即在调用异常处理函数之前,对要调用的异常处理函数进行一系列的有效性校验,如果发现异常处理函数不可靠(被覆盖了,被篡改了),立即终止异常处理函数的调用。不过SafeSEH需要编译器和系统双重支持,缺少一个则保护能力基本就丧失了。下面从两个方面来阐述怎样来实现SafeSEH

1)二进制层面

首先我们先看看编译器做了些什么事情(通过启用链接选项/SafeSEH即可使编译出来的二进制文件具备SafeSEH功能,微软VS2003及以后的编译器已经默认支持)。在编译器生成二进制IMAGE的时候,把所有合法的SEH函数的地址解析出来,在IMAGE里生成一张合法的SEH函数表,用于异常处理时候进行严格的匹配检查。可以使用VC下面的dumpbin工具查看一个二进制文件的config信息,这样调用dumpbin /loadconfig file_all_path_filename

输出的可能是下面这样(注:tttt.exe使用vs2005编译):

Dump of file H:\Prj_N\tttt\Release\tttt.exe

 

File Type: EXECUTABLE IMAGE

 

  Section contains the following load config:

 

            00000048 size

                   0 time date stamp

                0.00 Version

                   0 GlobalFlags Clear

                   0 GlobalFlags Set

                   0 Critical Section Default Timeout

                   0 Decommit Free Block Threshold

                   0 Decommit Total Free Threshold

            00000000 Lock Prefix Table

                   0 Maximum Allocation Size

                   0 Virtual Memory Threshold

                   0 Process Heap Flags

                   0 Process Affinity Mask

                   0 CSD Version

                0000 Reserved

            00000000 Edit list

            00403018 Security Cookie

            00402360 Safe Exception Handler Table

                   1 Safe Exception Handler Count

 

    Safe Exception Handler Table

 

          Address

          --------

          004018A1  __except_handler4

 

  Summary

 

        1000 .data

        1000 .rdata

        1000 .rsrc

        1000 .text

 

注意里面加粗标红的部分,这就是该二进制文件里面的SEH异常处理函数地址表。上面的输出实际上涉及如下的一个结构,是保存在二进制文件里面的一份配置表:

#include <windows.h>

extern DWORD_PTR __security_cookie;  /* /GS security cookie */

 

/*

 * The following two names are automatically created by the linker for any

 * image that has the safe exception table present.

*/

 

extern PVOID __safe_se_handler_table[]; /* base of safe handler entry table */

extern BYTE  __safe_se_handler_count;  /* absolute symbol whose address is

                                           the count of table entries */

typedef struct {

    DWORD       Size;

    DWORD       TimeDateStamp;

    WORD        MajorVersion;

    WORD        MinorVersion;

    DWORD       GlobalFlagsClear;

    DWORD       GlobalFlagsSet;

    DWORD       CriticalSectionDefaultTimeout;

    DWORD       DeCommitFreeBlockThreshold;

    DWORD       DeCommitTotalFreeThreshold;

    DWORD       LockPrefixTable;            // VA

    DWORD       MaximumAllocationSize;

    DWORD       VirtualMemoryThreshold;

    DWORD       ProcessHeapFlags;

    DWORD       ProcessAffinityMask;

    WORD        CSDVersion;

    WORD        Reserved1;

    DWORD       EditList;                   // VA

    DWORD_PTR   *SecurityCookie;

    PVOID       *SEHandlerTable;

    DWORD       SEHandlerCount;

} IMAGE_LOAD_CONFIG_DIRECTORY32_2;

 

const IMAGE_LOAD_CONFIG_DIRECTORY32_2 _load_config_used = {

    sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_2),

    0,

    0,

    0,

    0,

    0,

    0,

    0,

    0,

    0,

    0,

    0,

    0,

    0,

    0,

    0,

    0,

    &__security_cookie,

    __safe_se_handler_table,

    (DWORD)(DWORD_PTR) &__safe_se_handler_count

};

 

2)系统层面

基本过程如下(XP SP2VISTA一样)。

加载准备过程:

加载PE文件时,定位和读出合法SEH函数表的地址(如果该IMAGE是不支持SafeSEH的,则这个SEH函数表的地址为0),并使用共享内存中的一个随机数加密。将加密后的SEH函数表地址,IMAGE的开始地址,IMAGE的长度,合法SEH函数的个数,作为一条记录放入ntdllntdll模块是进行异常分发的模块)的加载模块数据内存中。

异常发生后,异常处理过程如下(RtlDispatchException框架伪码):

void RtlDispatchException(...)

{

if (exception record is not on the stack)

goto corruption;

if (handler is on the stack)

goto corruption;

if (RtlIsValidHandler(handler, process_flags) == FALSE)

goto corruption;

// execute handler

RtlpExecuteHandlerForException(handler, ...)

...

}

RtlDispatchException()这个函数的检测主要分三步,首先检查异常处理节点是否在栈上,如果不在栈上程序将终止异常处理,其次检查异常处理句柄是否在栈上,如果在栈上程序将止异常处理,这两个检测可以防止那种在堆上伪造异常链和把shellcode放置在栈上的情况。最后检测handler的有效性,这才是SafeSEH的重点。

下面看一下RtlIsValidHandler的伪码(vista sp1):

BOOL RtlIsValidHandler(handler)

{

if (handler is in an image)

{

         // 在加载模块的进程空间

if (image has the IMAGE_DLLCHARACTERISTICS_NO_SEH flag set)

return FALSE; // 该标志设置,忽略异常处理,直接返回FALSE

if (image has a SafeSEH table) // 是否含有SEH

if (handler found in the table)

return TRUE; // 异常处理handle在表中,返回TRUE

else

return FALSE; // 异常处理handle不在表中,返回FALSE

if (image is a .NET assembly with the ILonly flag set)

return FALSE; // .NET 返回FALSE

// fall through

}

 

if (handler is on a non-executable page)

{

         // handle在不可执行页上面

if (ExecuteDispatchEnable bit set in the process flags)

return TRUE; // DEP关闭,返回TRUE;否则抛出异常

else

raise ACCESS_VIOLATION; // enforce DEP even if we have no hardware NX

}

 

if (handler is not in an image)

{

         // 在加载模块内存之外,并且是可执行页

if (ImageDispatchEnable bit set in the process flags)

return TRUE; // 允许在加载模块内存空间外执行,返回验证成功

else

return FALSE; // don't allow handlers outside of images

}

 

// everything else is allowed

return TRUE;

}

 

对上面的伪码的理解,请看代码注释和流程图:

伪码里面的ExecuteDispatchEnableImageDispatchEnable位标志是内核KPROCESS结构的一部分,这两个位用来控制当异常处理函数在不可以执行内存或者不在异常模块的映像(IMAGE)内时,是否执行异常处理函数。这两个位的值可以在运行时修改,不过默认情况下如果进程的DEP被关闭,则这两个位置1,如果进程的DEP是开启状态,则这两个位被置0

在进程的DEP是开启的情况,有两种异常处理函数被异常分发器认为是有效的:

a)异常处理函数在进程映像的SafeSEH表中,并且没有NO_SEH标志。

b)异常处理函数在进程映像的可执行页,并且没有NO_SEH标志,没有SafeSEH表,没有.NETILonly标志。

在进程的DEP关闭的情况下,有三种情况异常处理函数被异常分发器认为是有效的:

a)异常处理函数在进程映像的SafeSEH表中,并且没有NO_SEH标志。

b)异常处理函数在进程映像的可执行页,并且没有NO_SEH标志,没有SafeSEH表,没有.NETILonly标志。

c)异常处理函数不在当前进程的映像里面,但是不在当前线程的堆栈上。

这里的伪码是非常简单的,还有很多值得探讨的问题,譬如ntdll里面,当前异常处理句柄是怎么和SEH表进行对比的等等,不过这暂时不列入讨论。

 

怎么关掉编译器的SafeSEH支持

虽然我不知道你为什么要这么做,而且我觉得很疯狂,但是方法还是有的,在编译器的属性框Liker|CommandLineAdditional options 加入/SAFESEH:NO即可,见下图。

 

怎样检测一个PE文件是否启用了SafeSEH

前面介绍过数据目录里面的一个结构(IMAGE_LOAD_CONFIG_DIRECTORY),该结构的成员SEHandlerTable是指向合法SEH处理程序地址列表的指针,成员SEHandlerCount是数目。而IMAGE_LOAD_CONFIG_DIRECTORY这个结构体只有/SAFESEH选项设置了才存在,因此,就可以根据它来判断PE文件是否加了/SAFESEH链接选项。这个结构在PE中的偏移由PE附加头IMAGE_DATA_DIRECTORY 数组的第11项指定。

#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG  10  // Load Configuration Directory

 

绕过方法简介

1)利用堆地址覆盖SEH结构绕过SafeSEH

上面讲过,在禁用DEP的进程中,异常分发器允许SEH handler位于除栈空间之外的非映像页面。也就是说我们可以把shellcode放置在堆中,然后通过覆盖SEH跳至堆空间以执行shellcode,这样即可绕过SafeSEH保护。

 

2)利用没有启用SafeSEH保护的模块绕过SafeSEH

在介绍原理时讲过,在国内,目前大部分的PC都是安装的Windows XP,也就是说对于大部分Windows操作系统,其系统模块都没有受到SafeSEH保护,可以选用未开启SafeSEH保护的模块来利用,另外,现在还有很多VC6编译的软件,这些软件本身和自带的dll文件,都是可能没有SafeSEH保护的。这时就可以使用它里面的指令作为跳板来绕过SafeSEH

3)利用加载模块之外的地址绕过SafeSEH

同样是根据SafeSEH的原理可知,对于加载模块之外的地址,SafeSEH同样是不进行有效性检测的(当然假设是DEP是关闭的,或者DEP已经被绕过)。

注:绕过方法这里没有细讲,原因是没有找到很好的例子,在《0day安全:软件漏洞分析技术》上面有自己书籍作者自己写的例子。以后这块再详说。

 

如何使用dumpbin.exe检测是否开启 : 

系统环境变量Path添加C:\Program Files (x86)\Microsoft Visual Studio 8\VC\bin和C:\Program Files (x86)\Microsoft Visual Studio 8\Common7\IDE


参考文献

[1] Preventing the Exploitation of Structured Exception Handler (SEH) Overwrites with SEHOP

http://blogs.technet.com/b/srd/archive/2009/02/02/preventing-the-exploitation-of-seh-overwrites-with-sehop.aspx(可以列入翻译计划)

[2] SafeSEH笔记http://pstgroup.blogspot.com/2007/08/tipssafeseh.html

[3] /SAFESEH (Image has Safe Exception Handlers)

http://msdn.microsoft.com/en-us/library/9a89h429(VS.80).aspx

[4] 0day安全:软件漏洞分析技术(第二版)

[5] Bypassing Browser Memory Protections

[6] pecoff_v8

这篇关于/SafeSEH编译选项 : 原理及绕过技术浅析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/681998

相关文章

Redis主从/哨兵机制原理分析

《Redis主从/哨兵机制原理分析》本文介绍了Redis的主从复制和哨兵机制,主从复制实现了数据的热备份和负载均衡,而哨兵机制可以监控Redis集群,实现自动故障转移,哨兵机制通过监控、下线、选举和故... 目录一、主从复制1.1 什么是主从复制1.2 主从复制的作用1.3 主从复制原理1.3.1 全量复制

Redis主从复制的原理分析

《Redis主从复制的原理分析》Redis主从复制通过将数据镜像到多个从节点,实现高可用性和扩展性,主从复制包括初次全量同步和增量同步两个阶段,为优化复制性能,可以采用AOF持久化、调整复制超时时间、... 目录Redis主从复制的原理主从复制概述配置主从复制数据同步过程复制一致性与延迟故障转移机制监控与维

SpringCloud配置动态更新原理解析

《SpringCloud配置动态更新原理解析》在微服务架构的浩瀚星海中,服务配置的动态更新如同魔法一般,能够让应用在不重启的情况下,实时响应配置的变更,SpringCloud作为微服务架构中的佼佼者,... 目录一、SpringBoot、Cloud配置的读取二、SpringCloud配置动态刷新三、更新@R

Redis主从复制实现原理分析

《Redis主从复制实现原理分析》Redis主从复制通过Sync和CommandPropagate阶段实现数据同步,2.8版本后引入Psync指令,根据复制偏移量进行全量或部分同步,优化了数据传输效率... 目录Redis主DodMIK从复制实现原理实现原理Psync: 2.8版本后总结Redis主从复制实

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

【专题】2024飞行汽车技术全景报告合集PDF分享(附原数据表)

原文链接: https://tecdat.cn/?p=37628 6月16日,小鹏汇天旅航者X2在北京大兴国际机场临空经济区完成首飞,这也是小鹏汇天的产品在京津冀地区进行的首次飞行。小鹏汇天方面还表示,公司准备量产,并计划今年四季度开启预售小鹏汇天分体式飞行汽车,探索分体式飞行汽车城际通勤。阅读原文,获取专题报告合集全文,解锁文末271份飞行汽车相关行业研究报告。 据悉,业内人士对飞行汽车行业

hdu4407(容斥原理)

题意:给一串数字1,2,......n,两个操作:1、修改第k个数字,2、查询区间[l,r]中与n互质的数之和。 解题思路:咱一看,像线段树,但是如果用线段树做,那么每个区间一定要记录所有的素因子,这样会超内存。然后我就做不来了。后来看了题解,原来是用容斥原理来做的。还记得这道题目吗?求区间[1,r]中与p互质的数的个数,如果不会的话就先去做那题吧。现在这题是求区间[l,r]中与n互质的数的和

金融业开源技术 术语

金融业开源技术  术语 1  范围 本文件界定了金融业开源技术的常用术语。 本文件适用于金融业中涉及开源技术的相关标准及规范性文件制定和信息沟通等活动。

maven 编译构建可以执行的jar包

💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」👈,「stormsha的知识库」👈持续学习,不断总结,共同进步,为了踏实,做好当下事儿~ 专栏导航 Python系列: Python面试题合集,剑指大厂Git系列: Git操作技巧GO