第3章
PE文件结构分析 
3.1 实验概述 
本章实验旨在让学生深入了解Windows操作系统下的可执行文件格式(PE/PE+),并
通过学习各种PE编辑查看工具,详细了解PE文件的结构和组成部分。通过重点分析PE 
文件头、引入表、引出表以及资源表等关键部分,学生将深入了解PE 文件的内部结构和
功能。在
本章中,学生将熟悉各种PE编辑查看工具,从而能够对PE文件进行查看和分析。
然后,本章重点分析了PE文件的各部分,包括文件头、节表、导入表、导出表以及资源表等, 
通过分析这些部分,学生将了解PE文件的组织结构和功能。此外,学生将通过自己动手打
造一个尽可能小的PE文件的实践,加深对PE文件格式的理解,并学会如何通过编辑工具
来操作和修改PE文件。通过本章实验,学生将能够全面掌握PE文件的结构和格式,为进
一步学习逆向工程和漏洞分析奠定坚实的基础。 
3.2 实验预备知识与基础 
3.2.1 PE 查看、编辑与调试工具介绍
本次实验将使用相关工具对PE文件进行查看、编辑与调试。
1. PEview 
PEview能够快速简便地查看可移植可执行文件(PE),以及组件对象文件格式
(COFF)文件。该工具支持的文件类型包括EXE、DLL、OBJ、LIB、DBG 等,可以以树状目
录的方式显示文件的头部、节、引入表、引出表和资源等信息,以及更具体的各字段的含义。
2. StudyPE+ 
StudyPE+是一款国产的PE查看/分析集成工具,支持PE32与PE32+,能够显示PE 
文件的重要字段(但不像PEview一样显示所有字段)。StudyPE+提供了许多实用的功能, 
例如丰富的PE编辑功能、RVAFOA 互相转换功能、PE 反汇编及反汇编编辑功能、PE内
多种数据搜索功能、有限的查壳功能等。

第3章 PE文件结构分析 
3. 010Editor 
010Editor是一款专业的文本和十六进制编辑器,能够快速地编辑计算机上任何文件的
内容。该软件可以编辑文本文件,包括Unicode文件、批处理文件、C/C++、XML等;而在
编辑二进制文件时,010Editor不仅可以查看和编辑二进制文件的单个字节,还可以基于官
网提供的模板对各种类型的文件格式化显示,例如PE文件;此外,010Editor还能对内存中
的数据进行编辑。
4. OllyDbg 
OllyDbg(www.ollydbg.de)是Windows系统下的可视化的用户模式调试器,能够调试
32位程序。OllyDbg结合了动态调试与静态分析,它的反汇编能力很强,能够自动分析函
数、循环语句、字符串等,可以识别数千个API,并注释其参数。它具有用户友好的界面,其
功能可以由第三方插件扩展。其版本1.10为1.x系列的最终发布版本。2.0版本于2010年
6月发布。
3.2.2 函数引入机制
1. 引入函数节 
代码复用是程序的重要特性,PE文件也是如此,PE文件中使用的函数可能来自其他
图3-1 引入函数节
库,例如ExitProcess。这种被某模块调用但又不在调用者
模块中的函数称为引入函数。PE 文件通过引入函数机制
从其他(系统或第三方自定义的)DLL 中引入函数,例如
user32.dll、kernel32.dll等,存储这种引入函数机制的节称
为引入函数节,节名一般为.rdata。图3-1展示了引入函数
节的结构。
2. 引入机制
函数引入机制主要由3 个重要的数据结构完成,如图3-1 所示,分别是IMPORT 
DirectoryTable(IDT)、IMPORTNameTable(INT)、IMPORTAddressTable(IAT)。
图3-2 引入函数表
IDT是一个IMAGE_IMPORT_DESCRIPORTs数组,如图3-2所示,每个数组元素对
应一个DLL,以全零的数组元素结束数组。每个数组元素有5个DWORD 大小的项,第1
49

软件安全实践
项为INT的RVA(虚拟地址相对偏移),第5项为IAT的RVA,第4项对应DLL名称字符
串,告诉加载器这个结构对应的DLL。
INT与IAT在文件上是相同的,都是一系列的元素大小为DWORD 的数组,如图3-3 
所示,每个DWORD通常是指向引入函数的Hint及名称字符串的RVA(如最高位为1,即
第一字节为80,则指引入函数的序号),以全零结束。
图3-3 INT/IAT数据结构
IAT与INT的区别在于,如图3-4所示,当PE文件加载到内存中时,IAT 中原本存放
引入函数名称的DWORD会替换为该函数的内存地址,这样PE文件运行时可以通过IAT 
在内存中找到相应函数。
图3-4 文件与内存中的引入表
3.2.3 函数引出机制
1. 引出函数节 
引出函数节的节名一般为.edata,用来描述本文件引出函数的列表等信息及各函数具
体代码位置。引出函数节的具体结构如图3-5所示。
2. 引出函数机制
引出函数机制主要由3个数据结构完成。如图3-6所示,AddressOfFunctions(对应
PEview中的EXPORT AddressTable)存放函数地址,AddressOfNames(对应PEview 中
50

第3章 PE文件结构分析 
图3-5 引出函数节具体结构
的EXPORT NamePointerTable)存放函数名所在地址,AddressOfNameOrdinals(对应
PEview中的EXPORTOrdinalTable)存放每个函数地址在函数地址表中对应的序号。
图3-6 引出函数机制
如果希望通过函数名获取引出函数的地址,需要经过以下流程: 
(1)在AddressOfNames找到目标函数的函数名地址,并记下该数组序号X; 
(2)定位AddressOfNameOrdinals的第X项,得到序号Y; 
(3)定位AddressOfFunctions的第Y项,获得该函数的RVA 函数地址。
3.2.4 资源节机制
1. 资源节 
资源节的节名一般为.rsrc,放有图标、对话框等程序需要的资源。资源节以树状结构组
织,它有一个主目录,主目录下又有子目录,子目录下可以是子目录或数据。通常有3层目
录(资源类型、资源标识符、资源语言ID),第4层是具体的资源。图3-7展示了资源节的树
状结构,即资源树。
2. 资源定位机制
资源一般使用树来保存,通常包含3层,最高层是类型,其次是名字,最后是语言。资源
的定位遵循以下步骤。
(1)定位资源节开始的位置,首先是一个IMAGE_RESOURCE_DIRECTORY 结构, 
51

软件安全实践
图3-7 资源节的树状结构
后面紧跟着IMAGE_RESOURCE_DIRECTORY_ENTRY 数组,这个数组的每个元素代
表的资源类型不同。
(2)通过每个元素,可以找到第二层IMAGE_RESOURCE_DIRECTORY,后面紧跟着
IMAGE_RESOURCE_DIRECTORY_ENTRY数组,这个数组的每个元素代表的资源名字
不同。
(3)然后可以找到第三层IMAGE_RESOURCE_DIRECTORY,后面同样紧跟着
IMAGE_RESOURCE_DIRECTORY_ENTRY数组,这个数组的每个元素代表的资源语言
不同,且直接指向最后的资源(IMAGE_RESOURCE_DATA_ENTRY)。
以上三类IMAGE_ RESOURCE_ DIRECTORY 在PEview 中分别带有Type、
NameID、Language的后缀,如图3-8左侧边栏所示。
(4)最后通过每个IMAGE_RESOURCE_DIRECTORY_ENTRY 找到每个IMAGE_ 
RESOURCE_DATA_ENTRY,从而找到每个真正的资源。
3.2.5 重定位机制
重定位节存放了一个重定位表,定位了代码中使用了绝对地址的地方。若装载器在程
序默认的基地址加载映像文件,就不需要重定位,否则需要通过重定位表做一些调整,步骤
如下。
(1)计算地址差异delta。操作系统加载程序会计算默认的基地址(PE头的ImageBase 
字段)与实际加载的映像文件的基地址的差异(delta)。
(2)根据重定位的类型,将这个delta应用到重定位表指向的需要修改的地方。
重定位节是一个IMAGE_BASE_RELOCATION 结构,该结构的每一项如表3-1 
52

第3章PE文件结构分析
图3-8 资源节示例

所示。

表3-
1 
重定位节的数据结构

顺序名字大小(字节) 描述
1 VirtualAddres 
4 重定位数据开始的RVA地址
2 SizeOfBlock 4 本结构大小
3 TypeOfset[] 不定重定项数组,每个元素占2字节

IMAGE_BASE_RELOCATION的每项都代表了一个4K(一页)大小的内存区域中需
要重定位的地址。图3-9是kenldl 
的重定位表,其中定位项的个数的计算方式为,

re32.
SizeOfBlock的大小减去前两项的字节数8得到TypeOfset数组的大小,再除以2得到定
位项的个数;定位项每项为16位(4字节),高4位代表重定位的类型,剩下的12位代表页
内的偏移量,加上页地址VirtualAddres 
就得到了具体的内存地址。

图3-9 重定位表示例
35

软件安全实践 
3.3 PE查看、编辑与调试工具的用法 
3.3.1 实验目的
了解PE编辑查看与调试工具的用法,了解PE 文件在磁盘上的结构与在内存上的
结构。通
过本实验,学生能够查看PE文件的各部分,包括文件头、节表、导入表、导出表以及
资源表等,并可以进行调试和分析,这有助于深入理解Windows可执行文件的工作原理和
运行机制。
3.3.2 实验内容及实验环境
1. 实验内容 
(1)使用二进制查看工具PEview观察PE文件例子程序hello25.exe的十六进制数据, 
并定位其中重要数据结构。
(2)使用StudyPE+观察PE32+格式的目标程序pe32+.exe,了解32位PE程序与64 
位PE程序的差异。
(3)使用OllyDbg对hello25.exe进行初步调试,初步了解OllyDbg的用法,理解该程
序功能结构,在内存中观察该程序的完整结构。
(4)使用PE编辑工具010Editor修改hello25.exe,使得该程序仅弹出第二个对话框。
2. 实验环境
(1)系统:WindowsXP版本及以上的操作系统,实机、虚拟机均可。
(2)工具:OllyDbg1.10、010Editor、PEview、StudyPE+。
3.3.3 实验步骤
1. 观察PE 文件示例程序hello25.exe 的十六进制数据 
(1)用PEview打开示例程序。
使用PEview打开示例程序hello25.exe(通过菜单栏的File→Open打开示例程序或者
直接将示例程序拖入打开的OllyDbg中),如图3-10所示,左侧的目录中显示了hello25.exe 
的结构,单击即可查看;右侧是对应的十六进制数据。
可以看到,该PE 文件由MZ 头部(DOS_HEADER)、DOSStub、PE 文件头(NT_ 
HEADER)、可选文件头、节表、节组成,其中节分为代码节(.text),引入函数节(.rdata)与数
据节(.data)。
(2)观察PE文件头。
PEview左侧目录显示,PE文件头由签名(0x4550,即PE)、文件头与可选文件头组成。
在目录中单击各组成部分可查看详细信息,例如单击可选文件头,如图3-11所示,右侧显示
了可选文件头的各个字段,包括入口点、映像基址、对齐粒度等重要的文件信息。
54

第3章 PE文件结构分析 
图3-10 PEview查看示例程序hello25.exe 
图3-11 hello25.exe的PE文件头
(3)观察引入函数节。
引入函数节(.rdata)是PE文件的重要数据结构。展开查看该节,如图3-12所示,引入
函数节包含引入地址表、引入目录表、引入名字表等内容。
图3-12 hello25.exe的引入函数节
2. 查看PE32+文件结构与PE32 的差异
PE32+是64位Windows所使用的文件格式,在PE格式的基础上做了一些简单的修
55

软件安全实践
改。虽然PEview能够很直观地展示各字段的意义,但这个工具不支持PE32+格式,所以
接下来使用StudyPE+观察PE32+格式。将程序pe32+.exe拖到打开的StudyPE+中,即
可观察该程序的重要信息,如图3-13所示,单击“PE头”标签,可以查看PE头的重要字段。
图3-13 StudyPE+打开pe32+.exe 
PE32+文件结构与PE32的差异主要有3方面。
(1)Magic。PE32+中的Magic值为020B,PE32中的Magic值为010B,PE装载器通
过检查该字段值来判断文件是64位还是32位。
(2)BaseOfData。图3-13中可以看到,PE32+中删除了该字段,在32位PE文件格式
中该字段表示指向数据段开头位置(RVA)。
(3)字段大小变化。PE32+中共5个字段由4字节拓展为8字节,来表达更大的内存范
围。例如图3-13中的ImageBase字段,除此之外还有堆栈相关的4个字段:SizeOfStackReverse、
SizeOfStackCommit、SizeOfHeapReverse、SizeOfHeapCommit。
3. 初步调试该程序
(1)用OD打开程序。
使用PE调试工具OllyDbg打开示例程序hello25.exe(通过菜单栏的File→Open打开
示例程序或者直接将示例程序拖入打开的OllyDbg中),图3-14 展示了程序加载后的
OllyDbg界面,主要分为4个区域,左上角是反汇编界面,显示了程序的反汇编代码;右上角
是寄存器界面,显示各寄存器的值;左下角显示程序内存的值;右下角显示栈的内容。
(2)单步步过到第一个弹框。
接下来让程序单步运行到第一个弹框,快捷键F8代表单步步过(每次执行一条指令, 
不跟踪到调用内部),这个过程中观察程序的行为:首先压栈4个参数,第一个参数代表弹
框类型,第二、三个参数分别为对话框的标题字符串地址与内容字符串地址;再调用user32. 
dll中的弹框函数MessageBoxA。运行到这里就会出现图3-15所示的弹框。
56

第3章 PE文件结构分析 
图3-14 OllyDbg打开hello25.exe 
(3)单步运行到程序结束。
继续单步运行,接下来的代码是第二个MessageBoxA 弹框,如图3-16所示,与第一个
弹框标题相同但内容不同。反汇编代码中也体现了这一点,第一个弹框压入的文本参数为
0x403009,第二个弹框压入的文本参数则是0x403031(如果在调用MessageBoxA 函数的位
置按快捷键F7来单步步入,跟踪到函数内部,会发现程序停在了连续的JMP指令中,这些
JMP指令指向了函数的IAT表,来获得函数的内存地址)。
图3-15 hello25.exe的第一个弹框 图3-16 hello25.exe的第二个弹框
继续单步,调用ExitProcess函数结束运行。按快捷键Ctrl+F2可以重新运行程序,帮
助再次分析。
4. 修改该程序,使该程序仅弹出第二个对话框
使程序仅弹出第二个程序可以有许多方法,可以修改程序的入口点(AddressofEntry 
Point),使程序加载后从第二个弹框处开始运行。
(1)定位入口点。
首先需要知道入口点字段在什么位置。010Editor官网提供了PE模板,可以辅助分析
PE文件字段;也可以用PEview打开一份程序副本,查看入口点位置,如图3-17所示,可见
入口点在D8位置。
57

软件安全实践
图3-17PEiw查看heee的入口点

velo25.x

(2)修改入口点。
在前面的调试过程中(图3-14),我们知道了第二个弹框的开始内存地址为
0x00401016,所以使用010Editor打开程序,将入口点(D8处)的值修改为第二个弹框的
RVA,即0x00001016,如图3-18所示。


图3-18 修改heee的入口点为0x00001016(D8处的4字节)

lo25.x

(3)确认修改效果。
保存文件之后重新运行程序,可见只弹出了第二个对话框。再次用OlyDbg打开程
序,如图3-19所示,程序代码从第二个弹框开始。

图3-19 OD调试修改入口点的hello25.exe 
85

第3章 PE文件结构分析 
除了快捷键调试程序,OllyDbg还在菜单栏下方提供了图形按钮进行调试,如图3-20 
所示,标注的4个按钮的功能分别为重新运行程序、关闭程序、单步步入、单步步过。
图3-20 OD中常用的图形调试按钮 
3.4 函数的引入/引出机制分析与修改 
3.4.1 实验目的
熟悉PE文件头部、引入表的结构,了解与使用引入表引入函数,了解与使用user32的
函数手动引入函数。本实验可以让学生深入了解Windows可执行文件的内部机制,并为软
件开发和逆向工程提供重要的基础,同时有利于学生理解恶意软件脱壳及进行API函数定
位的机制。
3.4.2 实验内容及实验环境
1. 实验内容 
(1)使用PE 查看工具PEview 与调试工具OllyDbg,结合预备知识与示例程序
hello25.exe,熟悉PE文件引入表结构,熟悉函数导入的基本原理。
(2)从hello25.exe的内存空间找到模块user32,进一步查找函数MessageBoxA 的地
址,并验证该地址是否正确。通过实例了解PE文件如何引入外部模块的函数。
(3)用二进制编辑工具修改hello25.exe程序的引入表,使该程序仅可以从kernel32.dll 
中引入LoadLibrary和GetProcAddress函数,而不从user32.dll导入任何函数,在代码节中
写入部分代码利用这两个函数获取MessageBoxA 的函数地址,使hello25.exe程序原有功
能正常。
2. 实验环境
(1)系统:WindowsXP以上的操作系统,实机、虚拟机均可。
(2)工具:OllyDbg1.10、010Editor、PEview。
3.4.3 实验步骤
1. 观察示例程序hello25.exe 的函数导入机制 
(1)文件中的IAT。
程序通过IAT 定位函数的地址,PE 文件的IAT 在文件中与内存中含义有区别。
hello25.exe 示例程序在文件中的IAT 如图3-21 所示,通过函数名称引入了函数
ExitProcess、MessageBoxA 与wsprintfA(也可以通过序号引入函数)。
(2)内存中的IAT。
而PE文件运行过程中,如何在内存中找到函数地址呢? 我们通过OllyDbg来观察。
用OD打开hello25.exe,如图3-22所示,在代码节尾部存放着通过IAT表间接跳转到引入
59

软件安全实践
图3-21 hello25.exe的IAT表
函数的指令,这就是跳转表。图3-22中用方框圈出了跳转表跳转的地址,分别是0x402000、
0x40200c、0x402008,换算为文件偏移FA就是0x600、0x60c、0x608,正好是图3-21显示的IAT 
的函数地址。
图3-22 hello25.exe的跳转表
(3)IAT的函数地址变化。
下面转到IAT的内存处查看,如图3-23所示,此时内存中IAT 的元素与文件IAT 的
元素不同,内存中IAT的元素是函数的内存地址,而不是指向函数名的RVA。这样跳转表
可以根据IAT找到函数在内存中的真实地址。
图3-23 内存中的IAT表
2. 从该程序的内存空间定位查找user32.dll 的函数MessageBoxA 的地址
(1)定位模块user32.dll。
要从程序的内存空间找到user32再找到MessageBoxA 地址,首先要找到user32模块
的内存地址。如图3-24 所示,单击M(即Memory)方块查看内存中的模块,然后找到
user32模块,基址为0x75b30000,双击即可进入该地址。
(2)user32的引出表。
弹出的Dump窗口显示了user32的头部信息。向下翻可以获取数据目录项中的引出
表的地址,如图3-25 所示,引出表RVA 为0x10548,加上基址,引出表的VA 就
是0x75B40548。
图3-25的Dump不适合查看内存,因此在OllyDbg的内存窗口中继续分析。单击上方
的C(即CPU)方块切换到之前的窗口,在内存窗口中按快捷键Ctrl+G跳转到user32的引
60

第3章PE文件结构分析
图3-24 OD查看程序的模块


图3-25user32的Dump界面

出表,地址为0x75B40548,如图3-26所示。

引出表的结构见图3-26 。图3-26中地址0x75B40548处是user32的引出表目录,图中
数据从0开始计数,偏移0C处的2个DWORD分别为函数名的起始RVA(所有函数的名
称字符串),起始函数序号0x5dc;偏移1C处的3个DWORD分别是函数地址表、函数名指
针表、函数序号表的RVA 。


图3-26 内存中查看user32的引出表

(3)定位MesageBoxA的字符串。
首先找到MesageBoxA字符串的地址。根据所有名称字符串的RVA(0x12860),跳转
到相应的VA(0x75b42860),按快捷键Ctrl+B搜索字符串MesaeBoxA,得知MesaeBoxA 
字符串的地址为0x75B44AF8,如图3-27所示。
gg


图3-27 内存中查找user32的MesageBoxA字符串

(4)计算MesageBoxA的项数。
然后计算MesageBoxA在函数名指针表中的表项位置。根据函数名指针表RVA 
(0x1151c),跳转到VA(0x75b4151c)。此时需要搜索的是MesageBoxA字符串的RVA, 
即0x14af8,因此搜索HEX值“F84A0100,(”) 得到相应地址0x75b41d54,如图3-28所示。
因为(0x75b41d54-0x75b4151c)/4为526,所以MesageBoxA是第526项。

61 


软件安全实践
图3-28 函数名指针表中查找对应MessageBoxA的项
(5)计算MessageBoxA 的函数序号。
接着,找到MessageBoxA 的函数序号。根据函数序号表RVA(0x121f4),跳转到序号
表的第526 项(0x75b42610=0x75b421f4+2×526,序号表元素大小为2 字节),得到
MessageBoxA 的函数序号为0x21b,如图3-29所示。
图3-29 函数序号表中查找MessageBoxA对应序号
(6)获取MessageBoxA 的RVA。
最后,函数地址表第0x21b项就是MessageBoxA 的RVA。根据函数地址表RVA 
(0x10570),跳转到目的VA(0x75b40ddc=0x75b40570+4×0x21b),得到MessageBoxA 函
数的RVA 为0x6fd1e,如图3-30所示。
图3-30 MessageBoxA函数的RVA 
(7)验证函数地址。
现在验证寻找的函数地址是否正确。手动寻找的MessageBoxA 的RVA 为0x6fd1e, 
则VA 为0x75b9fd1e;在反汇编窗口中单击跳转表中的MessageBoxA 函数,图3-31中的方
框提示了函数地址也是0x75b9fd1e,两者一致,说明地址正确。
图3-31 跳转表中的MessageBoxA的地址
3. 修改该程序,使该程序使用LoadLibrary 和GetProcAddress 导入函数
MessageboxA 
(1)修改引入表。
程序的引入表中没有LoadLibrary、GetProcAddress 这两个函数,因此先使用
010Editor修改PE文件引入表,才能在代码中使用这两个函数。
① 修改字符串。添加两个函数的字符串,如图3-32(注意函数LoadLibrary的字符串
是LoadLibraryA)。
62

第3章PE文件结构分析
②修改IDT 。函数LodLirry和Gtrcrs 
都在kre32.
l 
中,程序需要的
abaePoAddeenld
另一个函数ExitProces 
也在kernel32中,所以程序只需要从kernel32中引入所需函数,引
入表原来的user32部分可以去掉,改为全零表示IDT结束。

③修改IAT与INT 。IAT与INT在文件中完全一致,这里以IAT举例。IAT在
0x600的位置开始,0x604的DWORD引入LoadLibrary。LoadLibrary字符串的地址是
0x682,但字符串前两字节表示hit(hit的值不影响引入),所以引入的起始地址应为0x680, 
对应RVA2080 。对GetProcAddres 
以同样的方式引入,然后以全零DWORD结束IAT 。
图3-32 修改heee的引入表

lo25.x

(2)修改程序,手动加载函数MesageBoxA 。
成功引入函数之后,接下来使用OD修改代码,使程序通过LoadLibrary与
GetProcAddres 
引入函数MesageBoxA 。这里相当灵活,同学们可以用各种方法实现实验
目的,例如修改入口点在原代码尾部,引入MesageBoxA后再跳转到原代码开头。本书采
用另一种方法,完全重写代码,先引入函数MesageBoxA,再调用该函数弹框,最后结束程
序。使用OD打开修改好引入表的程序,如图3-33所示。


图3-33 OD打开修改了引入表的heee

lo25.x

双击汇编代码可以对代码进行修改,首先压栈字符串use32.
l,然后调用LodLirr

rdabay(这里直接calIAT中的函数地址);接着调用GetProcAddres 
加载MesageBoxA的地址, 
用寄存器ebx保存;然后是两次弹框代码与结束进程。可以反复单步调试与重新运行来检
查代码的正确性,如图3-34所示。

(3)运行程序。
反汇编窗口右击,选择“CopytoExecutable”(复制到可执行文件)→“Al 
Modifications”(所有修改),可以在修改后保存为可执行程序。保存后运行,确认功能正常。

63 


软件安全实践
图3-34 在OD中修改代码,手动加载MessageBoxA 
3.5 资源节资源操作 
3.5.1 实验目的
了解PE文件资源节结构,能够修改PE文件图标,汉化PE文件。
通过本实验,学生将深入了解PE 文件的资源节结构,包括资源的组织方式和存储位
置。这不仅有助于学生理解PE文件中资源的存储和调用方式,同时也能帮助学生理解病
毒经常采用的资源寄生与图标替换等机制。
3.5.2 实验内容及实验环境
1. 实验内容 
(1)用二进制编辑工具修改PEview.exe,使得该文件的图标变成csWhu.ico(图标见光
盘,也可以是任意ICO 图标文件)。
(2)熟悉eXeScope工具的使用,并利用该工具汉化程序PEview.exe。
2. 实验环境
(1)系统:WindowsXP及以上的操作系统,实机、虚拟机均可。
(2)工具:OllyDbg1.10、010Editor、PEview、eXeScope。
3.5.3 实验步骤
1. 修改程序PEview.exe 的图标 
修改程序的图标就是将程序资源节中的图标数据进行修改,因此需要了解资源节的结
构和图标的结构,然后进行修改。
(1)查看图标。
用010Editor打开要替换的图标csWhu.ico,如图3-35所示。前面6字节是图标的头
部,3个WORD分别是保留部分、类型、数量。接着是图标的目录项,可知图标大小为32× 
32,由于只有一个图标,0x16开始到文件末尾是图标数据。
(2)查看PEview的图标资源。
64

第3章 PE文件结构分析 
图3-35 图标csWhu.ico的十六进制数据
PE文件中的图标保存格式与ICO 文件中图标的保存格式略有不同。PE 文件中,把
ICON目录项和图标资源作为两种资源类型分别保存,前者是GROUP_ICON 类型,后者是
ICON 类型。用PEview 打开要修改的PEview,GROUP_ICON 如图3-36所示,可见该文
件包含3个图标数据(对应文件夹的不同大小的视图),其中20×20的图标刚好与csWhu. 
ico大小一致,所以可以对第二个图标进行替换。
图3-36 PEview的GROUP_ICON 
找到二号图标ICON0002,如图3-37所示,该图标从0xDB28开始,到0xDE0F 结束
(图3-37中未显示)。
图3-37 PEview的图标资源
(3)替换图标。
现在被替换的数据与替换的数据都已确认,用010Editor打开图标和程序,将图标数据
图3-38 替换后的PEview 
复制到程序中(使用快捷键Ctrl+Shift+C、Ctrl+Shift+V) 
并保存。如图3-38所示,切换到列表视图就可以看到被修改
的图标。
2. 汉化该程序
汉化程序就是将对应的英文字符串改为中文。实验使用的eXeScope可以很方便地辅
助汉化。
用eXeScope打开PEview,如图3-39所示,左边树状结构中,Resource→Menu→1是程
65

软件安全实践
序目录的字符串。
把“&”后的英文修改为中文(双击即可修改),如图3-40所示。
图3-39 exscope打开PEview 图3-40 汉化后的PEview 
3.6 手工重定位 
3.6.1 实验目的
通过手工对代码节进行重定位修复,理解绝对地址为什么需要重定位,明白重定位的机
制与原理。
在这个实验中,学生将深入研究可执行文件的代码节,了解其中绝对地址的特点和作
用。通过手工进行重定位修复,学生将亲身体验在程序加载和执行过程中,绝对地址需要进
行重定位的原因和必要性。通过实践操作,学生将理解重定位的需求、机制和原理,包括重
定位表的结构和使用方法。这将有助于学生在逆向工程、病毒与漏洞分析中更好地理解程
序的运行机制,提高其实践能力和分析水平。
3.6.2 实验内容及实验环境
1. 实验内容 
(1)修改hello25.exe的加载基址为0x600000,破坏程序的运行机制。
(2)在代码节中手工修正数据,使程序能够正常运行,理解重定位必要性。
2. 实验环境
(1)系统:WindowsXP以上的操作系统,实机、虚拟机均可。
(2)工具:OllyDbg1.10、010Editor。
66

第3章 PE文件结构分析 
3.6.3 实验步骤
1. 修改ImageBase 字段 
通过PEview(或下载010Editor的PE模板)获取ImageBase字段的偏移为0xE4,使用
010Editor打开hello25.exe,修改ImageBase字段为0x600000,保存本次修改,如图3-41 
所示。
图3-41 修改ImageBase 
2. 手工重定位
(1)观察程序异常。
使用OllyDbg打开程序,观察代码节的异常与需要修改的地方。图3-42中用方框标明
了需要修改的地方。首先,弹框函数中压栈字符串的地方直接使用了绝对地址,需要修改
(例如push0x403000,字符串地址已变为了0x603000);然后可以看到call指令的目标地址
没有解析为对应API,但这不是call指令的问题,而是call指令的目标对象———跳转表需要
修改(例如jmpdwordptr[0x402000])。这些需要修改的地方都有同样的特征:使用了绝
对地址,这正是重定位表的任务,即修正代码中的绝对地址。
图3-42 程序中的绝对地址
(2)手工修复。
计算加载的基址差delta(0x600000-0x400000=0x200000),然后对所有绝对地址加上
delta即可,如图3-43所示(跳转表的第二项没有正确解析,但没有影响,直接双击修改
即可)。
修改完毕后保存为新文件(反汇编窗口右击→复制到可执行文件→所有修改→全部复
制),运行查看是否功能正常。
67