从反汇编到符号解析:LinkLab实验通关指南(含PIC重定位避坑手册)
1. 实验环境与工具准备
在开始LinkLab实验之前,我们需要配置好必要的开发环境和工具链。这个实验的核心在于理解ELF文件结构和链接过程,因此需要准备以下工具:
- GCC编译器:用于编译和链接目标文件
- objdump:用于反汇编目标文件,查看机器指令
- readelf:用于分析ELF文件结构,查看节区、符号表和重定位信息
- hexedit:用于直接编辑二进制文件(可选)
推荐使用32位Linux环境进行实验,因为实验设计时主要考虑了32位架构。如果使用64位系统,需要确保安装了32位兼容库,并在编译时加上-m32选项。
工具安装示例:
sudo apt-get install gcc-multilib binutils hexedit
注意:实验过程中可能会频繁使用objdump和readelf,建议熟悉它们的常用参数。例如
objdump -d用于反汇编,readelf -S查看节区头表,readelf -s查看符号表。
2. 实验基础:ELF文件结构解析
理解ELF(Executable and Linkable Format)文件结构是完成LinkLab实验的关键。ELF文件主要包含以下几个核心部分:
- ELF头(ELF Header):包含文件的基本信息,如魔数、目标架构、入口地址等
- 节区头表(Section Header Table):描述文件中各个节区的信息
- 程序头表(Program Header Table):描述程序加载到内存时的段信息
- 节区数据:实际的代码、数据等内容
对于LinkLab实验,我们需要特别关注以下几个节区:
| 节区名称 | 作用 | 实验相关度 |
|---|---|---|
| .text | 代码节 | 阶段2、5、6 |
| .data | 已初始化数据 | 阶段1 |
| .rodata | 只读数据 | 阶段4 |
| .symtab | 符号表 | 阶段3 |
| .rel.text | 代码重定位表 | 阶段5、6 |
| .rel.data | 数据重定位表 | 阶段5、6 |
查看ELF节区的命令:
readelf -S phase1.o
3. 阶段1-4实验详解
3.1 阶段1:数据节修改
阶段1要求修改phase1.o的.data节内容,使其输出指定学号。具体步骤如下:
- 使用objdump查看反汇编代码,定位printf调用参数
- 使用readelf找到.data节的偏移量和大小
- 使用hexedit修改.data节中的字符串内容
关键操作:
objdump -d phase1.o # 查看反汇编
readelf -x .data phase1.o # 查看.data节内容
hexedit phase1.o # 编辑二进制文件
3.2 阶段2:代码节修改
阶段2需要修改phase2.o的.text节,替换nop指令以实现学号输出。操作流程:
- 反汇编phase2.o,分析do_phase函数
- 编写汇编代码实现字符串输出功能
- 将汇编代码转换为机器码,替换原nop指令
示例汇编代码:
movl $0x333231, %eax # 学号"123"的ASCII码
push %eax
call puts
add $4, %esp
3.3 阶段3:符号解析
阶段3需要创建phase3_patch.o来修正phase3.o的输出。关键点:
- 分析phase3.o的符号表和重定位信息
- 确定COOKIE字符串的映射关系
- 创建补丁模块覆盖特定符号
符号表分析命令:
readelf -s phase3.o # 查看符号表
objdump -d -r phase3.o # 查看重定位信息
3.4 阶段4:switch与重定位
阶段4涉及switch语句的重定位处理:
- 分析.rodata节中的跳转表
- 确定需要修改的重定位项
- 调整跳转表内容以输出正确学号
跳转表分析技巧:
readelf -x .rodata phase4.o # 查看只读数据
objdump -d -j .text phase4.o # 查看代码段
4. 阶段5-6:重定位高级技巧
4.1 阶段5:基础重定位
阶段5需要修复phase5.o中被清零的重定位记录。操作步骤:
- 使用readelf查看.rel.text和.rel.data节
- 分析缺失的重定位项类型和位置
- 根据符号表补充正确的重定位信息
重定位类型常见问题:
- R_386_32:绝对地址重定位
- R_386_PC32:PC相对地址重定位
- 错误的重定位会导致段错误或错误输出
4.2 阶段6:PIC重定位
阶段6是实验中最具挑战性的部分,涉及位置无关代码(PIC):
-
PIC特点:
- 使用GOT(全局偏移表)访问全局变量和函数
- 通过PLT(过程链接表)实现延迟绑定
- 代码不依赖绝对地址,可加载到任意内存位置
-
与Non-PIC的差异:
- 访问全局数据使用
movl GOT(%ebx), %eax而非直接地址 - 函数调用通过PLT间接跳转
- 需要额外的重定位类型如R_386_GOT32
- 访问全局数据使用
-
解决方案:
- 分析GOT和PLT结构
- 修复被清零的PIC相关重定位项
- 确保ebx寄存器正确指向GOT基址
PIC与Non-PIC对比表:
| 特性 | PIC代码 | Non-PIC代码 |
|---|---|---|
| 编译选项 | -fPIC | 无 |
| 全局变量访问 | 通过GOT间接访问 | 直接地址访问 |
| 函数调用 | 通过PLT跳转 | 直接调用 |
| 重定位类型 | R_386_GOT32等 | R_386_32等 |
| 代码大小 | 较大 | 较小 |
| 性能 | 稍低 | 较高 |
5. 常见问题与调试技巧
在实验过程中,可能会遇到以下典型问题:
-
段错误(Segmentation Fault):
- 原因:错误的重定位导致非法内存访问
- 解决方法:使用gdb调试,检查程序崩溃时的寄存器状态
-
错误输出:
- 原因:数据修改不完整或位置错误
- 解决方法:逐字节核对修改内容
-
链接错误:
- 原因:符号解析失败
- 解决方法:检查符号表和重定位项
gdb调试示例:
gdb ./linkbomb
(gdb) break main
(gdb) run
(gdb) x/i $eip # 查看当前指令
(gdb) info registers # 查看寄存器状态
6. 实验心得与进阶建议
完成LinkLab实验后,可以尝试以下进阶练习:
- 编写脚本自动化分析ELF结构
- 实现简单的链接器处理重定位
- 研究动态链接与加载过程
- 分析PIC代码的性能优化方法
实验中最关键的收获是理解链接器如何将多个目标文件合并成一个可执行程序,以及重定位在其中的作用。对于PIC代码,需要特别注意GOT/PLT机制和ebx寄存器的特殊用途。
&spm=1001.2101.3001.5002&articleId=155250349&d=1&t=3&u=25b12cbc33d84e708979e24b54e17b41)
183

被折叠的 条评论
为什么被折叠?



