学破解 <二> 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,由此可以判顿读取出的结构是否正确。




  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值