Archive for the '优美的C语言' Category

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

Wednesday, December 21st, 2005

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

今天终于把前几天那个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

限定printf打印字符串时输出的字符个数

Saturday, July 23rd, 2005

C语言里字符串copy有strncpy,比较有strncmp而输出呢?