《逆向工程核心原理》学习笔记(六)
发布日期:2021-08-15 09:33:37 浏览次数:5 分类:技术文章

本文共 5228 字,大约阅读时间需要 17 分钟。

《逆向工程核心原理》学习笔记(六)

记录一下关于书中PE文件格式内容的学习(上部分)


 

PE(Portable Executable)格式,是微软环境可移植(如exe、dll、、sys和等)的标准文件格式。

 

PE格式衍生于早期建立在VAX(R)(R)上的COFF(Common Object File Format)文件格式。

 


 

 

PE文件种类

 


 

PE文件的基本结构 

 

 

 

 

.text默认的代码区块,它的内容全是指令代码,链接器把所有目标文件的text块连接成一个大的.text块,使用Borland C++,编译器产生的代码存放在CODE的区域里 .data默认的读/写数据块,全局变量,静态变量一般放在这个区段 .rdata默认只读数据区块,但程序中很少用到该块中的数据,一般两种情况用到,一是MS 的链接器产生EXE文件中用于存放调试目录,二是用于存放说明字符串,如果程序的DEF文件中指定了DESCRIPTION,字符串就会出现在rdata中 .idata包含其他外来的DLL的函数及数据信息,即输入表,将.idata区块合并成另一个区块已成为一种惯例,典型的是.rdata区块,默认的,链接器只在创建一个Release模式的可执行文件时才能将idata合并到另外一个区块中 .edata输出表,当创建一个输出API或数据的可执行文件时,连接器会创建一个.EXP文件,这个.EXP文件包含一个.edata区块,其会被加载到可执行文件中,经常被合并到.text或.rdata 区块中 .rsrc资源,包括模块的全部资源,如图标,菜单,位图等,这个区块是只读的,无论如何不应该把它命名为.rsrc以外的名字,也不能合并到其他的区块里 .bss未初始化的数据,很少在用,取而代之的是执行文件的.data区块的的VirtualSize被扩展大的空间里用来装未初始化的数据. .crt用于C++ 运行时(CRT)所添加的数据 .tlsTLS的意思是线程局部存储器,用于支持通过_declspec(thread)声明的线程局部存储变量的数据,这包括数据的初始化值,也包括运行时所需要的额外变量 .reloc可执行文件的基址重定位,基址重定位一般仅Dll需要的 .sdata相对于全局指针的可被定位的 短的读写数据 .pdata异常表,包含CPU特定的IAMGE_RUNTIME_FUNTION_ENTRY结构数组,DataDirectory中的IMAGE_DIRECTORY_ENTRY_EXCEPTION指向它. .didat延迟装入输入数据,在非Release模式下可以找到

 

 

 


 

 

关于PE文件中那些"地址"

 

这里我就用自己总结的白话来描述

 

虚拟地址(VA):

在一个程序运行起来的时候,会被加载到内存中,并且每个进程都有自己的4GB,这个4GB当中的某个位置叫做**虚拟地址**,由物理地址映射过来的,4GB的空间,并没有全部被用到。

 

基地址(Imagebase):

磁盘中的文件加载到内存当中的时候可以加载到任意位置,而这个位置就是程序的基址。EXE默认的加载基址是400000h,DLL文件默认基址是10000000h。需要注意的是基地址不是程序的入口点。

 

相对虚拟地址(RVA):

为了避免PE文件中有确定的内存地址,引入了相对虚拟地址的概念。RVA是在内存中相对与载入地址(基地址)的偏移量,所以你可以发现前三个概念的关系:虚拟地址(VA)= 基地址+相对虚拟地址(RVA)

 

文件偏移地址(FOA):

当PE文件储存在某个磁盘当中的时候,某个数据的位置相对于文件头的偏移量。

 

入口点(OEP):

首先明确一个概念就是OEP是一个RVA,然后使用OEP +Imagebase ==入口点的VA,通常情况下,OEP指向的不是main函数。

 

 

 

PS:32位的windows系统,各进程会分配4gb的虚拟内存,所以VA范围为00000000-FFFFFFFF

 

 

 




 

 

 

 

PE头


 

 


 

DOS头

 

每个PE文件都是以DOS头开始的,IMAGE_DOS_HEADER 结构如下所示:

(最左边是文件头的偏移量。)

 

IMAGE_DOS_HEADER STRUCT{+0h WORD    e_magic//   MZ(4Dh 5Ah)     DOS可执行文件标记+2h     WORD    e_cblp+4h WORD    e_cp+6h WORD    e_crlc+8h WORD    e_cparhdr+0ah    WORD    e_minalloc+0ch    WORD    e_maxalloc+0eh    WORD    e_ss+10h    WORD    e_sp+12h    WORD    e_csum+14h    WORD    e_ip+16h    WORD    e_cs+18h    WORD    e_lfarlc+1ah    WORD    e_ovno+1ch    WORD    e_res[4]+24h    WORD    e_oemid+26h    WORD    e_oeminfo+29h    WORD    e_res2[10]+3ch    DWORD   e_lfanew//  RVA     指向PE文件头} IMAGE_DOS_HEADER ENDS

需要关注的点是结构体的第一个和第二个元素。

 

 e_magic: DOS头的标记位,值为 4D5Ah 。ASCII为 ”MZ“ ,判断一个文件是否为PE文件是会用。

 

 e_lfanew: 这是一个RVA,代表了PE文件头到基址的偏移量,我们可以用它来找到PE文件头的位置。

 

我们用010editor打开一个exe文件:

 


 

 

 

 

PE文件头

IMAGE_NT_HEADERS STRUCT 结构体

 

IMAGE_NT_HEADERS STRUCT{+0h       DWORD    Signature+4h       IMAGE_FILE_HEADER    FileHeader+18h      IMAGE_OPTIONAL_HEADER32   OptionalHeader} IMAGE_NT_HEADERS ENDS

Signature 字段

在一个PE文件中Signature字段被设置为 4550h ,ASCII码为 ”PE00“ 。如上图所示。

 

IMAGE_FILE_HEADER 结构体

structIMAGE_FILE_HEADER{WORD Machine;//运行平台WORD NumberOfSections;//区块表的个数DWORD TimeDataStamp;//文件创建时间,是从1970年至今的秒数DWORD PointerToSymbolicTable;//指向符号表的指针DWORD NumberOfSymbols;//符号表的数目WORD SizeOfOptionalHeader;//IMAGE_NT_HEADERS结构中OptionHeader成员的大小,对于win32平台这个值通常是0x00e0WORD Characteristics;//文件的属性值}

 

在010 Editor上查看一下:

 

 

 

IMAGE_OPTIONAL_HEADER 结构体

typedefstruct_IMAGE_OPTIONAL_HEADER{//// Standard fields.  //+18h    WORD    Magic;// 标志字, ROM 映像(0107h),普通可执行文件(010Bh)+1Ah    BYTE    MajorLinkerVersion;// 链接程序的主版本号+1Bh    BYTE    MinorLinkerVersion;// 链接程序的次版本号+1Ch    DWORD   SizeOfCode;// 所有含代码的节的总大小+20h    DWORD   SizeOfInitializedData;// 所有含已初始化数据的节的总大小+24h    DWORD   SizeOfUninitializedData;// 所有含未初始化数据的节的大小+28h    DWORD   AddressOfEntryPoint;// 程序执行入口RVA+2Ch    DWORD   BaseOfCode;// 代码的区块的起始RVA+30h    DWORD   BaseOfData;// 数据的区块的起始RVA//// NT additional fields.    以下是属于NT结构增加的领域。//+34h    DWORD   ImageBase;// 程序的首选装载地址+38h    DWORD   SectionAlignment;// 内存中的区块的对齐大小+3Ch    DWORD   FileAlignment;// 文件中的区块的对齐大小+40h    WORD    MajorOperatingSystemVersion;// 要求操作系统最低版本号的主版本号+42h    WORD    MinorOperatingSystemVersion;// 要求操作系统最低版本号的副版本号+44h    WORD    MajorImageVersion;// 可运行于操作系统的主版本号+46h    WORD    MinorImageVersion;// 可运行于操作系统的次版本号+48h    WORD    MajorSubsystemVersion;// 要求最低子系统版本的主版本号+4Ah    WORD    MinorSubsystemVersion;// 要求最低子系统版本的次版本号+4Ch    DWORD   Win32VersionValue;// 莫须有字段,不被病毒利用的话一般为0+50h    DWORD   SizeOfImage;// 映像装入内存后的总尺寸+54h    DWORD   SizeOfHeaders;// 所有头 + 区块表的尺寸大小+58h    DWORD   CheckSum;// 映像的校检和+5Ch    WORD    Subsystem;// 可执行文件期望的子系统+5Eh    WORD    DllCharacteristics;// DllMain()函数何时被调用,默认为 0+60h    DWORD   SizeOfStackReserve;// 初始化时的栈大小+64h    DWORD   SizeOfStackCommit;// 初始化时实际提交的栈大小+68h    DWORD   SizeOfHeapReserve;// 初始化时保留的堆大小+6Ch    DWORD   SizeOfHeapCommit;// 初始化时实际提交的堆大小+70h    DWORD   LoaderFlags;// 与调试有关,默认为 0+74h    DWORD   NumberOfRvaAndSizes;// 下边数据目录的项数,这个字段自Windows NT 发布以来一直是16+78h    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];// 数据目录表} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

重要的有:

AddressOfEntryPoint: 也就是上文提到的OEP,程序源入口点。 ImageBase: 默认加载基址。 SectionAlignment: 内存当中的块对齐数,一般为0x1000。 FileAlignment:磁盘当中块对齐数,一般为0x200。 SizeOfHeaders:所有头部大小 也就是DOS头 文件头 以及区块头的总大小,文件主体相对文件其实的偏移。 IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]:数据目录表,保存了各种表的RVA及大小。

来看一下数据目录的定义:

 
IMAGE_DATA_DIRECTORY STRUCTVirtualAddress    DWORD       ?   ; 数据的起始RVASize             DWORD       ?   ; 数据块的长度IMAGE_DATA_DIRECTORY ENDS
 

 

 





 

 

 

 

最后附上pe文件格式的脑图

 

 

转载于:https://www.cnblogs.com/-qing-/p/10749198.html

转载地址:https://blog.csdn.net/weixin_30757793/article/details/99546723 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:sql server 获取存储过程,表值,标量函数的参数
下一篇:JDK1.7特性

发表评论

最新留言

哈哈,博客排版真的漂亮呢~
[***.90.31.176]2024年04月12日 08时45分04秒