为什么Windows不能随机化我的可执行文件的基地址?

时间:2017-06-30 09:06:57

标签: c windows windows-10 aslr

我做了一个简单的C程序,只是在执行时打印main()的地址:

printf("%08X\n", &main);

我使用Visual C ++ 2015使用参数/DYNAMICBASE编译它,对于x86(编译x64时会发生同样的事情)。

前两次我运行它,返回的地址是不同的,正如预期的那样。 但是,两次之后,程序返回的地址保持不变:

00C31050
00221050
00221050
00221050 

重新编译或重命名可执行文件会再次使地址随机化。

这里发生了什么? Windows以某种方式缓存可执行文件吗?

1 个答案:

答案 0 :(得分:3)

首先,您不需要获取函数的地址,并且使用%p说明符更好地打印指针,这有助于编译器检查类型。更准确的代码是:

printf ("%p\n", main);

在主题上,负责重新定义可执行映像的ASLR technology是一项操作系统功能,旨在通过降低地址的可预测性来抵御缓冲区溢出攻击。 保证对于两次连续运行,图像将放置在不同的位置,但操作系统会不时尝试改变基础,具体取决于许多因素。对于我的测试,我得到(例如)Windows-7 32位构建的以下结果,在编译后立即进行10次连续运行:

00BE1260
00BE1260
00221260
00F71260
01391260
01391260
01391260
01391260
01391260
003A1260

正如您所看到的,即使连续的跑步被放置在相同的位置,基础也会在一段时间后发生变化。可以保证的是,不支持动态库的可执行映像将始终放在base,由exe-headers中的链接器设置。可执行映像的默认基础是400000h,因此打印值将是这样的:

00401260
00401260
00401260
00401260
00401260
...

至于你的情况,我认为操作系统重新定位算法的工作性更强,因为操作系统算法可能会减少可能的攻击威胁,或者由于缺乏熵或资源。重新定位需要额外的时间和资源来重新映射内存页并调整重定位,因此操作系统可能会决定在您的情况下不需要频繁的重新定位。

当然,Windows会缓存已加载的可执行文件以加速其启动。这就是为什么基数在下次运行中不会改变的概率足够高。如果你有足够的RAM,可以使用更多的RAM用于缓存,图像不会被重新设置的可能性就越大。如果图像完全重新加载,则没有理由保持相同的基础。此外,重新定位政策可能因操作系统版本而异。

关于缓存。它不仅是功能缓存。如果将图像加载到存储器中,则它可以同时用于多个进程(实例)。这些实例可以安全地共享代码页,因为代码是只读的。这是Windows在进程终止后不立即“卸载”映像的主要原因之一。但是两个进程只有在调整到相同的基础时才可以共享代码,因为重定位会将代码修补到内存中。如果我们迫使不同的流程被重新定位,我们不可避免地需要放弃代码共享,这会导致RAM的消耗增加。

编辑:

BTW,我发现VS2015忽略了/ DYNAMICBASE选项,并且链接器总是生成支持ASLR的可执行映像,即使我明确设置了/ DYNAMICBASE:NO。比特到比特的比较也表明编译的文件是相同的,除了一个时间戳和(结果)校验和。要获得VS2015构建的可执行文件而不支持ASLR,我必须手动删除exe-header中的IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE位。不知道它是故意制作还是MS错误。