二、ELF文件结构

环境准备

本次实验源码:

#include

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

二、ELF文件结构_第1张图片
编译后会生成5个文件,可以发现,ELF文件分为三类:可执行文件.exec可重定位文件.rel共享目标文件.dyn

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

请添加图片描述

ELF文件结构

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

以链接视角来看

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

ELF header

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

节头表

节头表记录了文件中含有的节的信息,由于它对于程序运行不是必须的,所以常有程序去掉节头表,以增加反编译的分析难度。
二、ELF文件结构_第4张图片

.text

之前写到,.text存储可执行指令。
如图所示,本次的.text占0x57个字节。
二、ELF文件结构_第5张图片
下面是.text的内容,最左侧是偏移量
二、ELF文件结构_第6张图片
下面紧跟的是反汇编的结果
二、ELF文件结构_第7张图片

.data和.bss

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

symbol table

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

以运行视角来看

可执行文件装载

系统其实并不关心每个节的实际内容,而是关心这些节的读写执行的权限,所以可以将相同权限的节统一为同一段,进行装载,从而节省资源。
二、ELF文件结构_第11张图片

你可能感兴趣的:(二进制安全,安全,linux,二进制)