4.1 堆溢出初探

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

“这周末大家过得还不错吧?”老师问道。

“好!”大家齐声回答。

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

“你帮我发给老师了吗?”小倩小声的问旁边的宇强。

“嗯,放心吧!发了。”宇强说。

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

老师歇了下说道:“当然,也有些轻松提取ShellCode的方法,等大家知识进一步巩固后,我再教给大家!”

“哦,那今天讲什么呢?”古风急着问道。

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

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

“是的。”

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

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

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

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

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

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

小知识:和“堆”有关的几个API函数

HeapAlloc 在堆中申请内存空间

HeapCreate 创建一个新的堆对象

HeapDestroy 销毁一个堆对象

HeapFree 释放申请的内存

HeapWalk 枚举堆对象的所有内存块

GetProcessHeap 取得进程的默认堆对象

GetProcessHeaps 取得进程所有的堆对象

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

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

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