简单的Elf解析器实现

  摘要:之前虽然了解过ELF文件的具体格式,但是对改文件的理解还是存在一些不足,因此本文尝试写了一个简单的ELF解析器对ELF文件进行解析并输出Header,Program Header Table和Section Header Table。
  关键字:C++,ELF
  读者注意:阅读本文是你需要对ELF有最基本的了解。

  源码链接

1 准备工作

  在进行文件解析之前当然需要将Linux的ELF头文件引入过来,但是需要注意的该文件依赖Linux内部的一些头文件,所以我们需要将上面include的头文件删除,并重新定义对应的类型。没必要把依赖的头文件全部引入到我们的工程。

typedef uint32_t __u32;
typedef uint16_t __u16;
typedef uint64_t __u64;
typedef  int32_t __s32;
typedef  int16_t __s16;
typedef  int64_t __s64;

2 解析

  解析的过程比较简单就是读文件然后将对应的内存转换为目标结构体即可一步一步解析,过程比较简单只是比较累人。
  在进行解析前需要注意因为我们可能是在Windows上解析另一台Linux或者其他机器编译的可执行文件或者二进制文件,我们无法确认改文件的具体位数和大小端。因此我们需要先将ELF中的EIDENT魔数读取出来,根据该数值来判断具体的文件位数和大小端。

Error ElfParser::identity() {
	unsigned char ident[EI_NIDENT]{};
	int readlen{};
	if (Error::None != readbuf(ident, sizeof(unsigned char) * EI_NIDENT)) {
		return Error::IO;
	}

	if (ident[EI_MAG0] != ELFMAG0 || ident[EI_MAG1] != ELFMAG1 || ident[EI_MAG2] != ELFMAG2 || ident[EI_MAG3] != ELFMAG3) {
		printf("the first fource byte is not 0x7f E L f but %x %c %c %c\n", ident[EI_MAG0], ident[EI_MAG1], ident[EI_MAG2], ident[EI_MAG3]);
		return Error::INVALID_DATA;
	}

	_bit = static_cast<FileBit>(ident[4]);
	_order = static_cast<BitOrder>(ident[5]);
	resetio();
	return Error::None;
}

  如果该文件的存储顺序和本地机器不同就需要将数据进行转换,比如大端转小端,小端转大端。比如下面将ELFHeader的每个成员的顺序都进行转换。

template<class T>
T swapEndian(const T v) {
	static_assert(CHAR_BIT == 8, "char_bit is not 8");
	union{
		T u;
		unsigned char u8[sizeof(T)];
	}src, dst;
	src.u = v;
	for (int i = 0; i < sizeof(T); i++) {
		dst.u8[i] = src.u8[sizeof(T) - i - 1];
	}

	return dst.u;
}

void Elf64::parseHeader(unsigned char *ptr) {
	_header = *(elf64_hdr*)ptr;
	if (_order == nativeMachineOrder()) {
		return;
	}

	swapEndian(_header.e_type);
	swapEndian(_header.e_machine);
	swapEndian(_header.e_version);
	swapEndian(_header.e_entry);
	swapEndian(_header.e_phoff);
	swapEndian(_header.e_shoff);
	swapEndian(_header.e_flags);
	swapEndian(_header.e_ehsize);
	swapEndian(_header.e_phentsize);
	swapEndian(_header.e_phnum);
	swapEndian(_header.e_shentsize);
	swapEndian(_header.e_shnum);
	swapEndian(_header.e_shstrndx);
}

  从内存中拿到ELF文件的结构体后,如果需要将ELF的数据转换为可读的形式,需要一一对着标准文档来进行解析,按照自己觉得舒适的方式转换为字符串输出即可。过程比较简单,但是真的很费时间,因为东西比较多。

3 结果

下面是readelf的输出:

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:                              DYN (Shared object file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x10c0
  Start of program headers:          64 (bytes into file)
  Start of section headers:          15336 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         13
  Size of section headers:           64 (bytes)
  Number of section headers:         31
  Section header string table index: 30

  下面是本文实现的解析器的输出(有些字段可能是Unknown之类,因为字段太多了,我偷了个懒,比如Machine是x86_64,该字段有100个选项,太多了我只写了10个,后面的都是Unknown):

Elf Header:
    Magic Number:                      0x7f 0x45 0x4c 0x46 0x2 0x1 0x1 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
    File Identity:                     ELF
    Arch Bit:                          64bit
    Arch Bit Order:                    LSB
    Version:                           1
    OSAbi:                             None
    Type:                              Shared object file
    Machine:                           Unknown
    Version:                           Current Version
    Entry Point:                       0x10c0
    Program Hedaer Table Offset:       64
    Section Hedaer Table Offset:       15336
    Flags:                             0x0
    Header Size:                       64(in bytes)
    Program Hedaer Size:               56(in bytes)
    Program Header Number:             13
    Section Hedaer Size:               64(in bytes)
    Section Header Number:             31
    String Table Seciton Index:        30

Segment Header
    Type        Offset      VirtAddr          PhyAddr           FileSz      MemSz       Flags       Align
    PT_PHDR     64          0x40              0x40              728         728         4           8
    PT_INTERP   792         0x318             0x318             28          28          4           1
    PT_LOAD     0           0x0               0x0               2112        2112        4           4096
    PT_LOAD     4096        0x1000            0x1000            725         725         5           4096
    PT_LOAD     8192        0x2000            0x2000            432         432         4           4096
    PT_LOAD     11640       0x3d78            0x3d78            664         992         6           4096
    PT_DYNAMIC  11664       0x3d90            0x3d90            512         512         6           8
    PT_NOTE     824         0x338             0x338             32          32          4           8
    PT_NOTE     856         0x358             0x358             68          68          4           4
    PT_INTERP   824         0x338             0x338             32          32          4           8
    PT_NULL     8212        0x2014            0x2014            84          84          4           4
    PT_LOAD     0           0x0               0x0               0           0           6           16
    PT_DYNAMIC  11640       0x3d78            0x3d78            648         648         4           1

Section Header
    Name                Type            Address Offset  Size    Entsize Flags   Link    Info    Align
                        SHT_NULL        0x0     0       0       0       0       0       0       0
    .interp             SHT_PROGBITS    0x318   792     28      0       2       0       0       1
    .note.gnu.property  SHT_NOTE        0x338   824     32      0       2       0       0       8
    .note.gnu.build-id  SHT_NOTE        0x358   856     36      0       2       0       0       4
    .note.ABI-tag       SHT_NOTE        0x37c   892     32      0       2       0       0       4
    .gnu.hash           SHT_SYMTAB      0x3a0   928     40      0       2       6       0       8
    .dynsym             SHT_DYNSYM      0x3c8   968     312     24      2       7       1       8
    .dynstr             SHT_STRTAB      0x500   1280    355     0       2       0       0       1
    .gnu.version        SHT_DYNSYM      0x664   1636    26      2       2       6       0       2
    .gnu.version_r      SHT_SHLIB       0x680   1664    64      0       2       7       2       8
    .rela.dyn           SHT_RELA        0x6c0   1728    288     24      2       6       0       8
    .rela.plt           SHT_RELA        0x7e0   2016    96      24      66      6       24      8
    .init               SHT_PROGBITS    0x1000  4096    27      0       6       0       0       4
    .plt                SHT_PROGBITS    0x1020  4128    80      16      6       0       0       16
    .plt.got            SHT_PROGBITS    0x1070  4208    16      16      6       0       0       16
    .plt.sec            SHT_PROGBITS    0x1080  4224    64      16      6       0       0       16
    .text               SHT_PROGBITS    0x10c0  4288    517     0       6       0       0       16
    .fini               SHT_PROGBITS    0x12c8  4808    13      0       6       0       0       4
    .rodata             SHT_PROGBITS    0x2000  8192    17      0       2       0       0       4
    .eh_frame_hdr       SHT_PROGBITS    0x2014  8212    84      0       2       0       0       4
    .eh_frame           SHT_PROGBITS    0x2068  8296    328     0       2       0       0       8
    .init_array         SHT_INIT_ARRAY  0x3d78  11640   16      8       3       0       0       8
    .fini_array         SHT_FINI_ARRAY  0x3d88  11656   8       8       3       0       0       8
    .dynamic            SHT_DYNAMIC     0x3d90  11664   512     16      3       7       0       8
    .got                SHT_PROGBITS    0x3f90  12176   112     8       3       0       0       8
    .data               SHT_PROGBITS    0x4000  12288   16      0       3       0       0       8
    .bss                SHT_NOBITS      0x4040  12304   280     0       3       0       0       64
    .comment            SHT_PROGBITS    0x0     12304   43      1       48      0       0       1
    .symtab             SHT_SYMTAB      0x0     12352   1800    24      0       29      50      8
    .strtab             SHT_STRTAB      0x0     14152   897     0       0       0       0       1
    .shstrtab           SHT_STRTAB      0x0     15049   282     0       0       0       0       1
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以为您提供一些基本的思路和步骤,但是由于篇幅限制,可能无法提供完整的代码实现。请注意,汇编语言的实现可能会因为不同的处理器架构而有所不同。 步骤: 1. 读取 ELF 文件头部信息。可以使用 Linux 系统提供的系统调用 open 和 read 从文件中读取 ELF 文件头部信息。 2. 解析 ELF 文件头部信息。按照 ELF 文件格式的规定,解析 ELF 文件头部信息,获取节头表偏移和节头表中节的数量等信息。 3. 读取节头表信息。根据第二步中获取的节头表偏移和节的数量,读取节头表信息。 4. 解析节头表信息。按照 ELF 文件格式的规定,解析节头表信息,获取节的名称、大小、类型等信息。 5. 读取和解析节的内容。根据第四步中获取的节的信息,读取和解析节的内容。 6. 打印或使用节的内容。根据需求,使用节的内容或者打印出来。 下面是一个简单的汇编代码实现,仅供参考: ``` section .data elf_header: db 0x7f, 'E', 'L', 'F', 1, 1, 1, 0 ; ELF header times 9 db 0 dw 2, 0x3e ; x86_64, ELF_VERSION dd _start, _header, 0, 0, 0, 0, 0 ; entry point, program header offset ; section header offset times 2 dw 0 ; ELF flags, header size dw 0x38 ; program header entry size dw 1 ; number of program header entries dw 0 ; section header entry size dw 0 ; number of section header entries dw 0 ; section name string table index section .text _start: xor eax, eax mov ebx, elf_header mov ecx, 16 mov edx, 1 int 0x80 ; write ELF header to stdout _header: ; read ELF header from stdin and parse it ; read section header table from stdin and parse it ; read sections from stdin and use them ; ... ; exit mov eax, 1 xor ebx, ebx int 0x80 ``` 这段汇编代码实现了一个简单ELF 文件解析器,它将 ELF 文件头部信息直接输出到标准输出。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值