redirect_to :future #思维驱动 » Blog Archive » 浅淡C语言程序安全(一)

浅淡C语言程序安全(一)

一年半前写的东西,呵呵,随便翻出来的。
——————-

今天终于把前几天那个Bug找出来了,找了好久,原来strncpy时多拷了两位,往栈的高位写了两个字节,结果刚好将一个关键变量的内容覆写了,晕….
说到堆栈想起前段时间说的缓冲区溢出来,小议一下。
说实话以下说的内容其实我自己也没做到,总觉自己老是在说什么这也不是那也不是的有些过意不去,特别是自己都没做到的时候。不过单纯从学习技术的角度上来看,简单的说说也是有必要的。

堆栈溢出非常的常见,比如往一个字符数组中写入一个大于定义长度的字符串时:
char sCont[5];
strcpy(sCont,”AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA”);
就产生一次溢出,比较常见的结果是出现Segmentation fault错误。当这个函数局部变量较多的时候甚至没有出错提示,比如我开篇时说的就是这那情况。

下文在X86平台下以C语言为例进行说明。

先从基础说起.

一.堆栈:
堆栈是一片连续的内存空间,是一种后进先出结构,其生长方向与内存的生长方向相反,内存由低位往高位生长,而堆栈由高位往低位生长。入栈操作push=ESP-4,出栈操作pop=ESP+4. 也就是说执行push eax;push ebx;序列后,eax的存放的地址比ebx的存放地址要小.这是堆栈溢出的理论依据。
二.函数调用:
执行一次函数调用时,将在堆栈中依次压入:参数(由右至左),返回地址,EBP,及可能有的局部变量,在函数执行结束后局部变量丢失,在函数返回的时候,弹出返回地址给EIP,以继续执行程序。

现在看看一下简单的溢出过程.

void OverflowTest(){
char sCont[5];
strcpy(sCont,”AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA”);
}

调用OverflowTest时堆栈如下分配:
–栈底(低位) —— 栈顶(高位)
局部变量 EBP 返回地址 参数
[…[XXXXX(注:sCont[5])] [0x…] [0x…] [没有参数]……]
当我们正常往sCont写字符串值时内存应该是这样子的:
–栈底(低位) ——栈顶(高位)
局部变量 EBP 返回地址 参数
[…[abcd\0] [0x….] [0x….] [没有参数]……]

这样的的结果是正确的,但是当我们写入一个大于5位的值时,因为sCont中存放不下所写的内容所以, 只好向内存顶部继续写
执行strcpy(sCont,”AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA”);这条语句后内存的情况是这样子的:
–栈底(低位) —— 栈顶(高位)
局部变量 EBP 返回地址 参数
[…[AAAAA] [AAAA] [AAAA] AAAAA…]

可以看出EBP和返回地址的值被改成成了AAAA,当函数执行结束要返回的时候,CPU将”AAAA”的ASCII码:0×41414141作为返回地址,CPU会试图执行0×41414141处的指令,CPU访问不存在的指令,结果出现错误。这就是一次堆栈溢出。Segmentation fault。

你可能已经注意到了,当溢出发生时我们改变了程序的执行过程,如果返回地址是一个有效的指令地址时,CPU将转向这个地址去执行从这个地址开始的指令序列,当你精心构造一个要执行的指令序列(当然要存放在进程的4Gb地址空间中)并将返回地址指向这个序列时,以实现缓冲区溢出攻击了,在UINX系统中,我们的指令可以执行一个shell,这个shell将获得和被我们堆栈溢出的程序相同的权限。如果这个程序是setuid的,那么我们就可以获得root shell。而这个指令序列就是我们常说的shell code。

这不是一篇缓冲区溢出的攻击教程,我也不打算详细说明如何去做下面的事情。相反我想通一些例子代码来说明如何编写缓冲区安全的程序。

HJLEOCHEN 2004/04/28

看完啦?再看看我们最近的10条记录对您是否有用呢?

要不,再找找其它内容:




或者给我们留下些意见、建议,这将给我们莫大的鼓励,促使我们做得更好。