欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

学写压缩壳心得系列之二 掌握PE结构 ,曲径通幽

学写压缩壳心得系列之二 掌握PE结构 ,曲径通幽

1.数据与指令,以及加载前期相关概念(已完成)
2.pe文件的结构解析(已完成)
3.pe文件load的过程(已完成)
4.壳的处理(已完成)
PE文件中的结构众多,但是其实,这些结构一多半是告诉loader怎么去加载自身PE的。正常的PE文件总是很严格去填充自身的内部结构,但是也有一小部分变形PE文件没有那么的中规中矩,所以PE结构中有的信息到了loader可能最终也只是起到了一个校验作用。在纵观PE整个大结构的时候,不应该去拘泥于每一个数据结构的特征,应该从大体上去把握它。本文试图通过loader加载方式的类比和举例的角度,介绍了loader是如何讲PE结构中相关重要数据进行加载的。因为介绍PE的优秀文章有很多加上作者水平很有限,如果有错误,请各位指正。
下面,就开始逐一的介绍相关重要的结构:
1.各种表头
  1.1 Dos表头
  1.2 pe文件头
  1.3 区块表头
2.导入表
3.导出表
4.重定位表
5.资源
6.附加数据

1.表头
一共包含了有三个类型的表头,一个是Dos表头,目的是为了兼容以前的平台。一个是pe文件头,包含了pe自身加载的各个配置信息,还有一个就是区块表头,包含pe文件各个区块的信息。整个表头,就给loader提供了一个这样的信息:“这个PE是什么属性,整个PE如何在内存分布的,又会被加载到哪里去“。
1.1 Dos表头
为了兼容以前的DOS平台,MS在设计的PE格式的时候考虑到了MS-DOS头部。其中有一个结构是MZ值,也就是相对偏移00处。在壳程序判断是否是PE文件的时候,这个是条件之一。另一个条件就是距离偏移0X3Ch处。这里保存了一个地址,这个地址指向了(PE header)PE文件头。这个才是装载器开始装载真正意义上的PE开始的地方。在相对于PE header偏移0h的地方,保存着一个“PE”标识。
根据上面两个信息,我们就可以判断被加壳的文件是否为PE文件了。
1.2 pe文件头
PE标识的下面一个偏移,就是FILE_HEADER了,它被称之为PE头,其中相对FILE_HEADER偏移0x06h的NumberOfSections表示的是文件的块数目。0x14h偏移处的SizeOfOptionalHeader表示的是可选头的大小,这个值可以用来定位节表可选头OptionalHeader的大小。
PE头0x18h偏移处就是OptionalHeader了。OptionalHeader是PE中相对重要的结构。虽然译做可选头,但是里面的很多选项都是必选的。
其中以下的结构是比较重要的:
AddressOfEntryPoint
指出文件载入内存后,代码开始执行的地方,也就是我们常说的OEP。例如我们对文件进行加壳之后,原来的入口点保存后,修改其中的值,指向外壳代码,使加壳的后PE能够先进行解压,再跳到原始的入口点继续运行。
ImageBase
指出文件的优先装入地址。Loader优先将文件装入到由ImageBase字段指定的地址中,若指定地址装载失效,才被装入到其他地址中。
EXE文件通常即指定,即装载,所以EXE总是能够按照这个地址装入。因为ms为每一个exe文件设定了单独的4GB进程空间,对于装载来说,exe的地位是优先的,所以总能够保证其装载ImageBase成功。
而DLL文件来说,不能保证优先装入地址没有被其他的DLL使用,因为一个exe可以装载不同的dll到其地址空间去,所以DLL通常含有重定位信息来进行二次定位,即该地址若被占用,调用重定位表的偏移,重新定位。
SectionAlignment 和 FileAlignment 
这里即是内存对齐粒度和文件对齐粒度,参见第一篇的阐述。
DataDirectory
数据表它由16个相同的IMAGE_DATA_DIRECTORY结构组成,因为PE文件中有很多的表块,例如导入表,导出表,重定位表等等,这样就需要有一个结构,保存着这些表的相关信息,来告诉Loader去加载哪里并且如何加载这些表。IMAGE_DATA_DIRECTORY结构指出了某种数据表的位置和长度。
1.3 区块表头
紧跟在IMAGE_NT_HEADERS后面的是区块表。在未载入这个块表信息之前Loader只知道根据前面提供的NumberOfSections,知道有多少个块,可是块的大小,块的各个偏移和名称却不知道,所以有一个结构来告诉Loader。这样loader就可以按照载入的规则将所以的块信息放入内存了。
注意:在进行区块装载的时候,有可能被装载的PE是个变形文件,所以,当其结构提供的数据代码和loader装载规律不一致的时候,就要按照loader对齐粒度来进行装载了。
那么如何读取了,首先要定位到各个头的起始位置(参见前面介绍的判断是否是PE文件中的方法定位),因为偏移是固定的,只需移动的固定的偏移读取即可。
至此,头表的重要结构都介绍完毕了,我们发现,这些结构都是按照固定偏移寻址读取的,告诉loader怎么去装载PE文件的,从宏观上,大致的表述了一个PE文件粗枝大叶的部分。可以说,这就如同之前所形容的那样,loader只是将这些作为数据来对待,进行读取,然后和自身的装载规则作比较。

下面就来介绍PE中比较重要的几个区块:
2.导入表
  Exe要用到外部DLL提供函数,在装载的时候,loader需要将各个函数的地址分配过去。在生成exe的时候,连接器为了统一一种形式将所有的调用函数统一成call [xxxxxxxx]的形式,而这个地址一开始会被PE文件预留,只有当要被加载的时候,loader进行填充真正的函数地址到xxxxxxxx地址处。
例如某一处的代码:

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途。
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功