2016年4月5日X1h关于NT头的详细了解文章开始嘻嘻嘻-0-
首先DOS头结束之后我们顺着DOS头中的e_lfanew可以找到NT头开始的部分。(e_lfanew用来表示DOS头后面NT头相对文件起始地址的偏移)
那么我们就找到了NT头,找到之后我们都能获得哪些信息呢?
以下就是NT头的结构
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
- 其中Signature,其高16位是0,低16是0x4550。这就是NT头的标志,其内容就是”PE”。那么我们就可以通过在文件中搜索这个Signature来定位PE头的位置,如果定位不到那么我们可以暂且认为这不是一个PE文件了。
- 那么关于IMAGE_FILE_HEADER 这个结构到底是什么呢
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;
这就是我们查找到的这样一个结构
他的名字叫做PE文件头,比如说像Machine他就定义了这个PE文件是运行于哪个平台下,NuberOfSections则定义了节表的数量,TimeDateStamp则是说这PE文件创建的时间,SizeOfOptionalHeader则是定义了在下面的PE可选头的长度大小,Characteristics则是说明了这个PE文件的一些说明,要不然它为啥叫做PE文件头呢。
3.那么接下来也很重要的一个部分就是Optionalheader部分则是PE可选头,根据学习得知,虽然叫做可选头,但是他的重要性并不亚于PE文件头,虽然他的名字很不好听。
那么这个可选头的结构是一定要大概了解的。
typedef struct _IMAGE_OPTIONAL_HEADER {
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData;
DWORD ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
DWORD SizeOfStackReserve;
DWORD SizeOfStackCommit;
DWORD SizeOfHeapReserve;
DWORD SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
非常冗长的一部分,部分我感觉了解即可,知道有这么个信息存在然后我们需要的时候再进行查询即可,我对于以上重要的变量进行一下解释。
- 其中Magic则是可选头的类型。ex:32位可选头,64位可选头。
- SizeofCode则是代码段的长度
- SizeOfInitializedData初始化的数据长度;SizeOfUninitializedData未初始化的数据长度
- AddressOfEntryPoint主程序的入口RVA(我们在做程序破解的时候这个东东很重要的)
- BaseOfCode代码段的起始地址,BaseOfData数据段的起始地址
- ImageBase映像的基地址,但是如果对于DLL而言无法加载到这个地址上的时候我们则需要运用重定位来找到一个新的内存地址加载进去
- SectionAlignment节对齐,即加载到内存当中的时候,每一节的起始地址都必须是这个数的倍数
- FileAlignment文件对齐,即在文件中按照此值进行对齐,所以讲道理的话他的值一定要大于SectionAlignment
- SizeOfImage PE文件加载到内存当中的时候空间是连续的,这个数值定义了我们映像的大小,即在内存空间中的大小,这个值指定占用虚拟空间的大小(这句话没看明白说不定以后会明白)
- SizeOfHeaders所有文件头的大小,包括节表在内。他是按照FileAlignment方式对齐的
- CheckSum映像文件的校验和(这个可能会导致破解失败,我猜的,啥叫做映像文件的校验和呢?)
- NumberOfRvaAndSizes 数据目录的其中的项数
- DataDirectory数据目录,这是一个数据结构其中有 RVA还有size两个属性。这是个很重要的东东。他有个索引,他是一个负责定义了不同的数据结构的一个数据结构(我觉得我这个叙述说的还比较到位= =)
关于反编译的一些经验
n LoaderFlags = ABDBFFFFh (其默认值为0)
n NumberOfRvaAndSizes = DFFDEEEEh (其默认值为10h)
一些调试工具或反编译工具会认为这个PE文件是损坏的。有的会直接执行,如果是病毒的话,就会被直接感染;有的则会重启工具。所以最好在查看调试一个PE文件前,先看一下这里的取值是否被人赋予一个伪造的很大的值。如果是的话,先修改成默认的值。
l 有人可能注意到在一些PE文件(MS的链接器链接的PE文件)的DOS Stub部分跟PE header部分之间存在一部分垃圾数据。标识为其倒数第二非0的双字节是一个“Rich ”。这部分数据包含了一些加密数据,来标识编译这个PE文件
以上就是我关于NT头文件的三个部分的理解,这三个部分分别是PE签名,PE文件头,PE可选头。
接下来我们需要对一些表的结构进行学习