CTF权威指南 笔记 -第二章二进制文件- 2.2 -ELF文件格式

目录

ELF的文件类型

ELF文件的结构

 ELF文件头

 节头表

代码节

数据节和只读数据节

 bss节

字符串表

 符号表

重定位

可执行文件的装载

常见的段


ELF就是可执行可连接格式 为linux运行文件格式

ELF的文件类型

我们使用复杂的例子进行演示

#include

int global_init_var = 10;
int global_uninit_var;
定义全局变量

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

void main(void){
    static int local_static_init_var=20;
    static int local_static_uninit_var;
局部变量
只能在main函数中访问
    int local_init_val=30;
    int local_uninit_var;

    func(global_init_var + local_init_val + local_static_init_var);
打印这个的值    10+20+30
}

发现确实是60 

gcc elfDemo.c -o elfDemo.exce

这里把他编译成windows 的pe文件 exe文件类型

gcc -static elfDemo.c -o elfDEmo_static.exec

这里也是windows的但是是静态链接的


gcc -c elfDemo.c -o elfDemo.rel

这里编译成 rel文件  为 可重定位文件 这里就是之前的汇编阶段


gcc -c -fPIC elfDemo.c -o elfDemo_pic.rel && gcc -shared elfDemo_pic.rel -o elfdemo.dyn

这里把文件编译 为pic类型 位置独立代码 就是 代码可以是在不固定的虚拟内存区域 我们无法预测

同时前面成功的话编译一个 共享库的这个文件 名为 .dyn


&&是前面成功 才执行后面的
 这样可以实现代码函数的共享和重用

CTF权威指南 笔记 -第二章二进制文件- 2.2 -ELF文件格式_第1张图片

 这里可以发现 ELF文件 分为3类

可执行文件 exec :经过链接 可执行的目标文件  叫做程序

可重定位文件 : 预编译文件编译但没有实现链接的文件 .o结尾 通常是PIC类型 

共享目标文件 :动态链接库文件 用于在链接过程中和其他共享库进行链接 构建目标文件
或者在可执行文件运行的时候 链接到进程中 成为运行代码 的一部分

ELF文件的结构

在看目标文件的两个视角

链接视角 通过节进行划分

运行视角 通过段进行划分 

节和段 

节是一种逻辑的划分 一个程序中 具有很多节 可以是代码 调试信息等
段是一种物理的划分 一个段中 具有一组节 段又可以分为代码段 数据段等

CTF权威指南 笔记 -第二章二进制文件- 2.2 -ELF文件格式_第2张图片

这里section header tables 为节头表 用来保存节的信息 内容等

而我们例子的链接视角为

CTF权威指南 笔记 -第二章二进制文件- 2.2 -ELF文件格式_第3张图片

 ELF文件头

后面的所有是在汇编链接过程中得到的

文件头是在目标文件最开始的位置 包含着基本信息

ELF文件类型 版本 目标机器 程序入口 段表和节表的位置和长度等

ELF的魔术字符 和jpg的文件头字符一样 遇到就说明是这个类型

7f 45 4c 46   \177ELF

我们可以使用readelf来查看文件头

readelf -h elfDemo.rel

 CTF权威指南 笔记 -第二章二进制文件- 2.2 -ELF文件格式_第4张图片

 节头表

一个文件包含很多个节 这些节的信息存放在节头表中 这个表的每一个项都是 Elf64_Shdr结构体

记录着 节的名字 长度 偏移 读写权限等信息  

节头表的位置记录在e_shoff域中

但是节头表在程序中并不是必须的

所以一些会除去节头表来增加反编译的难度

我们通过readelf查看

readelf -S elfDemo.rel

CTF权威指南 笔记 -第二章二进制文件- 2.2 -ELF文件格式_第5张图片

我们进行深入查看节头表的.text , .data, .bss节

代码节

objdump -x -s -d elfDemo.rel


-x 列出完整的头部信息

-s 显示每一个节的大小

-d  把符号的地址和名字列出来

 我们只看text

 CTF权威指南 笔记 -第二章二进制文件- 2.2 -ELF文件格式_第6张图片

 先看这个 这个内容为

Contents of section .text:

是text数据的十六进制形式 总共有 0x4e个字节 4个算一个字节

最左边为偏移量 中间为内容 最右边为ASCII表示

CTF权威指南 笔记 -第二章二进制文件- 2.2 -ELF文件格式_第7张图片

 这里是反汇编的结果

数据节和只读数据节

data节保存着初始化的全局变量和局部静态变量

global_init_var  (0a000000)
全局变量

local_static_init_var  (14000000)
局部静态变量

每一个变量4个字节 一共8个字节

.rodata为只读数据 包括只读变量和字符串常量

源代码中调用printf函数 用到了字符串常量 "%d\n" 这是一种只读数据

所以保存在rodata中

 bss节

 这个节用于保存 未初始化的全局变量和局部静态变量 这个节在文件中其实不存在

只是变量预留的空间

 所以下面没有contents属性

CTF权威指南 笔记 -第二章二进制文件- 2.2 -ELF文件格式_第8张图片

 这里继续给出其他常见的节

CTF权威指南 笔记 -第二章二进制文件- 2.2 -ELF文件格式_第9张图片

字符串表

包括了以 null结尾的字符序列 用来表示符号名和节名

所以引用字符串只需要给出在字符序列中的偏移量即可

字符串表达第一个字符和最后一个字符 都是 null

readelf -x .strtab elfDemo.rel


readelf -x .shstrtab elfDemo.rel

CTF权威指南 笔记 -第二章二进制文件- 2.2 -ELF文件格式_第10张图片

 能发现第一个和最后一个都是 00000000 null

CTF权威指南 笔记 -第二章二进制文件- 2.2 -ELF文件格式_第11张图片

 符号表

是记录了 目标文件中 所需要用到的所有符号信息 分为 .dynsym .dyntab

前者是后者的子集
 .dynsym 保存了引用外部文件的符合 只能在运行的时候被解析
.dyntab 不仅保存了外部 还保存了本地符合 用于调试和链接

索引值都是从 0 开始计数 但是0不具有实际意义

每一个符号都要符号值

readelf -s elfDemo.rel

CTF权威指南 笔记 -第二章二进制文件- 2.2 -ELF文件格式_第12张图片

这里能看见 printf 为 und 说明是外部 需要程序运行才能知道地址

重定位

重定位是连接符号定义和符号引用的过程  

可重定位文件在构建可执行文件和共享目标文件的过程中 需要把节中符号引用转换为这些符号在进程空间的虚拟地址

包含这些转换信息的数据 就是重定位项

readelf -r elfDemo.rel

CTF权威指南 笔记 -第二章二进制文件- 2.2 -ELF文件格式_第13张图片

可执行文件的装载

这里是从运行视角进行审视

运行一个可执行文件

 将该文件和动态链接库 装载到进程空间中 形成进程镜像
每一个进程都有自己的独立虚拟地址空间  这个空间如何布局是段头表中的程序头决定
readelf -l elfDemo.exce
root@lxz-virtual-machine:~/下载# readelf -l elfDemo.exce

Elf 文件类型为 DYN (Position-Independent Executable file)
Entry point 0x1060
There are 13 program headers, starting at offset 64

程序头:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000000040 0x0000000000000040
                 0x00000000000002d8 0x00000000000002d8  R      0x8
  INTERP         0x0000000000000318 0x0000000000000318 0x0000000000000318
                 0x000000000000001c 0x000000000000001c  R      0x1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000628 0x0000000000000628  R      0x1000
  LOAD           0x0000000000001000 0x0000000000001000 0x0000000000001000
                 0x00000000000001b1 0x00000000000001b1  R E    0x1000
  LOAD           0x0000000000002000 0x0000000000002000 0x0000000000002000
                 0x0000000000000114 0x0000000000000114  R      0x1000
  LOAD           0x0000000000002db8 0x0000000000003db8 0x0000000000003db8
                 0x0000000000000260 0x0000000000000270  RW     0x1000
  DYNAMIC        0x0000000000002dc8 0x0000000000003dc8 0x0000000000003dc8
                 0x00000000000001f0 0x00000000000001f0  RW     0x8
  NOTE           0x0000000000000338 0x0000000000000338 0x0000000000000338
                 0x0000000000000030 0x0000000000000030  R      0x8
  NOTE           0x0000000000000368 0x0000000000000368 0x0000000000000368
                 0x0000000000000044 0x0000000000000044  R      0x4
  GNU_PROPERTY   0x0000000000000338 0x0000000000000338 0x0000000000000338
                 0x0000000000000030 0x0000000000000030  R      0x8
  GNU_EH_FRAME   0x0000000000002008 0x0000000000002008 0x0000000000002008
                 0x000000000000003c 0x000000000000003c  R      0x4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x10
  GNU_RELRO      0x0000000000002db8 0x0000000000003db8 0x0000000000003db8
                 0x0000000000000248 0x0000000000000248  R      0x1

 Section to Segment mapping:
  段节...
   00     
   01     .interp 
   02     .interp .note.gnu.property .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt 
   03     .init .plt .plt.got .plt.sec .text .fini 
   04     .rodata .eh_frame_hdr .eh_frame 
   05     .init_array .fini_array .dynamic .got .data .bss 
   06     .dynamic 
   07     .note.gnu.property 
   08     .note.gnu.build-id .note.ABI-tag 
   09     .note.gnu.property 
   10     .eh_frame_hdr 
   11     
   12     .init_array .fini_array .dynamic .got 

 这里我们可以从段节发现 一个段中出现了很多的节

段的出现就是对这些节进行分类

实际上 系统并不关心这些节的内容 而是关心这些的读写执行权限

那么就将不同权限的节分组 这样就可以同时装载多个节

.data .bss 具有读写权限

.text .plt.got 具有读和执行权限

常见的段

可执行文件至少有一个 PT_LOAD类型的段 用于描述可装载的节
而动态链接的可执行文件包含两个  .data 和 .plt 分开存放
动态段 PT_DYNAMIC包含动态链接器所必须的信息 
例如 动态共享库 GOT表 和 重定位表等
PT_NOTE 类型的段 保存了系统相关的附加信息 
但是程序运行不需要这个内容
PT_INTERP段 将位置和大小信息存放在一个字符串中 
是对程序解释器的位置描述
PT_PHDR段保存了 程序头本身的位置和大小

在进程中 使用段是不够的  还需要使用到栈堆vDSO等空间

动态链接的可执行文件装载后  还需要进行动态链接才可以顺利执行

你可能感兴趣的:(pwn的前置知识,笔记)