c语言 - 为什么 Windows 不一直随机化我的可执行文件的基址?



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

printf("%08Xn", &main);

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

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

00C31050
00221050
00221050
00221050 

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

这是怎么回事?Windows 是否以某种方式缓存可执行文件?

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

printf ("%pn", main);

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

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

如您所见,即使连续运行放置在相同的位置,基数也会在一段时间后更改。可以保证的是,不支持动态基的可执行映像将始终放置在基上,由链接器在 exe-header 中设置。可执行图像的默认基数是 400000h,因此打印的值将如下所示:

00401260
00401260
00401260
00401260
00401260
...

至于您的情况,我认为操作系统变基算法更具预测性,因为操作系统算法处理攻击威胁的可能性较小,或者由于缺乏熵或资源。变基需要额外的时间和资源来重新映射内存页和调整重定位置,因此操作系统可能会决定在您的情况下不需要频繁的变基。

当然,Windows缓存加载的可执行文件以加快其启动速度。这就是为什么基数在下一次运行时不会改变的可能性足够高的原因。如果有足够的 RAM,则用于缓存的 RAM 越多,该映像不重定基址的可能性就越大。如果图像完全重新加载,则没有理由保留相同的基础。此外,变基策略可能因操作系统版本而异。

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

编辑:

顺便说一句,我发现 VS2015 忽略了/DYNAMICBASE 选项,并且链接器始终生成支持 ASLR 的可执行映像,即使我明确设置/DYNAMICBASE:NO。此外,位到位比较显示编译的文件是相同的,除了一个时间戳和(结果(校验和。为了在没有 ASLR 支持的情况下获得由 VS2015 构建的可执行文件,我必须手动删除 exe-header 中的 IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 位。不知道它是故意制作的还是MS错误。

相关内容

最新更新