关于pe结构

每一种操作系统它最重要的格式就是它的可执行文件格式,
因为操作系统就是为了支持这些文件而生成的,内核里面有很多机制,也是配合这种文件格式设计的。
换句话说,这种文件格式也是适合操作系统设计的。
比如: PE 它是 windows 下的文件格式,是 MZ 打头的(4D5A)只有两个字节,后面很大一片就是对这个结构体的管理,
比如:声音在什么位置,图像在什么位置,文字在什么位置,在前面这一片都是有记录的,
也就是说,前面开头不只是标志而已,前面这一片是一个结构体。
 
PE(Portable Execute)文件是Windows下可执行文件的总称,常见的有DLL,EXE,OCX,SYS等
 
1.pe文件的两种状态
pe文件,例如.exe文件,在磁盘中和内存中的状态是不同的;
 
例如:用winhex打开一个测试程序crackme.exe;
直接打开,和在内存中打开,比较两种状态的区别;
 
1)直接打开
运行winhex    ->file    ->open    ->选择crackme.exe
 
2)在内存中打开
运行winhex    ->运行crackme.exe使程序加载到内存    ->tools->    open Memory    ->找到crackme.exe
 
3)比较pe文件两种状态下的区别
1】开头部分
磁盘中:
内存中:
可以看到:
    两种状态下开头部分都一样;
    磁盘中的pe文件总是以00000000开头,而内存中的不一定;
 
2】分节处
磁盘中:
内存中:
可以看到:
    pe文件从某处开始,有一堆补0的地方,也就是留出来的空隙;
    空隙结束后,内存和磁盘中的内容一样;
    但内存中空隙比磁盘中的多,如上图,磁盘中空隙结束后到600;而内存中到1000;
 
4)结论
pe文件有两种状态:磁盘文件和内存映像;
pe文件被加载到内存中时可能被拉伸,但绝大多数数据还是一样的;
 
2.关于pe文件的分节
pe文件在磁盘和在内存中都是分为一段一段的;
段与段之间是空白区,用0填充;
这种机制就是分节,每一段数据就是一个节;
 
1)为什么要分节
1】节省硬盘空间;
任何一个exe程序都有一个独立的4gb的虚拟内存空间;(2的32次方为4gb,也就是寻址范围32位)
2g是给我们写的应用程序用的,还有2g是给操作系统用的;
因此不涉及到内存比硬盘贵的问题,因为是虚拟内存;
 
关于硬盘对齐和内存对齐:
    pe文件用对齐的方式保存;
    对齐可以提高读写速度;
    以前硬盘比较贵,一些老的编译器为了节省硬盘,可能存储方式为硬盘的对齐小于内存对齐;
    例如:硬盘对齐200h,内存对齐1000h时,pe文件可能是这样的:
    这样的pe结构在执行时会有一段拉伸过程;
 
因为硬盘水平发展的成本降低;可能也有一些新编译器编译出来的exe文件,硬盘中和内存中的对齐是一样的;
可以减少运算时间,相当于用空间换时间;
 
2】节省内存
解决应用程序多开的问题;
应用程序中有一些只读的数据,
无论开多少次这些数据都是一样的;
如果每开一次相同的程序,只读数据都要加载一次,会造成不必要的内存浪费;
分节机制可以将只读数据和可读写数据分开,多开时只复制一份可读写数据的节,而保存只读数据的节不复制;
 
例如:开5次exe
 
3.节表和pe头
节表(块表):
    PE文件分了很多个节,那每个节在文件中从哪里开始?有多大?在内存中从哪里开始,有多大?这些信息都保持在节表中;
 
pe头:
    对pe文件做概要性描述;
 
4.手动找头pe头文件
用winhex打开crackme.exe
 
1)doc头
pe结构最开始是dos头,根据dos头的结构和结构中数据的类型,对造上图填入对应的数据
注意:windows中数据是从高到低排列的,要注意顺序
struct _IMAGE_DOS_HEADER {
    0x00 WORD e_magic;            //5a4d    
    0x02 WORD e_cblp;             //0050               
    0x04 WORD e_cp;               //0002                 
    0x06 WORD e_crlc;             //0000   
    0x08 WORD e_cparhdr;          //0004  
    0x0a WORD e_minalloc;         //000f   
    0x0c WORD e_maxalloc;         //ffff   
    0x0e WORD e_ss;               //0000 
    0x10 WORD e_sp;               //00b8 
    0x12 WORD e_csum;             //0000   
    0x14 WORD e_ip;               //0000 
    0x16 WORD e_cs;               //0000 
    0x18 WORD e_lfarlc;           //0040 
    0x1a WORD e_ovno;             //001a   
    0x1c WORD e_res[4];           //0000,0000,0000,0000 
    0x24 WORD e_oemid;            //0000    
    0x26 WORD e_oeminfo;          //0000  
    0x28 WORD e_res2[10];         //0000,0000,0000,0000,0000,0000,0000,0000,0000,0000   
    0x3c DWORD e_lfanew;          //00000100
};
可以用petool来对比看是否找错
dos头的作用:
    当解析一个pe文件时,首先一定要解析头两个字节是否是5a4d;
    需要通过dos头去找真正的pe头部;
 
dos头中有两个重要的数据:
    第一个 e_magic    ->可执行文件是5a4d,对应mz;
    最后一个 e_lfanew    ->pe头的地址;
 
2)标准pe头
通过dos头最后一个字段的值找pe头部,也就是nt头:
    00000100:对应的前两个字节是50 45,即asci的pe
 
从dos头到pe头之间的数据是长度不确定的垃圾数据;
这段空间可以自由发挥,将自己的代码放上去之类的;
 
dos头紧接着的是nt头;
nt头结构:
struct _IMAGE_NT_HEADERS {
0x00 DWORD Signature;
0x04 _IMAGE_FILE_HEADER FileHeader;
0x18 _IMAGE_OPTIONAL_HEADER OptionalHeader;
};
nt头中包括两个内容:pe头和可选pe头;
 
Signature    ->pe标记,有4个字节,也就是50 45 00 00;
 
紧接pe标记的是标准pe头FileHeader,根据其结构从pe标记往后开始查找它的值:
struct _IMAGE_FILE_HEADER {
0x00 WORD Machine;                    //014c
0x02 WORD NumberOfSections;           //0006
0x04 DWORD TimeDateStamp;             //0ad92429   
0x08 DWORD PointerToSymbolTable;      //00000000  
0x0c DWORD NumberOfSymbols;           //00000000
0x10 WORD SizeOfOptionalHeader;       //00e0 
0x12 WORD Characteristics;            //818e
};
petools对比是否找错:
 
3)可选pe头
可选pe头虽然名字叫可选,但它其实很重要,甚至比标准pe头还重要;
标准pe头紧接的就是可选pe头,结构如下:
struct _IMAGE_OPTIONAL_HEADER {
0x00 WORD Magic;
0x02 BYTE MajorLinkerVersion;
0x03 BYTE MinorLinkerVersion;
0x04 DWORD SizeOfCode;
0x08 DWORD SizeOfInitializedData;
0x0c DWORD SizeOfUninitializedData;
0x10 DWORD AddressOfEntryPoint;
0x14 DWORD BaseOfCode;
0x18 DWORD BaseOfData;
0x1c DWORD ImageBase;
0x20 DWORD SectionAlignment;
0x24 DWORD FileAlignment;
0x28 WORD MajorOperatingSystemVersion;
0x2a WORD MinorOperatingSystemVersion;
0x2c WORD MajorImageVersion;
0x2e WORD MinorImageVersion;
0x30 WORD MajorSubsystemVersion;
0x32 WORD MinorSubsystemVersion;
0x34 DWORD Win32VersionValue;
0x38 DWORD SizeOfImage;
0x3c DWORD SizeOfHeaders;
0x40 DWORD CheckSum;
0x44 WORD Subsystem;
0x46 WORD DllCharacteristics;
0x48 DWORD SizeOfStackReserve;
0x4c DWORD SizeOfStackCommit;
0x50 DWORD SizeOfHeapReserve;
0x54 DWORD SizeOfHeapCommit;
0x58 DWORD LoaderFlags;
0x5c DWORD NumberOfRvaAndSizes;
0x60 _IMAGE_DATA_DIRECTORY DataDirectory[16];
};
 
 
 
posted @ 2019-10-14 18:54  L丶银甲闪闪  阅读(419)  评论(0编辑  收藏  举报