Windows 笔记

 

windows 笔记

pe

由coff发展而来,32位称之为pe32,64位称之为pe+或者pe32+。

pe文件与elf文件差不多,也分为几类具体来说如下表:

种类 扩展名
可执行 exe,scr
dll,ocx,cpl,drv
驱动 sys,vxd
对象 obj

pe文件格式如下图:

pe文件格式

image-20210714161235160

头部信息相对于elf文件内容更多一些,从cos头到节区头都是头部信息,映射关系与elf大同小异。

1.dos头:主要为了向后兼容,大小为64字节,两个重要字节:e_magic:dos签名,e_lfanew:指示nt头的偏移

IMAGE_DOS_HEADER {
    WORD   e_magic;                // +0000h   -   EXE标志,“MZ”
    WORD   e_cblp;                 // +0002h   -   最后(部分)页中的字节数
    WORD   e_cp;                   // +0004h   -   文件中的全部和部分页数
    WORD   e_crlc;                 // +0006h   -   重定位表中的指针数
    WORD   e_cparhdr;              // +0008h   -   头部尺寸,以段落为单位
    WORD   e_minalloc;             // +000ah   -   所需的最小附加段
    WORD   e_maxalloc;             // +000ch   -   所需的最大附加段
    WORD   e_ss;                   // +000eh   -   初始的SS值(相对偏移量)
    WORD   e_sp;                   // +0010h   -   初始的SP值
    WORD   e_csum;                 // +0012h   -   补码校验值
    WORD   e_ip;                   // +0014h   -   初始的IP值
    WORD   e_cs;                   // +0016h   -   初始的CS值
    WORD   e_lfarlc;               // +0018h   -   重定位表的字节偏移量
    WORD   e_ovno;                 // +001ah   -   覆盖号
    WORD   e_res[4];               // +001ch   -   保留字00
    WORD   e_oemid;                // +0024h   -   OEM标识符
    WORD   e_oeminfo;              // +0026h   -   OEM信息
    WORD   e_res2[10];             // +0028h   -   保留字
    LONG   e_lfanew;               // +003ch   -   PE头相对于文件的偏移地址
  }

2.dos存根:stub,位于dos下方,可选且大小不固定,代码数据混合,整个DOS Stub是一个字节块,其内容随着链接时使用的链接器不同而不同.

3.nt头:结构体为IMAGE_NT_HEADERS,大小是f8,三个成员组成:

IMAGE_NT_HEADERS {
    DWORD Signature;                      // +0000h   -   PE文件标识,“PE00”
    IMAGE_FILE_HEADER FileHeader;                   // +0004h   -   PE标准头
    IMAGE_OPTIONAL_HEADER32 OptionalHeader;         // +0018h   -   PE扩展头
}

​ 签名结构体:值为0x50450000(PE00)

​ 文件头,表示文件大致属性,结构体为IMAGE_FILEE_HEADER,有四个重要成员:

IMAGE_FILE_HEADER {
    WORD    Machine;                             // +0004h   -   运行平台
    WORD    NumberOfSections;                    // +0006h   -   PE中节的数量
    DWORD   TimeDateStamp;                       // +0008h   -   文件创建日期和时间
    DWORD   PointerToSymbolTable;                // +000ch   -   指向符号表
    DWORD   NumberOfSymbols;                     // +0010h   -   符号表中的符号数量
    WORD    SizeOfOptionalHeader;                // +0014h   -   扩展头结构的长度
    WORD    Characteristics;                     // +0016h   -   文件属性

}

​ Machine:每个CPU都拥有唯一的Machine码,简容32位intel x86芯片的Machine码为14c。

​ NumberOfSections:指出文件中的节区数量

​ SizeOfOptionalHeader:指出结构体IMAGE_OPTIONAL_GEADER32的长度(32位系统)

​ Characteriss:表示文件属性,是否可运行,是否为dll等

​ 可选头,结构体为IMAGE_OPRTIONAL_HEADER32,九个重要成员:

IMAGE_OPTIONAL_HEADER {
    WORD    Magic;                                 // +0018h   -   魔术字107h = ROM Image,10bh = exe Image
    BYTE    MajorLinkerVersion;                    // +001ah   -   链接器版本号
    BYTE    MinorLinkerVersion;                    // +001bh   -   
    DWORD   SizeOfCode;                            // +001ch   -   所有含代码的节的总大小
    DWORD   SizeOfInitializedData;                 // +0020h   -   所有含已初始化数据的节的总大小
    DWORD   SizeOfUninitializedData;               // +0024h   -   所有含未初始化数据的节的大小
    DWORD   AddressOfEntryPoint;                   // +0028h   -   程序执行入口RVA
    DWORD   BaseOfCode;                            // +002ch   -   代码的节的起始RVA
    DWORD   BaseOfData;                            // +0030h   -   数据的节的起始RVA
    DWORD   ImageBase;                             // +0034h   -   程序的建议装载地址
    DWORD   SectionAlignment;                      // +0038h   -   内存中的节的对齐粒度
    DWORD   FileAlignment;                         // +003ch   -   文件中的节的对齐粒度
    WORD    MajorOperatingSystemVersion;           // +0040h   -   操作系统版本号
    WORD    MinorOperatingSystemVersion;           // +0042h   -   
    WORD    MajorImageVersion;                     // +0044h   -   该PE的版本号
    WORD    MinorImageVersion;                     // +0046h   -   
    WORD    MajorSubsystemVersion;                 // +0048h   -   所需子系统的版本号
    WORD    MinorSubsystemVersion;                 // +004ah   -   
    DWORD   Win32VersionValue;                     // +004ch   -   未用
    DWORD   SizeOfImage;                           // +0050h   -   内存中的整个PE映象尺寸
    DWORD   SizeOfHeaders;                         // +0054h   -   所有头+节表的大小
    DWORD   CheckSum;                              // +0058h   -   校验和
    WORD    Subsystem;                             // +005ch   -   文件的子系统
    WORD    DllCharacteristics;                    // +005eh   -   DLL文件特性
    DWORD   SizeOfStackReserve;                    // +0060h   -   初始化时的栈大小
    DWORD   SizeOfStackCommit;                     // +0064h   -   初始化时实际提交的栈大小
    DWORD   SizeOfHeapReserve;                     // +0068h   -   初始化时保留的堆大小
    DWORD   SizeOfHeapCommit;                      // +006ch   -   初始化时实际提交的堆大小
    DWORD   LoaderFlags;                           // +0070h   -   与调试有关
    DWORD   NumberOfRvaAndSizes;                   // +0074h   -   下面的数据目录结构的项目数量
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];     // 0078h   -   数据目录
}

​ magic:32为10B,64为20B

​ AddressOfEntryPoint:保存RVA值,指出程序最先执行的代码起始地址

​ ImageBase:指出文件的优先装入地址

​ SectionAlignment,FileAlignment:前者制定节区在内存中最小单位,后者代表磁盘中的最小单位。

​ SizeOfHeaders:整个pe文件的大小

​ SubSystem:区分系统驱动文件和可执行文件

​ NUmberOfRvaAndSize:指定datadirectory数组个数

​ Datadirectory:由IMAGE_DATA_DIRECTORY结构体组成的数组

IMAGE_DATA_DIRECTORY {
    DWORD   VirtualAddress;                 // +0000h   -   数据的起始RVA
    DWORD   Size;                           // +0004h   -   数据块的长度
}
  • 总的数据目录一共由16个相同的IMAGE_DATA_DIRECTORY结构连续排列在一起组成。
  • 如果想在PE文件中寻找特定类型的数据,就需要从该结构开始。
  • 这16个元组的数组每一项均代表PE中的某一个类型的数据,各数据类型为:

数组编号中文描述0导出表地址和大小1导入表地址和大小2资源表地址和大小3异常表地址和大小4属性证书数据地址和大小5基地址重定位表地址和大小6调试信息地址和大小7预留为08指向全局指针寄存器的值9线程局部存储地址和大小10加载配置表地址和大小11绑定导入表地址和大小12导入函数地址表地址和大小13延迟导入表地址和大小14CLR运行时头部数据地址和大小15系统保留

4.节区头

​ 节区头中定义了各个节区的属性,包括不同的特性、访问权限等等,结构体为IMAGE_SECTION_HEADER,五个重要成员:

​ Virtualsize:内存中节区所占大小

​ VirtualAddress:内存中节区起始地址(RVA)

​ SizeOfRawData:磁盘中文件各节区所占大小

​ Characteristic:节区属性

如何映射?1.查找RVA所在的节区

2.RAW - PointerToRawData = RVA - ImageBase

RAW = RVA - ImageBase + PointerToRawData

example:ImageBase为0x10000000,节区为.text,文件中起始地址为0x00000400,内存中的起始地址为0x01001000,RVA = 5000,RAW = 5000 - 1000 + 400 = 4400。

地址:

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

  • 相对虚拟内存地址(Reverse Virtual Address, RVA相对虚拟地址是内存地址相对于映射基址的偏移量。

  • 文件偏移地址(File Offset Address, FOA)数据在PE文件中的地址叫文件偏移地址,这是文件在磁盘上存放时相对于文件开头的偏移。

  • 装在基址(Image base)PE装入内存时的基地址。默认情况下,EXE文件在内存中的基地址时0x00400000, DLL文件是0x10000000。这些位置可以通过修改编译选项更改。

  • 虚拟内存地址、映射基址、相对虚拟内存地址的关系:

  • va = imagebase + rva

  • 文件偏移是相对于文件开始处0字节的偏移,相对虚拟地址则是相对于装载基址0x00400000处的偏移。(1)PE文件中的数据按照磁盘数据标准存放,以0x200字节为基本单位进行组织,PE数据节的大小永远是0x200的整数倍。(2)当代码装入内存后,将按照内存数据标准存放,并以0x1000字节为基本单位进行组织,内存中的节总是0x1000的整数倍。

  • 内存中数据节相对于装载基址的偏移量和文件中数据节的偏移量的差异称为节偏移。

  • 文件偏移地址 = 虚拟内存地址(VA) - 装载基址(Image Base) - 节偏移 
                 = RVA - 节偏移