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

我知道,剥离后,符号表将被删除,但始终需要一些信息来链接动态库。因此,我想无论是否剥离了可执行文件,都会保留其他符号。

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

实际上,获取一个已剥离的二进制文件并查看readelf的输出,将为您提供以下输出:
我的问题的另一部分也将涉及如何使用这些动态符号。因为它们都指向零而不是有效地址。您会像objdump一样识别它们各自指向存储在PLT中的代码的链接。例如,在下面我从objdump -D获得的转储中,我们可以看到.plt部分已拆分,我认为这是由于符号的缘故,将其划分为与每个动态函数对应的小节,我想知道这是否来自另一个动态函数。我不知道的符号表,或者objdump是否重建此信息(然后,我想知道如何):

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)


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

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  
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
x0 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 q4312078qx1 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 q4312078qx2 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 q4312078qx3 40044b: e9 b0 ff ff ff jmpq 400400 <puts@plt-0x10>


评论

也打印重定位。

啊,我没有首先注意到它!我想你的意思是.rela.dyn和.rela.plt。

.rela.dyn可能与动态链接有关-您会对uclibc.org/docs/SysV-ABI.pdf
的第4/5部分感兴趣

#1 楼

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


什么是不能从ELF二进制文件中删除的符号?


确实,strip从ELF文件中删除了很多信息,但是可以做更多的事情(有关此信息,请参阅--strip-unneeded中的选项strip或程序sstrip)。因此,我最初的问题更多是关于在ELF文件上进行了任何修改后都可以假定可执行文件中包含哪些符号。

实际上,您只需要一种类型的符号为了保持所有发生的情况,我们将其称为动态符号(与静态符号相对)。它们与静态变量有些不同,因为我们永远不会事先知道它们将在内存中指向何处。确实,由于它们应该指向外部二进制对象(库,插件),因此二进制blob在进程运行时会动态加载到内存中,我们无法预测它将位于什么地址。

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



.rela.dyn:如果不使用PLT,则动态链接对象(数据或过程)的重定位。 />
.rela.plt:PLT(过程链接表)中的元素列表,这些元素在动态链接期间可能会发生重定位(如果使用了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


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



PLT和GOT-关键代码共享和动态库;

剥离共享库;

重定向共享ELF库中的函数;

ELF的艺术:分析和开发;

全局偏移表;


评论


感谢您的来信。但是,严格来说,这不取决于您是否链接到任何东西吗?我意识到,使用GCC,您必须克服困难,以实现这一目标,但这仍然是可能的。就我所知,您可以创建一个ELF文件,该文件实际上仅使用syscall而不是任何类型的外部库。这是否意味着您可以省去更多您必须始终使用的功能?

– 0xC0000022L♦
13年8月14日在19:58

是的,你是对的。但是,实际上,即使您没有在代码中调用libc函数,也将获得__libc_start_main函数和所有常规框架。要实现所需的功能,您需要将-nostdlib选项传递给编译器。然后,您将获得没有外部调用libc的原始ELF二进制文件。

–恐怖
13年8月14日在21:51



您在关于动态符号表的回答中说:“它们应该指向外部二进制对象”。但是,它们还可以定义文件中函数或数据的位置,这些位置将被导出以供您链接或链接的代码使用。通常,这在动态库中可见,但是您也可以在顶级可执行文件中看到它-例如,许多C ++程序将重新定义“ operator new”和“ operator delete”,它们将出现在动态符号表中,从而覆盖标准C ++库提供的内容。

– sheltond
16 Mar 16 '16 at 10:10

@sheltond:你是对的。实际上,我在日常使用中不会大量操作C ++二进制文件,因此我倾向于忘记它们的特殊性,但是,是的,我同意,它们显然会这样做。

–恐怖
16-3-16在11:53