图片 24

二进制文件概述

  按上表,比如计算虚拟内存中0x00404141处的一条指令,要换算出该指令在文件中的偏移量:

+———+———+———+———+———+———+

接下来我么就看一看这个数据目录表,数据目录表简单点说就是一个长度为16的IMAGE_DATA_DIRECTORY结构体数组而已

① 在代码中使用__declspec(dllexport)关键字

  内存管理器可以使进程在实际只有512MB物理内存的情况下使进程“认为”自己拥有4GB内存(其中包括代码,
栈空间,资源区,动态链接库等)。

+———+———+———+———+———+———+

基地址:

  • Base

《软件漏洞分析技术》笔记

文件虚拟偏移地址和文件物理偏移地址的计算公式如下:

文件偏移地址:

用于exe可执行文件、obj目标文件和dll动态链接库等文件格式。PE32+是PE的64位扩展,其并未添加额外结构,只是把原来32位的字段变成了64位。

  (1).文件偏移地址(File Offset):数据在PE文件中的地址,在磁盘上存放时相对于文件开头的偏移。

如VA = 00401000 (虚拟地址)

     
在WIN32中,可以使用HMODULE GetModuleHandle(LPCTSTR
lpModuleName)来获得一个模块的名称。当传递一个可执行文件或者DLL作为参数,

        ② 在def中只导出Ordinal而不导出符号名称  如:DllAdd @ 1 NONAME

  这种情况和实际生活中银行相似,你需要用的钱其实并不等于你拥有的财富,银行实际存有的金额数小于所有储户的财富和。


确地说,RVA就是当PE文件被装载到内存中后,某个数据的位置相对于文件头的偏移量。举个例子,如果Windows装载器将一个PE文件装入
00400000h处的内存中,而某个节中的某个数据被装入0040xxxxh处,那么这个数据的RVA就是(0040xxxxh-
00400000h)=xxxxh,反过来说,将RVA的值加上文件被装载的基地址,就可以找到数据在内存中的实际地址。

如果系统成功找到这个文件,就会返回该可执行文件或者DLL文件映像加载到的基地址。

图片 1

  一些PE工具也提供这类地址转化:Lord PE.

 

   
因为我们的文件是存储在磁盘上的,某个数据相对于文件头的偏移量就是这个数据的偏移地址,称为文件偏移地址(File
Offset)或者物理地址(RAW Offset),偏移地址的起始值是0。

且在载入外部模块未发生重定基址,则直接使用该地址来访问该符号;否则,使用IAT进行符号解析与查找。

图片 2

>>>>>>FileOffsetToVa( 文件偏移地址转虚拟地址)

 

按照基址生成出的绝对地址,如果目标地址被占用或基于安全考虑,则这些地址就需要重定位。

  (3).虚拟内存地址(Virtual
Address,VA):PE文件中的指令被装入内存后的地址。

 

 


一般来说,导出表、导入表、导入地址表存放在.rdata段(只读数据段)、重定位表存放在独立的.reloc段(重定位表段)

        = VA – Image Base – (相对虚拟偏移量 – 文件偏移量)

|  段名称   虚拟地址  虚拟大小  物理地址  物理大小   标志   |

   
为了让程序的载入更加的灵活-也为了在PE文件中出现有确定的内存地址,出现了相对虚拟地址(Relative
Vritual Address, RVA),当你的程序加载后,假设你的text块的RVA =
0x00001000h,映射到程序中时,VA(虚拟地址) =
ImagineBase(基地址)+RVA(相对虚拟地址),你的代码区块在内存中就开始与0x00401000h。

IAT中IMAGE_THUNK_DATA最高位为1,则低31位为导入符号的序号值;否则IMAGE_THUNK_DATA为指向IMAGE_IMPORT_BY_NAME结构的RVA。

      文件偏移量 = 0x00404141 – 0x00400000(默认的Image
Base)-(0x1000 – 0x400)(代码存于.text文件中) = 0x3541

>>>>>>>VaToFileOffset( 虚拟地址转文件偏移地址)

如上就是一个PE文件的结构图,PE文件使用的是一个平面地址空间,所有的数据都融合在一起,文件的内容又被分割为不同的区块(Section),

③ windows系统使用的x86的处理器,为小端序;WORD的二进制1A15实际的数值为:5402(数学写法为:151A);

  Windows内存:1.物理内存层面;2.虚拟内存层面。

FileOffset = VA – ImageBase – VRk = 00401325 – 00400000 – C00 = 725

     
当PE文件被装载器装载了之后,内存中的板块被称为模块。映射文件的起始地址被称为模块句柄—内存中的模块代表这进程从这个可执行文件中所需要的代码、数据、资源、输入表、输出表及其他东西所使用的东西放在一个连续的内存块中。在装载中,PE文件的一个字段会告诉系统把文件映射到内存需要多少内存,不能被映射的数据被放置在文件的尾部。

               DllAdd @ 1

  那么就可以计算出:

|  .rsrc   00004000   000003A0  00000A00  00000400  C0000040|

本次的了解主要讲解
PE的基本概念、MS-DOS文件头、PE文件头、区块、输入表、输出表等。


RVA (Relative Virtual Address):相对于PE文件装载基地址(Base
Address)的一个地址偏移。

  (4).相对虚拟地址(Relative Virtual
Address,RVA):内存地址相对于映射基址(即装载基址)的偏移量。

 

我们可以看到0x000000F8在偏移60字节的地方,看到PE文件头在0x000000F8,具体可以自己找一个文件来测试(加深印象)

(4)重定位表

  PE(Portable
Executable)是win32平台下可执行遵守的数据格式。平时常见的比如*.exe和*.dll都是PE文件。

 

     
在PE文件中,有一个字符设置了基地址,VC++建立的exe文件的基地址是0x00400000h,DLL文件的基地址是0x10000000h。

注1:在动态链接器刚完成映射还没有开始重定位和符号解析时,IAT中元素值表示相对应的导入符号的序号或者是符号名;当完成该模块链接时,元素值会被动态链接器改成该符号的真正地址。

  运行时操作系统会按PE文件的约定定位资源并装载入内存。可执行文件
——>拆分——>若干数据节<——不同的资源。

+———+———+———+———+———+———+

 LONG
e_lfanew是指向PE文件头的一个地址,它的文件偏移地址是0x3C–也就是60字节开始,然后占据了4个字节,指向PE文件头,具体看下图

导出方法:

  而在内存中,则以0x1000字节(4byte)为基本单位进行组织,其他与前者类似。

|  .rdata  00002000   000000F6  00000600  00000200  40000040|

 

导出符号:当前模块(DLL)导出供其他模块使用的函数和变量。

 

|  .data   00003000   0000018E  00000800  00000200  C0000040|

#ifdef _WIN64             //如果采用64的架构
    typedef IMAGE_OPTIONAL_HEADER64 IMAGE_OPTIONAL_HEADER;
    typedef PIMAGE_OPTIONAL_HEADER64 PIMAGE_OPTIONAL_HEADER;
#define IMAGE_SIZEOF_NT_OPTIONAL_HEADER IMAGE_SIZEOF_NT_OPTIONAL64_HEADER
#define IMAGE_NT_OPTIONAL_HDR_MAGIC IMAGE_NT_OPTIONAL_HDR64_MAGIC
#else  /* _WIN64 */      //如果不是采用64而是32位的架构
    typedef IMAGE_OPTIONAL_HEADER32 IMAGE_OPTIONAL_HEADER;
    typedef PIMAGE_OPTIONAL_HEADER32 PIMAGE_OPTIONAL_HEADER;
#define IMAGE_SIZEOF_NT_OPTIONAL_HEADER IMAGE_SIZEOF_NT_OPTIONAL32_HEADER
#define IMAGE_NT_OPTIONAL_HDR_MAGIC IMAGE_NT_OPTIONAL_HDR32_MAGIC
#endif /* _WIN64 */ //上面的typedef都是改变结构体的名称

    typedef struct _IMAGE_NT_HEADERS64 {//这里是64位的PE头的结构体的定义
      DWORD Signature;
      IMAGE_FILE_HEADER FileHeader;
      IMAGE_OPTIONAL_HEADER64 OptionalHeader;
    } IMAGE_NT_HEADERS64,*PIMAGE_NT_HEADERS64;

    typedef struct _IMAGE_NT_HEADERS {//这里是32位的PE头的结构体的定义
      DWORD Signature;
      IMAGE_FILE_HEADER FileHeader;
      IMAGE_OPTIONAL_HEADER32 OptionalHeader;
    } IMAGE_NT_HEADERS32,*PIMAGE_NT_HEADERS32;

PE文件结构:

  可理解为: 实际 = 基点 + 位移.

PE文件中出现RVA的
概念是因为PE的内存映像和磁盘文件映像是不同的,同一数据相对于文件头的偏移量在内存中和在磁盘文件中可能是不同的,为了提高效率,PE文件头中使用的
都是内存映像中的偏移量,也就是RVA。从图17.3中也可以得到另一个结论,那就是RVA仅仅是对于处于节中的数据而言的,对于文件头和节表来说无所谓
RVA和文件偏移,因为它们在被映射到内存中后不管是大小还是偏移都不会有任何改变。

 这个IMAGE_OPTIONAL_HEADER只需要关注一些关键字段就行了,记住。。

图片 3

  那万一有用户需要取出超出实际金额数怎么办,操作系统原理中有“虚拟内存”概念,
即在这种情况下有时会将“部分硬盘空间”暂时作为内存使用。(两者“虚拟内存“概念对象不一,不宜混淆)

|  .text   00001000   00000092  00000400  00000200  60000020|

  图片 4

导入方法:

  典型PE文件通常包含:.text(编译器产生,存放二进制代码,
反汇编和调试的对象)、.data(初始化数据块)、.idata(使用的外来函数如动态链接库与文件信息)、.rsrc(存放程序资源),还包括其他如.reloc、.edata、.tls、.rdata等。

如FileOffset = 435(文件偏移地址)

#define IMAGE_DIRECTORY_ENTRY_EXPORT 0              //Export Table
#define IMAGE_DIRECTORY_ENTRY_IMPORT 1        //Import Table              输入表这里比较重要
#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3
#define IMAGE_DIRECTORY_ENTRY_SECURITY 4
#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5
#define IMAGE_DIRECTORY_ENTRY_DEBUG 6
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8
#define IMAGE_DIRECTORY_ENTRY_TLS 9
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11
#define IMAGE_DIRECTORY_ENTRY_IAT 12          //IAT (import address table), 这里也很重要
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14

图片 5

虚拟内存:

VA = FileOffset + ImageBase + VRk = 435 + 00400000 + C00 = 00401035(虚拟地址)

接下来我们看一下IMAGE_OPTIONAL_HEADER这个结构体,一样下面的是这个结构体在winnt.h中的定义,下面这个是32位的,还有64位的,但是差不多的,可以看winnt.htypedef
struct _IMAGE_OPTIONAL_HEADER

每个导入模块(dll)使用一个IMAGE_IMPORT_DESCRIPTOR结构体存放,并以全0内容的IMAGE_IMPORT_DESCRIPTOR作为结尾。 

 

如VA = 00401325,则:

MS-DOS头部(IMAGE_DOS_HEADER):

数据目录

  在进行File
Offset和VA换算时,会由存储单位引起节基址差称为节偏移。比如:

ImageBase = 00400000 (基地址)

使用学习工具:有StudyPE、LordPE、PEID。

① 在dll的头文件中,对导出的函数和变量使用__declspec(dllimport)

PE文件与虚拟内存之间的映射

FileOffset = VA – ImageBase – VRk = 00401000 – 00400000 – C00 = 400(文件物理地址的偏移地址)

PE的第一阶段到这里,接下来会继续学习,增加熟悉度。。。。。。。。。。———–好好学习,天天向上!

注:导入地址表(IAT)存放着所有导入模块的地址信息(外部地址),各个模块之间用一个4字节的0隔开。模块的先后顺序不保证与导入表模块的顺序一致。 

图片 6

RVA是相对虚拟地址(Relative Virtual
Address)的缩写,顾名思义,它是一个“相对”地址,也可以说是“偏移量”,PE文件的各种数据结构中涉及到地址的字段大部分都是以RVA表示的。

图片 7

与COFF一样,PE也是基于段(Segment,注:有时也被叫节Section)的结构,

  FileOffset = VA – Image Base -节偏移

|  Name     VOffset    VSize    ROffset    RSize      Flags |

typedef struct _IMAGE_FILE_HEADER {
      WORD Machine;                     //这里定义的是运行平台,i386= 0x014Ch这个值,还有其他平台,看书吧。。
      WORD NumberOfSections;       //这个是标识区块的数目,紧跟在PE头的后面,也就是IMAGE_NT_HEADERS的后面
      DWORD TimeDateStamp;
      DWORD PointerToSymbolTable;
      DWORD NumberOfSymbols;
      WORD SizeOfOptionalHeader;     //这里表明了IMAGE_NT_HEADERS中的大小(RAW SIZE),32位一般是0x00E0, 64位PE+一般是0x00F0
      WORD Characteristics;       //普通的EXE是0x010fh, DLL文件是0x210Eh
    } IMAGE_FILE_HEADER,*PIMAGE_FILE_HEADER;

图片 8

  可执行文件:包含二进制代码,字符串,菜单,图标,位图字体等。

 

  在WIN上,32位的可执行文件是PE文件,64位的是PE32+文件
,DLL文件的格式和PE格式差不多,唯一的区别是PE和DLL的有一个字段标识这个文件是EXE还是DLL。

     
 主要的原因是段的最小单元为4KB,所以为了节省PE文件、内存占用及加快PE文件的映射过程,vc编译器会尽量将属性一致的段进行合并

  装载PE文件时,文件偏移地址(磁盘上)与RVA(内存上)有很大一致性(操作系统会尽量保持PE中各数据结构),那同样会有细微差异,由文件数据和内存数据的存放单位不同造成。

 

typedef struct _IMAGE_DATA_DIRECTORY {
      DWORD VirtualAddress;            //数据块的其实RVA,很重要
      DWORD Size;             //数据块的长度
    } IMAGE_DATA_DIRECTORY,*PIMAGE_DATA_DIRECTORY;

③ 链接器/export参数

图片 9

+———+———+———+———+———+———+

相对虚拟地址:

图片 10

 

源文档 <>

 

图片 11

        VA = Image Base + RVA;

VRk = VOffset – ROffset = 00001000 – 00000400 = C00 (得出文件虚拟地址和文件物理址之间的VRk值)

 学习PE建议看书。。和自己动手。。。

 图片 12

  (2).装载基址(Image Base):PE装入内存时的基地址。默认EXE文件在内存中的基地址为0x00400000,DLL为0x10000000。当然位置可由编译选项更改。

2、汇编中虚拟地址(VRA)与文件偏移地址(FileOffset)的相互转换:

各个区块按页的边界来对齐。每个块都有自己的属性(是否可读,是否可写,是否可执行等等)。

图片 13

  则有:

 

        ①
当在def文件中指定的符号Ordinal不连续时,会导致一些空地址(即:0地址)元素

PE文件:

#define IMAGE_DOS_SIGNATURE 0x5A4D
#define IMAGE_OS2_SIGNATURE 0x454E
#define IMAGE_OS2_SIGNATURE_LE 0x454C
#define IMAGE_VXD_SIGNATURE 0x454C
#define IMAGE_NT_SIGNATURE 0x00004550

#include "pshpack2.h"
//这里就是IMAGE_DOS_HEADER的结构了。
    typedef struct _IMAGE_DOS_HEADER {
      WORD e_magic; // DOS可执行文件标记“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;   // 指向PE文件头, “PE”,0,0
    } IMAGE_DOS_HEADER,*PIMAGE_DOS_HEADER;

注1:Base一般为1,AddressOfNames、AddressOfNameOrdinals指向的表的size一致,且里面元素一一对应

  在PE文件中,以磁盘数据标准存放(我们知道硬盘以一个section为基本单位,即512byte),0x200字节,当一个数据节不足0x200字节时填充0x00;

图片 14

程序编译后,变量与函数存储在PE文件中的位置如下: 

  物理内存通常内核级别ring0才能看到;通常用户模式下看见的为Windows用户态内存映射机制下的虚拟内存。

typedef struct _IMAGE_OPTIONAL_HEADER {

      WORD Magic;
      BYTE MajorLinkerVersion;
      BYTE MinorLinkerVersion;
      DWORD SizeOfCode;           //这里定义了包含代码区块的大小
      DWORD SizeOfInitializedData;    //这里定义了已经初始化的变量的区块的大小
      DWORD SizeOfUninitializedData;   //这里是未初始化的变量的区块的大小
      DWORD AddressOfEntryPoint;     //这里是程序入口的RVA(相对虚拟地址)
      DWORD BaseOfCode;          //这里是程序代码块的起始RVA
      DWORD BaseOfData;          //这里是数据块起始RVA
      DWORD ImageBase;           //这里是程序默认装入的基地址(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;   //这里定义了数据目录表的项数,一直保持为16
      IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; //这个是数据目录表,指向输入、输出表、资源块等数据,很重要
    } IMAGE_OPTIONAL_HEADER32,*PIMAGE_OPTIONAL_HEADER32;


PE
(Portable Executable):微软参考COFF(Common
Object File Format)规范,在Windows NT系统上制定的一种标准,

  默认情况下:一般PE文件的0字节
=》虚拟内存0x00400000位置,即所谓的装载地址。

 

       
若将这些导出函数的地址保存到模块的导入表中,就可以省去每次启动时符号解析过程,这种DLL性能优化方式被叫做DLL绑定(DLLBinding)。

  VA、Image Base、RVA之间关系:

 

typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header
    WORD   e_magic;                     // Magic number
    WORD   e_cblp;                      // Bytes on last page of file
    WORD   e_cp;                        // Pages in file
    WORD   e_crlc;                      // Relocations
    WORD   e_cparhdr;                   // Size of header in paragraphs
    WORD   e_minalloc;                  // Minimum extra paragraphs needed
    WORD   e_maxalloc;                  // Maximum extra paragraphs needed
    WORD   e_ss;                        // Initial (relative) SS value
    WORD   e_sp;                        // Initial SP value
    WORD   e_csum;                      // Checksum
    WORD   e_ip;                        // Initial IP value
    WORD   e_cs;                        // Initial (relative) CS value
    WORD   e_lfarlc;                    // File address of relocation table
    WORD   e_ovno;                      // Overlay number
    WORD   e_res[4];                    // Reserved words
    WORD   e_oemid;                     // OEM identifier (for e_oeminfo)
    WORD   e_oeminfo;                   // OEM information; e_oemid specific
    WORD   e_res2[10];                  // Reserved words
    LONG   e_lfanew;                    // File address of new exe header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_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;

typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD   VirtualAddress;
    DWORD   Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES    16

typedef struct _IMAGE_OPTIONAL_HEADER64 {
    WORD        Magic;
    BYTE        MajorLinkerVersion;
    BYTE        MinorLinkerVersion;
    DWORD       SizeOfCode;
    DWORD       SizeOfInitializedData;
    DWORD       SizeOfUninitializedData;
    DWORD       AddressOfEntryPoint;
    DWORD       BaseOfCode;
    ULONGLONG   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;
    ULONGLONG   SizeOfStackReserve;
    ULONGLONG   SizeOfStackCommit;
    ULONGLONG   SizeOfHeapReserve;
    ULONGLONG   SizeOfHeapCommit;
    DWORD       LoaderFlags;
    DWORD       NumberOfRvaAndSizes;
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;

typedef struct _IMAGE_OPTIONAL_HEADER {
    //
    // Standard fields.
    //

    WORD    Magic;
    BYTE    MajorLinkerVersion;
    BYTE    MinorLinkerVersion;
    DWORD   SizeOfCode;
    DWORD   SizeOfInitializedData;
    DWORD   SizeOfUninitializedData;
    DWORD   AddressOfEntryPoint;
    DWORD   BaseOfCode;
    DWORD   BaseOfData;

    //
    // NT additional fields.
    //

    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;

typedef struct _IMAGE_NT_HEADERS64 {
    DWORD Signature;
    IMAGE_FILE_HEADER FileHeader;
    IMAGE_OPTIONAL_HEADER64 OptionalHeader;
} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;

typedef struct _IMAGE_NT_HEADERS {
    DWORD Signature;
    IMAGE_FILE_HEADER FileHeader;
    IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

typedef struct _IMAGE_ROM_HEADERS {
    IMAGE_FILE_HEADER FileHeader;
    IMAGE_ROM_OPTIONAL_HEADER OptionalHeader;
} IMAGE_ROM_HEADERS, *PIMAGE_ROM_HEADERS;

#ifdef _WIN64
typedef IMAGE_NT_HEADERS64                  IMAGE_NT_HEADERS;
typedef PIMAGE_NT_HEADERS64                 PIMAGE_NT_HEADERS;
#else
typedef IMAGE_NT_HEADERS32                  IMAGE_NT_HEADERS;
typedef PIMAGE_NT_HEADERS32                 PIMAGE_NT_HEADERS;
#endif

///////////////////////////////////////////////////////////////////////////
#define IMAGE_SIZEOF_SHORT_NAME              8

typedef struct _IMAGE_SECTION_HEADER {
    BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];
    union {
        DWORD   PhysicalAddress;
        DWORD   VirtualSize;
    } Misc;
    DWORD   VirtualAddress;
    DWORD   SizeOfRawData;
    DWORD   PointerToRawData;
    DWORD   PointerToRelocations;
    DWORD   PointerToLinenumbers;
    WORD    NumberOfRelocations;
    WORD    NumberOfLinenumbers;
    DWORD   Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

 =_=..

我么接下来观看PE文件头中的IMAGE FILE_HEADER FileHeader这个结构体

(1)导出表

主要是IMAGE_FILE_HEADER和IMAGE_OPTIONAL_HEADER这两个结构体中的几个字段重要。

IAT、INT中的元素使用IMAGE_THUNK_DATA结构体存放,
并以全0内容的IMAGE_THUNK_DATA作为结尾。 

这里我将会结合一个简单的小程序来加深我对PE文件结构的了解。

因为PE文件在装载时被直接映射到进程的虚拟空间中运行,它是进程的虚拟空间的映像。所以PE文件很多时侯被叫做映像文件(Image
File)。

“This program cannot be run is MS-DOS
mode”,DOS头部中主要是WORD e_magic和 LONG
e_lfanew这个字段比较重要。这些数据结构可以在winnt.h中找到。

 

这是一个16位的数组,最有一个数组元素作为保留,全部为0,其他的从开头一直到倒数第二个数据都是已经规定好了的,我们看一下这个数据目录表成员

按照不同属性将信息分段存放,常见的段有:代码段(.text)、数据段(.data)、只读数据段(.rdata)、资源表(.rsrc)、重定位表(.reloc)等。

还有也可以使用LordPE的PE
editor来查看这个目录,现在的查看目录表的工具很多。。。。。

图片 15

在PE的文件头中,第一个DWORD Signature,
被定义为了0x00004550h,也就是”PE”这四个字符。这个标志没有什么作用。(DOS中指向的地方)。

导入符号:在当前模块使用,但在其他模块(DLL)中定义实现的函数和变量。

和对应的数据结构定义。

(3)ExeinfoPe

 DOS文件头就到这里了,接下来继续介绍PE头,也就是IMAGE_NT_HEADER,下面的代码是对于定义32还是64的PE头,我们可以看到相应的宏语句

注2:程序每次运行时,所有被依赖的dll都会被装载,然后一系列的导入导出符号依赖关系都会被重新解析。然而在大多数情况下,这些dll都会以同样的顺序被装载到同样的内存地址,所以它们的导出符号的地址都是不变的。

   每个PE文件是以一个DOS程序开始的,还有MZ
header之后的DOS
stub(DOS块)。如果这个可执行文件不能被这个系统支持,会打印一串提示符

注1:IMAGE_DOS_HEADER、IMAGE_NT_HEADER、IMAGE_SECTION_HEADER等都定义在winNT.h中
注2:IMAGE_DOS_HEADER和DOS
Stub是兼容DOS应用而存在的结构,PE文件的内容从IMAGE_NT_HEADER开始

PE文件:

        def文件符号导出如下:

注:BSS段存放的是未初始化的全局变量与静态变量(含全局和局部);以上代码,vc编译器会去除BSS段,将这部分内容直接放到数据段中

图片 16

② def文件

 (1)PE-Explorer

图片 17

PE文件工具

(2)导入表:

类型(高4位) 含义
0 无意义,仅用来做对齐用
1 高16位需要修正
2 低16位需要修正
3 32位都需要修正

图片 18

(2)LordPE

    假设落在段A中,则所求RVA在PE文件中offset为:段A的PointerToRawData +
(所求RVA – 段A的VirtualAddress)

图片 19

INT未绑定时,内容与IAT一样;绑定时,IMAGE_THUNK_DATA中存放的是导入符号运行时的虚拟内存地址,如果外部模块的时间戳与TimeDataStamp一致,

               g_nDll @ 3

注2:AddressOfFunctions指向的表的size
>= AddressOfNames指向的表的size,主要有以下两点原因导致:

图片 20

       
如:导出2个Orinal为1,3的符号,那么AddressOfFunctions指向的表的size为3(第2个元素为空地址),AddressOfNames指向的表的size为2

图片 21

(3)导入地址表

https://down.52pojie.cn/Tools/PEtools/

图片 22

DWORD的二进制B2356A18实际的数值为:409613746(数学写法为:186A35B2)

动态连接器使用IMAGE_IMPORT_BY_NAME的Hint值去定位符号在目标导出表中的位置,若刚好找到则命中,如果没命中就按照二分查找方法进行符号查找。

 

图片 23

注3:符号在导出地址表(EAT)中的索引号为:名字序号对应表中的Ordinal

② 链接dll的导入库

图片 24

图片 25


在PE文件中,将段表中各段的VirtualAddress(段的RVA)及VirtualSize(段的实际大小)与所求RVA比较,就可知该RVA落在哪个段中