1. ELF文件与链接漏洞实战入门第一次接触LinkLab实验时我被那些晦涩的ELF文件结构和复杂的链接过程搞得晕头转向。直到有一次在调试程序时意外触发了段错误才真正意识到理解这些底层机制的重要性。这次我们就从一个安全研究员的视角重新审视这个实验把它变成一次真实的漏洞挖掘与修复实战。ELF文件就像程序的DNA它包含了代码、数据以及如何将它们组装成可执行程序的所有信息。常见的ELF文件类型包括可重定位文件(.o)编译生成的中间文件等待链接可执行文件完成链接后的程序共享库(.so)动态链接库在LinkLab实验中我们主要操作的是可重定位文件。使用readelf工具查看phase1.o文件时你会看到类似这样的结构$ readelf -a phase1.o ELF Header: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2s complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: REL (Relocatable file) Machine: Advanced Micro Devices X86-64理解这些字段的含义对后续的漏洞分析至关重要。比如Type字段显示这是一个可重定位文件而Machine字段告诉我们这是x86-64架构的程序。2. 阶段1数据节篡改实战这个阶段我们要修改phase1.o中的数据节内容让程序输出指定学号。听起来简单但其中涉及几个关键知识点数据节定位通过readelf找到.data节的偏移量符号解析确定输出函数使用的全局变量地址二进制编辑精确修改指定位置的数据实际操作时我习惯先用objdump查看反汇编代码$ objdump -d phase1.o phase1.o: file format elf64-x86-64 Disassembly of section .text: 0000000000000000 do_phase: 0: 55 push %rbp 1: 48 89 e5 mov %rsp,%rbp 4: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # b do_phase0xb b: e8 00 00 00 00 callq 10 do_phase0x10 10: 5d pop %rbp 11: c3 retq注意到lea指令中的0x0(%rip)实际上是一个待重定位的地址它最终会指向.data节中的字符串。通过交叉查看符号表和重定位表我们可以确定具体修改位置。修改数据节时我推荐使用hexedit工具它比纯命令行编辑器更直观$ hexedit phase1.o 00000000 7F 45 4C 46 02 01 01 00 00 00 00 00 00 00 00 00 .ELF............ 00000010 01 00 3E 00 01 00 00 00 00 00 00 00 00 00 00 00 ............... 00000020 00 00 00 00 00 00 00 00 40 01 00 00 00 00 00 00 ............... ... 000000B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000C0 32 33 32 31 35 31 35 30 33 31 38 00 00 00 00 00 23215150318.....记住字符串要以null字符(0x00)结尾否则可能导致程序读取越界。这是我在第一次尝试时犯的错误结果程序输出了乱码。3. 阶段2强弱符号劫持技术这个阶段展示了链接过程中一个经典的安全问题——强弱符号解析。当遇到同名的强符号和弱符号时链接器会选择强符号。我们可以利用这个特性劫持原本的弱符号定义。具体操作步骤创建一个新的.c文件定义同名的强符号精心构造数据布局使学号出现在正确偏移位置编译成.o文件并与原文件一起链接我创建的phase2_patch.c文件内容如下char g_myCharArray[256] { 0,0,0,0,0, // 填充0x5c个0 // ... 更多0 2,3,2,1,5,1,5,0,3,1,8,\0 // 学号 };编译时要注意对齐问题我建议使用packed属性确保布局准确struct __attribute__((packed)) { char padding[0x5c]; char id[12]; } g_myCharArray {.id 23215150318};这个阶段最有趣的部分是理解链接器如何处理COM类型的符号Common Block。在C语言中未初始化的全局变量会被视为弱符号而初始化的则是强符号。这种差异在安全领域经常被利用来实现函数劫持。4. 阶段3代码节注入技术现在难度升级——我们要直接修改代码节(.text)的内容。这类似于现实中的代码注入攻击只是我们是在链接阶段而非运行时进行。关键步骤分解分析程序控制流确定注入点编写机器指令注意相对地址计算精确修改二进制文件中的代码节通过objdump分析phase3.o$ objdump -d phase3.o phase3.o: file format elf64-x86-64 Disassembly of section .text: 0000000000000000 do_phase: 0: 55 push %rbp 1: 48 89 e5 mov %rsp,%rbp 4: e8 00 00 00 00 callq 9 do_phase0x9 9: 48 89 c7 mov %rax,%rdi c: e8 00 00 00 00 callq 11 do_phase0x11 11: 5d pop %rbp 12: c3 retq我们需要在0x40(.text节偏移)0x31(函数内偏移)0x71处注入新指令。call指令使用相对寻址所以需要计算目标函数的偏移量。这是我总结的计算公式相对偏移 目标地址 - (当前指令地址 指令长度)例如要调用位于0x1b的myFunc20x1b - (0x31 5) -0x1b → 补码表示就是0xffffffe5所以机器码是e8 e5 ff ff ff。5. 阶段4与5重定位表攻防最后两个阶段涉及更复杂的重定位表操作。重定位表告诉链接器如何修改代码中的地址引用篡改这些信息可能导致程序执行非预期代码。在阶段4中我们需要恢复被抹除的重定位信息修正数据节引用处理可能的段错误问题使用readelf查看重定位表$ readelf -r phase4.o Relocation section .rela.text at offset 0x3b8 contains 2 entries: Offset Info Type Sym. Value Sym. Name Addend 00000000000c 00050000000b R_X86_64_32S 0000000000000000 .data 0 000000000011 000a00000002 R_X86_64_PC32 0000000000000000 puts - 4阶段5则引入了更高级的ROP(Return-Oriented Programming)技术。虽然实验要求比较简单但实际应用中ROP可以绕过DEP等防护机制。我们需要分析程序gadget构造调用链精确控制内存布局一个典型的ROP攻击链可能如下pop rdi; ret address of /bin/sh systemplt通过这五个阶段的实战我们不仅理解了链接过程的工作原理还掌握了如何利用和防御这些机制中的安全漏洞。这种底层知识对于安全研究员和系统开发者都至关重要。