layout: post title: 理解DynELF excerpt: “关于DynELF的总结” categories: [知识总结] comments: true —
symbol resolution
就是在不知道offset的情况下,如何找到offset,从而return to libc
ELF结构如下:
指定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