0x8000131
0x8000133
0x8000136
0x800013d
0x8000144
0x8000146
0x8000149
0x800014a
0x800014d
下面让我们了解一下缓存溢出的原理。众说周知,c语言不进行数组的边界检查,在许多运用c语言实现的应用程序中,都假定缓冲区的大小是足够的,其容量肯定大于要拷贝的字符串的长度。然而事实并不总是这样,当程序出错或者恶意的用户故意送入一过长的字符串时,便有许多意想不到的事情发生,超过的那部分字符将会覆盖与数组相邻的其他变量的空间,使变量出现不可预料的值。如果碰巧,数组与子程序的返回地址邻近时,便有可能由于超出的一部分字符串覆盖了子程序的返回地址,而使得子程序执行完毕返回时转向了另一个无法预料的地址,使程序的执行流程发生了错误。甚至,由于应用程序访问了不在进程地址空间范围的地址,而使进程发生违例的故障。这种错误其实是编程中常犯的。
http://hi.baidu.com/caterqiu/item/29598d475bcbf8af61d7b922 参考这哥们汇编调试。
|
使用Buffer Overflow 方法来入侵目的主机是黑客们经常采用的一种手段,本文将几篇介绍其机理的文章作了一些加工整理, 对它的机理作出了由浅入深的剖析.
本文分为下面几个部分, 朋友们可以按照自己的兴趣选择不同的章节:
1.关于堆栈的基础知识
2.Buffer Overflow 的原理
3.Shell Code 的编写
4.实际运用中遇到的问题
5.附录 I 若干操作系统/平台上的 Shell Code
6.附录 II 通用 Buffer Overflow 攻击程序
--------------------------------------------------------------------------------
1. 关于堆栈的基础知识
一个应用程序在运行时,它在内存中的映像可以分为三个部分: 代码段, 数据段和堆栈段(参见下图). 代码段对应与运行文件中的 Text Section ,其中包括运行代码和只读数据,这个段在内存中一般被标记为只读,任何企图修改这个段中数据的指令将引发一个 Segmentation Violation 错误.
数据段对应与运行文件中的 Data Section 和 BSS Section ,其中存放的是各种数据(经过初始化的和未经初始化的)和静态变量.
下面我们将详细介绍一下堆栈段.
|--------| 虚存低端
| |
| 代码段 |
| |
|--------|
| |
| 数据段 |
| |
|--------|
| |
| 堆栈段 |
| |
|--------| 虚存高端
堆栈是什么?
如果你学过<<数据结构>>这门课的话, 就会知道堆栈是一种计算机中经常用到的抽象数据类型. 作用于堆栈上的操作主要有两个: Push 和 Pop , 既压入和弹出. 堆栈的特点是LIFO(Last in , First out), 既最后压入堆栈的对象最先被弹出堆栈.
堆栈段的作用是什么?
现在大部分程序员都是在用高级语言进行模块化编程, 在这些应用程序中,不可避免地会出现各种函数调用, 比如调用C 运行库,Win32 API 等等. 这些调用大部分都被编译器编译为Call语句. 当CPU 在执行这条指令时, 除了将IP变为调用函数的入口点以外, 还要将调用后的返回地址放入堆栈. 这些函数调用往往还带有不同数量的入口参数和局部变量, 在这种情况下,编译器往往会生成一些指令将这些数据也存入堆栈(有些也可通过寄存器传递).
我们将一个函数调用在堆栈中存放的这些数据和返回地址称为一个栈帧(Stack Frame).
栈帧的结构:
下面我们通过一个简单的例子来分析一下栈帧的结构.
void proc(int i)
{
int local;
local=i;
}
void main()
{
proc(1);
}
这段代码经过编译器后编译为:(以PC为例)
|
2. Buffer Overflow 的机理
我们先举一个例子说明一下什么是 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时堆栈中的情况:
16 4 4 4
...[buffer] [ebp] [ret地址] [large_string地址]
esp ebp
当执行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.
下面我们就来看一下如何编写Shell Code.(待续)
|
3. Shell Code 的编写
下面是一个创建Shell的C程序shellcode.c: (本文以IntelX86上的Linux为例说明)
void main() {
char *name[2];
name[0] = "/bin/sh";
name[1] = NULL;
execve(name[0], name, NULL);
}
我们先将它编译为执行代码,然后再用gdb来分析一下.(注意编译时要用-static选项,否则execve的代码将不会放入执行代码,而是作为动态链接在运行时才链入.)
------------------------------------------------------------------------------
[aleph1]$ gcc -o shellcode -ggdb -static shellcode.c
[aleph1]$ gdb shellcode
GDB is free software and you are welcome to distribute copies of it
under certain conditions; type "show copying" to see the conditions.
There is absolutely no warranty for GDB; type "show warranty" for
details.
GDB 4.15 (i586-unknown-linux), Copyright 1995 Free Software Foundation, Inc...
(gdb) disassemble main
Dump of assembler code for function main:
0x8000130
0x8000131
0x8000133
0x8000136
0x800013d
0x8000144
0x8000146
0x8000149
0x800014a
0x800014d
|