为何Windows程序运行时入口点地址与PE头中记录地址不匹配?
我希望定位Windows程序的入口点。根据微软文档描述,ImageBase是链接器生成可执行文件时假定的文件内存映射起始地址。我使用dumpbin工具查看PE头中的相关信息,执行命令:
dumpbin /HEADERS my.exe
得到如下输出:
... 13D0 entry point (00000001400013D0) mainCRTStartup 1000 base of code 140000000 image base (0000000140000000 to 0000000140006FFF) ...
由此可知,入口函数mainCRTStartup的虚拟地址应为0x00000001400013D0,但当我运行程序并打印相关地址时,得到的结果为:
with __ImageBase = 0x7ff771e60000 with mainCRTStartup = 0x7ff771e61390
该运行时打印的地址位于用户空间的低位区域(靠近Windows DLL),与“ImageBase + 入口点偏移”计算出的地址相差甚远。同时,我检查0x00000001400013D0地址处的内存,发现该地址并未被映射。请问这一现象的原因是什么?
以下是我使用VS2019编译的x64架构源码:
#include <Windows.h> #include <stdio.h> extern "C" const IMAGE_DOS_HEADER __ImageBase; extern "C" int mainCRTStartup(); int main() { printf("with __ImageBase = 0x%llx\r\n", (HINSTANCE)&__ImageBase); printf("with mainCRTStartup = 0x%llx\r\n", mainCRTStartup); return 0; }
你遇到的这个问题核心原因是**Windows的地址空间布局随机化(ASLR)**在起作用,再加上对PE文件中entry point字段的含义理解偏差,咱们一步步拆解:
1. PE文件中entry point的真实含义
你用dumpbin /HEADERS my.exe看到的13D0 entry point (00000001400013D0) mainCRTStartup里,括号外的13D0才是相对虚拟地址(RVA)——也就是相对于编译时默认ImageBase的偏移量。括号里的00000001400013D0是链接器用默认ImageBase(0x140000000)加上这个RVA计算出来的静态假设地址,但这只是编译链接阶段的理论值,不是程序运行时的真实加载地址。
2. ASLR导致运行时ImageBase随机化
ASLR是Windows默认开启的安全机制,VS2019编译x64程序时,默认会启用/DYNAMICBASE选项(你可以在项目属性→链接器→高级→随机基址里确认)。这个机制的作用是让每个进程启动时,可执行文件和DLL都会被加载到虚拟内存的随机地址,而不是PE头里指定的默认ImageBase,以此来防范内存溢出类的攻击。
你运行时打印的__ImageBase = 0x7ff771e60000就是程序实际加载的基地址,这时候入口点的真实地址应该是实际ImageBase + 入口点RVA。咱们来算一下:
- dumpbin里的入口点RVA是
0x13D0 - 运行时实际ImageBase是
0x7ff771e60000 - 理论上真实入口地址应该是
0x7ff771e60000 + 0x13D0 = 0x7ff771e613D0
你打印的mainCRTStartup = 0x7ff771e61390有一点小偏差,这是因为VS的CRT(C运行时库)在启动时会做一些包装或者重定位操作,实际暴露的mainCRTStartup地址和PE头里的入口点有细微差异,但核心逻辑还是实际基地址加上RVA。
3. 为什么0x00000001400013D0地址未被映射?
因为ASLR已经把你的程序加载到了0x7ff771e60000附近的随机地址区域,原来PE头里指定的默认ImageBase0x140000000并没有被当前进程映射,所以你访问这个地址时会发现它是无效的。
验证方法
如果你想验证这个逻辑,可以临时关闭ASLR来测试:
- 打开VS项目属性→链接器→高级→随机基址,改成
否(/DYNAMICBASE:NO) - 重新编译并运行程序
这时候你会发现,运行时打印的__ImageBase就是0x140000000,mainCRTStartup的地址也会和dumpbin里显示的0x1400013D0基本一致(可能因为CRT的细微包装有极小偏差,但整体会吻合)。
内容的提问来源于stack exchange,提问作者H3d9

