45、PE文件修改实战:修改资源、修改导入表、添加节区、修改入口点
说实话,PE文件修改这事儿,是逆向工程里最“硬核”的活儿之一。你光会看反汇编还不够,得能动手改文件本身。我当年第一次改PE文件时,差点把一个系统DLL改废了——嗯,从那以后我就养成了“改之前先备份”的强迫症。
这一章,咱们直接上实战。四个核心操作:改资源、改导入表、加节区、改入口点。每个操作我都会讲清楚原理,再给实操步骤。你跟着走一遍,基本就能应付大部分PE修改场景了。
4.1 修改资源:给程序换个图标
资源修改是最常见的需求。比如你想把某个EXE的图标换成自己的,或者改掉它的版本信息。
实操步骤:
- 用
Resource Hacker或Restorator打开目标PE文件 - 找到
Icon目录下的1(通常是主图标) - 右键选择“替换资源”,导入你的ICO文件
- 保存即可
但如果你要批量改资源,或者程序有资源校验,就得用十六进制编辑器手动改了。资源数据在PE文件中的位置,可以通过 IMAGE_RESOURCE_DIRECTORY 结构定位。
4.2 修改导入表:让程序加载你的DLL
修改导入表,说白了就是让目标程序在启动时自动加载你指定的DLL。这是DLL注入的经典手法之一。
核心原理: PE文件的导入表记录了程序需要哪些DLL和函数。我们往这个表里加一条记录,程序启动时就会加载我们的DLL。
实操步骤:
- 用
LordPE或CFF Explorer打开PE文件 - 找到“导入表”选项卡
- 右键添加一个新的DLL条目,比如
MyHook.dll - 添加你想劫持的函数,比如
MessageBoxA - 保存文件
我曾经在分析一个恶意软件时,发现它通过修改系统DLL的导入表来实现持久化。它把自身DLL的路径写进了 kernel32.dll 的导入表里——这招够狠,但也很容易被安全软件检测到。
4.3 添加节区:给PE文件“加料”
添加节区,就是给PE文件新增一个段。这个段可以用来存放你的代码、数据,或者作为后续修改的“缓冲区”。
为什么需要加节区? 因为直接修改原有节区,可能会破坏文件对齐,或者空间不够。加一个新节区,干净利落。
实操步骤:
- 用十六进制编辑器打开PE文件
- 定位到节区表(Section Table),通常在PE头后面
- 在最后一个节区后面,添加一个新的
IMAGE_SECTION_HEADER结构(40字节) - 设置新节区的名称、虚拟大小、原始大小、属性(比如可读可写可执行)
- 在文件末尾追加新节区的数据
- 更新PE头中的
NumberOfSections字段
VirtualAddress 必须是上一个节区 VirtualAddress + VirtualSize 向上对齐到 SectionAlignment 的值。文件偏移同理,要对齐到 FileAlignment。
我习惯用 010 Editor 的PE模板来做这件事,它能自动计算对齐值,省得我手动算偏移。你想想看,要是算错一个字节,整个PE文件就废了。
4.4 修改入口点:劫持程序执行流
修改入口点(Entry Point),是让程序启动后先执行你的代码,再跳回原入口。这是代码注入的经典手法。
原理: PE头中的 AddressOfEntryPoint 字段指向程序的第一条指令。我们把它改成新节区中我们的代码地址,等我们的代码执行完,再跳回原入口。
实操步骤:
- 在新节区中写入你的Shellcode(比如弹个MessageBox)
- Shellcode末尾加上一条
jmp指令,跳回原入口点 - 用十六进制编辑器修改PE头中的
AddressOfEntryPoint,指向新节区 - 保存文件
ret 回去。
下面这张图展示了修改入口点的完整流程:
4.5 综合实战:给计算器加个“关于”弹窗
咱们把上面四个操作串起来,做个完整的例子。目标:修改Windows计算器(calc.exe),让它启动时弹个“Hello, Reverse!”的对话框。
步骤概览:
| 步骤 | 操作 | 工具 |
|---|---|---|
| 1 | 添加新节区 .mycode |
010 Editor + PE模板 |
| 2 | 在新节区写入Shellcode(调用MessageBoxA) | 汇编器 + 十六进制编辑器 |
| 3 | 修改入口点指向Shellcode | 十六进制编辑器 |
| 4 | 修改导入表,添加 user32.dll 的 MessageBoxA |
CFF Explorer |
| 5 | 保存并测试 | 运行修改后的calc.exe |
; 新节区代码
push 0 ; MB_OK
push caption ; "Title"
push message ; "Hello, Reverse!"
push 0 ; hWnd
call MessageBoxA
; 跳回原入口
jmp original_entry
这里有个坑:MessageBoxA 的地址在Shellcode里不能写死,因为不同系统版本地址不同。我一般用导入表的方式,让PE加载器帮我们解析地址。说白了,就是让导入表里的 IAT 自动填好函数地址,Shellcode里直接call那个地址就行。
做完这步,你基本就掌握了PE修改的核心技能。以后遇到需要给程序“加料”的场景,不管是加功能还是做调试,都能游刃有余。
公众号:蓝海资料掘金营,微信deep3321