3.9 异质的数据结构
3.9.1 结构 struct
结构的实现类似于数组的实现
3.9.2 联合 union
联合能够规避c语言的类型系统,允许以多种类型来引用同一个对象。
一个联合的总大小等于它最大的字段的大小。
在一些上下文中联合十分有用。
一种应用情况是:事先知道对一个数据结构中的两个不同字段的使用是互斥的,那么将这两个字段声明为联合的一部分,而不是结构的一部分,会减小分配空间的总量。
比如想实现一个特殊的二叉树结构,每个叶子节点都有两个double类型的数据值,而每个内部节点都有指向两个孩子节点的指针,但是没有数据。将左右孩子节点与数据值视为两个不同字段,则可以认为它们互斥。
形式1:每个节点需要32个字节
形式2: 每个节点需要16个字节(但无法确定一个给定的节点是叶子节点还是内部节点了)
形式3: 每个节点需要24个字节(字段type和联合的元素之间需要4个字节填充)
联合还可以用来访问不同类型的位模式。
3.9.3 数据对齐
对齐限制:许多计算机系统对基本数据类型的合法地址做出了一些限制,要求某种类型对象的地址必须是某个值K(通常是2,4,8的倍数)
但无论数据是否对齐,x86-64硬件都能正确工作。但对齐数据能够提高内存系统的性能。
对齐原则:任何K字节的基本对象的地址必须是K的倍数。
对于包含结构(struct)的代码,编译器可能需要在字段的分配中插入间隙,以保证每个结构元素都满足它的对齐要求。而结构本身对它的起始地址也有一些对齐要求。
编译器可能需要在结构的字段的分配中插入间隙,也可能在结构的末尾做一些填充。
3.10 在机器级程序中将控制与数据结合起来
3.10.1 理解指针
指针是C语言的一个核心特色。它们以一种统一方式,对不同数据结构中的元素产生引用。
指针是C语言提供的一种抽象,与机器代码形成映射。
&运算符的机器代码的实现常常用leaq指令来计算表达式的值。
将指针从一种类型强制转换成另一种类型,只改变它的类型(指针运算时的伸缩),而不改变它的值。
指针也可以指向函数。比如: int (*f)(int )表示:f是指向以int为参数并返回int的函数的指针。
3.10.2 应用:使用GDB调试器
支持机器级程序的运行时评估和分析。
3.10.3 内存越界引用和缓冲区溢出
C对于数组引用不进行任何边界检查,而且局部变量和状态信息(例如保存的寄存器值和返回地址)都存放在栈中。这两种情况结合到一起就能导致严重的程序错误,对越界的数组元素的写操作会破坏存储在栈中的状态信息。当程序使用这个被破坏的状态,试图重新加载寄存器或者执行ret指令时,就会出现很严重的错误。
一种常见的状态破坏被称为缓冲区溢出(buffer overflow)。通常在栈中分配某个字符数组来保存一个字符串,但是字符串的长度超出了为数组分配的空间。
库函数gets()的实现就有问题:
字符串到23个字符之前都没有严重的后果,但是超过以后,返回指针的值以及更多可能的保存状态会被破坏。如果存储的返回地址的值被破坏了,那么ret指令会导致程序跳转到一个完全意想不到的位置。如果只看C代码,根本就不可能看出会有上面这些行为。
蠕虫(worm)和病毒(virus)都试图在计算机中传播它们自己的代码段。蠕虫可以自己运行,但病毒不能独立运行。
3.10.4 对抗缓冲区溢出攻击
现代的编译器和操作系统实现了很多机制,以避免操守缓冲区溢出攻击。
-
栈随机化
为了在系统中插入攻击代码,攻击者既要插入代码,也要插入指向这段代码的指针,这个指针也是攻击字符串的一部分。产生这个指针需要知道这个字符串放置的栈地址。
现在,linux系统中,栈随机化已经变成了标准行为。
-
栈破坏检测
破坏通常发生在当超越局部缓冲区的边界时。虽然C语言中,没有可靠的方法来防止对数组的越界写。但是我们能够在发生了越界写的时候,在造成任何有害结果之前,尝试检测到它并终止程序。
GCC在产生的代码中加入了一种栈保护者机制,来检测缓冲区越界。其思想是在栈状态之间存储一个特殊的金丝雀(canary)值,也称为哨兵值,是在程序每次运行的时候随机产生的。
使用栈保护者来编译前面的echo函数,得到:
、
-
限制可执行代码区域
消除攻击者向系统中插入可执行代码的能力。一种方法是限制哪些内存区域能够存放可执行代码。
3.10.5 支持变长栈帧
不变长栈帧: 编译器能够预先确定需要为栈帧分配多少空间。
变长栈帧: 编译器无法预先确定需要为栈帧分配多少空间。局部存储是变长的。当函数调用alloca()或者声明局部变长数组的时候,就会发生这种情况。
函数返回时,必须释放栈帧,并将栈指针设置为存储返回地址的位置。对于不变长栈帧,编译器事先就能确定需要分配多少栈帧大小以及需要返回多少栈帧大小;而对于不变长栈帧,就需要使用寄存器%rbp作为帧指针(frame pointer),有时称为基指针(base pointer)