ELF 重定位实战:5 个 LinkLab 实验阶段解析符号表与节偏移计算
ELF 重定位深度实战从符号表解析到二进制修补的完整指南在计算机系统底层开发中理解 ELF 文件的重定位机制是掌握程序链接过程的关键。本文将带您深入探索 LinkLab 实验的五个阶段通过实际操作演示如何解析符号表、计算节偏移以及修改重定位条目最终形成一套完整的 ELF 文件分析与修改方法论。1. ELF 重定位基础与实验环境搭建ELFExecutable and Linkable Format是现代 Linux 系统中最常用的二进制文件格式它包含了程序运行所需的代码、数据以及元信息。理解 ELF 重定位首先要掌握几个核心概念可重定位目标文件.o 文件包含未链接的机器代码其中的符号引用尚未解析符号表.symtab记录所有定义的符号及其属性重定位条目.rel.text/.rel.data指示链接器如何修改符号引用实验环境准备# 基础工具安装 sudo apt-get update sudo apt-get install -y binutils hexedit gcc # 验证工具版本 readelf --version | head -1 objdump --version | head -1关键工具说明工具用途常用参数示例readelf查看ELF文件结构信息-a显示全部信息objdump反汇编和查看目标文件信息-d反汇编代码段hexedit二进制文件编辑直接编辑文件gcc编译和链接-o指定输出文件名2. 阶段一全局变量与数据节修改第一阶段的核心任务是修改全局变量在.data节中的内容。以下是详细操作步骤使用readelf分析符号表readelf -a phase1.o | grep -A5 Symbol table典型输出示例Symbol table .symtab contains 18 entries: Num: Value Size Type Bind Vis Ndx Name 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND 1: 0000000000000000 0 FILE LOCAL DEFAULT ABS phase1.c ... 8: 0000000000000000 16 OBJECT GLOBAL DEFAULT 3 g_data定位.data节信息readelf -S phase1.o | grep -A3 .data输出示例[Nr] Name Type Address Offset Size EntSize Flags Link Info Align [ 3] .data PROGBITS 0000000000000000 00000060 0000000000000010 0000000000000000 WA 0 0 4计算修改位置.data节偏移 0x60 g_data在.data节中的偏移 0x11 实际修改位置 0x60 0x11 0x71使用hexedit修改二进制hexedit phase1.o在0x71位置输入学号的ASCII码如23215150438对应32 33 32 31 35 31 35 30 34 33 38验证修改结果gcc -o linkbomb main.o phase1.o -no-pie ./linkbomb关键提示在修改.data节时务必注意数据的字节序和对齐要求。x86架构采用小端字节序但ELF文件中的偏移量总是以大端格式表示。3. 阶段二强弱符号解析与覆盖第二阶段重点在于理解强弱符号的交互规则强符号已初始化的全局变量/函数弱符号未初始化的全局变量COMMON符号操作流程分析弱符号特征readelf -a phase2.o | grep -A3 g_myCharArray输出示例10: 0000000000000000 256 OBJECT WEAK DEFAULT COM g_myCharArray创建强符号覆盖 创建phase2_patch.c文件char g_myCharArray[256] { 0x00, 0x00, /* 前0x11个字节填充0 */ /* 学号ASCII码 */ 0x32, 0x33, 0x32, 0x31, 0x35, 0x31, 0x35, 0x30, 0x34, 0x33, 0x38 };编译与链接gcc -c phase2_patch.c gcc -o linkbomb2 main.o phase2.o phase2_patch.o -no-pie处理偏移问题 当出现字符偏移时可通过辅助程序计算实际偏移量// hack.c #include stdio.h int main() { char buf[256]; fgets(buf, sizeof(buf), stdin); printf(Actual output: %s\n, buf); return 0; }技术细节弱符号的Size字段表示其最小分配空间而强符号的实际大小必须至少达到这个值否则可能导致内存越界。4. 阶段三代码节注入与函数调用重定位第三阶段涉及代码节的直接修改和函数调用重定位这是最复杂的环节之一。以下是关键步骤分析函数调用关系objdump -d linkbomb3输出示例0000000000001149 do_phase: 1149: e8 d2 ff ff ff call 1120 myFunc2 114e: 48 89 c7 mov %rax,%rdi 1151: e8 ba ff ff ff call 1110 myFunc1计算注入位置readelf -S phase3.o | grep -A1 .text输出示例[ 1] .text PROGBITS 0000000000000000 00000040 0000000000000031 0000000000000000 AX 0 0 1注入起始地址 0x40.text节偏移 0x31do_phase函数偏移 0x71构造机器指令call myFunc2相对调用计算偏移量目标地址 myFunc2地址 0x1b 当前指令结束地址 0x31 5 0x36 相对偏移 0x1b - 0x36 -0x1b → 补码表示0xff ff ff e5 完整指令e8 e5 ff ff ffmov %rax,%rdi机器码为48 89 c7call myFunc1类似方法计算二进制修补hexedit phase3.o在0x71位置依次写入构造的指令e8 e5 ff ff ff 48 89 c7 e8 c2 ff ff ff修改数据节内容readelf -S phase3.o | grep -A1 .data.data节偏移 0x1a0 学号位置 0x1a0 0x11 0x1b1深度解析call指令使用PC相对寻址其机器码格式为E8后跟4字节的相对偏移目标地址 - 下条指令地址。理解这一点对正确构造调用指令至关重要。5. 阶段四与五重定位表修复与符号交换最后两个阶段涉及更复杂的重定位表操作阶段四关键步骤识别异常重定位条目readelf -r phase4.o输出示例Relocation section .rela.text at offset 0x250 contains 3 entries: Offset Info Type Sym. Value Sym. Name Addend 0000000000000000 000300000001 R_X86_64_64 0000000000000000 .data 0 0000000000000000 000300000001 R_X86_64_64 0000000000000000 .data 0 0000000000000000 000300000001 R_X86_64_64 0000000000000000 .data 0修正重定位偏移量 根据objdump输出确定正确偏移量0x6, 0x11, 0x19在hexedit中修改0x250位置的数据。阶段五关键步骤调试分析符号引用gdb ./linkbomb5 (gdb) b do_phase (gdb) run (gdb) x/s 0x601040交换重定位条目readelf -r phase5.o找到两个相关条目的偏移量通常在0x340附近交换它们的符号引用。6. 重定位类型速查与实战总结常见重定位类型对比类型计算方式用途字节数R_X86_64_32S A32位绝对地址4R_X86_64_PC32S A - P32位PC相对地址4R_X86_64_64S A64位绝对地址8R_X86_64_PLT32L A - P过程链接表相对地址4实战经验总结系统化分析方法始终从readelf -a开始全面分析ELF结构使用objdump -d交叉验证代码逻辑通过gdb动态调试确认运行时行为常见问题解决段错误通常由重定位地址计算错误导致输出乱码检查数据节的偏移和编码链接失败确认符号可见性和强弱关系进阶技巧# 使用Python辅助计算重定位值 def calc_reloc(S, A, P, type): if type R_X86_64_32: return S A elif type R_X86_64_PC32: return S A - P # 其他类型...通过这五个阶段的系统实践我们不仅掌握了ELF重定位的核心机制还建立了一套完整的二进制分析与修改方法论。这种底层技能在逆向工程、安全分析和系统编程等领域都具有极高的实用价值。