为何32位EXE加载到内存时.text节内容会被Windows修改?
32位Windows可执行文件.text节运行时地址修改的原因分析
现象回顾
文件中存储的.text节起始字节:
68 50 a0 d0 00 e8 3c 68 84 00 59 c3 cc cc cc cc ..
对应反汇编:
push 0xd0a050 call 0x846846 pop ecx ret int3 int3 int3 int3 ..
程序运行后,内存中.text节的对应字节变为:
68 50 a0 1b 01 e8 3c 68 84 00 59 c3 cc cc cc cc ..
对应反汇编:
push 0x11ba050 call 0x846846 pop ecx ret int3 int3 int3 int3 ..
可见push指令后的立即数地址发生了变化,而64位版本的可执行文件运行时,文件与内存中的.text节完全一致。
核心原因:32位PE的基地址重定位机制
- 32位程序的默认加载基地址:MSVC 2019 x86编译的32位Windows程序,默认编译基地址为
0x00400000。 - 动态加载地址调整:当系统加载该程序时,如果默认基地址已被其他进程占用,Windows加载器会将程序重新映射到一个未被占用的备选基地址。
- 绝对地址的重定位修正:你代码中
push的是一个硬编码的绝对内存地址,这类地址会被PE加载器识别为需要重定位的目标。加载器会读取PE文件中的重定位表,将原地址修正为实际加载基地址对应的目标地址。- 计算验证:原地址
0xd0a050与内存中地址0x11ba050的差值为0x4b0000,这正是实际加载基地址与默认基地址的偏移量(默认基地址0x00400000+0x4b0000= 实际加载基地址0x008b0000),修正后的地址 = 实际加载基地址 + (原地址 - 默认基地址),完全匹配内存中的值。
- 计算验证:原地址
64位版本无变化的原因
64位Windows程序默认采用地址无关代码(PIC),结合RIP相对寻址方式:
- 所有内存地址引用都是相对于当前指令指针(RIP)的固定偏移量,而非硬编码的绝对地址。
- 无论程序加载到哪个基地址,RIP的位置加上固定偏移就能得到正确的目标地址,因此不需要修改.text节的内容,文件与内存中的代码完全一致。
补充验证点
- 可以查看PE文件的重定位表(在PE可选头的Data Directory中找到Base Relocation Table),里面会记录所有需要修正的绝对地址位置。
- 如果编译32位程序时使用
/FIXED:YES链接选项,程序会强制要求加载到默认基地址,此时若基地址被占用程序会启动失败,且.text节不会被修改。
内容的提问来源于stack exchange,提问作者Alexander Trotsenko
相关产品推荐
相关产品推荐

