《Windows核心编程》之“ASLR .vs Rebasing dlls”

来源:互联网 发布:qq飞车锐速数据 编辑:程序博客网 时间:2024/05/16 18:48

        《Windows核心编程》第14章最后部分提到了“ASLR - Address Sppace Layout Randomization,地址空间分布随机化”特性。该特性允许Windows在第一载入一个DLL时,为它选择一个不同的地址,而不是它的PE文件中指定的“prefered address”,默认为“0x10000000”。在载入DLL时,系统内核会执行ASLR基地址重定位(ASLR rebase fixup),经过修改后的页面为所有使用该DLL的进程所共享。也就是说,由于不需要对每个进程都执行ASLR 基地址重定位,内存的使用效率还是挺高的。

        顺便说一下:关于ASLR基地址重定位的效率,可以参考博文:https://blogs.msdn.microsoft.com/mattev/2007/08/21/enabling-aslr-for-memory-savings/

        在《Windows核心编程》第20章的20.7节描述了“模块的基地址重定位”的原理和方法。当编译器在编译模块代码时,会生成一个“relocation section”,它包含了所有可能需要偏移的机器指令内存地址(一般是导出函数和全局变量),即字节偏移量列表。链接器会将“relocation section”嵌入到PE文件中。在系统Loader载入DLL时,如果DLL能够载入到它的“prefered address”,则不访问“relocation section”,否则需要遍历该列表,并对其中的所有机器指令的内存地址进行修正,即加上“prefered address”与实际载入地址的差值。这个修正的过程,就是“基地址重定位”。

       顺便说一下:为了减少地址空间碎片化(fragmentation),系统一般总是从高内存地址开始载入DLL,然后再是低内存地址。使用sysinternals的vmmap工具观察进程地址空间分布,即可发现在(用户分区)高地址段一般分布的都是系统DLL,而且大多数32系统下ntdll.dll排在最高位,表示它是第一个被载入的DLL。

        为了减少进程启动时,DLL加载过程中的“基地址重定位”操作,可以事先将待加载的DLL的“prefered address”合理错开来。《Windows核心编程》中提高了一个工具“rebase.exe”可以批量执行该操作,并将调整好的“prefered address”重新写入各个DLL的PE文件中。但是,我并未找到该工具,可能是它已被淘汰。事实上,我找到了VS自带的“EDITBIN.exe”工具,它能指定rebase和bind操作。参考链接:EDITEBIN.exe and DUMPBIN.exe


        那么,问题来了。如果我们使用editbin.exe工具对相关DLL执行了rebase操作,在进程启动时,操作系统又会根据ASLR特性,再一次执行“ASLR rebase fixup”,这么说,我们之前进行的rebase貌似没有任何意义啦?在stack overflow上,有人提出了和我一样的疑问:http://stackoverflow.com/questions/32380206/does-aslr-mean-rebasing-dlls-isnt-required

        但是,《Windows核心编程》一书第20章又明确提到,Microsoft在发布Windows时,已对大部分系统自带的dlls进行了实现的rebase。特别是known dlls。(通过regedit,可以在注册表中搜到“KnownDLLs”项中的dlls列表“)。Mircrosoft应该不会做无用功。

        这个问题使我困惑不已,我尝试做了如下研究和猜测:

        Windows的系统dlls还是在发布时通过rebase工具,将它们的“prefered address”合理错开,并写在PE文件中。而ASLR是在每次系统reboot时,产生一个delta值。这样,在DLL加载的时候,使用PE中的“prefered address”加上delta值,就是实际的加载地址。

        如果猜测成立,那么重启系统,各个系统DLLs的加载地址不同,但是两个系统DLLs的加载地址差值应该相等。

        事实上,我用vmmap.exe工具来观察系统自带的calc.exe和mspaint.exe,发现在同一次系统启动中,两个进程的ntdll.dll、kernel32.dll和user32.dll的加载地址是相同的,但是不同次系统重启中,这些dll的相对加载位置不同,即差值不想等。我的上述猜测不成立。

        此后,我查看《Windows Internals》一书第9章”Memory Management“中的”Image Randomization“,它是通过0x78000000 - MiImageBias * 0x10000来计算下载地址,跟DLL的加载顺序有关。而每次reboot,DLL的加载顺序是不一样的。

0 0
原创粉丝点击