layout: post title: 理解DynELF excerpt: “关于DynELF的总结” categories: [知识总结] comments: true —

symbol resolution

就是在不知道offset的情况下,如何找到offset,从而return to libc

ELF结构如下:

image

指定dynamic linker

gcc a.c -o a -Wl,-dynamic-linker /home/xxxx/lib/ld-2.19.so -g

当函数第一次被呼叫时候,通过dynamic resolver 来计算函数地址,并卸载got位置上

gdb-peda$ disass 0x8048340
Dump of assembler code for function write@plt:
   0x08048340 <+0>:	jmp    DWORD PTR ds:0x804a018
   0x08048346 <+6>:	push   0x18
   0x0804834b <+11>:jmp    0x8048300

0x8048300这个位置其实是plt的第0个entry,plt entry的结构如下

gdb-peda$ x/32i 0x8048300
   0x8048300:	push   DWORD PTR ds:0x804a004
   0x8048306:	jmp    DWORD PTR ds:0x804a008
   0x804830c:	add    BYTE PTR [eax],al
   0x804830e:	add    BYTE PTR [eax],al
   0x8048310 <read@plt>:	jmp    DWORD PTR ds:0x804a00c
   0x8048316 <read@plt+6>:	push   0x0
   0x804831b <read@plt+11>:	jmp    0x8048300
   0x8048320 <__gmon_start__@plt>:	jmp    DWORD PTR ds:0x804a010
   0x8048326 <__gmon_start__@plt+6>:	push   0x8
   0x804832b <__gmon_start__@plt+11>:	jmp    0x8048300
   0x8048330 <__libc_start_main@plt>:	jmp    DWORD PTR ds:0x804a014
   0x8048336 <__libc_start_main@plt+6>:	push   0x10
   0x804833b <__libc_start_main@plt+11>:	jmp    0x8048300
   0x8048340 <write@plt>:	jmp    DWORD PTR ds:0x804a018
   0x8048346 <write@plt+6>:	push   0x18
   0x804834b <write@plt+11>:	jmp    0x8048300

其中0x8048306这一行所作的事情就是进入到_dl_runtime_resolve

ELF结构与运行时ELF结构(执行时候没有section header table,且结构变为Segment)

ELF Header  			| ELF Header 
Program header table	| Program header table
Section1				| Segment1
Section2				| Segment2
...						| ...
Section header table

查看所有elf结构:readelf -aW level4 |less

查看某个section

objdump -j .got.plt -s level4

level4:     文件格式 elf32-i386

Contents of section .got.plt:
 804a000 149f0408 00000000 00000000 16830408  ................
 804a010 26830408 36830408 46830408           &...6...F...    

解析elf

运行readelf -aW test |less查看ELF Header如下

ELF 头:
  Magic:  7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  类别:                              ELF64
  数据:                              2 补码,小端序 (little endian)
  版本:                              1 (current)
  OS/ABI:                            UNIX - System V
  ABI 版本:                          0
  类型:                              EXEC (可执行文件)
  系统架构:                          Advanced Micro Devices X86-64
  版本:                              0x1
  入口点地址:              0x400470
  程序头起点:              64 (bytes into file)
  Start of section headers:          6512 (bytes into file)
  标志:             0x0
  本头的大小:       64 (字节)
  程序头大小:       56 (字节)
  Number of program headers:         9
  节头大小:         64 (字节)
  节头数量:         31
  字符串表索引节头: 30

通过程序头起点Start of section headers来查找segments和section地址

查看是否为elf文件起始地址

gdb-peda$ x/s 0x400000
0x400000:	"\177ELF\002\001\001"

格式化elfheader

gdb-peda$ p/x *(Elf64_Ehdr*)0x400000
$1 = {
  e_ident = {0x7f, 0x45, 0x4c, 0x46, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, 
  e_type = 0x2, 
  e_machine = 0x3e, 
  e_version = 0x1, 
  e_entry = 0x400470, 
  e_phoff = 0x40, 	//program header offset
  e_shoff = 0x1970, 
  e_flags = 0x0, 
  e_ehsize = 0x40, 
  e_phentsize = 0x38, 
  e_phnum = 0x9, 		//number of program header
  e_shentsize = 0x40, 
  e_shnum = 0x1f, 
  e_shstrndx = 0x1e
}

e_phoff和e_shoff分别是program header和 section header的offset,和readelf -aW test |less给出的结果一致

查看第0个program header

gdb-peda$ p/x *(Elf64_Phdr*)0x400040
$1 = {
  p_type = 0x6, 
  p_flags = 0x5, 
  p_offset = 0x40, 
  p_vaddr = 0x400040, 
  p_paddr = 0x400040, 
  p_filesz = 0x1f8, 
  p_memsz = 0x1f8, 
  p_align = 0x8
}

查看第5个program header

gdb-peda$ p/x ((Elf64_Phdr*) 0 x 400040) [ 5]
$ 2 = 【
  p_type = 0 x 4, 
  p_flags = 0 x 4, 
  p_offset = 0 x 254, 
  p_vaddr = 0 x 400254, 
  p_paddr = 0 x 400254, 
  p_filesz = 0 x 44, 
  p_memsz = 0 x 44, 
  p_align = 0 x 4
】

查看前三个program header

gdb-peda$ p/x *(Elf64_Phdr*)0x400040@3
$3 = 【
{
    p_type = 0x6, 
    p_flags = 0x5, 
    p_offset = 0x40, 
    p_vaddr = 0x400040, 
    p_paddr = 0x400040, 
    p_filesz = 0x1f8, 
    p_memsz = 0x1f8, 
    p_align = 0x8
  }, {
    p_type = 0x3, 
    p_flags = 0x4, 
    p_offset = 0x238, 
    p_vaddr = 0x400238, 
    p_paddr = 0x400238, 
    p_filesz = 0x1c, 
    p_memsz = 0x1c, 
    p_align = 0x1
  }, {
    p_type = 0x1, 
    p_flags = 0x5, 
    p_offset = 0x0, 
    p_vaddr = 0x400000, 
    p_paddr = 0x400000, 
    p_filesz = 0x7b4, 
    p_memsz = 0x7b4, 
    p_align = 0x200000
  }
】

从program header table中找出dynamic section位置:

  • 找出p_type的值为PT_DYNAMIC(值为0x2)的program header
  • Base加上p_offset 即为.dynamic

找到的内容如下

  p_type = 0x2, 
    p_flags = 0x6, 
    p_offset = 0xe28, 
    p_vaddr = 0x600e28, 	\\.dynamic section 位置
    p_paddr = 0x600e28, 
    p_filesz = 0x1d0, 
    p_memsz = 0x1d0, 
    p_align = 0x8

在没有开aslr情况下,通过readelf -aW test |less从section header中找到

  [22] .dynamic          DYNAMIC         0000000000600e28 000e28 0001d0 10  WA  6   0  8
  [23] .got              PROGBITS        0000000000600ff8 000ff8 000008 08  WA  0   0  8
  [24] .got.plt          PROGBITS        0000000000601000 001000 000030 08  WA  0   0  8

记录一下dynamic

set $dynamic = 0x600e28

dynamic section也是table,查看第0个dynamic section

gdb-peda$ set $dynamic = (Elf64_Dyn*)0x600e28
gdb-peda$ p/x *$dynamic
$12 = 【
  d_tag = 0x1, 
  d_un = 【
    d_val = 0x1, 
    d_ptr = 0x1
  】
】

查看前6个dynamic section

gdb-peda$ p/x *$dynamic@6
$6 = 【【
    d_tag = 0x1, 
    d_un = {
      d_val = 0x1, 
      d_ptr = 0x1
    }
  }, {
    d_tag = 0xc, 
    d_un = {
      d_val = 0x400400, 
      d_ptr = 0x400400
    }
  }, {
    d_tag = 0xd, 
    d_un = {
      d_val = 0x400664, 
      d_ptr = 0x400664
    }
  }, {
    d_tag = 0x19, 
    d_un = {
      d_val = 0x600e10, 
      d_ptr = 0x600e10
    }
  }, {
    d_tag = 0x1b, 
    d_un = {
      d_val = 0x8, 
      d_ptr = 0x8
    }
  }, {
    d_tag = 0x1a, 
    d_un = {
      d_val = 0x600e18, 
      d_ptr = 0x600e18
    }
  】】

同样通过readelf -aW test |less也可以找到

Dynamic section at offset 0xe28 contains 24 entries:
  标记        类型                         名称/值
 0x0000000000000001 (NEEDED)             共享库:[libc.so.6]
 0x000000000000000c (INIT)               0x400400
 0x000000000000000d (FINI)               0x400664
 0x0000000000000019 (INIT_ARRAY)         0x600e10
 0x000000000000001b (INIT_ARRAYSZ)       8 (bytes)
 0x000000000000001a (FINI_ARRAY)         0x600e18
 0x000000000000001c (FINI_ARRAYSZ)       8 (bytes)
 0x000000006ffffef5 (GNU_HASH)           0x400298
 0x0000000000000005 (STRTAB)             0x400330
 0x0000000000000006 (SYMTAB)             0x4002b8
 0x000000000000000a (STRSZ)              68 (bytes)
 0x000000000000000b (SYMENT)             24 (bytes)
...
...

dynsym Section表示program中用到的所有symbol

查找.dynsym Section:.dynamic中d_tag为DT_SYMTAB(为6)的entry,d_ptr指向.dynsym section

如上边显示的

   d_tag = 0x6, 
    d_un = {
      d_val = 0x4002b8, 
      d_ptr = 0x4002b8

或者
0x0000000000000006 (SYMTAB)             0x4002b8

在setion header中也可以看到

[ 5] .dynsym           DYNSYM          00000000004002b8 0002b8 000078 18   A  6   1  8

d_tag为5的是synstr

0x0000000000000005 (STRTAB)             0x400330
或者
    d_tag = 0x5, 
    d_un = {
      d_val = 0x400330, 
      d_ptr = 0x400330

gdb-peda$ set $dynstr = (char*)0x400330
gdb-peda$ set $dynsym = (Elf64_Sym*)0x4002b8

查看dynstr

gdb-peda$ x/20s 0x400330
0x400330:	""
0x400331:	"libc.so.6"
0x40033b:	"puts"
0x400340:	"memset"
0x400347:	"__libc_start_main"
0x400359:	"__gmon_start__"
0x400368:	"GLIBC_2.2.5"
0x400374:	""

查看第一个dynsym

gdb-peda$ p/x $dynsym[1]
$8 = {
  st_name = 0xb, 
  st_info = 0x12, 
  st_other = 0x0, 
  st_shndx = 0x0, 
  st_value = 0x0, 
  st_size = 0x0
}

查看第一个symbol name

gdb-peda$ p/s $dynstr+0xb
$9 = 0x40033b "puts"

查看.rel.plt(.rela.plt)(看到了这里52分钟)

gdb-peda$ set $rela = (Elf64_Rela*)0x400458
gdb-peda$ p/x *$rela@3
$12 = 【【
    r_offset = 0x601018, //每一个offset就是一个got的位置
    r_info = 0x100000007, 
    r_addend = 0x0
  }, {
    r_offset = 0x601020, 
    r_info = 0x200000007, 
    r_addend = 0x0
  }, {
    r_offset = 0x601028, 
    r_info = 0x300000007, 
    r_addend = 0x0
  】】

gdb-peda$ p/s $dynstr + $dynsym[1]->st_name
$15 = 0x400396 "printf"

push的index,就是告诉resolver需要解析的symbol时什么

Dynamic Resolver流程

_dl_runtime_resolve(link_map,reloc_arg)
                               |
           __________          |
          |Elf64_Rel |<--------+
          |----------|
    +-----|r_offset  |        ___________
    |     |r_info    |------>| Elf64_Sym |           __________
    |     |__________|       |-----------|          |          |
    |       .rel.plt         | st_name   |--------->| printf\0 |
 ___|_____                   |___________|          |__________|
|         |                     .dynsym                .dynstr
|<printf> |
|_________|
  .got.plt

指定要载入的libc路径:

LD_LIBRARY_PATH=./path/to/libc
例如:
LD_LIBRARY_PATH=. ldd ./p3

已知两个function的address时可以在libcdb里找有没有对应的版本:libcdb.com

symbol resolution