PE格式详解

PE文件的一般格式如下

                                     
IMAGE_DOS_HEADER  40H字节       //DOS 头
DOS STUB  70H字节               //DOS实模式残余程序
NT Signature  4H字节             //PE文件标志 
IMAGE_FILE_HEADER  14H字节      //PE文件头
IMAGE_OPTIONAL_HEADER  E0H字节  //PE可选头部
IMAGE_SECTION_HEADER(.text段结构属性) 28H字节 //.text段
IMAGE_SECTION_HEADER(.idata段结构属性)28H字节 //.idata段
IMAGE_SECTION_HEADER (.data段结构属性)28H字节 //.data段
.text段的代码开始       200H字节
.idata段的导入表开始 200H字节
.data段的数据开始      200H字节

 

我们用十六进制编辑器打开的一个DOS文件,我们发现有好多都为00只是为了对齐用的。

相对虚拟地址RVA(Relative Virtual Address)= 虛址地址VA(VirtualAddress)) - 基址BA(Base Address)
例VA=0x405000 ,BA=0x400000,那么RVA=0x405000 - 0x400000=5000

在十六进制编辑器里面:如果是2字节的地址0X7899 在十六进制工具里面为99 87低地址在前,高地址在后,如果是4字节的的地址OX55667788在十六进制工具里面则为88 77 66 55 我们取地址的也是高位在前低位在后。

 

--->DOS模式开始IMAGE_DOS_HEADER

typedef struct _IMAGE_DOS_HEADER{
   WORD    e_magic;//必须为4D 5A 字符为“MZ”
   WORD    e_cblp;
   WORD    e_cp;
   WORD    e_crlc;
   WORD    e_cparhdr;
   WORD    e_minalloc;
   WORD    e_maxalloc;
   WORD    e_ss;
   WORD    e_sp;
   WORD    e_csum;
   WORD    e_ip;  //DOS代码入口的IP
   WORD    e_cs;  //DOS代码入口的CS
   WORD    e_lfarlc;
   WORD    e_ovno;
   WORD    e_res[4];
   WORD    e_oemid;
   WORD    e_oeminfo;
   WORD    e_res2[10];
   LONG    e_lfanew; 
 // IMAGE_NT_HEADERS结构文件便宜地址..就是PE文件标志的偏移地址
//共占64个字节其中上面两个和最下面四个是固定的 其它可以用00填充…… 如下图


上IMAGE_DOS_HEADER这个结构最重要的是第一个成员e_magic和最后一个成员e_lfanew图大红色矩形为IMAGE_DOS_HEADER的范围工64字节  最后的四个字节为PE标志的偏移地址,图上可看到。
IMAGE_DOS_HEADER这个结构最重要的是第一个成员e_magic和最后一个成员e_lfanew

文件编移00000040—000000A0的共70H字节为DOS STUB,WINDOWS下并不用,我们相应保留绿色矩形的范围。

 

-->PE开始


PE格式详解

Characteristics:
0:置1表示文件中没有重定向信息
1:置1表示文该件是可执行文件EXE
2:置1表示没有行数信息
3:置1表示没有局部符号信息
8:表示希望机器为32位机
9:表示没有调试信息
11:置1表示程序不能上网运行
12:置1表示文件是一个系统文件,如驱动…………
13:置1表示文件是一个动态链接库DLL

-->PE可选头  IMAGE_OPTIONAL_HEADER

IMAGE_OPTIONAL_HEADER结构
typedef struct _IMAGE_OPTIONAL_HEADER {
  //
    // Standard fields.
    //

    WORD   Magic; //标志字,总是0B 01
    BYTE   MajorLinkerVersion; //链接器主版本号
    BYTE   MinorLinkerVersion; //链接器副版本号
    DWORD  SizeOfCode;         // 所有含有代码区块的总大小      
    DWORD  SizeOfInitializedData; //所有初始化数据区块总大小
    DWORD  SizeOfUninitializedData; //所有未初始化数据区块总大小

    DWORD  AddressOfEntryPoint; //OEP代码入口的RVA地址,

    DWORD  BaseOfCode; //代码区块起始RVA
    DWORD  BaseOfData; //数据区块起始RVA
    //
    // NT additional fields.
    //

    DWORD  ImageBase; //在内存中的基址 基址 00 00 40 00
    DWORD  SectionAlignment; //段加载后在内存的对齐值,一般为 00 10 00 00
    DWORD  FileAlignment; //段在文件中的对齐值       一般为 00 02 00 00
    WORD   MajorOperatingSystemVersion; //操作系统主版本号
    WORD   MinorOperatingSystemVersion; //操作次系统主版本号

    WORD   MajorImageVersion;          //用户自定义主版本号
    WORD   MinorImageVersion;          //用户自定义次版本号
    WORD   MajorSubsystemVersion; //子系统版本号基本是04 00 如果不是则没有3D风格
    WORD   MinorSubsystemVersion;  //子系统次版本号
    DWORD  Win32VersionValue;   //保留,通常设置为00 00
    DWORD  SizeOfImage; //代码的映像长度,,我们暂时还不知道耶,用00填充……
    DWORD  SizeOfHeaders; //所有文件头长度之和
    DWORD  CheckSum; //校验和
    WORD   Subsystem; //子系统 02 00 或03 00
    WORD   DllCharacteristics; //现实DLL特性的旗标
    DWORD  SizeOfStackReserve; //初始化实际提交堆栈大小
    DWORD  SizeOfStackCommit; //初始化保留堆栈大小
    DWORD  SizeOfHeapReserve; //初始化保留堆栈大小
    DWORD  SizeOfHeapCommit;  //初始化实际保留堆栈大小

    DWORD  LoaderFlags; // 与调试有关,默认为00 00 00 00
    DWORD  NumberOfRvaAndSizes;// 目录表个数,目录表是16个十六进制为10H   10 00 00 00每个都占8个字节共8*16个字节
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; 
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
SectionAlignment 在内存中是以1000倍往上前进的如果不足则用1000的倍数填充,如:如果对齐值是800 我们要填写为1000
FileAlignment  在段中以200倍往上前进的  如果不足则用200的倍数填充 如:如果对齐值是700 我们要填写为800
SizeOfHeaders  也是以1000为倍数,MZ,PE..的长度相加不足1000用1000填充一般不会大于1000,再加上段的个数*1000


PE格式详解

OEP的RVA=.text段在内存中的偏移地址-.text段在文件中的偏移地址+.text段FirstThunk在文件中的偏移地址。
VirtualAddress=.idata段在内存中的偏移地址-.idata在文件中的偏移地址+。idata段FirstThunk在文件中的偏移地址。

Size:我们只引入了二个结构数组,每个20个字节+最后一个全为00结尾,共60字节所以为:3C 00 00 00,全部填写00 应该可行的
--> NumberOfRvaAndSizes目录个数(可以从下面定义看到有16个,16进制值0x10)

#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
#define 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
                                             15   // 保留

可以看到我们上面只列到了二个表,IMAGE_DIRECTORY_ENTRY_EXPORT和IMAGE_DIRECTORY_ENTRY_IMPORT
并且只有IMAGE_DIRECTORY_ENTRY_IMPORT表有值(看00000130外的前8字节),可以知道一个表占可选头的8字节,它们按数组排列

现在我们要关注的是第二个

-->IMAGE_DATA_DIRECTORY DataDirectory

typedef struct _IMAGE_DATA_DIRECTORY{
DWORD VirtualAddress  ;表起始RVA地址
DWORD Size;表的大小,我们只引入二个结构数组每个20字节+最后一个全0结尾 
}IMAGE_DATA_DIRECTORY,*PIMAGE_DATA_DIRECTORY;
上面16个只有第二个表8字节有数字,其它全为00

我们暂时不知道第二是什么我们先用AA来代替了…………

 

-->各个段

就以.text为例

PE格式详解

typedef struct_IMAGE_SECTION_HEADER{     
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];节名称.text 8字节 2E 74 65 78 74 00 00 00
union{
       DWORD PhysicalAddress;//物理大小
       DWORD VirtualSize;                //两个一共4个字节。。
}Misc;
DWORD VitualAddress;//文件.text段在内存的偏移地址  就是RVA
DWORD SizeOfRawData;//.text段在文件中的长度(对齐后 以200为倍数)一般是00 02 00 00
DWORD PointerToRawData;//.text在文件中的偏移,就是十六进制编辑器里面的offset

DWORD PointerToRelocation;
DWORD PointerTOLinenmbers;
WORD  NumberOfRelocations;
WORD  NumberOfLinenumbers;
DWORD Characteristics;//节属性可执行...可读可写设置为:20 00 00 60
}IMAGE_SECTION_HEADER,*PIMAGE_SECTION_HEADER;

***********

IMAGE_SECTION_HEADER的最后一个成员Characteristics是段的属性代表如这段是否可读可写等

 

#define IMAGE_SCN_CNT_CODE                  0x00000020  // Section contains code .
#define IMAGE_SCN_CNT_INITIALIZED_DATA      0x00000040  // Section contains initialized data.
#define IMAGE_SCN_CNT_UNINITIALIZED_DATA    0x00000080  // Section contains uninitialized data.
#define IMAGE_SCN_LNK_NRELOC_OVFL           0x01000000  // Section contains extended relocations. 
#define IMAGE_SCN_MEM_DISCARDABLE           0x02000000  // Section can be discarded.
#define IMAGE_SCN_MEM_NOT_CACHED            0x04000000  // Section is not cachable.
#define IMAGE_SCN_MEM_NOT_PAGED             0x08000000  // Section is not pageable.
#define IMAGE_SCN_MEM_SHARED                0x10000000  // Section is shareable.
#define IMAGE_SCN_MEM_EXECUTE               0x20000000  // Section is executable.
#define IMAGE_SCN_MEM_READ                  0x40000000  // Section is readable.
#define IMAGE_SCN_MEM_WRITE                 0x80000000  // Section is writeable.

.text段000001CC处的4字节 20 00 00 60 是从下面的三个标志与得来的
#define IMAGE_SCN_CNT_CODE                  0x00000020  // 表示该段为代码段.
#define IMAGE_SCN_CNT_INITIALIZED_DATA      0x00000040  // 表示该段包含初始化数据
#define IMAGE_SCN_MEM_EXECUTE               0x20000000  // 表示该段可执行

.idata段000001F4处的4字节 40 00 00 40 是从下面的三个标志与得来的
#define IMAGE_SCN_CNT_INITIALIZED_DATA      0x00000040  // 表示该段包含初始化数据
#define IMAGE_SCN_MEM_READ                  0x40000000  // 表示该段可读

.data段0000021C处的4字节 40 00 00 C0 是从下面的三个标志与得来的
#define IMAGE_SCN_CNT_INITIALIZED_DATA      0x00000040  // 表示该段包含初始化数据
#define IMAGE_SCN_CNT_UNINITIALIZED_DATA    0x00000080  // 表示该段包含未初始化数据
#define IMAGE_SCN_MEM_READ                  0x40000000  // 表示该段可读

*************************

上面只包含了.text .idata .data三段,这些段名是可以改成别的名称。因为三段所以上面IMAGE_FILE_HEADER结构第二个成员NumberOfSections文件编移000000B6处二字节为03 00
如果有更多的段可以跟在上面三段的后面也是相同的结构

以前应该有注意到。文件对齐度为200 不足200的用00填充到200 有上图可以看到文件的偏移地址为210所以我们必须要填充到偏移地址为400的地方,OX00000400这行不填充…………

  读完段的结构后,我们就要读到各个段的机器码了,从上往下以次 .text为机器码对齐度为200,机器码只有25字节,不足用00填充 

-->机器码(.text段)

机器码:前五个字节为MeaageBoxA的机器码,接着4个字节为字符串的地址 ,然后是一个CALL 一个导入表的地址,接着是call一个ex的地址,地址为4个字节

PE格式详解
地址为:基址加上RVA

-->导入表处理(就是idata段)

PE格式详解

Typedef struct——IMAGE_IMPORT_DESCRIPTOR{

  Union {

     DWORD  Characteristics;          //和下面那个共占4个字节

     DWORD  OriginalFirstThunk;//指向IMAGE_THUNK_DATA32结构

}

   DWORD  TimeDateStamp;

   DWORD  ForwarderChain;

   DWORD  Name;   //user32.dll字符串的RVA地址

   DWORD  FirstThunk;// 指向IMAGE_THUNK_DATA32结构

}IMAGE_IMPORT_DESCRIPTOR;

_IMAGE_IMPROT_DESCRIPTOR第一个成员指向的机构,最后以一个全00的4字节结尾

   共8个字节

Typedef struct_IMAGE_THUNK_DATA32{

 Union{

      PBYTE   ForwarderString;

      PDWORD  Function;

      DWORD   Ordinal;

      PIMAGE_IMPORT_BY_NAME    AddressOfData;

      }u1;

}IMAGE_THUNK_DATA32; 

上面这个结构共20个字节。

--->然后是kernel32结构

和上面的那个结构是一样的 都占20个字节
--->最后是以一个全00结束的20字节。。。就是导入表结束要以全00结尾

 -->IMAGE_THUNK_DATA32结构

_IMAGE_IMPROT_DESCRIPTOR第一个成员指向MessageBoxA地址,最后以一个全00的4字节结尾

   共8个字节

Typedef struct_IMAGE_THUNK_DATA32{

 Union{

      PBYTE   ForwarderString;

      PDWORD  Function;

      DWORD   Ordinal;

      PIMAGE_IMPORT_BY_NAME    AddressOfData;

      }u1;

}IMAGE_THUNK_DATA32; 

 

 

 

-->最后完成字符串输入 函数前两位为函数号这里为00 00 最后对齐全写00 如下图

 

 PE格式详解

 PE格式详解

idata段结束

----最后只剩下一个data段了。。

PE格式详解

他也是一个节 ,也要有200字节的对齐度。。。。。

结束!!!!!!!!!!!!!!!!!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值