pe结构第一节

1.什么是可执行文件(executable file)

指的是由操作系统进行加载执行的文件。

windows平台PE(protable Executable)文件结构
Linux平台ELF(Executable Linking Format)文件结构

哪些领域用到PE文件格式:

1.病毒反病毒

2.外挂反外挂

3.加壳与脱壳(保护与破解)

2.如何识别PE文件?

1.PE文件的特征(PE指纹)

打开exe dll sys等观察前两个字节 PE文件的前两个字节为"MZ"(4D5A) 再看3c位置存储的地址偏移 ,看偏移处存储的值是否为“PE”字符(50 45)

 

3.PE文件整体结构

PE 结构学习,就是学习一些结构体

2.PE文件的两种状态

目的:学习如何从一个二进制文件拆分成PE文件格式

2.1PE文件的第一部分  DOS Header部分

该部分对应结构体为dos mz文件头IMAGE_DOS_HEADER(64个字节)、DOS STUB运行时跟在硬盘中的PE文件是有差异的。

dos HEADER部分如图所示:

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;

IMAGE_DOS_HEADER 的e_ifanew 储存的是 PE文件头地址,如上如 存储的是c0,所以PE头首地址是c0

2.1PE文件的第2部分  PE文件头

typedef struct _IMAGE_NT_HEADERS {
    DWORD Signature;//PE标识
    IMAGE_FILE_HEADER FileHeader;//标准PE头
    IMAGE_OPTIONAL_HEADER32 OptionalHeader;//扩展pe头
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

2.1.1  IMAGE_FILE_HEADER (20个字节)为标准PE头,结构如下:

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;

上图所示,从c0(pe头基址开始)往后数四个字节(Signature部分)后面的20个字节便是标准PE头

 

2.0.2  扩展PE头 IMAGE_OPTIONAL_HEADER32

IMAGE_OPTIONAL_HEADER32 (在32位下大小是224字节

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;

扩展PE头大小可改,大小可由标准PE头IMAGE_FILE_HEADER 中的 SizeOfOptionalHeader指定。如果是32位的PE程序,默认值是E0(十进制224),如果是64位程序,则存储的是F0。

 

扩展PE头如图所示:

 

2.3PE文件的第3部分  节表

  真的的数据存储在一个个节中,所以节表非常重要。

_IMAGE_SECTION_HEADER(大小为40个字节)

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头后再查40个字节便是第一个节表中的数据。

 

节表有n个成员,节表后面的数据是编译器加的垃圾数据。

节表后面有多少垃圾数据可以从 扩展pe头属性计算出来。_IMAGE_OPTIONAL_HEADER 下的SizeOfHeaders 值(dos头家上pe头加上节表按照文件对齐(FileAlignment)大小),查出来为0x200,查找SizeOfHeaders 为0x400.所以节表+pe头+dos头总共占用400地址空间

FileAlignment  pe中剩下的所有的节都按照这个200标准对齐。

此时做个实验最能直观:打开notpad++,用winhex打开硬盘中的notpad++.exe 然后再用winhex-工具-打开ram-找到notpad++.exe 发现因公暗中的0x400是第一个节的起始地址,而内存中第一个节的起始地址是0x1000(IMAGE_OPTIONAL_HEADER32 ->SectionAlignment)所以,pe文件在内存中和在硬盘中有两种状态。

                                    三. DOS头属性说明

插入上面已经插过的dos头结构体:

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;

实际上除了第一个跟最后一个成员,其它都没影响,删了,所以没必要了解

                                         04 标准PE头属性说明

typedef struct _IMAGE_FILE_HEADER {
    WORD    Machine;//标识cpu运行在什么cpu上。任意0 intel386以及后续 14c x64:8664
    WORD    NumberOfSections;//节的数量
    DWORD   TimeDateStamp;//编译器填写的时间搓
    DWORD   PointerToSymbolTable;//调试相关
    DWORD   NumberOfSymbols;//调试相关
    WORD    SizeOfOptionalHeader;//可选pe头大小(32位0xe0 64位0xf0)
    WORD    Characteristics;//文件属性
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

打开windows-system32 找到notepadd.exe 查找machine为8664,证明是在64为可运行

属性位含义如下图

                                              05  扩展PE头属性说明

在介绍扩展pe头属性之前,我们需要区别一下32位和64位程序,上述扩展pe头结构体是32位的,64位有一点不同。

//32位pe文件头

typedef struct _IMAGE_NT_HEADERS {
    DWORD Signature;//PE标识
    IMAGE_FILE_HEADER FileHeader;//标准PE头
    IMAGE_OPTIONAL_HEADER32 OptionalHeader;//扩展pe头
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

//64位pe文件头
typedef struct _IMAGE_NT_HEADERS64 {
    DWORD Signature;
    IMAGE_FILE_HEADER FileHeader;
    IMAGE_OPTIONAL_HEADER64 OptionalHeader;
} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;

实际上没什么太大差异,64位可选pe头展开为下:

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;

32位如下:

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

    WORD    Magic;                    //PE32:10B PE32+:20B
    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;               //内存中整个PE文件的映射尺寸,一定是内存对齐的整数倍
    DWORD   SizeOfHeaders;            //dos+pe+节表 大小 按文件对齐整数倍
    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;

                                             06  PE节表

  节表用来描述节的特性。节表是一个结构体数组,单个节表结构体总共40个字节,结构乳如下:

typedef struct _IMAGE_SECTION_HEADER {
    BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];    //ascall吗字符串 8字节
    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;

                                             07  RVA与FOA转换

此过程简单省略

                                             08  空白区添加代码

如图MessageBox(0,0,0,0)  硬编码为6A 00 6A 00 6A 00 6A 00 FF15 30834100

(有的od显示 的是FF15为E8,我观察了一下这个主要还是跟被调函数自身是 stdcall 还是cdecl相关, 在编译的时候就由编译器决定了.)

FF15后面这个值不是要跳转的地址,而是通过计算算出来的,公式如下:要跳转地址-指令当前地址-5=得到实际值

JMP硬编码是E9计算方法跟刚才是一样的。

所以构造出来的硬编码为:6A 00 6A 00 6A 00 6A 00 E8 00 00 00 00 E9 00 00 00 00 

插入至文件的0x300位置,也就是节区与头区之间的空白位置,如下图:

 

要跳转地址-指令当前地址-5=得到实际值

od拿到messagebox地址 0x77D66534

0x77D66534-0x309-imgbase(0x400000)-5=0x77966226

E9后面跳转的实际上是原程序的entryPoint 0x411104

0x411104 -0x30D-imgbase(0x400000)-5=0x00010DF2

然后把entryPoint改为现在的base = 0x300

imgbase 位于可选pe头第17个字节(原来是0x00011104)现在是0x300,修改后如图所示:

                                             09  扩大节

  如果添加的代码过多,空白区不够用,则需要扩大节。由于扩大前面的节,后面的偏移需要修正,所以最简单的方法是扩大最后一个节。手动扩大一个节步骤如下:

节表结构如下:

typedef struct _IMAGE_SECTION_HEADER {
    BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];    //ascall字符串 可自定义 共8个字节
    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文件最后一个节的实际大小,visualsize+扩大的字节数=n,把n按照内存对齐计算出值m,visualsize=m。

文件对齐值 = 文件对齐值+扩大的字节数。

更改sizeofimage=sizeofimage+扩大的字节数

                                                  10 新增节

1.新增节,复制第一个节的结构体一共40个字节,

2.更改numberofSections = numberofSections+1

3.磁盘文件新增字节(内存对齐的倍数,自然也是文件对齐的倍数)

4.更改新增节结构体 实际大小、文件对齐后大小 为新增字节大小,

5.更改内存偏移= 上一节内存偏移 + 上一节内存对齐大小,文件偏移 = 上一节文件偏移+上一节文件对齐大小

5.更改sizeofimage =sizeofimage + 新增字节

                                                  11  合并节

视频无声音,未完待续...

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值