为啥这个操作PE文件头的代码使用了这么奇怪的偏移值?

Posted

技术标签:

【中文标题】为啥这个操作PE文件头的代码使用了这么奇怪的偏移值?【英文标题】:Why does this code for manipulating PE file header use such strange offset value?为什么这个操作PE文件头的代码使用了这么奇怪的偏移值? 【发布时间】:2012-09-28 11:48:16 【问题描述】:

Here's a piece of code for obtaining the time when a .NET assembly was built。注意:

const int c_LinkerTimestampOffset = 8;

及以后:

int secondsSince1970 = System.BitConverter.ToInt32(b, i + c_LinkerTimestampOffset);

此代码提取存储在程序集中的IMAGE_FILE_HEADER structure 的TimeDateStamp 成员。结构定义如下:

typedef struct _IMAGE_FILE_HEADER 
    WORD  Machine;
    WORD  NumberOfSections;
    DWORD TimeDateStamp;
    DWORD PointerToSymbolTable;
    DWORD NumberOfSymbols;
    WORD  SizeOfOptionalHeader;
    WORD  Characteristics;
 IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

WORD 是两个字节,应该是两个字节对齐的。当我使用 Visual C++ 10 编译以下代码时:

IMAGE_FILE_HEADER header;
char* start = (char*)&header;
char* field = (char*)(&header.TimeDateStamp);
int diff = field - start;

diff 等于 4,正如我个人预期的那样。

这是 C# 代码中的错误吗?为什么使用8的偏移值?

【问题讨论】:

【参考方案1】:

它用于跳过附加签名,因为i 包含到 NT 标头的偏移量,而不是文件图像标头(参见 formal PE structure):

typedef struct _IMAGE_NT_HEADERS 
    DWORD Signature; //<- we need to skip this
    IMAGE_FILE_HEADER FileHeader;
    IMAGE_OPTIONAL_HEADER32 OptionalHeader;
 IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

所以实际总和是sizeof(WORD /* FileHeader.Machine */) + sizeof(WORD /* FileHeader.NumberOfSections */) + sizeof(DWORD /* Signature */)

底线,不是错误,只是跳过一些结构嵌套/内联的一些魔法。

【讨论】:

【参考方案2】:

DWORD 是一个 int 无符号值,4 个字节。

C# 代码不正确,它读取了 8 个字节。我认为应该是 4。

int secondsSince1970 = System.BitConverter.ToInt32(b, i + c_LinkerTimestampOffset);

几天前我写了一个PE阅读器。我将它用于“图像文件头”:

public class IMAGE_FILE_HEADER 

     public UInt16 Machine;
     public UInt16 NumberOfSections;
     public UInt32 TimeDateStamp;
     public UInt32 PointerToSymbolTable;
     public UInt32 NumberOfSymbols;
     public UInt16 SizeOfOptionalHeader;
     public UInt16 Characteristics;

【讨论】:

为什么DWORD突然变成了8个字节?【参考方案3】:

您很可能会得到 8 字节的差异,因为您使用的是 64 位系统/目标平台,如果您使用的是 32 位,那将是您所期望的。

在 32 位系统上,DWORD 定义为 uint(32 位/4 字节),而在 64 位系统上,它定义为 ulong(64 位/8 字节)。

一个字本质上代表架构的地址大小。当它被创造出来时,WORD 是 16 位,而 DWORD 是双字,32 位(4 字节)。

人们会期望 DWORD 在 x86 系统上为 64 位/8 字节,但 MS 希望保留旧版支持,因此保留了旧定义:http://msdn.microsoft.com/en-us/library/aa383751(VS.85).aspx

【讨论】:

不,DWORD 总是 4 个字节。

以上是关于为啥这个操作PE文件头的代码使用了这么奇怪的偏移值?的主要内容,如果未能解决你的问题,请参考以下文章

PE文件结构详解

逆向调试入门-PE中的VA与RVA换算04/07

PE文件格式偏移参考

PE文件格式偏移参考

逆向工程——PE

Android 逆向ELF 文件格式 ( 程序头偏移量 | 节区头偏移量 | 处理器特定标志 | ELF 文件头大小 )