如何基于节对齐字段重对齐x64 PE文件并计算节虚拟地址
x64 PE手动调整节对齐为0x10000的节虚拟地址正确计算方法
你之前对比VS编译产物得到的结论大部分是对的,手动修改失败核心是节VA递推逻辑错了,漏了对齐规则的硬约束,以下是可直接复用的计算逻辑:
前置规则
当x64 PE的SectionAlignment设为大于等于0x1000(页大小)的值(包括0x10000)时:
PointerToRawData、SizeOfRawData两个字段仅受FileAlignment约束,完全不需要修改,直接保留原0x1000对齐版本的取值即可,和VS的行为一致。- 节的
VirtualSize是节在内存中实际占用的未对齐大小,不需要对齐到0x10000,直接保留原值即可。 - 所有虚拟地址的对齐操作,都是将地址向上取整到SectionAlignment的整数倍,没有其他特殊偏移。
逐节VA计算步骤
- 计算第一个节的起始VA
先统计PE头总大小:即从映像基址偏移0开始,到节表末尾的总长度,将这个长度向上取整到0x10000,得到的值就是第一个节的合法起始VA。
绝大多数普通x64程序的PE头总大小不会超过1KB,所以取整后第一个节VA固定为0x10000,和你观察到的VS输出一致。BaseOfCode字段直接填第一个可执行节(通常是.text)的VA即可。 - 递推后续所有节的VA
从第一个节开始,按节表顺序逐个计算:- 先算出当前节前一个节的内存结束地址:
前一个节的VirtualAddress + 前一个节的VirtualSize - 将这个结束地址向上取整到0x10000的整数倍,结果就是当前节的正确VirtualAddress
注意:这里绝对不能用
SizeOfRawData代替VirtualSize计算结束地址,这是手动修改最容易犯的错,会直接导致节内存范围重叠,PE加载失败。 - 先算出当前节前一个节的内存结束地址:
- 计算最终
SizeOfImage
取最后一个节的内存结束地址(最后一个节的VirtualAddress + 最后一个节的VirtualSize),向上取整到0x10000的整数倍,这个值就是正确的SizeOfImage,不能直接在原SizeOfImage基础上硬凑对齐值。
计算示例
以典型的Notepad++版本通用节布局为例,原0x1000对齐下的节参数:
- .text:VirtualAddress=0x1000,VirtualSize=0x79800
- .rdata:VirtualAddress=0x7B000,VirtualSize=0x34A00
- .data:VirtualAddress=0xB0000,VirtualSize=0x11C00
- .rsrc:VirtualAddress=0xC2000,VirtualSize=0x26000
调整为0x10000节对齐后的正确参数:
- .text:VirtualAddress=0x10000,其余字段(包括Raw相关、属性、VirtualSize)不变
- .rdata:上一节结束地址=0x10000+0x79800=0x89800,取整到0x10000为0x90000,所以VA=0x90000
- .data:上一节结束地址=0x90000+0x34A00=0xC4A00,取整到0x10000为0xD0000,所以VA=0xD0000
- .rsrc:上一节结束地址=0xD0000+0x11C00=0xE1C00,取整到0x10000为0xF0000,所以VA=0xF0000
- SizeOfImage:最后一节结束地址=0xF0000+0x26000=0x116000,取整到0x10000为0x120000
常见失效原因排查
如果按上述方法计算后PE仍无法运行,优先检查两个点:
- 确认修改字段时没有误改相邻的PE头字段,比如x64 OptionalHeader的
Magic值必须保持为0x20B,节属性字段不要改动 - 原PE必须包含完整的重定位表(即编译时开启了
/DYNAMICBASE选项,绝大多数现代VS编译程序默认开启),否则节VA变动后没有重定位修正,程序会直接崩溃。
内容的提问来源于stack exchange,提问作者tzaxast
相关产品推荐
相关产品推荐

