-1

本质上,我正在做类似于https://wiki.osdev.org/ELF_Tutorial的事情,我将数据加载到结构中并通过它们的偏移量读取各个部分。主机是小端,我正在尝试分析为大端目标交叉编译的文件。我尝试对这些大端文件执行与小端文件相同的代码序列,但是在尝试访问这些部分时代码段错误。

int fd = open(filename, O_RDONLY);
char *header_start = (char *)mmap(0, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
Elf32_Ehdr* elf_ehdr = (Elf32_Ehdr *)header_start;
Elf32_Shdr* elf_shdrs = (Elf32_Shdr *)((int)header_start + elf_ehdr->e_shoff);
Elf32_Shdr* sh_strtab = &elf_shdrs[elf_ehdr->e_shstrndx];
// code segfaults here when trying to access sh_strtab->sh_offset for big endian
// files, but works just fine for little endian files

为什么大端文件的代码会失败?

4

1 回答 1

1

在大端文件elf_ehdr->e_shoff中将是一个大端整数,并且需要尊重大端字节顺序。

假设我们正在处理 32 位,并且e_shoff是一个不错的小数字,例如 64。在大端序中,它将在文件中记录为 0x00000040。但是你在一个小端 CPU 上读取这个文件,所以 0x00000040 作为二进制 blob 从文件中读出,CPU 会将其解释为 1073741824。

Elf32_Shdr* elf_shdrs = (Elf32_Shdr *)((int)header_start + elf_ehdr->e_shoff);

解决为

Elf32_Shdr* elf_shdrs = (Elf32_Shdr *)((int)header_start + 1073741824);

不是

Elf32_Shdr* elf_shdrs = (Elf32_Shdr *)((int)header_start + 64);

并且会以很大的差距错过目标。试图访问由此产生的成员会elf_shdrs陷入未定义的行为。

快速破解修复是

Elf32_Shdr* elf_shdrs = (Elf32_Shdr *)(header_start + ResolveEndian(elf_ehdr->e_shoff));

whereResolveEndian是一系列重载函数,它们要么完全不做任何事情,因为文件字节序与系统字节序匹配或翻转字节顺序。有关如何执行此操作的许多示例,请参阅如何在 C++ 中的大端和小端值之间进行转换?

较长的修复将不使用内存映射文件,而是考虑到 32 位和 64 位程序以及字节序之间变量大小的差异(以及由此产生的偏移量差异)来反序列化文件。这将产生一个更健壮和可移植的解析器,无论源 ELF 和用于构建解析器的编译器实现如何,它都将始终工作。

于 2018-03-23T18:17:28.770 回答