没有合适的资源?快使用搜索试试~ 我知道了~
资源推荐
资源详情
资源评论
本章提要
文件格式概述
文件结构
如何获取 文件中的
如何获取 文件中的资源
如何修改 文件使其显示 的实例
2.1 引言
通常 下的 文件都采用 格式。 是英文 的缩写,它是一种
针对于微软 、 和 系统,由微软公司设计的可执行的二进制文件
(!"" 和执行程序)格式,目标文件和库文件通常也是这种格式。这种格式由 #$(#%
$)委员会(%、#、、&、# 等)在 ' 进行了标准化。显然,
它参考了一些 (# 和 )$ 的 *++(*&&,++&)格式。
认识可执行文件的结构非常重要,在 !$ 下是这样,在 系统下更是如此。了解了这种结
构后就可以对可执行程序进行加密、加壳和修改等,一些黑客也利用了这些技术。为了使读者对 文件
格式有进一步的认识,本章从一个程序员的角度出发再次介绍 文件格式。如果已经熟悉这方面的知识,
可以跳过这一章。
2.2 PE 文件格式概述
认识 文件,既要懂得它的结构布局,又要知道它是如何装载到计算机内存中的。下面分别对它们进
行说明。
PE 文件结构总体层次分布
2.2.1 PE 文件结构布局
1
找到文件中某一结构信息有两种定位方法。第一种是通过链表方法,对于这种方法,数据在文件的
存放位置比较自由。第二种方法是采用紧凑或固定位置存放,这种方法要求数据结构大小固定,它在文件
中的存放位置也相对固定。在 文件结构中同时采用以上两种方法。
因为在 文件头中的每个数据结构大小是固定的,因此能够编写计算程序来确定某一个 文件中
的某个参数值。在编写程序时,所用到的数据结构定义,包括数据结构中变量类型、变量位置和变量数组
大小都必须采用 提供的原型。图 -' 所示的 文件结构的总体层次分布如下:
!$./
所有 文件(甚至 位的 !"")必须以简单的 !$.0 开始,它是一个
#123!$3/1!4 结构。有了它,一旦程序在 !$ 下执行,!$ 就能识别出这是有效的执行体,
然后运行紧随 ./ 之后的 !$$。
!$$
!$$ 实际上是个有效的 ,在不支持 文件格式的操作系统中,它将简单显示一个错误
提示,类似于字符串“05&67或者程序员可根据自己的意图实现完整的 !$
代码。大多数情况下 !$$ 由汇编器8编译器自动生成。
/
紧接着 !$$ 的是 /。它是一个 #1233/1!4$ 结构。其中包含了很多
文件被载入内存时需要用到的重要域。执行体在支持 文件结构的操作系统中执行时, 装载器将从
!$.0 中找到 0 的起始偏移量。因而跳过 !$$ 直接定位到真正的文件头
0。
$
/ 之后是数组结构 $(节表)。如果 文件里有 个节,那么此 $
结构数组内就有 个(#123$*#3/1!4)成员,每个成员包含对应节的属性、文件偏
移量、虚拟偏移量等。排在节表中的最前面的第一个默认成员是 ,即代码节头。通过遍历查找方法
可以找到其他节表成员(节表头)。
$
文件的真正内容划分成块,称为 $(节)。每个标准节的名字均以圆点开头,但也可以
不以圆点开头,节名的最大长度为 9 个字节。$ 是以其起始位址来排列,而不是以其字母次序来
排列。通过节表提供的信息,可以找到这些节。程序的代码,资源等就放在这些节中。
节的划分是基于各组数据的共同属性,而不是逻辑概念。每节是一块拥有共同属性的数据,比如代
码8数据、读8写等。如果 文件中的数据8代码拥有相同属性,它们就能被归入同一节中。节名称仅仅是
个区别不同节的符号而已,类似“7:;7的命名只为了便于识别,唯有节的属性设置决定了节的特
性和功能。
2.2.2 PE 文件内存映射
在 系统下,当一个 应用程序运行时,这个 文件在磁盘中的数据结构布局和内存中
的数据结构布局是一致的。系统在载入一个可执行程序时,首先是 装载器(又称 装载器)
把磁盘中的文件映射到进程的地址空间,它遍历 文件并决定文件的哪一部分被映射。其方式是将文件
较高的偏移位置映射到较高的内存地址中。磁盘文件一旦被装入内存中,其某项的偏移地址可能与原始的
偏移地址有所不同,但所表现的是一种从磁盘文件偏移到内存偏移的转换,如图 - 所示。
2
PE 文件内存映射
当 文件被加载到内存后,内存中的版本称为模块(),映射文件的起始地址称为模块句
柄(0),可以通过模块句柄访问内存中的其他数据结构。这个初始内存地址也称为文件映像基
址(#&)。载入一个 程序的主要步骤如下:
(')当 文件被执行时, 装载器首先为进程分配一个 <2 的虚拟地址空间,然后把程序所占
用的磁盘空间作为虚拟内存映射到这个 <2 的虚拟地址空间中。一般情况下,会映射到虚拟地址空间中
=<===== 的位置。装载一个应用程序的时间比一般人所设想的要少,因为装载一个 文件并不是把这
个文件一次性地从磁盘读到内存中,而是简单地做一个内存映射,映射一个大文件和映射一个小文件所花
费的时间相差无几。当然,真正执行文件中的代码时,操作系统还是要把存在于磁盘上的虚拟内存中的代
码交换到物理内存(41)中。但是,这种交换也不是把整个文件所占用的虚拟地址空间一次性地全部
从磁盘交换到物理内存中,操作系统会根据需要和内存占用情况交换一页或多页。当然,这种交换是双向
的,即存在于物理内存中的一部分当前没有被使用的页,也可能被交换到磁盘中。
( ) 装载器在内核中创建进程对象和主线程对象以及其他内容。
() 装载器搜索 文件中的 #&5(引入表),装载应用程序所使用的动态链接库。
对动态链接库的装载与对应用程序的装载方法完全类似。
(<) 装载器执行 文件首部所指定地址处的代码,开始执行应用程序主线程。
2.2.3 Big-endian 和 Little-endian
/ 中 #123+#"3/1!4 的成员 0中的值,根据 -0 中的定义,对于
#*( 应该为 =='<。但是用十六进制编辑器打开 文件时,看到这个 4! 显示的却是 <
='。其实 <=' 就是 =='<,只不过由于 #*( 是 ">,所以显示出来是这样的。对于
> 和 ">,请看下面的例子。一个整型 变量,长度为 < 个字节。当这个整形变量
的值为 =' <?@9 时,对于 > 来说,显示的是A' ,<,<,@9B,而对于 ">
来说,显示的却是A@9,<,<,' B。注意 # 使用的是 ">。
2.2.4 3 种不同的地址
文件的各种结构中,涉及到很多地址、偏移。有些是指在文件中的偏移,有些 是指在内存中的
偏移。以下的第一种是指在文件中的地址,第二、三种是指在内存中的地址。
3
第一种,文件中的地址。比如用十六进制编辑器打开 文件,看到的地址(偏移)就是文件中的地
址,使用某个结构的文件地址,就可以在文件中找到该结构。
第二种,当文件被整个映射到内存时,例如某些 分析软件,把整个 文件映射到内存中,这时
是内存中的虚拟地址()1)。如果知道在这个文件中某一个结构的内存地址的话,那么它等于这个 文
件被映射到内存的地址加上该结构在文件中的地址。
第三种,当执行 时, 文件会被载入器载入内存,这时经常需要的是 4)1。例如知道一个结构
的 4)1,那么程序载入点加上 4)1 就可以得到该结构的内存地址。比如,如果 文件装入虚拟地址
()1)空间的 =<===== 处,某一结构的 4)1为 ='===,那么其虚拟地址为 =<='===。
文件格式要用到 4)1,主要是为了减少 装载器的负担。因为每个模块都有可能被重载到任何
虚拟地址空间,如果让 装载器修正每个重定位项,这肯定是个梦魇。相反,如果所有重定位项都使用
4)1,那么 装载器就不必操心那些东西了,即它只要将整个模块重定位到新的起始 )1。这就像相对
路径和绝对路径的概念:4)1 类似相对路径,)1 就像绝对路径。
注意,4)1 和 )1 是指内存中,不是指文件中。是指相对于载入点的偏移而不是一个内存地址,只
有 4)1 加上载入点的地址,才是一个实际的内存地址。
2.3 PE 文件结构
在 $!C 的文件 -0 中有 文件格式的定义。本文所用到的变量,如果没有特别说明,
都在文件 -0 中定义。
有关一些 头文件结构一般都有 位和 ?< 位之分,如 #1233/1!4$ 和
#1233/1!4$?< 等,除了在 ?< 位版本中的一些扩展域外,这些结构总是一样的。是采用
位还是 ?< 位,需要用DE3#?< 来定义,如果没有这种定义,则采用的是 位的文件结构。编
译器将根据此定义选择相应的编译模式。
2.3.1 MS-DOS 头部
$>!$ 头部占据了 文件的头 ?< 个字节,描述它内容的结构如下:
88此结构包含于 #-/ 中
88
F5%3#123!$3/1!4A88!$ 的- 头部
4!3&G88魔术数字
4!35G88文件最后页的字节数
4!35G88文件页数
4!3G88重定义元素个数
4!350G88头部尺寸,以段落为单位
4!3&G88所需的最小附加段
4!3&G88所需的最大附加段
4!3G88初始的 $$ 值H相对偏移量I
4!35G88初始的 $ 值
4!3&G88校验和
4!35G88初始的 # 值
4
剩余25页未读,继续阅读
资源评论
arecraft
- 粉丝: 12
- 资源: 49
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功