我正在try 将任意数据嵌入到ELF可执行文件中,并让Linux在加载时自动映射它.最近向another question询问了这一点,最终支持将这个用例添加到mold
链接器中.
我已经编写了一个工具,可以在可执行文件的末尾附加任意数据,并在指向附加数据的PT_LOAD
ELF程序头中打补丁.这是修补逻辑:
appended_data_file_offset = /* ... seek(elf file, SEEK_END) ... */;
appended_data_size = /* ... stat(data file) ... */;
phdr->p_type = PT_LOAD;
phdr->p_filesz = phdr->p_memsz = appended_data_size;
size_t base = phdr->p_vaddr - phdr->p_offset; // calculate program's base load address
phdr->p_vaddr = phdr->p_paddr = base + appended_data_file_offset;
phdr->p_offset = appended_data_file_offset;
phdr->p_align = 1;
phdr->p_flags = PF_R;
运行我的修补程序会得到一个ELF文件,其中在偏移量0xAD78
处附加了以下数据:
0000ad70: 00 00 00 00 00 00 00 00 74 65 73 74 20 64 61 74 ........test dat
0000ad80: 61 0a a.
而这PT_LOAD
个细分市场又增加了:
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x000000000000ad78 0x000000000020ad78 0x000000000020ad78
0x000000000000000a 0x000000000000000a R 0x1
这个新的段和末尾的10字节块是对非常好的、工作正常的ELF可执行文件所做的only项更改.通过二进制比对确认.
在运行时,该程序应该能够访问该数据.它通过辅助向量来执行此操作:
Elf64_Phdr *header = (Elf64_Phdr *) getauxval(AT_PHDR);
size_t count = getauxval(AT_PHNUM);
size_t size = getauxval(AT_PHENT);
assert(size == sizeof(Elf64_Phdr));
for (size_t i = 0; i < count; ++header, ++i) {
if (header->p_type != PT_LOAD) { continue; }
if (0 == memcmp(header->p_vaddr, "test", sizeof("test") - 1)) {
// found it
}
}
为了清楚起见,我使用了libc
个函数.我的实际程序是一个用独立C编写的静态EXEC
ELF文件,它不链接到libc
,直接使用Linux系统调用.
在以这种方式修补可执行文件之后,我希望发生这种情况:
- Linux自动将附加到可执行文件中的数据加载到内存中.
- 位于文件中偏移量
0xAD78
处的10字节块.
- 位于文件中偏移量
- 程序通过辅助向量中的
AT_PHDR
值找到节目头表. - Program scans
PT_LOAD
segments until it finds the data.- 这些标头中的
p_vaddr
个应该指向包含"test data\n"
的内存块
- 这些标头中的
相反,这个程序完全崩溃了.不执行任何指令.甚至没有到达入口点.即使是gdb
个人也不能调试它:
(gdb) run
Starting program: exe.patched
During startup program terminated with signal SIGSEGV, Segmentation fault.
(gdb) info registers
The program has no registers now.
(gdb) step
The program is not being run.
它运行没有任何问题,没有PT_LOAD
头虽然.如果我将类型更改为PT_LOOS
或任何其他类型,它也可以工作.
我想不通了.我到底做错了什么?
按要求完成readelf
打印输出:
$ readelf --file-header --program-headers program.patched
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: AArch64
Version: 0x1
Entry point address: 0x2037d8
Start of program headers: 64 (bytes into file)
Start of section headers: 43512 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 5
Size of section headers: 64 (bytes)
Number of section headers: 8
Section header string table index: 6
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x000000000000abf8 0x000000000020abf8 0x000000000020abf8
0x000000000000000a 0x000000000000000a R 0x1
LOAD 0x0000000000000000 0x0000000000200000 0x0000000000200000
0x00000000000027d8 0x00000000000027d8 R 0x1000
LOAD 0x00000000000027d8 0x00000000002037d8 0x00000000002037d8
0x0000000000005ed8 0x0000000000005ed8 R E 0x1000
LOAD 0x00000000000086b0 0x000000000020a6b0 0x000000000020a6b0
0x0000000000000000 0x0000000000100015 RW 0x1000
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 0x0
Section to Segment mapping:
Segment Sections...
00
01 .rodata
02 .text
03 .bss
04