灯火互联
管理员
管理员
  • 注册日期2011-07-27
  • 发帖数41778
  • QQ
  • 火币41290枚
  • 粉丝1086
  • 关注100
  • 终身成就奖
  • 最爱沙发
  • 忠实会员
  • 灌水天才奖
  • 贴图大师奖
  • 原创先锋奖
  • 特殊贡献奖
  • 宣传大使奖
  • 优秀斑竹奖
  • 社区明星
阅读:2243回复:0

学破解 *二* PE格式之IMAGE_NT_HEADERS

楼主#
更多 发布于:2012-08-24 20:03

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

喜欢0 评分0
游客

返回顶部