You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何Windows程序运行时入口点地址与PE头中记录地址不匹配?

问题:Windows程序入口点地址静态分析与运行时不一致的原因

我希望定位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来测试:

  1. 打开VS项目属性→链接器→高级→随机基址,改成否(/DYNAMICBASE:NO)
  2. 重新编译并运行程序

这时候你会发现,运行时打印的__ImageBase就是0x140000000,mainCRTStartup的地址也会和dumpbin里显示的0x1400013D0基本一致(可能因为CRT的细微包装有极小偏差,但整体会吻合)。


内容的提问来源于stack exchange,提问作者H3d9

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 11:33:10