ELF文件格式详解(Linux可执行文件格式)

一、ELF文件格式概述

ELF(Executable and Linkable Format)是一种可执行文件和可链接文件格式,被广泛地应用于Unix和类Unix系统中。ELF格式具有以下特点:

1、二进制格式,支持可执行文件、共享目标文件、目标文件等多种类型;

2、通过段(Section)概念对文件进行组织,段包含代码、数据、符号表等信息;

3、支持动态链接,共享目标文件可以在运行时被加载,共享;

4、可通过读取和修改ELF文件中的信息,实现二进制文件的混淆、加密等操作。

二、ELF文件格式组成

一个ELF文件主要包含三个部分:文件头(ELF header)、节区表(Section header table)和节区(Sections)。

1、文件头(ELF Header)

文件头定义了整个ELF文件的组织结构和属性信息,包括标识、目标类型、节区表的位置和大小等。文件头的结构定义如下:

struct Elf32_Ehdr {
    unsigned char e_ident[EI_NIDENT];
    Elf32_Half e_type;
    Elf32_Half e_machine;
    Elf32_Word e_version;
    Elf32_Addr e_entry;
    Elf32_Off e_phoff;
    Elf32_Off e_shoff;
    Elf32_Word e_flags;
    Elf32_Half e_ehsize;
    Elf32_Half e_phentsize;
    Elf32_Half e_phnum;
    Elf32_Half e_shentsize;
    Elf32_Half e_shnum;
    Elf32_Half e_shstrndx;
};

2、节区表(Section Header Table)

节区表是一张记录了整个ELF文件中每个节区的信息的表格。节区表一般位于ELF文件的开头末尾,节区表的信息包括节区的名称、偏移地址、大小等。节区表的结构定义如下:

struct Elf32_Shdr {
    Elf32_Word sh_name;
    Elf32_Word sh_type;
    Elf32_Word sh_flags;
    Elf32_Addr sh_addr;
    Elf32_Off sh_offset;
    Elf32_Word sh_size;
    Elf32_Word sh_link;
    Elf32_Word sh_info;
    Elf32_Word sh_addralign;
    Elf32_Word sh_entsize;
};

3、节区(Sections)

每个节区对应一个文件中的逻辑块,例如代码段、数据段、符号表段、字符串表段等等。节区的定义如下:

struct elf_section {
    Elf32_Word sh_name;
    Elf32_Word sh_type;
    Elf32_Word sh_flags;
    Elf32_Addr sh_addr;
    Elf32_Off sh_offset;
    Elf32_Word sh_size;
    Elf32_Word sh_link;
    Elf32_Word sh_info;
    Elf32_Word sh_addralign;
    Elf32_Word sh_entsize;
    void *data;
};

三、ELF文件格式分析

1、ELF文件标识(ELF Identification)

在ELF文件中,前4个字节称为文件的魔数,标识文件的类型。ELF文件标识的结构定义如下:

#define EI_NIDENT 16

struct Elf32_Ehdr {
    unsigned char e_ident[EI_NIDENT];
    //...
};

e_ident数组前四个字节是文件魔数,分别为0x7F、’E’、’L’、’F’。文件是否是64位还是32位,处理器类型等信息,也在e_ident表中。

2、节区表(Section Header Table)

节区表是定义每个节(Section)的一张表,表中包含了每个节的信息,如名称、大小、对齐方式等。节区表的格式由Elf32_Shdr或Elf64_Shdr表示。

ELF文件中,标准的节区表通常包含以下几个节区:

代码段(.text):存放程序的机器代码,只读不可写。

数据段(.data):存放程序中已初始化的全局变量和静态变量,可读可写。

只读数据段(.rodata):存放程序中的只读数据,如字符串常量等,只读不可写。

符号表(.symtab):存放程序中的全局符号信息,符号表中包含符号名、符号类型、符号地址等。

字符串表(.strtab):存放程序中使用的字符串常量的名称。

重定位表(.rel.text):用于对代码段进行重定位操作。

3、程序头表(Program Header(TWo-level))

程序头表由Elf32_Phdr或Elf32_Phdr数组表示,其名称中的“P”代表“Program”,也就是程序头表。在程序执行时,程序头表会被内核解析并用来对整个进程的映像文件进行映像。

struct Elf32_Phdr {
    Elf32_Word p_type;
    Elf32_Off  p_offset;
    Elf32_Addr p_vaddr;
    Elf32_Addr p_paddr;
    Elf32_Word p_filesz;
    Elf32_Word p_memsz;
    Elf32_Word p_flags;
    Elf32_Word p_align;
};

4、重定位(Relocation)

重定位是指将静态编译的程序映射到内存中执行时,因为内存的地址可能与链接时的地址不同,需要对代码中的地址进行调整,以便代码可以在内存中正常运行。这个过程称为重定位(Relocation),在ELF中,重定位相关的信息保存在重定位表(.rel.text)和重定位表(.rel.data)中。

5、链接(Linking)

链接(Linking)是指将多个目标文件和库,最终链接成一个可执行文件或共享目标文件的过程。链接器会将多个目标文件的节合并为一个文件,并进行符号重定位等处理。

四、ELF文件格式示例

1、C语言程序生成ELF文件示例

// demo.c
#include 

void main() {
    printf("Hello World!n");
}

使用gcc生成可执行程序demo:

gcc -o demo demo.c

使用objdump查看demo的汇编代码:

objdump -d demo

通过objdump生成的结果可以看到,ELF二进制文件包含了很多信息,包括符号表、重定位表、反汇编的代码等信息。

2、动态链接库(shared object)生成ELF文件示例

多个进程间同时使用同一个共享库,可以减少内存使用,应用程序所用的内存被调用库的代码部分所共享,因为在内存中只有一份副本。所以,动态链接库的好处就在于节约了内存。以下是使用gcc来创建动态链接库:

// demo.c
#include 

void foo() {
    printf("Hello World");
}

void bar() {
    printf("Hello Bar");
}

编译动态链接库:

gcc -shared -o libdemo.so demo.c

编译完成后会生成一个名为libdemo.so的动态链接库文件,接下来需要将生成的动态库文件放到OS默认的动态库搜索目录中(例如/usr/lib目录)。

程序可以通过使用动态链接库进行编译,而不需要手动将库打包进可执行文件中:

// main.c
#include 
#include 

int main() {
    void *handle;
    void (*foo)(), (*bar)();

    handle = dlopen("libdemo.so", RTLD_LAZY);
    if (handle == NULL) {
        printf("Failed to open library.n");
        return 1;
    }

    foo = dlsym(handle, "foo");
    bar = dlsym(handle, "bar");

    foo();
    bar();

    dlclose(handle);
    return 0;
}

编译可执行程序main:

gcc -o main main.c -ldl

动态链接库和可执行程序main都是ELF文件格式。

五、ELF文件格式的应用

在Linux/BSD等类UNIX系统中,ELF文件格式被广泛应用于可执行程序、共享库、目标文件等类型的二进制文件。ELF文件格式将程序代码、数据、符号表、重定位等信息完整地保存在二进制文件中,为程序的开发、调试、优化提供了很好的基础。

同时,ELF文件格式也被用于二进制代码混淆、加密等操作中,通过修改ELF文件中的信息,可以达到有效保护程序机密性的目的。

六、总结

本文对ELF文件格式进行了详细的阐述,介绍了ELF文件格式的组成、特点以及应用。ELF文件格式是Unix和类Unix系统中常用的二进制文件格式,通过ELF格式,程序的开发、调试、优化等方面得到了很好的支持。同时,ELF文件格式也为程序的保护提供了很好的基础。

Published by

风君子

独自遨游何稽首 揭天掀地慰生平