第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