ELF文件

1.ELF文件是什么?

ELF文件是linuxmacOS等下的可执行可连接文件,用于存储可执行程序、共享库、

目标代码以及其他一些二进制文件,相当于windows下的exe文件。

2.ELF文件结构

1ELF头部【ELF Header】:文件的开头部分,包含了关于文件本身的基本信息,比如文件类型、目标体系结构、入口点地址等。

root@DESKTOP-SQ76TQN:/home/deerl# readelf -h main
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI(系统):                     UNIX - System V
  ABI Version:                       0
  Type(文件类型):                   EXEC (Executable file)可执行文件
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x80481a0
  Start of program headers:          52 (bytes into file)
  Start of section headers:          8324 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         4
  Size of section headers:           40 (bytes)
  Number of section headers:         8
  Section header string table index: 7

其中下标1-4即 7f 45 4c 46 是ELF魔数,是一个固定的字节序列,用于识别该文件是否为ELF格式。32位x86架构上,ELF魔数通常是一个四字节的整数值,十六进制表示为0x7F454C46,这对应ASCII码中的“ELF”三个字符。在64位架构上也有类似的魔数,但具体的值会有所不同。无论具体的值如何,ELF魔数的存在使得操作系统和程序能够快速地识别并处理ELF格式的文件。

Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
          1  2  3  4  5  6  7  8  9  10 11 12 ..........

2)EI_CLASS:下标5,用于判断文件是32位的还是64位的, 32位显示01, 64位显示02.

如图所示显示01是个32位的文件。

3)EI_DATA:下标6,用于判断文件是大端序还是小端序,一般程序都为小端序。小 端序显示01,大端序显示02. 如图所示显示01是个小端序文件。

4)EI_VERSION:下标7,ELF规范版本显示01。

5)后面的EI_OSABI,EI_ABIVERSION,EI_PAD,EI_NIDENT皆为0,在此不做过多阐述。

3.节与段的关系

节区头表(Section Header Table):这个表格记录了文件中各个节区的信息。每个表项描述了一个节区的名称、大小、在文件中的偏移量等。

节区(Sections):文件中的每个节区都包含了特定类型的数据。常见的节区类型包括代码段、数据段、符号表、字符串表等。每个节区的数据和元数据都在节区头表中有对应的条目。

elf文件节信息

root@DESKTOP-SQ76TQN:/home/deerl# readelf -S main 
There are 8 section headers, starting at offset 0x2084:

Section Headers:
  [Nr] Name              Type        Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL        00000000 000000 000000 00      0   0  0
  [ 1] .note.gnu.build-i NOTE        080480b4 0000b4 000024 00   A  0   0  4
  [ 2] .text             PROGBITS    080480e0 0000e0 0000f1 00  AX  0   0 16
  [ 3] .rodata           PROGBITS    080481d4 0001d4 000fd8 01 AMS  0   0  4
  [ 4] .eh_frame         PROGBITS    080491ac 0011ac 00007c 00   A  0   0  4
  [ 5] .data             PROGBITS    0804b000 002000 000008 00  WA  0   0  4
  [ 6] .comment          PROGBITS    00000000 002008 000035 01  MS  0   0  1
  [ 7] .shstrtab         STRTAB      00000000 00203d 000045 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  p (processor specific)

- **W (write)**: 表示该节区包含可写的数据。
    
- **A (alloc)**: 表示该节区将在程序加载时分配内存并填充数据。
    
- **X (execute)**: 表示该节区包含可执行的代码。
    
- **M (merge)**: 表示该节区的数据可能会与其他节区的数据合并。
    
- **S (strings)**: 表示该节区包含字符串。
    
- **I (info)**: 表示该节区包含一些特定信息。
    
- **L (link order)**: 表示该节区的内容在链接时可能需要按特定的顺序进行。
    
- **O (extra OS processing required)**: 表示该节区需要额外的操作系统处理。
    
- **G (group)**: 表示该节区是一个组的一部分。

  **T (TLS)**: 表示该节区包含线程本地存储(Thread-Local Storage)数据。
    
- **C (compressed)**: 表示该节区的内容已被压缩。
    
- **x (unknown)**: 表示该节区的属性未知。
    
- **o (OS specific)**: 表示该节区的属性与操作系统相关。
    
- **E (exclude)**: 表示该节区不会被链接器包含在输出文件中。
    
- **p (processor specific)**: 表示该节区的属性与处理器架构相关。

由此可见,可写的数据在.data里,可执行代码在.text里, .rodata 中包含字符串。其实,在ELF文件的结构里,这些称谓段,段的划分是根据各个节是否可读可写可执行来分配段的。

.bss:存储未初始化的变量(可写)
.rodata:用于存储常量、字符串等(只读)
.data:已经初始化的变量(可读可写)
.text:存储程序的机器指令(可读可执行)

4.程序头表(Program Header Table)

这个表格只在可执行文件和共享库中出现。它描述了可执行文件在内存中的布局,包括需要加载的段的信息,如加载地址、大小等。

**elf文件程序头表信息**

root@DESKTOP-SQ76TQN:/home/deerl# readelf -l main

Elf file type is EXEC (Executable file)#可执行文件
Entry point 0x80481a0
There are 4 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000000 0x08048000 0x08048000 0x01228 0x01228 R E 0x1000
  LOAD           0x002000 0x0804b000 0x0804b000 0x00008 0x00008 RW  0x1000
  NOTE           0x0000b4 0x080480b4 0x080480b4 0x00024 0x00024 R   0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x10

 Section to Segment mapping:
  Segment Sections...
   00     .note.gnu.build-id .text .rodata .eh_frame 
   01     .data 
   02     .note.gnu.build-id 
   03     

这里的

Entry point 0x80481a0

是一个程序的入口点,在IDA中查找到相关位置如下

这里的

There are 4 program headers, starting at offset 52

是提示有四个程序头条目,从偏移量 52 处开始。这意味着在文件的偏移量 52 处有一个程序头表,它包含了四个程序头条目。每个程序头条目描述了一个段的加载信息,如加载地址、段大小、权限(可读、可写、可执行)、对齐等等。

0x80481a0+0x34(52)=0x80481d4,在这段地址间

0x080481D1: LOAD段

1)Segment type: Pure code:纯代码段

2)Segment permissions: Read/Execute:该段权限可读取和执行

3)org 80481D1:这个段在文件中的偏移量

4)segment mempage public 'CODE' use32: 这句话指明了这个段的一些属性,如所属内存页面、类型为 'CODE'(代码),使用32位模式等。

5)assume cs:LOAD: 这个指令用于设置代码段寄存器(CS)的默认值。

6)align 4: 这个指令表示这个段在内存中的对齐方式,这里是按4字节对齐。

符号表(Symbol Table): 符号表记录了在程序中定义和使用的符号,如变量、函数等。它为调试和动态链接提供了重要信息。字符串表(String Table): 字符串表存储了各种字符串,如节区名称、符号名称等。节区和符号表中的许多信息都是通过索引引用字符串表中的实际字符串。

6.ELF文件结构与虚拟内存空间之间有什么关系?

1) 加载(Loading):当操作系统启动一个程序时,它会将程序的ELF文件从磁盘加载到进程的虚拟内存空间中。不同的ELF节区会被加载到虚拟内存的不同区域,如代码段被加载到代码段区域、数据段被加载到数据段区域等。

2)地址映射(Address Mapping):虚拟内存空间的各个部分与ELF文件中的不同节区之间建立了映射关系。例如,代码段被映射到虚拟内存的代码段区域,数据段被映射到虚拟内存的数据段区域。如图所示

1.控制eip为PLT[0]的地址,只需传递一个index_arg参数

2.控制index_arg的大小,使reloc的位置落在可控地址内

3.伪造reloc的内容,使sym落在可控地址内

4.伪造sym的内容,使name落在可控地址内

5.伪造name为任意库函数,如system

1. **静态符号表(`symtab`)**:每个可执行文件和共享库都有一个静态符号表(`symtab`)。该表包含关于在文件内定义和使用的所有符号的信息。静态符号表用于链接和静态编译过程。

2. **动态符号表(`dynsym`)**:与静态符号表相反,动态符号表包含与动态链接相关的信息。这包括可在运行时由动态链接器解析的符号。动态符号表允许程序在运行时通过共享库进行符号解析,以便能够加载和调用库中的函数和访问全局变量。

**动态字符串表(`dynstr`)**:动态字符串表是与动态链接相关的信息的一部分。它包含在共享库中定义的符号的字符串名称。这些字符串名称用于符号解析,以便在运行时能够加载和调用库中的函数以及访问全局变量。

`rel_plt` (重定位过程链接表):指的是与动态链接中 PLT 部分的重定位相关的信息。它包含了在程序运行时将共享库中的函数链接到程序中的代码的详细信息。这些信息告诉动态链接器如何正确地调整函数的地址,以便在运行时进行函数调用。

为什么要重定位?

在动态链接期间,共享库的代码和数据被加载到进程的内存中。由于地址空间的不确定性,共享库中的符号引用可能需要调整,以便正确指向它们在内存中的位置。

leave_ret

确保在函数返回时正确地恢复堆栈状态并跳转到调用者的地址。`leave` 指令用于恢复堆栈帧,然后 `ret` 指令用于返回到调用函数。

#ELF文件#
全部评论

相关推荐

牛客154160166号:9月底还给我发短信,好奇怪,我24届的
点赞 评论 收藏
分享
joe2333:怀念以前大家拿华为当保底的日子
点赞 评论 收藏
分享
不愿透露姓名的神秘牛友
昨天 19:05
点赞 评论 收藏
分享
点赞 1 评论
分享
牛客网
牛客企业服务