Linux下的Object文件加载器
字数 1409 2025-08-24 10:10:13
Linux下的Object文件加载器技术详解
前言
本文详细讲解Linux环境下如何实现一个Object文件(.o)加载器,类似于Windows下的CoffLoader和Cobalt Strike的BOF功能。我们将深入分析ELF文件结构、重定位机制,并提供实现思路。
目标设定
我们以下面的C代码为例,目标是加载其生成的test.o文件:
void println(char *buf);
void debugln(char *buf);
void hello_world();
int test_func_call(unsigned char *buf){
println(buf);
return 0;
}
int main(){
char *buf = "Hello World!";
test_func_call(buf);
debugln(buf);
hello_world();
return 1;
}
ELF文件结构分析
反汇编分析
使用objdump -d test.o -M intel查看.text节的汇编代码:
Disassembly of section .text:
0000000000000000 <test_func_call>:
0: f3 0f 1e fa endbr64
4: 55 push rbp
5: 48 89 e5 mov rbp,rsp
8: 48 83 ec 10 sub rsp,0x10
c: 48 89 7d f8 mov QWORD PTR [rbp-0x8],rdi
10: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8]
14: 48 89 c7 mov rdi,rax
17: e8 00 00 00 00 call 1c <test_func_call+0x1c>
1c: b8 00 00 00 00 mov eax,0x0
21: c9 leave
22: c3 ret
0000000000000023 <main>:
23: f3 0f 1e fa endbr64
27: 55 push rbp
28: 48 89 e5 mov rbp,rsp
2b: 48 83 ec 10 sub rsp,0x10
2f: 48 8d 05 00 00 00 00 lea rax,[rip+0x0] # 36 <main+0x13>
36: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax
3a: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8]
3e: 48 89 c7 mov rdi,rax
41: e8 00 00 00 00 call 46 <main+0x23>
46: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8]
4a: 48 89 c7 mov rdi,rax
4d: e8 00 00 00 00 call 52 <main+0x2f>
52: b8 00 00 00 00 mov eax,0x0
57: e8 00 00 00 00 call 5c <main+0x39>
5c: b8 01 00 00 00 mov eax,0x1
61: c9 leave
62: c3 ret
可以看到call指令的操作数全是0,需要通过重定位信息来修正。
.rela节分析
.rela节存储重定位信息,结构如下:
typedef struct {
Elf32_Addr r_offset;
Elf32_Word r_info;
Elf32_Sword r_addend;
} Elf32_Rela;
使用readelf -r test.o查看.rela.text节:
Relocation section '.rela.text' at offset 0x268 contains 5 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000000018 000500000004 R_X86_64_PLT32 0000000000000000 println - 4
000000000032 000300000002 R_X86_64_PC32 0000000000000000 .rodata - 4
000000000042 000400000004 R_X86_64_PLT32 0000000000000000 test_func_call - 4
00000000004e 000700000004 R_X86_64_PLT32 0000000000000000 debugln - 4
000000000058 000800000004 R_X86_64_PLT32 0000000000000000 hello_world - 4
各字段含义:
r_offset: 重定位位置相对section首地址的偏移r_info: 64位ELF分为32位符号表索引和32位类型字段sym = r_info >> 32type = r_info & 0xFFFFFFFF
Type: 重定位类型,如R_X86_64_PLT32Sym. Name + Addend: 符号名和加数
.symtab节分析
符号表结构:
typedef struct {
Elf64_Word st_name;
unsigned char st_info;
unsigned char st_other;
Elf64_Half st_shndx;
Elf64_Addr st_value;
Elf64_Xword st_size;
} Elf64_Sym;
使用readelf -s test.o查看符号表:
Symbol table '.symtab' contains 9 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS test.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1 .text
3: 0000000000000000 0 SECTION LOCAL DEFAULT 5 .rodata
4: 0000000000000000 35 FUNC GLOBAL DEFAULT 1 test_func_call
5: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND println
6: 0000000000000023 64 FUNC GLOBAL DEFAULT 1 main
7: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND debugln
8: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND hello_world
关键字段:
st_name: 字符串表中的索引st_shndx: 符号所在节的序号SHN_UNDEF: 符号未在当前文件定义SHN_ABS: 符号是绝对地址SHN_COMMON: 符号用于定义对齐字节
st_value: 符号相对于所在节起始地址的偏移
重定位机制详解
重定位类型说明
根据Intel MPX Linux64 ABI文档,重定位类型含义如下:
R_X86_64_PLT32(4): PLT32重定位R_X86_64_PC32(2): PC相对32位重定位
计算符号说明:
A: 被加数B: 共享对象基地址G: 符号在GOT中的偏移L: PLT条目位置P: 重定位存储单元位置S: 符号值Z: 符号大小
程序内重定向
示例:main函数调用test_func_call
重定位表项:
000000000042 000400000004 R_X86_64_PLT32 0000000000000000 test_func_call - 4
对应汇编:
41: e8 00 00 00 00 call 46 <main+0x23>
计算规则:
L + A - P
由于没有PLT表,简化为:
sym_real_address + r_addend - patch_real_address
其中:
sym_real_address: 符号的真实内存地址patch_real_address: 需要修改的内存地址r_addend: 加数(这里是-4)
程序外重定向
示例:调用外部函数如hello_world
更简单,直接计算相对偏移:
sym_real_address - patch_real_address - 4/8
其中4/8取决于操作数长度(32位用4,64位用8)
实现总结
- 解析ELF文件,获取.text节和.rela.text节
- 处理重定位表:
- 区分程序内和程序外重定向
- 根据不同类型应用相应计算规则
- 修正call指令的操作数
- 将修正后的代码加载到内存执行
项目实现已用Rust编写,地址:https://github.com/Sndav/coffee
关键点回顾
- ELF文件结构分析,特别是.rela和.symtab节
- 重定位机制的理解和计算规则
- 区分程序内和程序外重定向的不同处理方式
- call指令操作数的修正方法
- 实际实现时的内存地址计算
通过以上分析,我们可以实现一个功能完整的Linux Object文件加载器,为安全研究提供更多可能性。