学破解 <二> PE格式之IMAGE_NT_HEADERS

这个IMAGE_NT_HEADERS其实就是PE相关结构的映像头,NT据我揣测应该是New Technology的缩写,区分于DOS WIN9X的新技术,您老要是非觉得是NTR什么的也没关系。
IMAGE_NT_HEADERS的结构是这个样子的
 
IMAGE_NT_HEADERS STRUCT  
{  
+0h     DWORD    Signature 
+4h         IMAGE_FILE_HEADER    FileHeader 
+18h        IMAGE_OPTIONAL_HEADER32    OptionalHeader 
} IMAGE_NT_HEADERS ENDS 
其中包含两个子结构体,和一个标志。
其中Signature字段被设置成00004550h ,ASCII码为PE00 ,标志着PE头文件的开始。上一篇中DOS头结构体中的e_lfanew正是指向这里。
 
IMAGE_FILE_HEADER这个结构是这样的
 
typedef     struct _IMAGE_FILE_HEADER  

+04h    WORD        Machine;                                // 运行平台 
+06h    WORD        NumberOfSections;       // 文件的区块数目 
+08h    DWORD   TimeDateStamp;          // 文件创建日期和时间 
+0Ch    DWORD   PointerToSymbolTable;       // 指向符号表(主要用于调试) 
+10h    DWORD   NumberOfSymbols;        // 符号表中符号个数(同上) 
+14h    WORD        SizeOfOptionalHeader;       // IMAGE_OPTIONAL_HEADER32 结构大小 
+16h    WORD        Characteristics;            // 文件属性 
} IMAGE_FILE_HEADER,  *PIMAGE_FILE_HEADER; 
 
Machine 代表了CPU的类型 ,定义在windows.h中
 
  #define IMAGE_FILE_MACHINE_UNKNOWN  0 
#define IMAGE_FILE_MACHINE_I386    0x014c // Intel 386. 
#define IMAGE_FILE_MACHINE_R3000   0x0162 // MIPS little-endian, 0x160 big-endian 
#define IMAGE_FILE_MACHINE_R4000   0x0166 // MIPS little-endian 
#define IMAGE_FILE_MACHINE_R10000   0x0168 // MIPS little-endian 
#define IMAGE_FILE_MACHINE_WCEMIPSV2 0x0169 // MIPS little-endian WCE v2 
#define IMAGE_FILE_MACHINE_ALPHA   0x0184 // Alpha_AXP 
#define IMAGE_FILE_MACHINE_POWERPC  0x01F0 // IBM PowerPC Little-Endian 
#define IMAGE_FILE_MACHINE_SH3    0x01a2 // SH3 little-endian 
#define IMAGE_FILE_MACHINE_SH3E    0x01a4 // SH3E little-endian 
#define IMAGE_FILE_MACHINE_SH4    0x01a6 // SH4 little-endian 
#define IMAGE_FILE_MACHINE_ARM    0x01c0 // ARM Little-Endian 
#define IMAGE_FILE_MACHINE_THUMB   0x01c2 
#define IMAGE_FILE_MACHINE_IA64    0x0200 // Intel 64 
#define IMAGE_FILE_MACHINE_MIPS16   0x0266 // MIPS 
#define IMAGE_FILE_MACHINE_MIPSFPU  0x0366 // MIPS 
#define IMAGE_FILE_MACHINE_MIPSFPU16 0x0466 // MIPS 
#define IMAGE_FILE_MACHINE_ALPHA64  0x0284 // ALPHA64 
#define IMAGE_FILE_MACHINE_AXP64   IMAGE_FILE_MACHINE_ALPHA64 
 
NumberOfSection 代表区块的数目,区块表紧跟在IMAGE_NT_HEADERS后面,区块表大概是一个链表结构,链表长度由NumberOfSection的数值决定。
TimeDataStamp 表明文件的创建时间
SizeOfOptionalHeader 是IMAGE_NT_HEADERS的另一个子结构IMAGE_OPTIONAL_HEADER的大小,32位的PE文件这个值一般是00E0,64位的PE文件一般是00F0
Characteristics 代表文件的属性EXE文件一般是0100h DLL文件一般是210Eh,多种属性可以用或运算同时拥有。
 
#define IMAGE_FILE_RELOCS_STRIPPED     0x0001 // 重定位信息被移除 
#define IMAGE_FILE_EXECUTABLE_IMAGE     0x0002 // 文件可执行 
#define IMAGE_FILE_LINE_NUMS_STRIPPED    0x0004 // 行号被移除 
#define IMAGE_FILE_LOCAL_SYMS_STRIPPED   0x0008 // 符号被移除 
#define IMAGE_FILE_AGGRESIVE_WS_TRIM    0x0010 // Agressively trim working set 
#define IMAGE_FILE_LARGE_ADDRESS_AWARE   0x0020 // 程序能处理大于2G的地址 
#define IMAGE_FILE_BYTES_REVERSED_LO    0x0080 // Bytes of machine word are reversed. 
#define IMAGE_FILE_32BIT_MACHINE      0x0100 // 32位机器 
#define IMAGE_FILE_DEBUG_STRIPPED      0x0200 // .dbg文件的调试信息被移除 
#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400 // 如果在移动介质中,拷到交换文件中运行 
#define IMAGE_FILE_NET_RUN_FROM_SWAP    0x0800 // 如果在网络中,拷到交换文件中运行 
#define IMAGE_FILE_SYSTEM          0x1000 // 系统文件 
#define IMAGE_FILE_DLL           0x2000 // 文件是一个dll 
#define IMAGE_FILE_UP_SYSTEM_ONLY      0x4000 // 文件只能运行在单处理器上 
#define IMAGE_FILE_BYTES_REVERSED_HI     0x8000 // Bytes of machine word are reversed. 
 
下面来编写一个程序来显示一下这个结构体
 
#include "windows.h"                //PE的那一套结构体大多都是定义在这个头文件里的 
#include "stdio.h" 
 
int main(int argc, char* argv[]) 

    FILE *p; 
    LONG e_lfanew; //指向IMAGE_NT_HEADERS32结构在文件中的偏移 
    IMAGE_FILE_HEADER myfileheader; 
     
    p = fopen("test.exe","r+b"); 
    if(p == NULL)return -1; 
 
    fseek(p,0x3c,SEEK_SET); 
    fread(&e_lfanew,4,1,p); 
    fseek(p,e_lfanew+4,SEEK_SET); //指向IMAGE_FILE_HEADER结构的偏移PE的标志位是DWORD占4字节 
    fread(&myfileheader,sizeof(myfileheader),1,p); 
 
    printf("IMAGE_FILE_HEADER结构:\n"); 
    printf("Machine       : %04X\n",myfileheader.Machine); 
    printf("NumberOfSections  : %04X\n",myfileheader.NumberOfSections); 
    printf("TimeDateStamp    : %08X\n",myfileheader.TimeDateStamp); 
    printf("PointerToSymbolTable : %08X\n",myfileheader.PointerToSymbolTable); 
    printf("NumberOfSymbols   : %08X\n",myfileheader.NumberOfSymbols); 
    printf("SizeOfOptionalHeader : %04X\n",myfileheader.SizeOfOptionalHeader); 
    printf("Characteristics   : %04X\n",myfileheader.Characteristics); 
    getch(); 
    return 0; 

总的来说IMAGE_FILE_HEADER是记录文件的各种信息的
 
下面说一下IMAGE_OPTIONAL_HEADER结构,他是一个可选结构,是对IMAGE_FILE_HEADER的一个补充,当然很多情况下它是必须的。
 
typedef struct _IMAGE_OPTIONAL_HEADER  

    // 
    // Standard fields.   
    // 
+18h    WORD    Magic;         // 标志字, ROM 映像(0107h),普通可执行文件(010Bh) 
+1Ah    BYTE      MajorLinkerVersion;     // 链接程序的主版本号 
+1Bh    BYTE      MinorLinkerVersion;     // 链接程序的次版本号 
+1Ch    DWORD   SizeOfCode;     // 所有含代码的节的总大小 
+20h    DWORD   SizeOfInitializedData;    // 所有含已初始化数据的节的总大小 
+24h    DWORD   SizeOfUninitializedData; // 所有含未初始化数据的节的大小 
+28h    DWORD   AddressOfEntryPoint;    // 程序执行入口RVA 
+2Ch    DWORD   BaseOfCode;      // 代码的区块的起始RVA 
+30h    DWORD   BaseOfData;      // 数据的区块的起始RVA 
    // 
    // NT additional fields.    以下是属于NT结构增加的领域。 
    // 
+34h    DWORD   ImageBase;      // 程序的首选装载地址 
+38h    DWORD   SectionAlignment;      // 内存中的区块的对齐大小 
+3Ch    DWORD   FileAlignment;      // 文件中的区块的对齐大小 
+40h    WORD    MajorOperatingSystemVersion;  // 要求操作系统最低版本号的主版本号 
+42h    WORD    MinorOperatingSystemVersion;  // 要求操作系统最低版本号的副版本号 
+44h    WORD    MajorImageVersion;       // 可运行于操作系统的主版本号 
+46h    WORD    MinorImageVersion;       // 可运行于操作系统的次版本号 
+48h    WORD    MajorSubsystemVersion;  // 要求最低子系统版本的主版本号 
+4Ah    WORD    MinorSubsystemVersion;  // 要求最低子系统版本的次版本号 
+4Ch    DWORD   Win32VersionValue;       // 莫须有字段,不被病毒利用的话一般为0 
+50h    DWORD   SizeOfImage;       // 映像装入内存后的总尺寸 
+54h    DWORD   SizeOfHeaders;       // 所有头+ 区块表的尺寸大小 
+58h    DWORD   CheckSum;       // 映像的校检和 
+5Ch    WORD    Subsystem;       // 可执行文件期望的子系统 
+5Eh    WORD    DllCharacteristics;       // DllMain()函数何时被调用,默认为0 
+60h    DWORD   SizeOfStackReserve;       // 初始化时的栈大小 
+64h    DWORD   SizeOfStackCommit;       // 初始化时实际提交的栈大小 
+68h    DWORD   SizeOfHeapReserve;        // 初始化时保留的堆大小 
+6Ch    DWORD   SizeOfHeapCommit;        // 初始化时实际提交的堆大小 
+70h    DWORD   LoaderFlags;        // 与调试有关,默认为0  
+74h    DWORD   NumberOfRvaAndSizes;  // 下边数据目录的项数,这个字段自Windows NT 发布以来        // 一直是16 
+78h    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; 
       // 数据目录表 
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32; 
 
这个结构东西各种多啊···非常蛋疼,不过很多都是optional的,可以不用管他们,重要的就那么几个。
AddressOfEntryPoint 指出文件执行时的入口地址,是一个RVA地址,就是大家常说的OEP,常常各种寻找的OEP,如果你有什么代码要先于程序主体执行,只需要将这个入口指向这段代码就行啦~
ImageBase 指向文件的优先装入地址,一般情况EXE文件不需要重定位,DLL文件可能需要重定位。
SectionAlignment 和FileAlignment 确定了内存中的节对齐单位和在磁盘中的节对齐单位。
DataDirectory 成员是一个比较牛逼的成员,它由16个IMAGE_DATA_DIRCTORY结构组成,用来定义多种不通用处的数据块。
这个结构体的内容很简单
 
typedef struct _IMAGE_DATA_DIRECTORY { 
  DWORD VirtualAddress; 相对虚拟地址 
  DWORD Size;      大小 
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; 
只有相对虚拟地址和大小两个成员。
 
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; 
IMAGE_NUMBEROF_DIRECTORY_ENTRIES 的值代表了数据块的用途
 
#define IMAGE_DIRECTORY_ENTRY_EXPORT     0 // Export Directory 
#define IMAGE_DIRECTORY_ENTRY_IMPORT     1 // Import Directory 
#define IMAGE_DIRECTORY_ENTRY_RESOURCE    2 // Resource Directory 
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION   3 // Exception Directory 
#define IMAGE_DIRECTORY_ENTRY_SECURITY    4 // Security Directory 
#define IMAGE_DIRECTORY_ENTRY_BASERELOC   5 // Base Relocation Table 
#define IMAGE_DIRECTORY_ENTRY_DEBUG     6 // Debug Directory 
//   IMAGE_DIRECTORY_ENTRY_COPYRIGHT   7 // (X86 usage) 
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE  7 // Architecture Specific Data 
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR   8 // RVA of GP 
#define IMAGE_DIRECTORY_ENTRY_TLS      9 // TLS Directory 
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG  10 // Load Configuration Directory 
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 // Bound Import Directory in headers 
#define IMAGE_DIRECTORY_ENTRY_IAT      12 // Import Address Table 
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Delay Load Import Descriptors 
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 // COM Runtime descriptor 
 
最后把这些结构·读出来看一看。
 
#include "windows.h" 
#include "stdio.h" 
 
int main(int argc, char* argv[]) 

    FILE *p; 
    unsigned long Signature; 
    IMAGE_FILE_HEADER myfileheader; 
    IMAGE_DOS_HEADER mydosheader; 
    IMAGE_OPTIONAL_HEADER myoptionalheader; 
 
    p = fopen("test.exe","r+b"); 
    if(p == NULL)return -1; 
 
    fread(&mydosheader,sizeof(mydosheader),1,p); 
    fseek(p,mydosheader.e_lfanew,SEEK_SET); 
    fread(&Signature,sizeof(Signature),1,p); 
 
    fseek(p,mydosheader.e_lfanew+sizeof(Signature),SEEK_SET);//指向IMAGE_FILE_HEADER结构的偏移 
    fread(&myfileheader,sizeof(myfileheader),1,p); 
 
    fseek(p,mydosheader.e_lfanew+sizeof(Signature)+sizeof(myfileheader),SEEK_SET); 
    fread(&myoptionalheader,sizeof(myoptionalheader),1,p); 
 
    printf("%X\n",mydosheader.e_lfanew); 
 
    printf("Signature          : %04X\n",Signature); 
    printf("IMAGE_FILE_HEADER结构:\n"); 
    printf("Machine            : %04X\n",myfileheader.Machine); 
 
    printf("IMAGE_OPTIONALHEADER_HEADER结构:\n"); 
    printf("Magic       : %04X\n",myoptionalheader.Magic); 
 
由于这个IMAGE_OPENTIONAL_HEADER结构成员太多,我就不都打出来了,Magic的值一般为010bH,由此可以判顿读取出的结构是否正确。

posted @ 2012-04-18 23:27  r3call  阅读(2392)  评论(0编辑  收藏  举报