[读书][笔记]WINDOWS PE权威指南《零》PE基础

参考:

https://zhuanlan.zhihu.com/p/47075612

https://docs.microsoft.com/zh-cn/windows/win32/debug/pe-format

<加密与解密 第4版>

<Windows PE 权威指南>

PE 基础

概念

PE文件是windows系统中遵循PE结构的文件,比如以.exe .dll为后缀名的文件以及系统驱动文件sys。

具体PE文件类型:

可执行系列:EXE,SCR;
驱动程序系列:SYS,VXD;
库系列:DLL,OCX,CPL,DRV;
对象文件系列:OBJ;

装载PE文件

https://blog.csdn.net/benny5609/article/details/1788067
装载一PE文件的主要步骤:

  • 当PE文件被执行,PE装载器检查 DOS MZ header 里的 PE header 偏移量。如果找到,则跳转到 PE header。
  • PE装载器检查 PE header 的有效性。如果有效,就跳转到PE header的尾部。
  • 紧跟 PE header 的是节表。PE装载器读取其中的节信息,并采用文件映射方法将这些节映射到内存,同时付上节表里指定的节属性。
  • PE文件映射入内存后,PE装载器将处理PE文件中类似 import table(引入表)逻辑部分

PE组成

PE文件大体分为两部分:

头 (包括下图中的DOS头,PE文件头,块表 Section Table)
主体 ( 块Section)。
在这里插入图片描述

Section又被翻译为节 故有节表 节体叫法.

PE文件头结构

PE文件头结构
在这里插入图片描述

PE头 IMAGE_NT_HEADERS

在这里插入图片描述

扩展PE头/可选头(IMAGE_OPTIONAL_HEADER32)

在这里插入图片描述

数据目录项 (IMAGE_DATA_DIRECTORY)

组成:

  • 导出表、
  • 导入表、
  • 资源表、
  • 异常处理表、
  • 安全表、(Certificate Table)
  • 重定位表、
  • 调试表、
  • 版权、
  • 指针目录、
  • TLS、
  • 载入配置、
  • 绑定输入目录、
  • 导入地址表、
  • 延迟载入、
  • COM信息。

PE文件地址与VA(virtualAddress)之间的转换。

虚拟内存

文件偏移地址(File Offset Address) :文件相对于文件开头的偏移

装载基址(Image Base) :PE装入内存时的基地址,EXE在内存中的基地址是0x00400000,DLL的基地址是0x10000000,这些位置可以通过修改编译选项更改

虚拟内存地址 (Virtual Address, VA) :PE文件中的指令被装入内存后的地址,在Windows系统中,PE文件被系统加载器映射到内存中。每个程序都有自己的虚拟空间,这个虚拟空间的内存地址称为虚拟地址(Virtual Address,VA)。

相对虚拟地址(Relative Virtual Address,RVA) :相对虚拟地址是内存地址相对于映射基地址偏移量

虚拟内存地址、装载基址、相对虚拟地址之间的关系:

VA= Image base+ RVA

节偏移

节偏移 :PE文件中的数据按照磁盘数据标准存放,以0x200字节为基本单位进行组织,当PE文件装载到内存时,将按照内存数据标准存放,以0x1000字节为基本单位,所以文件偏移地址和相对虚拟内存会有细微的差别,这种差别称为节偏移

SectionAlignment : 内存当中的块对齐数,一般为0x1000。

FileAlignment :磁盘当中块对齐数,一般为0x200。

文件偏移地址、虚拟内存地址、装载基址、相对虚拟地址和节偏移之间的关系:

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

入口点(OEP)

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

PE 文件磁盘与内存映像

PE文件不是作为单一内存映射文件,被载人内存是很重要的。Windows加载器(又称PE装载器)遍历PE文件并决定文件的哪一部分被映射,这种映射方式是将文件较高的偏移位置映射到较高的内存地址中。磁盘文件一旦被载入内存,磁盘上的数据结构布局和内存中的数据结构布局就是一致的。
在这里插入图片描述

当PE文件通过Windows加载器载人内存后,PE在内存中的版本称为模块(Module)。
映射文件的起始地址称为模块句柄(hModule),可以通过模块句柄访问内存中的其他数据结构。这个初始内存地址也称为基地址(ImageBase)。

可执行程序在内存中的组成

一般情况下,一个可执行C程序在内存中主要包含5个区域,分别是代码段(text)数据段(data)BSS段堆段(heap)栈段(stack)。其中前三个段(text,data,bss)是程序编译完成就存在的,此时程序并未载入内存进行执行。后两个段(heap,stack)是程序被加载到内存中时,才存在的。具体的样子可以如下图所示:

未初始化的全局变量和静态变量,存储在bss区, 未初始化的局部变量保存在栈区。

在这里插入图片描述

各段在加载到内存中所存储的内容

代码段(text):就是C程序编译后的机器指令,也就是我们常见的汇编代码。

数据段(data):用来存放显式初始化的全局变量或者静态(全局)变量,常量数据。

BSS段(Block Started by Symbol): 存储未初始化的全局变量或者静态(全局)变量。编译器给处理成0;

栈段(stack):存放函数调用相关的参数、局部变量的值,以及在任务切换的上下文信息。栈区是由操作系统分配和管理的区域。

堆段(heap): 动态内存分配的区域,也就是malloc申请的内存区,使用free()函数来释放内存,堆的申请释放工作由程序员控制,容易产生内存泄漏。

包含data段和bss段的整个区段此时通常称为数据区

几种存储类型在内存中,分别被分配在哪一段存储空间。(auto, extern, static, register,) 参考此文献
auto存储类型:auto只能用来标识局部变量的存储类型,对于局部变量,auto是默认的存储类型,不需要显示的指定。因此,auto标识的变量存储在栈区中。

extern存储类型:extern用来声明在当前文件中引用在当前项目中的其它文件中定义的全局变量。如果全局变量未被初始化,那么将被存在BBS区中,且在编译时,自动将其值赋值为0,如果已经被初始化,那么就被存在数据区中。

register存储类型:声明为register的变量在由内存调入到CPU寄存器后,则常驻在CPU的寄存器中,因此访问register变量将在很大程度上提高效率,因为省去了变量由内存调入到寄存器过程中的好几个指令周期。在C++中,例如 while(i–){}; 对变量 i 有频繁的操作,编译器会将其存储在寄存器中。

static存储类型:被声明为静态类型的变量,无论是全局的还是局部的,都存储在数据区中,其生命周期为整个程序,如果是静态局部变量,其作用域为一对{}内,如果是静态全局变量,其作用域为当前文件。静态变量如果没有被初始化,则自动初始化为0。静态变量只能够初始化一次。

字符串常量:字符串常量存储在数据区中,其生存期为整个程序运行时间,但作用域为当前文件。

原文链接

总结

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

二进制怪兽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值