zoukankan      html  css  js  c++  java
  • Linux课程实践四:ELF文件格式分析

    一、ELF文件格式概述

    1. ELF文件

    • ELF:Executable and Linking Format,是一种对象文件的格式,用于定义不同类型的对象文件(Object files)中都放了什么东西、以及都以什么样的格式去放这些东西。

    2. 三种类型

    (1)可重定位文件

    • 编译器和汇编器创建
    • 运行前需要被链接器处理

    (2)可执行文件

    • 完成了所有重定位工作和符号解析
    • 除了运行时解析的共享库符号

    (3)共享库文件

    • 链接器需要的符号信息
    • 运行时可以直接执行的代码

    二、分析一个ELF文件

    • 以一个最简单的helloworld程序为例

    1. ELF文件头

    • 使用工具查看ELF文件头:

        readelf -h obj
      

    • /usr/include/elf.h 中可以找到文件头结构定义:

    • 大小总共为64字节,换算成十六进制为0x40。在十六进制代码中找到前0x40字节,即为文件头信息部分(阅读时注意反序问题):

    (1)identification

    • 魔数部分:

    • 第一部分:占四个字节。

      • 7f 45 4c 46,应读为 4c 46 45 7f,对应ASCII码 ELF.,表示这是一个ELF对象。
    • 第二部分:占一个字节。

      • 02 表示是一个64位对象。
    • 第三部分:占一个字节。

      • 01 表示是小端表示法。
    • 第四部分:占一个字节。

      • 01 表示文件头版本。
    • 其余默认为0

    (2)information

    • 信息部分:

    • e_type:两个字节

      • 01 00 表示是一个重定位文件。
    • e_machine:两个字节

      • 3e 00 表示是intel80386处理器体系结构。
    • e_version:四个字节

      • 01 00 00 00 表示是当前版本。
    • e_entry:八个字节。

      • 00 00 00 00 00 00 00 00 表示当前程序没有入口点。
    • e_phoff:八个字节

      • 00 00 00 00 00 00 00 00 表示没有程序头表。
    • e_shoff:八个字节

      • 90 02 00 00 00 00 00 00 表示段表的偏移地址在 00 00 00 00 00 00 02 90处。
    • e_flags:四个字节

      • 00 00 00 00 表示未知处理器特定标志 #define EF_SH_UNKNOWN 0x0
    • e_ehsize:两个字节

      • 40 00 表示elf文件头大小为 00 40(64个字节)。
    • e_phentsize:两个字节

      • 00 00 表示重定位文件没有程序头表。
    • e_phnum:两个字节

      • 00 00 表示重定位文件没有程序头表。
    • e_ehentsize:两个字节

      • 40 00 表示段头大小为00 40(64字节),section header table中每个header的大小。
    • e_shnum:两个字节

      • 0d 00 表示段表入口有13个,即段表有13段。
    • e_shstrndx:两个字节

      • 0a 00 表示段表字符串在段表中的索引号,.shstrab段的段表索引号为00 0a,即10.

    2. 通过文件头找到各个节

    • 使用工具查看段表信息

        readelf -S obj
      

    (1)找到段表

    • 在文件头中 e_shoff可以找到段表偏移地址00 00 00 00 00 00 02 90,从这个地址去查找段表。

    • 段表长度由 e_ehentsize00 40(64字节)。

    • 段表个数由 e_shnum 可知有13个。

    • /usr/include/elf.h 中可以找到段表结构:

    (2)分析一个段表

    • 第一个段:全为零,不表示任何段。

    • 第二个段:

    • sh_name:四个字节

      • 20 00 00 00 表示该段名称在.shstrtab中偏移量,为.test节。
    • sh_type:四个字节

      • 01 00 00 00 表示这个段拥有程序所定义的信息,其格式和含义完全由该程序确定,这里表示PROGBITS。
    • sh_flags:八个字节

      • 06 00 00 00 00 00 00 00 表示alloc和execute。
    • sh_addr:八个字节

      • 00 00 00 00 00 00 00 00 表示是section在内存中的虚拟地址,.o文件不需要执行,这里都是0。
    • sh_offset:八个字节

      • 40 00 00 00 00 00 00 00 表示是section与文件头之间的偏移。
    • sh_size:八个字节

      • 11 00 00 00 00 00 00 00 表示文件里面section占用的大小。
    • sh_link:四个字节

      • 00 00 00 00 表示没有链接信息。
    • sh_info:四个字节

      • 00 00 00 00 表示没有辅助信息。
    • sh_addralign:八个字节

      • 01 00 00 00 00 00 00 00 表示字节对齐长度。
    • sh_entsize:八个字节

      • 00 00 00 00 00 00 00 00 表示没有入口。

    (3)所有段表

    • 第三个段:

      • 段名:.rel.text
      • 类型:RELA
      • 标志:info
      • 相对文件头偏移:0x1e0
      • 占用大小:0x30
    • 第四个段

      • 段名:.data
      • 类型:PROGBITS
      • 标志:write、alloc
      • 相对文件头偏移:0x51
      • 占用大小:0
    • 第五个段

      • 段名:.bss
      • 类型:NOBITS
      • 标志:write、alloc
      • 相对文件头偏移:0x51
      • 占用大小:0
    • 第六个段

      • 段名:.rodata
      • 类型:PROGBITS
      • 标志:alloc
      • 相对文件头偏移:0x51
      • 占用大小:0x0b
    • 第七个段

      • 段名:.comment
      • 类型:PROGBITS
      • 标志:merge、strings
      • 相对文件头偏移:0x5c
      • 占用大小:0x26
    • 第八个段

      • 段名:.note.GNU-stack
      • 类型:PROGBITS
      • 标志:无
      • 相对文件头偏移:0x82
      • 占用大小:0
    • 第九个段

      • 段名:.eh_frame
      • 类型:PROGBITS
      • 标志:alloc
      • 相对文件头偏移:0x88
      • 占用大小:0x38
    • 第十个段

      • 段名:.rela.eh_frame
      • 类型:RELA
      • 标志:info
      • 相对文件头偏移:0x210
      • 占用大小:0x18
    • 第十一段

      • 段名:.shstrtab
      • 类型:STRTAB
      • 标志:无
      • 相对文件头偏移:0x228
      • 占用大小:0x61
    • 第十二段

      • 段名:.symtab
      • 类型:STRTAB
      • 标志:无
      • 相对文件头偏移:0xc0
      • 占用大小:0x0108
    • 第十三段

      • 段名:.strtab
      • 类型:STRTAB
      • 标志:无
      • 相对文件头偏移:0x1c8
      • 占用大小:0x11

    三、理解常见节

    1. .text节

    本节中是可执行指令的集合
    
    • 通过刚才的信息,我们可以从文件中偏移0x40处找到大小为0x11的 .text节:

    • 可以通过反汇编该程序来查看:

    2. .rodata

    本节是只读数据,ro代表read only
    
    • 从偏移0x51处找到大小为0x0b的 .rodata节:

    • 使用ASCII码对照表翻译数据为hello 5317,即.c文件中的字符串:

    2. .comment

    本节用来存放编译器版本信息
    
    • 从偏移0x5c处找到大小为0x26的 .comment节:

    • 使用ASCII码对照表翻译出来数据为 GCC:(Debian 5.3.1-8) 5.3.1 20160205

    3. .symtab

    本节存放所有section中定义的符号名字,一般是变量、函数shstrtab及symtab经常引用strtab中的字符串
    
    • 从偏移0xc0处找到大小为0x0108的 .symtab节:

    4. .strtab

    本节是段表的字符串表
    
    • 从偏移0x1c8处找到大小为0x11的 .strtab节:

    • 数据用“0”分隔出了三部分,用ASCII码翻译:

      • 65 6c 66 2e 63elf.c
      • 6d 61 69 6emain
      • 70 75 74 73puts
  • 相关阅读:
    JAVA中的浅拷贝与深拷贝
    Spring异步-@Async注解
    Spring 事务传播行为的使用
    DecimalFormat的使用
    Java编码问题原因以及解决
    Spring
    [TimLinux] docker CentOS7 入门——容器(1)
    [TimLinux] asciinema Linux终端录制工具嵌入私有web中
    [TimLinux] docker CentOS7安装docker-ce最新版
    [TimLinux] systemd 精通CentOS7系统启动systemd
  • 原文地址:https://www.cnblogs.com/hyq20135317/p/5537506.html
Copyright © 2011-2022 走看看