简单演示Exploit SEH原理(未开启SafeSEH模块)
前面写了不少SEH相关文章,这里来个复杂点的栈溢出SEH。文章不重复解释SEH运行原理,但对主要步骤加以调试和注释,另外本文参考考了雪上 Exploit 编写系列教程第三篇_基于SEH的Exploit 一文。一般来说Exploit重在溢出,不过本文旨在是演示Exploit SEH的原理,因此省略溢出过程直接在栈上修改。
程序源码(vc++6.0 Debug版本)如下:
#include<windows.h>int ExceptionHandler(void);
void Fake_Handler();
void FakeShellcode();int main(int argc,char *argv[])
{__try{__asm -->这段__asm代码用于修改栈上的_EXCEPTION_REGISTRATION结构{mov edx,ebp;sub edx,0x10;mov DWORD ptr [edx],0x909006EB;lea eax,Fake_Handler;mov DWORD ptr [edx+4],eax;mov BYTE ptr [edx+8],0xE9;lea ebx,[edx+8];lea ecx,FakeShellcode;sub ecx,5;mov eax,ecx;sub eax,ebx;mov DWORD ptr [edx+9],eax;}__asm -->这段代码用于产生异常{xor eax,eax;mov [eax],eax;}}__except(ExceptionHandler()){}return 0;
}__declspec(naked) void Fake_Handler()
{__asm --> 当异常发生,控制流将进入ExceptHandle,pop/pop/ret指令流使控制流从ExcepHandle跳转到当前栈中_EXCEPTION_REGISTRATION_RECORD结构执行{pop edi;pop esi;ret;}
}__declspec(naked) void FakeShellcode()
{MessageBox(NULL,"","",MB_OK);
}int ExceptionHandler(void)
{return 0;
}
前面的博文"vc++6对windows SEH扩展分析"提到vc++对SEH机制做了扩展,将_EXCEPTION_REGISTRATION_RECORD结构
struct _EXCEPTION_REGISTRATION_RECORD { struct _EXCEPTION_REGISTRATION *prev; void (*handler)(PEXCEPTION_RECORD, PEXCEPTION_REGISTRATION, PCONTEXT, PEXCEPTION_RECORD);
}
扩展为
struct _EXCEPTION_REGISTRATION { struct _EXCEPTION_REGISTRATION *prev; void (*handler)(PEXCEPTION_RECORD, PEXCEPTION_REGISTRATION, PCONTEXT, PEXCEPTION_RECORD); struct scopetable_entry *scopetable; int trylevel; int _ebp; PEXCEPTION_POINTERS xpointers; };
代码L13,L14借由_EXCEPTION_REGISTRATION!_ebp的地址减去一个偏移,定位到_EXCEPTION_REGISTRATION!prev的地址,这同时是当前函数栈中的 EXCEPTION_REGISTRATION异常处理结构的起始地址。后面的代码全是在这个结构上倒腾~
L16,L17将EXCEPTION_REGISTRATION!handler从__except_handler3修改为Fake_Handler。
下面的代码片段是仅修改函数异常处理结构_EXCEPTION_REGISTRATION!prev后的栈内存状态。栈内存0012FF40处的0x4012A0为__except_handler3:
0012FF38 EB 06 90 90 A0 12 40 00 ..悙..@.
0012FF40 20 F0 41 00 00 00 00 00 餉.....
0012FF48 88 FF 12 00 69 14 40 00
mapfile内容:
0001:00000291 __NLG_Dispatch 00401291 f LIBCD:exsup.obj0001:000002a0 __except_handler3 004012a0 f LIBCD:exsup3.obj0001:0000035d __seh_longjmp_unwind@4 0040135d f LIBCD:exsup3.obj
可能有人会疑惑,为什么要把_EXCEPTION_REGISTRATION!prev改为"EB 06 90 90"?这是因为后面异常进入Fake_Handler后,执行pop/pop/ret指令流,跳转到_EXCEPTION_REGISTRATION!prev所在的地址。再往后,eip将在_EXCEPTION_REGISTRATION!prev所在的地址取指令运行。如果不修改,这块区域保存了下一个_EXCEPTION_REGISTRATION结构的地址值,鬼晓得这个地址值将是怎样的代码流。所以要改为一段有用的代码;理论上可以是任意可执行的代码,但是EXCEPTION_REGISTRATION!handler保存的是Fake_Handler的地址,如果_EXCEPTION_REGISTRATION!prev处的代码被改为顺序执行的指令,势必会执行到EXCEPTION_REGISTRATION!handler所在的地址。因此需要用跳转指令跳过EXCEPTION_REGISTRATION!handler。
模拟handler覆盖后EXCEPTION_REGISTRATION!handler内容:
0012FF38 EB 06 90 90 0F 10 40 00 ..悙..@.
0012FF40 20 F0 41 00 00 00 00 00 餉.....
0012FF48 88 FF 12 00 69 14 40 00
0x40100F是ILT增量连接表中跳转到Fake_Handler函数的地址
当异常发生,系统首先到fs:[0]队列中找最近节点的异常处理函数,如果处理不了,则通过该节点的prev域找次近节点处理。现在就假设出现一个异常,那得到执行的将是被覆盖了的Fake_Handler函数。我们跟着系统的节奏进入Fake_Handler函数。
Fake_Handler是个裸函数,进入该函数后,编译器并没有自作主张的生成函数帧,因此在执行L41前堆栈的布局如下:
[esp]:Fake_Handler
[esp+4]:_EXCEPTION_RECORD
[esp+8]:EXCEPTION_REGISTER
[esp+0x0C]:_CONTEXT *pContex
[esp+0x10]:pDispatcherContext
当然,你们可以质疑我是不是在大力乱神,我可以证明之:
进入该函数时堆栈布局:
0012FAFC B9 72 F2 76 E4 FB 12 00 箁騰潲..
0012FB04 38 FF 12 00 00 FC 12 00 8.......
0012FB0C B8 FB 12 00
内存0012FAFC处的0x76F272B9看着像是ntdll中的地址,
看下0x12FBE4处的内容,理应是_EXCEPTION_RECORD结构,里面至少包含了出错信息和出错指令地址:
0012FBE4 05 00 00 C0 00 00 00 00 ........
0012FBEC 00 00 00 00 99 10 40 00 ......@.
其内容是0xC0000005,违例访存和0x401099:mov [eax],eax所在指令地址:
30: xor eax,eax;
00401097 xor eax,eax
31: mov [eax],eax;
00401099 mov dword ptr [eax],eax
32: }
33: }
到这,可以确定堆栈上参数如上面假设那样。好,经过L41,L42的 两个pop操作目前esp指向了EXCEPTION_REGIST结构---- 当前被展开的异常处理节点,这个节点的内容已经被溢出覆盖。当执行L43 ret指令时会发生什么?想象一下pop eip,将esp的内容传递给eip,之后eip去那取指令运行。
看看执行ret时,esp指向:
ESI = 0012FBE4 EDI = 76F272B9EIP = 00401102 ESP = 0012FB04
...
0012FB04 38 FF 12 00 00 FC 12 00 8.......
0x12FB04处的内容是0x12FF38,跟过去看看:
0012FF38 EB 06 90 90 0F 10 40 00 ..悙..@.
0012FF40 E9 C0 10 2D 00 00 00 00 槔.-....
0012FF48 88 FF 12 00 69 14 40 00 ....i.@.
如果一时记不起这块内存的内容,麻烦向上滚动一下鼠标滚轮,前面提到这是程序开始时,由编译器在函数堆栈上安置的 EXCEPTION_REGIST异常处理节点!!
很明显了,eip将去堆栈上取指令运行,而且取出的指令来自EXCEPTION_REGIST!prev所在(已经被溢出覆盖)。
回到main函数L15处这里的指令mov DWORD ptr [edx],0x909006EB;将原本prev的内容改成一条跳转语句jmp 06和两个nop的opcode。这有个问题为什么是jmp 06?一般Exploit SEH溢出后内存布局是:buff+next SEH 的地址(被jmp 06;nop;nop代替)+SEH Handler 的地址(4字节地址,在本文被Fake_Handler覆盖)+shellcode。jmp 06执行时,eip指向第一个nop指令,jmp 06执行后,eip跳过中间2B的nop填充指令和4B被覆盖的异常处理过程函数的地址,之后eip直接去shellcode中取指运行~
main函数中在后面的内容已经没有解释的必要了,本篇完~
相关链接:
1.Exploit 编写系列教程第三篇_基于SEH的Exploit(+3b)
2.绕过SEHOP安全机制
简单演示Exploit SEH原理(未开启SafeSEH模块)相关推荐
- Oday安全 11.5利用未启用SafeSEH模块绕过SafeSEH一节注记
Oday安全一书的内容越往后越深奥,不得不做些注记备忘. 1.书P297 插图11.5.6写道__except函数地址根据EBP-4的值得出.这是目前为止,书中写的最含糊的地方,需要展开讨论一下.参考 ...
- java 反序列化 ysoserial exploit/JRMPClient 原理剖析
目录 0 前言 1 payloads/JRMPListener 1.1 payload生成 1.2 gadget链分析 2 使用RMIRegistryExploit攻击上述开启的监听 3 exploi ...
- XpSp3(未开启PAE模式)内存管理之系统PTE区域 上
前言 几年前就已经看过wrk中关于内存管理和缓存管理的实现,由于当时对内核调试尚不熟悉,因此仅仅停留在代码层面.现在结合windbg操作,希望能有新的收获.毛德操的<windows内核情景分析& ...
- java 反序列化 ysoserial exploit/JRMPListener 原理剖析
目录 0 前言 1 payloads/JRMPClient 1.1 Externalizable 1.2 生成payload 1.3 gadget链分析 2 exploit/JRMPListener ...
- Qt 多线程的简单演示
Qt 多线程的简单演示 任务:单击""开始""按钮将启动数个工作线程,线程由checkBox 来决定线程的启用或者关闭,各个线程循环打印数字,数字每秒累加1:单 ...
- 面试官: Flink双流JOIN了解吗? 简单说说其实现原理
摘要:今天和大家聊聊Flink双流Join问题.这是一个高频面试点,也是工作中常遇到的一种真实场景. 本文分享自华为云社区<万字直通面试:Flink双流JOIN>,作者:大数据兵工厂 . ...
- csrf攻击与防护—2用flask简单演示防范csrf攻击之referer
接上篇 csrf攻击与防护-1用flask简单演示csrf攻击 以下是根据referer字段防止csrf攻击的新代码: bank.py import uuid from flask import re ...
- Java简单演示悲观锁
每博一文案 看过这样一句话,时间在不断的筛选你身边的人和事.当你什么都不在乎的时候,你的人生才刚刚开始. 当我们什么都不在乎时,会发现什么都可能会进行经历,但什么都一定会过去,起始都会离开的或早或晚吧 ...
- 【组合数学】鸽巢原理 ( 鸽巢原理简单形式 | 鸽巢原理简单形式示例 1、2、3 )
文章目录 一.鸽巢原理简单形式 二.鸽巢原理简单形式示例 1 三.鸽巢原理简单形式示例 2 四.鸽巢原理简单形式示例 3 一.鸽巢原理简单形式 鸽巢原理 : 将 n+1n + 1n+1 个物体 放到 ...
最新文章
- Linux 组调度学习
- [组图]海报:计算机的爱
- 微信小程序 weui 使用方法
- Go标准库Context
- 2021年高考成绩什么时候查询辽宁,2021年辽宁高考成绩什么时候几点可以查
- print('HelloWorld'),Python为你打开一扇门。
- 结构体,文件操作,指针,简单练习
- 7. where loop
- 那些年,我深爱着的PPT
- 为什么WiFi自动信道选到的信道多数在1/6/11
- 利用CVE-2017-8464漏洞制作病毒U盘攻击
- 精英阶层是怎么心甘情愿被收割的
- IDLE Help | 汉化
- 全球及中国吊链总成行业研究及十四五规划分析报告
- [2009][note]构成理想导体超材料的有源THz欺骗表面等离子激元开关——
- 计算机如何添加新用户,Win10创建新用户图文教程 Win10怎么新建账户
- 【论文精读】基于周期编码深度自编码器的心肺音盲单耳声源分离
- 切换输入法导致程序死机的解决办法
- windowsxp最新版本_雨林木风U盘装系统启动盘制作工具更新最新版9.0
- java unparseable_java.text.ParseException: Unparseable date: 2015-06-09 hh:56:19
热门文章
- Spring Boot2 总结(二) Spring Security的基本配置
- storyboard搭建项目_轻松搞定一人一个storyboard开发
- 一行代码让你伪装成黑客惊艳世人
- 从程序员代码“呼救”大破传销组织_聊掌握一门编程语言的重要性!
- MKS TinyBee V1.0使用说明书
- 老闪创业那些事儿(外传)——流程下的工具人
- 单指标时间序列异常检测——基于重构概率的变分自编码(VAE)代码实现(详细解释)
- IT项目管理中projects、programs和portfolio之间的关系,附带operations以及OPM之间的关系分析
- MFC程序逆向 – 消息篇(上)+(下)
- 治疗狗狗常见疾病药品备忘