缓冲区溢出系列一之从内存窥视缓冲区溢出攻击的机理
一个应用程序在运行时,它在内存中的映像可以分为三个部分: 代码段 ,数据段和堆栈段.
1.代码段对应于运行文件中的文本区(Text Section ),其中存放着程序的代码, 这个段在内存中一般被标记为只读 , 任何企图修改这个段中数据的指令将引发一个 Segmentation Violation 错误. 文本区是可以被多个运行该可执行文件的进程所共享的.
2.数据段对应与运行文件中的数据区(Data Section),其可分为初始化数据区(INIT Section)和非初始化数据区(BSS Section),初始化数据(INIT)区用于存放可执行文件里的初始化数据;而非初始化数据(BSS)区用于存放程序的静态变量, BSS区内存都是被初始化为零的.
3.而堆栈段就是我们下面要重点关注的对象了。为了说明堆栈段,我们首先得解释一下函数的栈帧,为了说明函数的栈帧,首先最好还是看一个小例子:
void proc(int i)
{
int local;
local=i;
}
int main(void)
{
proc(2);
return 0;
}
这段代码经过编译后,反汇编的结果大致是(这里只是为了说明堆栈段而提取出了反汇编后的核心部分,实际情况还要更复杂一些):
main:push 2
call proc
...
proc:push ebp
mov ebp,esp
sub esp,4
mov eax,[ebp+08]
mov [ebp-4],eax
add esp,4
pop ebp
ret 4
我们先有点儿耐心,来分析一下这段代码:
main:push 2
call proc
首先, 将函数调用要用到的参数2压入堆栈,然后call proc
注意,这里call proc时,系统将自动将函数的返回地址,即main函数下一条语句(return 0)的地址压栈。所以,此时堆栈段的情况如下图:
(内存高端)
+-----------------+
+ 2 +
+-----------------+
+ 函数的返回地址RET +
+-----------------+
(内存底端)
接下来
proc:push ebp
mov ebp,esp
我们知道ebp是堆栈基址寄存器,esp是堆栈指针寄存器,esp指向堆栈的顶端.首先将ebp的原值存入堆栈,然后将esp的值赋给ebp。
sub esp,4
将esp减4(32位机,一个int占四字节),留出一个int的位置给局部变量 local 使用,
mov eax,[ebp+08]
mov [ebp-4],eax
就是 local=i;
此时,内存中堆栈段的映像为:
(内存高端)
+----------------+
+ (i) 2 +
+----------------+
+ 返回地址RET +
+----------------+
+ 原ebp +
+----------------+ <—现在的ebp
+ (local)2 +
+----------------+ <—现在的esp
(内存低端)
add esp,4
pop ebp
ret 4
首先esp加4,收回局部变量的空间,然后pop ebp, 恢复ebp原值,最后 ret 4,从堆栈中取得返回地址,将EIP改为这个地址,并且将esp加4,收回参数所占的空间.
以上分析的就是函数调用时,被调用函数的栈帧结构。总结一下,就是:
(内存高端)
+--------------------+
+ 参数一 +
+--------------------+
+ 参数二 +
+--------------------+
+ 。。。。。。 +
+--------------------+
+ 参数N +
+--------------------+
+ RET +
+--------------------+
+ 原ebp +
+--------------------+
+ local param N +
+--------------------+
+ 。。。。。。 +
+--------------------+
+ local param 二 +
+--------------------+
+ local param 一 +
+--------------------+
(内存低端)
可以看出,函数调用时所建立的栈帧包含了下面的信息:
i)为调用函数的局部变量分配的空间。
ii) 调用函数的返回地址. 返回地址是存放在调用函数的栈帧还是被调用函数的栈帧里, 取决于不同系统的实现.
iii) 调用函数的栈帧信息, 即bsp.
iv) 为被调用函数的参数分配的空间——取决于不同系统的实现.
4.至此,核心部分已经讲完了,我们将各个段串起来再看一下程序运行时内存的情况:
我们假设现在有一个程序, 它的函数调用顺序如下.
main(...) —> func_1(...) —> func_2(...) —> func_3(...)
即: 主函数main调用函数func_1; 函数func_1调用函数func_2; 函数func_2调用函数func_3
当程序被操作系统调入内存运行, 其相对应的进程在内存中的映像如下图所示:
(内存高端)
+--------------------+
+不需要关心的内存 +
+--------------------+
+ Env String (环境变量字符串) +
+--------------------+
+ Argv String (命令行参数字符串) +
+--------------------+
+Env Pointers (环境变量指针) +
+--------------------+
+ Argv Pointers (命令行参数指针) +
+--------------------+
+Argc (命令行参数个数) +
+--------------------+
+main函数的栈帧 +
+--------------------+
+func_1的栈帧 +
+--------------------+
+func_2的栈帧 +
+--------------------+
+func_3的栈帧 +
+--------------------+
+Stack (栈) +
+--------------------+
+Heap (堆) +
+--------------------+
+BSS data (非初始化数据区) +
+--------------------+
+INIT data (初始化数据区) +
+--------------------+
+Text (文本区) +
+--------------------+
(内存低端)
5.缓冲区溢出的机理
我们先举一个例子说明一下什么是 Buffer Overflow :
void function(char *str)
{
char buffer[16];
strcpy(buffer,str);
}
void main()
{
char large_string[256];
int i;
for( i = 0; i < 255; i++)
large_string[i] = 'A';
function(large_string);
}
这段程序中就存在 Buffer Overflow 的问题. 我们可以看到, 传递给function的字符串长度要比buffer大很多,而function没有经过任何长度校验直接用strcpy将长字符串拷入buffer. 如果你执行这个程序的话,系统会报告一个 Segmentation Violation 错误.下面我们就来分析一下为什么会这样?
首先我们看一下未执行strcpy时堆栈中的情况:
(内存高端)
+----------------+
+ large_string的地址 +
+----------------+
+ 返回地址RET +
+----------------+
+ 原ebp +
+----------------+ <—现在的ebp
+ 。。。。。。 +
+----------------+
+ 。。。。。。 +
+----------------+ <—现在的esp
(内存低端)
当执行strcpy时, 程序将256 Bytes拷入buffer中,但是buffer只能容纳16 Bytes,那么这时会发生什么情况呢? 因为C语言并不进行边界检查, 所以结果是buffer后面的250字节的内容也被覆盖掉了,这其中自然也包括ebp, ret地址 ,large_string地址.因为此时ret地址变成了0x41414141h ,所以当过程结束返回时,它将返回到0x41414141h地址处继续执行,但由于这个地址并不在程序实际使用的虚存空间范围内,所以系统会报 Segmentation Violation.
从上面的例子中不难看出,我们可以通过Buffer Overflow来改变在堆栈中存放的过程返回地址,从而改变整个程序的流程,使它转向任何我们想要它去的地方.这就为黑客们提供了可乘之机, 最常见的方法是: 在长字符串中嵌入一段代码,并将过程的返回地址覆盖为这段代码的地址, 这样当过程返回时,程序就转而开始执行这段我们自编的代码了. 一般来说,这段代码都是执行一个Shell程序(如\bin\sh),因为这样的话,当我们入侵一个带有Buffer Overflow缺陷且具有suid-root属性的程序时,我们会获得一个具有root权限的shell,在这个shell中我们可以干任何事。因此, 这段代码一般被称为Shell Code.
6.缓冲区在Heap(堆)区或BBS(非初始化数据)区的情况
前面的缓冲区溢出是发生在栈里面的,因为函数局部变量的空间是被分配在栈中的。我们知道进程对内存的动态申请是发生在Heap(堆)里的. 非初始化数据(BSS)区用于存放程序的静态变量, 这部分内存都是被初始化为零的.
i) 如果缓冲区的内存空间是在函数里通过动态申请得到的(如: 用malloc()函数申请), 那么在函数的栈帧中只是分配了存放指向Heap(堆)中相应申请到的内存空间的指针. 这种 情况下, 溢出是发生在(Heap)堆中的, 想要复盖相应的函数返回地址, 看来几乎是不可 能的. 这种情况的利用可能性要看具体情形, 但不是不可能的.
ii) 如果缓冲区在函数中定义为静态(static), 则缓冲区内存空间的位置在非初始化(BBS)区, 和在Heap(堆)中的情况差不多, 利用是可能的. 但还有一种特姝情况, 就是可以利用它来 复盖函数指针, 让进程后来调用相应的函数变成调用我们所指定的代码.
7.具有suid-root属性的程序
UNIX是允许其他用户可以以某个可执行文件的文件拥有者的用户ID或用户组ID的身份来执行该文件的,这是通过设置该可执行文件的文件属性为 SUID或SGID来实现的. 也就是说如果某个可执行文件被设了SUID或SGID, 那么当系统中其他用户执行该文件时就相当于以该文件属主的用户或用户组身份来执行该文件. 如果某个可执行文件的属主是root, 而这个文件被设了SUID, 那么如果该可执行文件存在可利 用的缓冲区溢出漏洞, 我们就可以利用它来以root的身份执行我们准备好的代码. 没有比让它为我们产生一个具有超级用户root身份的SHELL更吸引人了。
ii) 各种端口守护(服务)进程
UNIX中有不少守护(服务)进程是以root的身份运行的, 如果这些程序存在可利用的缓冲区溢出,
那么我们就可以让它们以当前运行的用户身份--root去执行我们准备被好的代码. 由于守护进程已经以root的身份在运行, 我们并不需要相对应的可执行文件为SUID或SGID属性. 又由于此类利用通常是从远程机器上向目标机器上的端口发送有恶意的数据造成的, 所以叫做 "远程溢出"利用.
我们下一步要做的,就是激动人心的缓冲区溢出攻击了。
参考资料:
http://www.newsmth.net/bbstcon.php?board=KernelTech&gid=35 水木社区的一篇关于buffer overflow的文章。
http://www.lilacbbs.com/bbscon.php?bid=91&id=72 紫丁香社区的一篇文章。
http://www.ibm.com/developerworks/cn/linux/l-overflow/ Linux下缓冲区溢出攻击的原理及对策
分享到:
相关推荐
Linux系统缓冲区溢出攻击是目前最常用的黑客攻击技术之一。本文分析了这种攻击技术的原理,指出了Linux系统中进行这种攻击时,本地攻击和远程攻击的差异,并总结和归纳了几种保护缓冲区免受缓冲区溢出攻击和影响的...
缓冲区溢出攻击是一种常见的网络攻击手段,通过溢出缓冲区来实现恶意代码的执行。缓冲区溢出攻击可以分为栈溢出、堆溢出和BSS 段溢出等类型。 缓冲区溢出的概念 缓冲区溢出是一种常见的软件漏洞,指的是在程序执行...
"缓冲区溢出攻击实例" 缓冲区溢出攻击是一种常见的网络攻击方式,它可以让攻击者获得系统的...缓冲区溢出攻击是一种非常危险的网络攻击方式,我们需要了解缓冲区溢出的原理和防范措施,以避免缓冲区溢出攻击的危害。
《计算机系统基础之缓冲区溢出攻击实验》是一份详细介绍了如何进行缓冲区溢出攻击的教学实验报告。 #### 实验目的 - **加深理解**:加深学生对IA-32架构下的函数调用机制和栈帧结构的理解。 - **攻击实践**:通过...
1. 缓冲区溢出攻击定义:缓冲区溢出攻击是一种攻击方式,攻击者可以通过向缓冲区中写入过长的数据,使得缓冲区溢出,并覆盖程序的返回地址,从而跳转到攻击者编写的攻击代码的位置上,开始运行攻击代码。 2. 缓冲区...
缓冲区溢出攻击是一种常见的网络安全威胁,它利用软件设计中的缺陷,尤其是与内存管理相关的漏洞,来破坏程序的正常运行,甚至获取对系统的非法控制。在本例中,我们将聚焦于Windows XP平台上的CCproxy服务,该服务...
3.缓冲区溢出演示 4.溢出攻击结果与危害 5.防御手段 适合人群: 具备一定编程基础,作业时间不够的学生,对缓冲区溢出原理不了解的初学者 环境: IDA pro7.6, vc++ ,x32dbg。 阅读建议: 有一点 x32dbg逆向工具...
- **缓冲区溢出后果**:如果溢出的数据覆盖了重要的内存区域(如函数的返回地址),可能会导致程序崩溃,甚至被攻击者利用来执行恶意代码。 - **根本原因**:C/C++等语言中的一些标准库函数(如`strcpy()`、`strcat...
Linux 缓冲区溢出攻击是一种常见的网络攻击方式,黑客通过溢出攻击可以获得系统的控制权。本文将详细介绍 Linux 缓冲区溢出攻击的原理、检测方法和防范技术。 缓冲区溢出攻击的原理 ------------------------ 缓冲...
缓冲区溢出是计算机安全领域的一个重要话题,尤其在Windows操作系统中,由于其广泛的应用,对缓冲区溢出的理解和防范显得尤为重要。缓冲区溢出通常发生在程序处理数据时,当输入的数据超过了预分配的内存空间(即...
一、缓冲区溢出原理 缓冲区溢出是因为在程序执行时数据的长度超出了预先分配的空间大小,导致覆盖了其他数据的分配区域,从而执行非授权指令,获取信息,取得系统特权进而进行各种非法操作导致程序运行失败、系统宕...
缓冲区溢出是计算机安全领域中的一种常见攻击手段,指的是攻击者向缓冲区输入过长的数据,使其溢出缓冲区,覆盖合法数据,导致程序崩溃或执行恶意代码。在本教程中,我们将从基本概念开始,逐步深入缓冲区溢出的原理...
缓冲区溢出是一种系统攻击的手段,借助于在程序缓冲区编写超出其长度的代码,造成溢出,从而破坏其堆栈,使程序执行攻击者在程序地址空间中早已安排好的代码,以达到其目的。缓冲区溢出攻击之所以常见,是因为它太...
缓冲区溢出攻击是一种常见的网络安全威胁,它利用了程序处理内存时的疏忽,尤其是当程序没有正确检查输入数据的大小时。这种攻击通常发生在程序尝试将数据写入固定大小的内存区域(即缓冲区)时,如果输入的数据超过...
当程序试图将更多的数据写入到一个固定大小的内存区域(即缓冲区)中时,就会发生缓冲区溢出。例如,在代码中定义了一个整型数组`int buff[10]`,这意味着只能安全地访问`buff[0]`到`buff[9]`这10个位置。但如果尝试...
缓冲区溢出是计算机安全领域中的一个重要概念,尤其在编程和系统安全方面具有深远的影响。Q版缓冲区溢出教程,正如其名,是一种以轻松、直观的方式介绍这一技术的教材,适合初学者和对安全感兴趣的IT从业者学习。本...