观察问题
用Visual Studio调试程序时,同一个代码的同一个断点处,打印栈变量地址,发现每次都不一样。
而如果是在Linux下用gdb调试,每次打印出的地址不变;直接执行程序,则每次地址又不一样
。
栈内存上变量的地址,为什么会不一样,又为什么会一样?
这其实是不同的ASLR设定下的表现。
ASLR概念
ASLR(Address Space Layout Randomization,地址空间布局随机化),是一种针对缓冲区溢出的安全保护技术。借助ASLR,PE文件每次加载到内存的起始地址都会随机变化。目前大部分主流操作系统都已经实现了ASLR,如Windows Vista、Linux 2.6.12、Mac OS X 10.7、iOS 4.3以及Android 4.0均从此版本开始支持ASLR。
简单说,ASLR使得渗透(基于缓冲区溢出)攻击的难度明显提升,增加了系统的安全性。但是,对于不是搞安全/逆向的Programmer来说,在调试程序时这就略显蛋疼。控制变量是调试阶段的一大原则。
临时修改ASLR
ASLR需要操作系统和程序自身的双重支持。Ubuntu16.04和Win10系统都默认开启了ASLR;对于自己有源码的情况,通过修改链接选项,生成的可执行程序可以关闭ASLR。
Visual Studio 2017
项目属性->链接器->高级->随机基址,改为“否(/DYNAMICBASE:NO)”即可:
ASLR_VisualStudio.pngGDB
GDB默认关闭了随机化的基址,这对于调试很友好。不过也可以在~/.gdbinit
中进行配置:
#关闭ASLR
set disable-randomization
#或:
set disable-randomization on
#打开ASLR
set disable-randomization off
注意,MinGW环境下disable-randomization
的设定不被支持。
举例
如下代码,打印&p
的值,当关闭ASLR后,可以保持不变。
#include <stdio.h>
int main()
{
char * p = malloc(10);
printf("heap=%p stack=%p\n", p, &p);
}
堆随机化
前面的原理和设定说明,都是在处理栈内存。堆内存能否关闭ASLR呢?
这里会有些复杂,因为 Linux 中堆空间可以通过 mmap() 以及 brk() 这两个系统调用完成的,而在不同的等级上面可能会只有部分接口被随机化。
一般谁使用 glibc 中的 malloc() 类接口分配内存,通过 man 3 malloc 可以发现其中有相关的介绍,也就是当超过了 MMAP_THRESHOLD 大小后会使用 mmap(),否则使用 brk() 申请。
如果当前 ASLR 等级为 1,那么当申请空间大于 128K 时,系统通过 mmap() 分配空间,得到的地址是随机的;而当申请空间小于 128K 时,系统是通过 brk() 进行分配的,得到的地址是静止的
参考
地址空间布局随机化(ASLR)
ASLR(Address space layout randomization)地址空间布局随机化
关于Linux下ASLR与PIE的一些理解
Disable randomization of memory addresses
GCC 安全编译选项
网友评论