ELF 格式的条带后保留哪些符号表?

逆向工程 小精灵 动态链接
2021-07-05 01:37:52

我目前正在研究 ELF 格式,尤其是剥离的 ELF 可执行程序文件。

我知道,当剥离时,符号表会被删除,但总是需要一些信息来链接动态库。所以,我猜还有其他符号会保留在可执行文件是否被剥离的情况下。

例如,动态符号表似乎总是被保留(实际上这是我的问题的一部分)。它包含来自程序中使用的动态库的所有函数名称。

确实,采用剥离的二进制文件并查看readelfon的输出将为您提供以下输出:

Symbol table '.dynsym' contains 5 entries:
 Num:    Value          Size Type    Bind   Vis      Ndx Name
 0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
 1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND puts@GLIBC_2.2.5 (2)
 2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.2.5 (2)
 3: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
 4: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND perror@GLIBC_2.2.5 (2)

我的问题是,即使在条带之后,系统始终需要保留在可执行文件中的所有符号表是什么(以及它们用于什么)?

我的问题的另一部分也是关于如何使用这些动态符号。因为,它们都指向零而不是有效地址。我们会识别objdump它们各自指向存储在 PLT 中的代码的链接。例如,在我得到的以下转储中objdump -D,我们可以看到该部分.plt被拆分,我假设这是由于符号,分成与每个动态函数对应的子部分,我想知道这是否来自另一个符号表我不知道或是否objdump重建此信息(然后,我想知道如何):

Disassembly of section .plt:

0000000000400400 <puts@plt-0x10>:
400400:       ff 35 6a 05 20 00       pushq  0x20056a(%rip)
400406:       ff 25 6c 05 20 00       jmpq   *0x20056c(%rip)
40040c:       0f 1f 40 00             nopl   0x0(%rax)

0000000000400410 <puts@plt>:
400410:       ff 25 6a 05 20 00       jmpq   *0x20056a(%rip)
400416:       68 00 00 00 00          pushq  $0x0
40041b:       e9 e0 ff ff ff          jmpq   400400 <puts@plt-0x10>

0000000000400420 <__libc_start_main@plt>:
400420:       ff 25 62 05 20 00       jmpq   *0x200562(%rip)
400426:       68 01 00 00 00          pushq  $0x1
40042b:       e9 d0 ff ff ff          jmpq   400400 <puts@plt-0x10>

0000000000400430 <__gmon_start__@plt>:
400430:       ff 25 5a 05 20 00       jmpq   *0x20055a(%rip)
400436:       68 02 00 00 00          pushq  $0x2
40043b:       e9 c0 ff ff ff          jmpq   400400 <puts@plt-0x10>

0000000000400440 <perror@plt>:
400440:       ff 25 52 05 20 00       jmpq   *0x200552(%rip)
400446:       68 03 00 00 00          pushq  $0x3
40044b:       e9 b0 ff ff ff          jmpq   400400 <puts@plt-0x10>

编辑:感谢 Igor 的评论,我发现了不同的偏移量允许重建信息.rela.plt(但是,.rela.dyn用于什么?)。

Relocation section '.rela.dyn' at offset 0x368 contains 1 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000600960  000300000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0

Relocation section '.rela.plt' at offset 0x380 contains 4 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000600980  000100000007 R_X86_64_JUMP_SLO 0000000000000000 puts + 0
000000600988  000200000007 R_X86_64_JUMP_SLO 0000000000000000 __libc_start_main + 0
000000600990  000300000007 R_X86_64_JUMP_SLO 0000000000000000 __gmon_start__ + 0
000000600998  000400000007 R_X86_64_JUMP_SLO 0000000000000000 perror + 0
1个回答

要回答这个问题,我们首先要重新表述一下。真正的问题可以这样表述:

哪些符号不能从 ELF 二进制文件中删除?

实际上,strip从 ELF 文件中删除了相当多的信息,但它可以做更多的事情(请参阅--strip-unneeded来自strip或程序的选项sstrip获取更多信息)。因此,我最初的问题更多是关于可以假设可执行文件中包含哪些符号,无论对 ELF 文件进行了何种修改。

事实上,无论发生什么,你只需要保留一种符号,我们称之为动态符号(而不是静态符号)。它们与静态的有点不同,因为我们永远不会事先知道它们将指向内存中的哪个位置。事实上,由于它们应该指向外部二进制对象(库、插件),二进制 blob 在进程运行时动态加载到内存中,我们无法预测它将位于哪个地址。

如果静态符号存储在该.symbtab部分中,则动态符号有自己的部分,称为.dynsym. 它们保持分开以简化重定位操作(该操作将为每个动态符号提供精确地址)。重定位操作还依赖于两个额外的表,即:

  • .rela.dyn : 动态链接对象(数据或过程)的重定位,如果不使用 PLT。
  • .rela.plt :PLT(Procedure Linkage Table)中的元素列表,这些元素在动态链接过程中容易发生重定位(如果使用PLT)。

以某种方式,把所有一起.dynsym.rela.dyn并且.rela.plt将允许修补初始存储器(如在ELF二进制映射),以便为动态符号指向正确的对象(数据或程序)。

为了更多地说明动态符号的重定位过程,我在 i386 和 amd64 架构中构建了示例。

i386

Symbol table '.dynsym' contains 6 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 00000000     0 FUNC    GLOBAL DEFAULT  UND perror@GLIBC_2.0 (2)
     2: 00000000     0 FUNC    GLOBAL DEFAULT  UND puts@GLIBC_2.0 (2)
     3: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     4: 00000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.0 (2)
     5: 080484fc     4 OBJECT  GLOBAL DEFAULT   15 _IO_stdin_used


Relocation section '.rel.dyn' at offset 0x28c contains 1 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
08049714  00000306 R_386_GLOB_DAT    00000000   __gmon_start__

Relocation section '.rel.plt' at offset 0x294 contains 4 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
08049724  00000107 R_386_JUMP_SLOT   00000000   perror
08049728  00000207 R_386_JUMP_SLOT   00000000   puts
0804972c  00000307 R_386_JUMP_SLOT   00000000   __gmon_start__
08049730  00000407 R_386_JUMP_SLOT   00000000   __libc_start_main

amd64

Symbol table '.dynsym' contains 5 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND puts@GLIBC_2.2.5 (2)
     2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.2.5 (2)
     3: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     4: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND perror@GLIBC_2.2.5 (2)



Relocation section '.rela.dyn' at offset 0x368 contains 1 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000600960  000300000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0

Relocation section '.rela.plt' at offset 0x380 contains 4 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000600980  000100000007 R_X86_64_JUMP_SLOT 0000000000000000 puts + 0
000000600988  000200000007 R_X86_64_JUMP_SLOT 0000000000000000 __libc_start_main + 0
000000600990  000300000007 R_X86_64_JUMP_SLOT 0000000000000000 __gmon_start__ + 0
000000600998  000400000007 R_X86_64_JUMP_SLOT 0000000000000000 perror + 0

一些关于动态链接的有趣网页和文章: