二、ELF文件结构

发布时间:2024年01月09日

环境准备

本次实验源码:

#include<stdio.h>

int global_init_var = 10;
int global_uninit_var;

void func(int sum){
    printf("%d\n", sum);
}

int main(){
        static int local_static_init_var = 20;
        static int local_static_uninit_var;

        int local_init_val = 30;
        int local_uninit_var;

        func(global_init_var + local_init_val + local_static_init_var);
}

编译

gcc example.c -o example.exec
gcc -static example.c -o example_static.exec
gcc -c example.c -o example.rel
gcc -c -fPIC example.c -o example_pic.rel && gcc -shared example_pic.rel -o example.dyn

请添加图片描述
编译后会生成5个文件,可以发现,ELF文件分为三类:可执行文件.exec可重定位文件.rel共享目标文件.dyn

  • 可执行文件:经过链接的、可执行的目标文件,通常为程序
  • 可重定位文件:由源文件编译而成且尚未链接的目标文件,通常是.o文件,用于与其他目标文件进行链接以构成可执行文件或动态链接库,通常是一段位置独立的代码PIC
  • 共享目标文件:动态链接库文件。

请添加图片描述

ELF文件结构

在审视一个目标文件时,有两种视角:一是链接视角,通过节(Section)划分;另一种是运行视角,通过段(Segment)进行划分。

以链接视角来看

链接视角来看,目标文件通常都会包括代码(.text),数据(.data)和BSS(.bss)三个节,分别存储可执行的机器指令、已初始化的全局变量和局部静态变量、为初始化的全局变量和局部静态变量。除了这三个节之外,简化的目标文件还应包含一个文件头。
请添加图片描述
将程序指令和程序数据分开,从安全角度考虑,数据和指令分别被映射到两个虚拟区域,数据是可读写的,而指令是只读的,防止程序的指令被改写和利用。

ELF header

ELF文件头位于目标文件最开始的位置,记录了文件的一些基本信息,由魔术字符7f 45 4c 46标志开始,即字符串“\177ELF”,当文件被映射到内存时,可以通过搜索该字符确定映射地址。
请添加图片描述

节头表

节头表记录了文件中含有的节的信息,由于它对于程序运行不是必须的,所以常有程序去掉节头表,以增加反编译的分析难度。
请添加图片描述

.text

之前写到,.text存储可执行指令。
如图所示,本次的.text占0x57个字节。
请添加图片描述
下面是.text的内容,最左侧是偏移量
请添加图片描述
下面紧跟的是反汇编的结果
请添加图片描述

.data和.bss

可以发现,.data占0x08个字节,.rodata占0x04个字节。.bss没有contents属性,表示该节其实并不存在。只是为它预留了位置
请添加图片描述
根据contents的内容,可以看到global_init_var(0a000000)和local_static_init_var(14000000)会存放在.data中,而局部变量local_init_val则会直接存储在指令中;.rodata保存只读数据,存放了printf中的参数。
请添加图片描述

symbol table

符号表记录了目标文件中所用到的所有符号信息,通常分为.dynsym和.symtab,前者是后者的子集。.dynsym保存了引用自外部文件的符号,只能在运行时被解析,而.symtab还保存了本地符号,用于调试和链接。目标文件通过符号在符号表中的索引值来使用该符号。
请添加图片描述

以运行视角来看

可执行文件装载

系统其实并不关心每个节的实际内容,而是关心这些节的读写执行的权限,所以可以将相同权限的节统一为同一段,进行装载,从而节省资源。
请添加图片描述

文章来源:https://blog.csdn.net/mirocky/article/details/135472571
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。