计算机内存主要有Stack、Heap、Data、Text/Shared Libraries。
stack一般会有8MB的大小限制,向低地址扩展(初始00007FFFFFFFFFFFF,即47个1,虽然有64为但只使用47位。)
Heap存储库函数,例如malloc(),calloc(),new()等;
Data存贮全局变量等数据;
Text则是“文本区”。
当超出为数组分配的内存大小时,我们称之为缓冲区溢出Buffer Overflow。主要原因通常是社会工程学/用户无知。
缓冲区溢出的常见形式有:未检查字符串长度、栈上的有边界数组超出范围(有时称为栈破坏Stack smashing)等。
对于库函数,例如:gets()、strcpy()、strcat()、scanf()、fscanf()、sscanf()等,均会出现由于没有边界条件检查导致的缓冲区溢出问题。超出缓冲区的数据可能会覆盖或替换储存在其他地址的数据,导致段错误等情况。
Code Infection Attacks代码注入攻击:即通过缓冲区溢出使得函数的返回值被覆盖,而跳转到其他 地址执行代码。(详情见Attack Lab)
Worms and Viruses蠕虫与病毒:
蠕虫:一个计算机程序,可以自己运行自己并且可以扩散/传播/繁殖(propagate)到其他地方;
病毒:一串代码,无法自动运行,而是将自己加入(攻击)其他程序
Avoid Overflow Vulnerabilities in Code避免使用相关库函数:
fgets->gets、strncpy->strcpy、避免使用scanf函数和%s,而是使用fgets或使用%ns(n为scanf可以读取的字符串最大长度)
System-Level Protections系统级保护措施?ASLP栈随机化! 栈随机化,表示地址空间布局随机化, 即每次程序运行时地址都是变化的,在栈上分配的空间也是随机的。 另一种方法是不可执行的代码段(Nonexecutable code segments),在x86-64架构上,相较于原有x86架构的“只读”(read-only)或“可写”(writeable),增加了“执行”(execute)权限,而栈堆被标记为不可执行(non-executable)。
Stack Canaries栈金丝雀:
GCC编译器提供了-fstack-protector方法来启用该方法。在现代版本,该选项是默认开启的。
挑战(对攻击者来说):
替代策略(Alternative Strategy):
利用现有的代码:
例如,来自标准库(stdlib)的库代码。
将现有代码片段串联起来以实现所需的功能。
注意:这种方法不能绕过栈金丝雀(stack canaries)的保护。
通过“代码小片段(gadgets)”构建程序:
ret 指令(返回指令)结尾的一系列指令片段。(面向返回编程)
    ret 指令的机器码表示为单字节 0xC3。00000000004004d0 <ab_plus_c>
  4004d0:  48 0f af fe imul %rsi,%rdi
  4004d4:  48 8d 04 17 lea (%rdi,%rdx,1),%rax
  4004d8:  c3
4004d4行代码实现了rax <- rdi + rdx,代码块(gadgets)从4004d4开始;
<setval>
  4004d9:  c7 07 d4 48 89 c7  movl  $0xc78948d4,(%rdi)
  4004df:  c3
从48开始到c3的代码正好可以编码为:movl  %rax,%rdi和ret,代码判断片段从0x4004dc开始。
Union的构造方式与struct很像,但相较于后者为每个数据分配内存,union会使用 占用空间最大的域 的大小分配内存。
例如:
typedef union {
    float f;
    unsigned u;
} bit_float_t;
float bit2float(unsigned u){
    bit_float_t arg;
    arg.u = u;
    return arg.f;
}
在结构体bit_float_t中,浮点数f和无符号数u存储在同一地址。这一操作完全不同于类型转换。类型转换是将两者转换为最接近的数,而这个操作中,位没有发生变化,是相同的二进制数在不同数据类型下的数。
大端序(Big Endian)和小端序(Little Endian)是计算机系统中数据在内存中的存储方式,主要针对多字节数据(例如int、long等)的排列顺序。
在大端序中,高位字节存储在内存的低地址位置,数据从高位到低位依次排列;而在小端序中,低位字节存储在内存的低地址位置,数据从低位到高位依次排列。
例如,一个32位整数0x12345678(占用4字节),在内存中的存储方式如下:
0x12 0x34 0x56 0x78。0x78 0x56 0x34 0x12。大端序的存储方式符合日常习惯,因为我们通常将高位放在前面,因此它常用于网络协议(如TCP/IP)。小端序则更适合计算机底层操作,读取数据时处理低地址优先,简化了硬件设计,因此像Intel x86和x64等绝大多数计算机系统都采用小端序。
要判断系统的字节序,可以通过编程实现。例如,C语言可以通过以下代码简单测试: