Linux逆向---ELF格式分析之节头

1.查看节头

段是程序执行的必要组成部分,段可以被分割成若干个节,而节头表是对这些节的位置和大小的描述,主要是链接和调试使用的,而对程序的执行却不是必需的。因为对程序内存布局的描述已经由程序头表描述了,而节头表则是对其的补充。即使节头不存在,节依然存在,只是无法通过节头去引用。

查看程序的节头:

readelf -S hello.out

输出:

共有 31 个节头,从偏移量 0x19e0 开始:

节头:
  [] 名称              类型             地址              偏移量
       大小              全体大小          旗标   链接   信息   对齐
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000400238  00000238
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.ABI-tag     NOTE             0000000000400254  00000254
       0000000000000020  0000000000000000   A       0     0     4
  [ 3] .note.gnu.build-i NOTE             0000000000400274  00000274
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .gnu.hash         GNU_HASH         0000000000400298  00000298
       000000000000001c  0000000000000000   A       5     0     8
  [ 5] .dynsym           DYNSYM           00000000004002b8  000002b8
       0000000000000060  0000000000000018   A       6     1     8
  [ 6] .dynstr           STRTAB           0000000000400318  00000318
       000000000000003f  0000000000000000   A       0     0     1
  [ 7] .gnu.version      VERSYM           0000000000400358  00000358
       0000000000000008  0000000000000002   A       5     0     2
  [ 8] .gnu.version_r    VERNEED          0000000000400360  00000360
       0000000000000020  0000000000000000   A       6     1     8
  [ 9] .rela.dyn         RELA             0000000000400380  00000380
       0000000000000018  0000000000000018   A       5     0     8
  [10] .rela.plt         RELA             0000000000400398  00000398
       0000000000000030  0000000000000018  AI       5    24     8
  [11] .init             PROGBITS         00000000004003c8  000003c8
       000000000000001a  0000000000000000  AX       0     0     4
  [12] .plt              PROGBITS         00000000004003f0  000003f0
       0000000000000030  0000000000000010  AX       0     0     16
  [13] .plt.got          PROGBITS         0000000000400420  00000420
       0000000000000008  0000000000000000  AX       0     0     8
  [14] .text             PROGBITS         0000000000400430  00000430
       0000000000000182  0000000000000000  AX       0     0     16
  [15] .fini             PROGBITS         00000000004005b4  000005b4
       0000000000000009  0000000000000000  AX       0     0     4
  [16] .rodata           PROGBITS         00000000004005c0  000005c0
       0000000000000010  0000000000000000   A       0     0     4
  [17] .eh_frame_hdr     PROGBITS         00000000004005d0  000005d0
       0000000000000034  0000000000000000   A       0     0     4
  [18] .eh_frame         PROGBITS         0000000000400608  00000608
       00000000000000f4  0000000000000000   A       0     0     8
  [19] .init_array       INIT_ARRAY       0000000000600e10  00000e10
       0000000000000008  0000000000000000  WA       0     0     8
  [20] .fini_array       FINI_ARRAY       0000000000600e18  00000e18
       0000000000000008  0000000000000000  WA       0     0     8
  [21] .jcr              PROGBITS         0000000000600e20  00000e20
       0000000000000008  0000000000000000  WA       0     0     8
  [22] .dynamic          DYNAMIC          0000000000600e28  00000e28
       00000000000001d0  0000000000000010  WA       6     0     8
  [23] .got              PROGBITS         0000000000600ff8  00000ff8
       0000000000000008  0000000000000008  WA       0     0     8
  [24] .got.plt          PROGBITS         0000000000601000  00001000
       0000000000000028  0000000000000008  WA       0     0     8
  [25] .data             PROGBITS         0000000000601028  00001028
       0000000000000010  0000000000000000  WA       0     0     8
  [26] .bss              NOBITS           0000000000601038  00001038
       0000000000000008  0000000000000000  WA       0     0     1
  [27] .comment          PROGBITS         0000000000000000  00001038
       0000000000000035  0000000000000001  MS       0     0     1
  [28] .shstrtab         STRTAB           0000000000000000  000018ce
       000000000000010c  0000000000000000           0     0     1
  [29] .symtab           SYMTAB           0000000000000000  00001070
       0000000000000648  0000000000000018          30    47     8
  [30] .strtab           STRTAB           0000000000000000  000016b8
       0000000000000216  0000000000000000           0     0     1

2.分析输出内容

下面结合输出来分析十六进制的程序代码。

.interp

实例中这一段的偏移量为0x238~0x254,可以用hexedit工具来查看这一节的内容:

									 2F 6C 69 62  36 34 2F 6C  ......../lib64/l
00000240   64 2D 6C 69  6E 75 78 2D  78 38 36 2D  36 34 2E 73  d-linux-x86-64.s
00000250   6F 2E 32 00  									   o.2.

可以看出,这一节保存的是程序解释器的位置。

.note.ABI-tag

实例中这一段的偏移量为0x254~0x274,内容中有GNU三个可见字符,应该是指明运行环境的相关信息。

.note.gnu.build-i

实例中这一段偏移量为0x274~0x298,内容中仍然只有GNU三个可见字符。

.gun.hash

实例中这一段偏移量为0x298~0x2b4,内容中的的仍然只有GNU三个字符,这里保存了一个用来查找符号的散列表。

.dynsym

实例中这一段偏移量为0x2b8~0x318,这里保存了从共享库导入的动态符号信息。

.dynstr

实例中这一段偏移量为0x318~0x357,这里保存了动态符号字符表,可以查看一下这一段的内容:

									 00 6C 69 62  63 2E 73 6F  .........libc.so
00000320   2E 36 00 70  72 69 6E 74  66 00 5F 5F  6C 69 62 63  .6.printf.__libc
00000330   5F 73 74 61  72 74 5F 6D  61 69 6E 00  5F 5F 67 6D  _start_main.__gm
00000340   6F 6E 5F 73  74 61 72 74  5F 5F 00 47  4C 49 42 43  on_start__.GLIBC
00000350   5F 32 2E 32  2E 35 00 00  00 00 02 00  02 00 00 00  _2.2.5..........

.gnu.version与.gnu.version_r

实例中这一段偏移量为0x358~0x360 和 0x360~0x380,含义不是很懂,不过似乎参考书中也没有提及,以后用到了再说吧。

.rela.dyn与.real.plt

实例中这一段偏移量为0x380~0x398 和 0x398~0x3c8。这里保存了重定位相关的信息,这些信息描述了如何在链接或者运行时,对ELF目标文件的某部分内容或者进程镜像进行补充或者修改。

.init

实例中这一段偏移量为0x3c8~0x3e2。这一节是可执行的进程初始化代码,进程进入主方法之前会运行这一段代码。

这一段通过objdump -d hello.out可以查看到相关的代码段:

00000000004003c8 <_init>:
  4003c8:	48 83 ec 08          	sub    $0x8,%rsp
  4003cc:	48 8b 05 25 0c 20 00 	mov    0x200c25(%rip),%rax        # 600ff8 <_DYNAMIC+0x1d0>
  4003d3:	48 85 c0             	test   %rax,%rax
  4003d6:	74 05                	je     4003dd <_init+0x15>
  4003d8:	e8 43 00 00 00       	callq  400420 <__libc_start_main@plt+0x10>
  4003dd:	48 83 c4 08          	add    $0x8,%rsp
  4003e1:	c3                   	retq   

.plt

实例中这一段偏移量为0x3f0~0x420。这一节是过程链接表,包含了动态链接器调用从共享库导入的函数所必需的相关代码。

也可以用objdump来查看相关代码:

00000000004003f0 :
  4003f0:	ff 35 12 0c 20 00    	pushq  0x200c12(%rip)        # 601008 <_GLOBAL_OFFSET_TABLE_+0x8>
  4003f6:	ff 25 14 0c 20 00    	jmpq   *0x200c14(%rip)        # 601010 <_GLOBAL_OFFSET_TABLE_+0x10>
  4003fc:	0f 1f 40 00          	nopl   0x0(%rax)

0000000000400400 :
  400400:	ff 25 12 0c 20 00    	jmpq   *0x200c12(%rip)        # 601018 <_GLOBAL_OFFSET_TABLE_+0x18>
  400406:	68 00 00 00 00       	pushq  $0x0
  40040b:	e9 e0 ff ff ff       	jmpq   4003f0 <_init+0x28>

0000000000400410 <__libc_start_main@plt>:
  400410:	ff 25 0a 0c 20 00    	jmpq   *0x200c0a(%rip)        # 601020 <_GLOBAL_OFFSET_TABLE_+0x20>
  400416:	68 01 00 00 00       	pushq  $0x1
  40041b:	e9 d0 ff ff ff       	jmpq   4003f0 <_init+0x28>

.plt.got

实例中这一段偏移量为0x420~0x428。这一节提供了对共享库函数的访问入口,由动态链接器在运行时进行修改。

也可以用objdump来查看相关代码:

0000000000400420 <.plt.got>:
  400420:	ff 25 d2 0b 20 00    	jmpq   *0x200bd2(%rip)        # 600ff8 <_DYNAMIC+0x1d0>
  400426:	66 90                	xchg   %ax,%ax

.text

保存了程序代码,在实例中偏移量为0x430~0x5b2

可以用objdump来查看:

0000000000400430 <_start>:
  400430:	31 ed                	xor    %ebp,%ebp
  400432:	49 89 d1             	mov    %rdx,%r9
  400435:	5e                   	pop    %rsi
  400436:	48 89 e2             	mov    %rsp,%rdx
  400439:	48 83 e4 f0          	and    $0xfffffffffffffff0,%rsp
  40043d:	50                   	push   %rax
  40043e:	54                   	push   %rsp
  40043f:	49 c7 c0 b0 05 40 00 	mov    $0x4005b0,%r8

.........
  40059b:	5d                   	pop    %rbp
  40059c:	41 5c                	pop    %r12
  40059e:	41 5d                	pop    %r13
  4005a0:	41 5e                	pop    %r14
  4005a2:	41 5f                	pop    %r15
  4005a4:	c3                   	retq   
  4005a5:	90                   	nop
  4005a6:	66 2e 0f 1f 84 00 00 	nopw   %cs:0x0(%rax,%rax,1)
  4005ad:	00 00 00 

00000000004005b0 <__libc_csu_fini>:
  4005b0:	f3 c3                	repz retq 

.fini

实例中偏移量为0x5b4~0x5bd,当程序终止时会运行这一段代码。

00000000004005b4 <_fini>:
  4005b4:	48 83 ec 08          	sub    $0x8,%rsp
  4005b8:	48 83 c4 08          	add    $0x8,%rsp
  4005bc:	c3                   	retq   

.rodata

实例中偏移量为0x5c0~0x5d0,保存了只读数据,对于这个程序的只读数据就是"hello world",可以用hexedit来查看:

000005C0   01 00 02 00  68 65 6C 6C  6F 20 77 6F  72 6C 64 00  ....hello world.

.eh_frame_hdr与.eh_frame

实例中偏移量为0x5d0~0x604和0x608 ~0x6fc。通过对相关资料的查找,这部分似乎是和异常处理有关,记录函数调用堆栈的,以后要是用到了再了解吧。

到这里程序段的部分也结束了,从这里到数据段的起点都是用0填充的了。

.init_array与.fini_array

实例中偏移量为0xe10~0xe18与0xe18 ~0xe20。不过并不清楚这两个节是做什么用的,但是从名字上看可能与程序.init节和.fini节有某种联系。

.jcr

实例中偏移量为0xe20~0xe28。又是一个不明物体emmm。

.dynamic

实例中偏移量为0xe28~0xff0。保存着动态链接信息。

.got

实例中偏移量为0xff8~0xfff。保存着全局偏移表。

.got.plt

实例中偏移量为0x1000~0x1028。它和plt节一起提供了对导入的共享库函数的访问入口。可以看一下它的内容:

00001000   28 0E 60 00  00 00 00 00  00 00 00 00  00 00 00 00  (.`.............
00001010   00 00 00 00  00 00 00 00  06 04 40 00  00 00 00 00  ..........@.....
00001020   16 04 40 00  00 00 00 00

这个字符不可见,但是从数值上有三个有意义的数 0x600e28 0x400406 0x400416,分别表示一个数据段和两个代码:

0000000000400400 :
  400400:	ff 25 12 0c 20 00    	jmpq   *0x200c12(%rip)        # 601018 <_GLOBAL_OFFSET_TABLE_+0x18>
  400406:	68 00 00 00 00       	pushq  $0x0
  40040b:	e9 e0 ff ff ff       	jmpq   4003f0 <_init+0x28>

0000000000400410 <__libc_start_main@plt>:
  400410:	ff 25 0a 0c 20 00    	jmpq   *0x200c0a(%rip)        # 601020 <_GLOBAL_OFFSET_TABLE_+0x20>
  400416:	68 01 00 00 00       	pushq  $0x1
  40041b:	e9 d0 ff ff ff       	jmpq   4003f0 <_init+0x28>

0x600e28则指向的是.dynamic节的内容。

.data

实例中偏移量为0x1028~0x1038。它保存着初始化的全局变量等数据。

.bss

实例中偏移量为0x1038~0x1038…,它保存的是未进行初始化的全局数据,程序被加载时数据被初始化为0。

到这里数据段的部分也都结束了,剩下的应该是和节头自身相关的东西了。

.comment

实例中偏移量为0x1038~0x106d。这一节保存了版本控制信息,并且也是可见的:

00001030   00 00 00 00  00 00 00 00  47 43 43 3A  20 28 55 62  ........GCC: (Ub
00001040   75 6E 74 75  20 35 2E 34  2E 30 2D 36  75 62 75 6E  untu 5.4.0-6ubun
00001050   74 75 31 7E  31 36 2E 30  34 2E 31 30  29 20 35 2E  tu1~16.04.10) 5.
00001060   34 2E 30 20  32 30 31 36  30 36 30 39  00 00 00 00  4.0 20160609....

.symtab

实例中偏移量为0x1070~0x16b8。这节保存了符号表,与ELF符号和重定位有关。

.symtab

实例中偏移量为0x16b8~0x18ce。这部分保存了符号字符串表,表中的内容会被.symtab的ElfN_Sym结构中的st_name条目引用。

									 00 63 72 74  73 74 75 66  .........crtstuf
000016C0   66 2E 63 00  5F 5F 4A 43  52 5F 4C 49  53 54 5F 5F  f.c.__JCR_LIST__
000016D0   00 64 65 72  65 67 69 73  74 65 72 5F  74 6D 5F 63  .deregister_tm_c
000016E0   6C 6F 6E 65  73 00 5F 5F  64 6F 5F 67  6C 6F 62 61  lones.__do_globa
000016F0   6C 5F 64 74  6F 72 73 5F  61 75 78 00  63 6F 6D 70  l_dtors_aux.comp
......	
00001860   64 6C 65 00  5F 49 4F 5F  73 74 64 69  6E 5F 75 73  dle._IO_stdin_us
00001870   65 64 00 5F  5F 6C 69 62  63 5F 63 73  75 5F 69 6E  ed.__libc_csu_in
00001880   69 74 00 5F  5F 62 73 73  5F 73 74 61  72 74 00 6D  it.__bss_start.m
00001890   61 69 6E 00  5F 4A 76 5F  52 65 67 69  73 74 65 72  ain._Jv_Register
000018A0   43 6C 61 73  73 65 73 00  5F 5F 54 4D  43 5F 45 4E  Classes.__TMC_EN
000018B0   44 5F 5F 00  5F 49 54 4D  5F 72 65 67  69 73 74 65  D__._ITM_registe
000018C0   72 54 4D 43  6C 6F 6E 65  54 61 62 6C  65 00 00 2E  rTMCloneTable...

.shstrtab

实例中偏移量为0x18ce~0x19da。这里保存的是节头字符串表,该字符串保存了每个节的节名。e_shstrndx中保存了.shstrtab的偏移量,前者在readelf -h hello.out的时候能够找到。

Number of program headers: 9
节头大小: 64 (字节)
节头数量: 31
字符串表索引节头: 28

剩余还有地址段为0x19e0(19da向下取整)~0x21a0的部分

为了理解这部分,首先需要知道节头的结构体,这里我展示64位下的结构体:

typedef struct {
	uint32_t   sh_name;
	uint32_t   sh_type;
	uint64_t   sh_flags;
	Elf64_Addr sh_addr;
	Elf64_Off  sh_offset;
	uint64_t   sh_size;
	uint32_t   sh_link;
	uint32_t   sh_info;
	uint64_t   sh_addralign;
	uint64_t   sh_entsize;
} Elf64_Shdr;

可以进行一个简单的计算:

(0x21a0-0x19e0)/64(节头项目大小)=31,也就是说,剩下的部分应该就是节头的内容。

可以看一下最后64个字节的内容:

00002160   09 00 00 00  03 00 00 00  00 00 00 00  00 00 00 00  ................
00002170   00 00 00 00  00 00 00 00  B8 16 00 00  00 00 00 00  ................
00002180   16 02 00 00  00 00 00 00  00 00 00 00  00 00 00 00  ................
00002190   01 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  ................

在这里出现了熟悉的0x16b8,也就是.strtab的偏移量。这也说明最后的部分就是节头表的内容。

3.目标文件与可执行文件的节头对比

目标文件指的是.o文件,而可执行文件则指的是.out文件,通过对他们所拥有的节的对比也可以知道哪些节是一个可执行程序所必须的:

1.获取目标文件

gcc -c hello.c

执行上面的编译命令就可以看到目录中出现了hello.o。

### 2.目标文件与可执行文件节头对比

可执行文件的文件头如下:

节头:
  [号] 名称              类型             地址              偏移量
       大小              全体大小          旗标   链接   信息   对齐
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .text             PROGBITS         0000000000000000  00000040
       000000000000001a  0000000000000000  AX       0     0     1
  [ 2] .rela.text        RELA             0000000000000000  000001f8
       0000000000000030  0000000000000018   I      11     1     8
  [ 3] .data             PROGBITS         0000000000000000  0000005a
       0000000000000000  0000000000000000  WA       0     0     1
  [ 4] .bss              NOBITS           0000000000000000  0000005a
       0000000000000000  0000000000000000  WA       0     0     1
  [ 5] .rodata           PROGBITS         0000000000000000  0000005a
       000000000000000c  0000000000000000   A       0     0     1
  [ 6] .comment          PROGBITS         0000000000000000  00000066
       0000000000000036  0000000000000001  MS       0     0     1
  [ 7] .note.GNU-stack   PROGBITS         0000000000000000  0000009c
       0000000000000000  0000000000000000           0     0     1
  [ 8] .eh_frame         PROGBITS         0000000000000000  000000a0
       0000000000000038  0000000000000000   A       0     0     8
  [ 9] .rela.eh_frame    RELA             0000000000000000  00000228
       0000000000000018  0000000000000018   I      11     8     8
  [10] .shstrtab         STRTAB           0000000000000000  00000240
       0000000000000061  0000000000000000           0     0     1
  [11] .symtab           SYMTAB           0000000000000000  000000d8
       0000000000000108  0000000000000018          12     9     8
  [12] .strtab           STRTAB           0000000000000000  000001e0
       0000000000000015  0000000000000000           0     0     1

通过对比可得到如下结果:

.o文件特有

  • .rela.text
  • .rela.eh_frame
  • .note.GNU-stack

.out文件特有

  • .interp
  • .note.ABI-tag
  • .note.gnu.build-i
  • .gnu.hash
  • .dynsym
  • .dynstr
  • .gnu.version
  • .gnu.version_r
  • .rela.dyn
  • .rela.plt
  • .init
  • .plt
  • .plt.got
  • .fini
  • .eh_frame_hdr
  • .init_array
  • .fini_array
  • .jcr
  • .dynamic
  • .got
  • .got.plt

.o和.out文件共有

  • .text 代码节

  • .rodata 只读数据节

  • .data 初始化的全局数据节

  • .bss 未初始化的全局数据节

  • .comment 版本控制信息

  • .eh_frame

  • .shstrtab 节头字符串表

  • .symtab 符号节

  • .strtab 符号字符串表节

你可能感兴趣的:(ELF,节头,逆向)