• 沒有找到結果。

计算机内存

在文檔中 第 3 部分 攻击 101 (頁 192-196)

攻击 101

7.3 计算机内存

以最简单的名词来说,计算机内存是一种电子装置,能够存储和检索数据。内存能够 存储的最少的数据量是 1 bit,由内存中的 1 或 0 表示。当把 4 个 bit 放在一起时,称作“半 字节”(nibble),可以表示从 0000~-1111 的值。这刚好是 16 个二进制值,对应于十进制下 的 0~-15。当把两个半字节或 8 个 bit 放到一起时,即为一个“字节”(byte),这可以表示 十进制中的 0~255。如果把两个字节放到一起,即为一个“字”(word),可以表示十进制 下的 0~65 535。继续把数据放到一起,如果把两个“字”放到一起,则得到一个“双字 DWORD”,可以表示十进制下的 0~4 294 967 295。

计算机内存有多种类型,这里将主要讨论随机存储器(Random Access Memory,RAM)

和寄存器。寄存器是一种特殊形式的内存,嵌入到处理器内部,将在 7.4.1 节讨论。

7.3.1 RAM

在 RAM 中,任意存储的任意数据块可以在任意时间访问,这也是随机存取(Random Access)这个名字的由来。但 RAM 是易变的,这意味着如果计算机关闭,那么 RAM 中所 有的数据都将丢失。在讨论 Intel 的产品(x86)时,内存是按 32 位寻址的,这意味着处理

器用来选择特定内存地址的地址总线为 32 位宽。因此,x86 处理器最多能够寻址 4 294 967 295 字节。

7.3.2 字节序

Danny Cohen 在 1980 年曾对 Swift 的格列佛游记(Gulliver travels)做过综述:

“对格列佛游记的一些附注:

格列佛发现有一条法律,由现在的统治者的祖父颁布,要求所有小人国

(Lilliput)的公民在打蛋时,都只能将鸡蛋的小头打破。当然,那些在鸡蛋大头 打蛋的公民都被这条法律激怒了。对打破鸡蛋的方向,持有两种不同观点的人群 发生了内战,导致坚持在鸡蛋大头打蛋的人逃到附近的一个岛上,成立了新的王 国 kingdom of Blefuscu…”。

他接着描述了双方之间爆发的一场圣战。他的文章实际上描述了把数据写入内存时的 两种不同意见。有些人认为高位字节应该首先写入(称作“Little Endian”),而其他人则认

为低位字节应该首先写入(称作“Big Endian”)。这种差别,实际上依赖于所使用的硬件。

例如,在基于 Intel 的处理器上,使用 Little Endian;而在基于 Motorola 的处理器上,则使 用 Big Endian。在稍后讨论 shellcode 时,两种字节序是有区别的。

7.3.3 内存分段

内存分段的主题,可以用一整章来讲述,但基本概念比较简单。每个进程(在特别简 化的情况下,可以认为是一个执行的程序)需要访问内存中属于自身的区域,因为没有人 希望一个进程去改写另一个进程的数据。因此,可将内存划分为小的段,按需分发给进程。

寄存器用来存储和跟踪进程当前维护的段(寄存器将稍后讨论)。偏移寄存器(Offset Registers)用来跟踪关键的数据放在段中的位置。

7.3.4 内存中的程序

在进程被载入内存中时,基本上被分裂成许多小的节(section)。我们比较关注的是 6 个主要的节,将在以后几节里分别讨论。

.text节

.text 节基本上相当于二进制可执行文件的.text 部分,它包含了完成程序任务的机器指 令。该节标记为只读,如果发生写操作,会造成 segmentation fault。在进程最初被加载到 内存中开始,该节的大小就被固定。

.data节

.data 节用来存储初始化过的变量,如:

int a = 0;

该节的大小在运行时是固定的。

.bss节

栈下节(below stack section,即.bss)用来存储未初始化的变量,如:

int a;

该节的大小在运行时是固定的。

第 3 部分

堆节

堆节(heap section)用来存储动态分配的变量,位置从内存的低地址向高地址增长。

内存的分配和释放通过 malloc()和 free()函数控制。例如,在运行时声明一个整数并分配内 存,读者可能想到这样做:

int i = malloc (sizeof (int)); //动态分配一个整数(指针)变量,

//其值是分配之前该处内存的值。

栈节

栈节(stack section)用来跟踪函数调用(可能是递归的),在大多数系统上从内存的 高地址向低地址增长。读者会看到,栈增长的这种方式,导致了缓冲区溢出的可能性。

环境/参数节

环境/参数节(environment / arguments section)用来存储系统环境变量的一份复制文件,

进程在运行时可能需要。例如,运行中的进程,可以通过环境变量来访问路径、shell 名称、

主机名等信息。该节是可写的,因此在格式串(format string)和缓冲区溢出(buffer overflow)

攻击中都可以使用该节。另外,命令行参数也保存在该区域中。上述各节在内存中的顺序,

与笔者介绍的顺序相同。进程的内存空间如下所示:

低地址 高地址

未使用 环境

.text .data .bss

7.3.5 缓冲区

缓冲区(或缓存,buffer)是指这样的一个存储区域:该区域用来接收和保存数据,直 至进程对数据进行处理。由于各个进程都有自己的缓冲区,所以保持各进程的缓冲区彼此 无关是很重要的。通过在进程内存的.data 或.bss 节分配内存,可以做到这一点。要记住,

在内存分配之后,缓冲区的长度是固定的。缓冲区可以保存任何预定义类型的数据,但我 们目前将主要注意基于字符串的缓冲区,这种缓存区用来保存用户的输入和变量。

7.3.6 内存中的字符串

简而言之,字符串数据只不过是内存中连续的字符构成的数组。在内存中,是通过字 符串第一个字符的地址来引用一个字符串。字符串结束于空字符(C 语言中的\0)。

7.3.7 指针

指针是内存中特定的数据,用于保存其他内存区的地址。在内存中移动数据是相对较 慢的操作。如果不移动数据,只是跟踪数据项在内存中的位置(通过指针)并改变指针的 值,那要容易得多。由于内存地址是 32 位长(4 字节),因此指针保存在内存中 4 个连续 的字节里。例如,字符串是通过字符数组中第一个字符的地址进行引用,该地址值被称作 一个指针。因此 C 语言中一个字符串变量的声明如下:

char * str; //代码的意思是,要提供4个字节来保存str,而str是一个指向字符变量 //(可以是字符数组的第一个字节)的指针。

重要的是注意到,即使指针的大小是 4 个字节,但上述的声明并没有限定字符串的长 度,因此编译器将认为该数据是未初始化的,会将其放到进程内存的.bss 节。

以下是另一个例子,如果读者需要存储一个指针,指向内存中的一个整数,可以在 C 程序中使用下述声明:

int * point1; // 代码的意思是,要提供4个字节来保存point1,

// 这是个指向整型变量的指针。

为读取指针指向的内存地址中的值,可以使用*符号对指针反引用。因此,如果打算输 出上述代码中 point1 指向的整数的值,可以使用下述语句:

printf("%d", *point1);

其中*用来反引用 point1 指针,而 printf()函数则用来输出整数的值。

7.3.8 操作不同的内存区

现在读者已经了解了基础知识,我们来给出一个简单的例子,以声明如何在程序中使 用内存:

/* memory.c */ // 注释,给出程序名

int index = 5; // 存储在.data节中的整数(已经初始化)

char * str; // 存储在.bss节中的字符串(未初始化)

int nothing; // 存储在.bss节中的整数(未初始化)

void funct1(int c){ // 大括号,标志funct1的开始

int i=c; // 该变量存储在栈中

str = (char*) malloc (10 * sizeof (char)); // 在堆中分配10个字符的空间 strncpy(str, "abcde", 5); // 将5个字符"abcde"复制到str

} // funct1的结束

main (){ // main函数,必需

funct1(1); // main用参数1调用funct1

} //main函数的结束

第 3 部分

这个程序没有做什么有用的事情。首先,在进程内存不同的节中分配了几块内存。在 main 执行时,用参数 1 调用 funct1()。在调用 funct1()时,参数被传递到函数变量 c。接下 来,在堆上为字符串 str 分配了 10 字节的内存。最后,把 5 字节长度的字符串“abcde”复 制到变量 str 中。函数 funct1 结束后,main()程序结束。

警告:在阅读后文前,必须扎实地掌握了上述知识。如果需要复习本章的哪个 部分,请在继续阅读前进行。

参考文献

[1] Smashing the Stack.., Aleph One www.mindsec.com/files/p49-14.txt [2] How Memory Works http://computer.howstuffworks.com/c23.htm [3] Memory Concepts www.groar.org/expl/beginner/buffer1.txt

[4] Little-Endian vs. Big Endian www.rdrop.com/~cary/html/endian_faq.html

在文檔中 第 3 部分 攻击 101 (頁 192-196)