定义:
ELF: Executable and Linkable Format
ELF文件常见的三种文件类型:
- relocatable (可重定位)
- executable(可执行)
- shared object(共享目标)
- core dumps (核心转储)
[root@centosgpt 10]# file process.o
process.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
[root@centosgpt 10]# file dynamiccreateprocess
dynamiccreateprocess: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=d6fa7d670a47b7aaf05d17c92c82508bcabc48dc, not stripped
[root@centosgpt 10]# file libdynamicprocess.so
libdynamicprocess.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=0a9fb4457819ecf54d9762f3aff5eda0c19600fc, not stripped
组成:
- ELF header
- program header table
- section header table
- binary data
常用的elf文件分析工具, readelf, objdump, nm, hexdump
下面的程序验证用于相关信息验证(代码极客时间刘超趣谈操作系统专栏,为了实验方便,做了一些调整。global_var1,global_var2, static_var1, static_var2为测试弱符号增加)
/* process.c */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
extern int create_process(char *program, char **arg_list);
int global_var1=100;
int global_var2;
int create_process(char *program, char **arg_list)
{
static int static_var1 = 15;
static int static_var2 ;
pid_t child_pid;
printf("%s %d sizeof(global_var2)=%d\n",__FILE__,__LINE__, sizeof(global_var2));
child_pid = fork();
if (child_pid!=0){
return child_pid;
}
else{
execvp(program, arg_list);
abort();
}
}
/*create_process.c*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
extern int create_process(char *program, char **arg_list);
double global_var2;
int main()
{
char *arg_list[]={
"ls",
"-l",
"/etc/yum.repos.d",
NULL
};
printf("%s %d sizeof(global_var2)=%d\n",__FILE__,__LINE__, sizeof(global_var2));
create_process("ls", arg_list);
create_process("ls", arg_list);
return 0;
}
## makefile
.SUFFIXES: .c .o
.c.o:
gcc -g -c -fPIC $<
PROGS=libstaticprocess.a libdynamicprocess.a dynamiccreateprocess staticcreateprocess pthread-single pthread-multi
LFLAGS=-L.\
-ldynamicprocess
all: $(PROGS)
libstaticprocess.a: process.o
ar cr $@ process.o
libdynamicprocess.a: process.o
gcc -g -shared -fPIC -o $@ process.o
dynamiccreateprocess: createprocess.o
gcc -g -o $@ createprocess.o -L. -ldynamicprocess
staticcreateprocess: createprocess.o
gcc -g -o $@ createprocess.o -L. -lstaticprocess
pthread-single:pthread.o
gcc -g -o $@ pthread.o
pthread-multi:pthread.o
gcc -g -o $@ pthread.o -lpthread
clean:
@rm -f *.o
@rm -f *.a
文件解析:
ELF header结构定义:
#define EI_NIDENT 16
typedef struct elf32_hdr{
unsigned char e_ident[EI_NIDENT]; /*魔数*/
Elf32_Half e_type; /*ELF文件类型*/
Elf32_Half e_machine; /*ELF文件CPU属性*/
Elf32_Word e_version; /*ELF文件版本*/
Elf32_Addr e_entry; /*入扣地址, ELF入口虚拟地址 */
Elf32_Off e_phoff; /*Programer header table偏移*/
Elf32_Off e_shoff; /*Section header table 偏移*/
Elf32_Word e_flags; /*处理器特定标识*/
Elf32_Half e_ehsize; /*ELF头大小*/
Elf32_Half e_phentsize; /*Programer header大小*/
Elf32_Half e_phnum; /*Programer 个数*/
Elf32_Half e_shentsize; /*Section header大小*/
Elf32_Half e_shnum; /*Section header个数*/
Elf32_Half e_shstrndx; /*字符串索引*/
} Elf32_Ehdr;
typedef struct elf64_hdr {
unsigned char e_ident[EI_NIDENT]; /* ELF "magic number" */
Elf64_Half e_type;
Elf64_Half e_machine;
Elf64_Word e_version;
Elf64_Addr e_entry; /* Entry point virtual address */
Elf64_Off e_phoff; /* Program header table file offset */
Elf64_Off e_shoff; /* Section header table file offset */
Elf64_Word e_flags;
Elf64_Half e_ehsize;
Elf64_Half e_phentsize;
Elf64_Half e_phnum;
Elf64_Half e_shentsize;
Elf64_Half e_shnum;
Elf64_Half e_shstrndx;
} Elf64_Ehdr;
解析ELF header:
- readelf -h ./process.o
[root@centosgpt 10]# readelf -h ./process.o
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: REL (Relocatable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x0
Start of program headers: 0 (bytes into file)
Start of section headers: 3000 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 0 (bytes)
Number of program headers: 0
Size of section headers: 64 (bytes)
Number of section headers: 20
Section header string table index: 19
SECTION结构定义:
typedef struct elf32_shdr {
Elf32_Word sh_name;
Elf32_Word sh_type;
Elf32_Word sh_flags;
Elf32_Addr sh_addr;
Elf32_Off sh_offset;
Elf32_Word sh_size;
Elf32_Word sh_link;
Elf32_Word sh_info;
Elf32_Word sh_addralign;
Elf32_Word sh_entsize;
} Elf32_Shdr;
typedef struct elf64_shdr {
Elf64_Word sh_name; /* Section name, index in string tbl */
Elf64_Word sh_type; /* Type of section */
Elf64_Xword sh_flags; /* Miscellaneous section attributes */
Elf64_Addr sh_addr; /* Section virtual addr at execution */
Elf64_Off sh_offset; /* Section file offset */
Elf64_Xword sh_size; /* Size of section in bytes */
Elf64_Word sh_link; /* Index of another section */
Elf64_Word sh_info; /* Additional section information */
Elf64_Xword sh_addralign; /* Section alignment */
Elf64_Xword sh_entsize; /* Entry size if section holds table */
} Elf64_Shdr;
常见的section:
.text: 代码段
.data: 数据段(已初始化)
.bss: 数据段(未初始化)
.rodata: 只读数据段
.comment: 备注
.symtab: 符号表
.strtab: 字符串表
.plt: 过程链接表
.got.plt:全局偏移量表
解析sections:
- readelf -S -W ./process.o
## by readelf
[root@centosgpt 10]# readelf -S -W ./process.o
There are 20 section headers, starting at offset 0xbb8:
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 0000000000000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 0000000000000000 000040 00003d 00 AX 0 0 1
[ 2] .rela.text RELA 0000000000000000 000768 000048 18 I 17 1 8
[ 3] .data PROGBITS 0000000000000000 000080 000008 00 WA 0 0 4
[ 4] .bss NOBITS 0000000000000000 000088 000004 00 WA 0 0 4
[ 5] .debug_info PROGBITS 0000000000000000 000088 000144 00 0 0 1
[ 6] .rela.debug_info RELA 0000000000000000 0007b0 000300 18 I 17 5 8
[ 7] .debug_abbrev PROGBITS 0000000000000000 0001cc 00008b 00 0 0 1
[ 8] .debug_aranges PROGBITS 0000000000000000 000257 000030 00 0 0 1
[ 9] .rela.debug_aranges RELA 0000000000000000 000ab0 000030 18 I 17 8 8
[10] .debug_line PROGBITS 0000000000000000 000287 000081 00 0 0 1
[11] .rela.debug_line RELA 0000000000000000 000ae0 000018 18 I 17 10 8
[12] .debug_str PROGBITS 0000000000000000 000308 000164 01 MS 0 0 1
[13] .comment PROGBITS 0000000000000000 00046c 00002e 01 MS 0 0 1
[14] .note.GNU-stack PROGBITS 0000000000000000 00049a 000000 00 0 0 1
[15] .eh_frame PROGBITS 0000000000000000 0004a0 000038 00 A 0 0 8
[16] .rela.eh_frame RELA 0000000000000000 000af8 000018 18 I 17 15 8
[17] .symtab SYMTAB 0000000000000000 0004d8 000210 18 18 15 8
[18] .strtab STRTAB 0000000000000000 0006e8 00007c 00 0 0 1
[19] .shstrtab STRTAB 0000000000000000 000b10 0000a8 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
- objdump -h ./process.o
### by objdump
[root@centosgpt 10]# objdump -h ./process.o
./process.o: file format elf64-x86-64
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 0000003d 0000000000000000 0000000000000000 00000040 2**0
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
1 .data 00000008 0000000000000000 0000000000000000 00000080 2**2
CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000004 0000000000000000 0000000000000000 00000088 2**2
ALLOC
3 .debug_info 00000144 0000000000000000 0000000000000000 00000088 2**0
CONTENTS, RELOC, READONLY, DEBUGGING
4 .debug_abbrev 0000008b 0000000000000000 0000000000000000 000001cc 2**0
CONTENTS, READONLY, DEBUGGING
5 .debug_aranges 00000030 0000000000000000 0000000000000000 00000257 2**0
CONTENTS, RELOC, READONLY, DEBUGGING
6 .debug_line 00000081 0000000000000000 0000000000000000 00000287 2**0
CONTENTS, RELOC, READONLY, DEBUGGING
7 .debug_str 00000164 0000000000000000 0000000000000000 00000308 2**0
CONTENTS, READONLY, DEBUGGING
8 .comment 0000002e 0000000000000000 0000000000000000 0000046c 2**0
CONTENTS, READONLY
9 .note.GNU-stack 00000000 0000000000000000 0000000000000000 0000049a 2**0
CONTENTS, READONLY
10 .eh_frame 00000038 0000000000000000 0000000000000000 000004a0 2**3
CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
查看某一个section内容:
以.data .rel.plt为例:
### by readelf -x
[root@centosgpt 10]# readelf -x .data ./process.o
Hex dump of section '.data':
0x00000000 64000000 0f000000 d.......
#### readelf -r
[root@centosgpt 10]# readelf -r ./dynamiccreateprocess
Relocation section '.rela.dyn' at offset 0x4a8 contains 2 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000600ff0 000100000006 R_X86_64_GLOB_DAT 0000000000000000 global_var2 + 0
000000600ff8 000400000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0
Relocation section '.rela.plt' at offset 0x4d8 contains 3 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000601018 000200000007 R_X86_64_JUMP_SLO 0000000000000000 printf@GLIBC_2.2.5 + 0
000000601020 000300000007 R_X86_64_JUMP_SLO 0000000000000000 __libc_start_main@GLIBC_2.2.5 + 0
000000601028 000500000007 R_X86_64_JUMP_SLO 0000000000000000 create_process + 0
[root@centosgpt 10]# readelf -x .rel.plt ./dynamiccreateprocess
readelf: ./dynamiccreateprocess: Warning: Section '.rel.plt' was not dumped because it does not exist!
### by objdump -s -j
[root@centosgpt 10]# objdump -s -j .data ./process.o
./process.o: file format elf64-x86-64
Contents of section .data:
0000 64000000 0f000000 d.......
### by objdump -r
[root@centosgpt 10]# objdump -r ./createprocess.o
./createprocess.o: file format elf64-x86-64
RELOCATION RECORDS FOR [.text]:
OFFSET TYPE VALUE
000000000000000b R_X86_64_PC32 .rodata-0x0000000000000004
0000000000000016 R_X86_64_PC32 .rodata-0x0000000000000001
0000000000000021 R_X86_64_PC32 .rodata+0x0000000000000002
0000000000000034 R_X86_64_GOTPCREL global_var2-0x0000000000000004
0000000000000043 R_X86_64_PC32 .rodata+0x0000000000000013
000000000000004a R_X86_64_PC32 .rodata+0x0000000000000024
0000000000000054 R_X86_64_PLT32 printf-0x0000000000000004
0000000000000062 R_X86_64_PC32 .rodata-0x0000000000000004
0000000000000067 R_X86_64_PLT32 create_process-0x0000000000000004
0000000000000075 R_X86_64_PC32 .rodata-0x0000000000000004
000000000000007a R_X86_64_PLT32 create_process-0x0000000000000004
RELOCATION RECORDS FOR [.debug_info]:
OFFSET TYPE VALUE
0000000000000006 R_X86_64_32 .debug_abbrev
000000000000000c R_X86_64_32 .debug_str+0x0000000000000062
0000000000000011 R_X86_64_32 .debug_str+0x0000000000000052
0000000000000015 R_X86_64_32 .debug_str+0x00000000000000cc
0000000000000019 R_X86_64_64 .text
0000000000000029 R_X86_64_32 .debug_line
0000000000000030 R_X86_64_32 .debug_str+0x0000000000000029
0000000000000037 R_X86_64_32 .debug_str+0x00000000000000b0
000000000000003e R_X86_64_32 .debug_str+0x00000000000000de
0000000000000045 R_X86_64_32 .debug_str+0x000000000000000e
000000000000004c R_X86_64_32 .debug_str+0x00000000000000f1
0000000000000053 R_X86_64_32 .debug_str+0x0000000000000106
0000000000000061 R_X86_64_32 .debug_str+0x00000000000000c3
0000000000000068 R_X86_64_32 .debug_str+0x00000000000000fd
0000000000000075 R_X86_64_32 .debug_str+0x00000000000000be
000000000000007c R_X86_64_32 .debug_str
0000000000000083 R_X86_64_32 .debug_str+0x000000000000003b
0000000000000088 R_X86_64_32 .debug_str+0x000000000000001b
0000000000000092 R_X86_64_64 .text
00000000000000a9 R_X86_64_32 .debug_str+0x0000000000000020
00000000000000c8 R_X86_64_32 .debug_str+0x0000000000000110
RELOCATION RECORDS FOR [.debug_aranges]:
OFFSET TYPE VALUE
0000000000000006 R_X86_64_32 .debug_info
0000000000000010 R_X86_64_64 .text
RELOCATION RECORDS FOR [.debug_line]:
OFFSET TYPE VALUE
0000000000000033 R_X86_64_64 .text
RELOCATION RECORDS FOR [.eh_frame]:
OFFSET TYPE VALUE
0000000000000020 R_X86_64_PC32 .text
Segment定义:
typedef struct elf32_phdr{
Elf32_Word p_type;
Elf32_Off p_offset;
Elf32_Addr p_vaddr;
Elf32_Addr p_paddr;
Elf32_Word p_filesz;
Elf32_Word p_memsz;
Elf32_Word p_flags;
Elf32_Word p_align;
} Elf32_Phdr;
typedef struct elf64_phdr {
Elf64_Word p_type;
Elf64_Word p_flags;
Elf64_Off p_offset; /* Segment file offset */
Elf64_Addr p_vaddr; /* Segment virtual address */
Elf64_Addr p_paddr; /* Segment physical address */
Elf64_Xword p_filesz; /* Segment size in file */
Elf64_Xword p_memsz; /* Segment size in memory */
Elf64_Xword p_align; /* Segment alignment, file & memory */
} Elf64_Phdr;
解析Segments
- readelf -l -W ./libdynamicprocess.so
[root@centosgpt 10]# readelf -l -W ./libdynamicprocess.so
Elf file type is DYN (Shared object file)
Entry point 0x6b0
There are 7 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x0008ac 0x0008ac R E 0x200000
LOAD 0x000df8 0x0000000000200df8 0x0000000000200df8 0x000248 0x000258 RW 0x200000
DYNAMIC 0x000e18 0x0000000000200e18 0x0000000000200e18 0x0001c0 0x0001c0 RW 0x8
NOTE 0x0001c8 0x00000000000001c8 0x00000000000001c8 0x000024 0x000024 R 0x4
GNU_EH_FRAME 0x000828 0x0000000000000828 0x0000000000000828 0x00001c 0x00001c R 0x4
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10
GNU_RELRO 0x000df8 0x0000000000200df8 0x0000000000200df8 0x000208 0x000208 R 0x1
Section to Segment mapping:
Segment Sections...
00 .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame
01 .init_array .fini_array .jcr .data.rel.ro .dynamic .got .got.plt .data .bss
02 .dynamic
03 .note.gnu.build-id
04 .eh_frame_hdr
05
06 .init_array .fini_array .jcr .data.rel.ro .dynamic .got
Segment与Section:
Segment是从装载角度对ELF文件进行划分, Section是从链接的角度对ELF进行存储。通过readelf -l 的输出可以看到,链接器会尽量把相同权限属性的段分配在同一空间。如:可读可执行的section放到一起,典型的是代码段, 可读可写的section放到一起典型的是数据段。
符号表与弱符号、弱引用:
查看符号表三种方法
- readelf -s ./process.o
### by readelf -s
[root@centosgpt 10]# readelf -s ./process.o
Symbol table '.symtab' contains 22 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS process.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
5: 0000000000000000 4 OBJECT LOCAL DEFAULT 4 static_var2.3291
6: 0000000000000004 4 OBJECT LOCAL DEFAULT 3 static_var1.3290
7: 0000000000000000 0 SECTION LOCAL DEFAULT 5
8: 0000000000000000 0 SECTION LOCAL DEFAULT 7
9: 0000000000000000 0 SECTION LOCAL DEFAULT 8
10: 0000000000000000 0 SECTION LOCAL DEFAULT 10
11: 0000000000000000 0 SECTION LOCAL DEFAULT 12
12: 0000000000000000 0 SECTION LOCAL DEFAULT 14
13: 0000000000000000 0 SECTION LOCAL DEFAULT 15
14: 0000000000000000 0 SECTION LOCAL DEFAULT 13
15: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 global_var1
16: 0000000000000004 4 OBJECT GLOBAL DEFAULT COM global_var2
17: 0000000000000000 61 FUNC GLOBAL DEFAULT 1 create_process
18: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _GLOBAL_OFFSET_TABLE_
19: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND fork
20: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND execvp
21: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND abort
- objdump -t ./process.o
### by objdump -t
[root@centosgpt 10]# objdump -t ./process.o
./process.o: file format elf64-x86-64
SYMBOL TABLE:
0000000000000000 l df *ABS* 0000000000000000 process.c
0000000000000000 l d .text 0000000000000000 .text
0000000000000000 l d .data 0000000000000000 .data
0000000000000000 l d .bss 0000000000000000 .bss
0000000000000000 l O .bss 0000000000000004 static_var2.3291
0000000000000004 l O .data 0000000000000004 static_var1.3290
0000000000000000 l d .debug_info 0000000000000000 .debug_info
0000000000000000 l d .debug_abbrev 0000000000000000 .debug_abbrev
0000000000000000 l d .debug_aranges 0000000000000000 .debug_aranges
0000000000000000 l d .debug_line 0000000000000000 .debug_line
0000000000000000 l d .debug_str 0000000000000000 .debug_str
0000000000000000 l d .note.GNU-stack 0000000000000000 .note.GNU-stack
0000000000000000 l d .eh_frame 0000000000000000 .eh_frame
0000000000000000 l d .comment 0000000000000000 .comment
0000000000000000 g O .data 0000000000000004 global_var1
0000000000000004 O *COM* 0000000000000004 global_var2
0000000000000000 g F .text 000000000000003d create_process
0000000000000000 *UND* 0000000000000000 _GLOBAL_OFFSET_TABLE_
0000000000000000 *UND* 0000000000000000 fork
0000000000000000 *UND* 0000000000000000 execvp
0000000000000000 *UND* 0000000000000000 abort
- nm ./process.o
### by nm
[root@centosgpt 10]# nm ./process.o
U abort
0000000000000000 T create_process
U execvp
U fork
U _GLOBAL_OFFSET_TABLE_
0000000000000000 D global_var1
0000000000000004 C global_var2
U printf
0000000000000004 d static_var1.3290
0000000000000000 b static_var2.3291
变量存放空间 :
.data : 全局变量、局部静态变量已初始化的变量 global_var1, static_var1
.bss: 未初始化局部静态变量 static_var2;
符号表:未初始化的的全局变量 global_var2做为弱符号,符号表中global_var2类型为 COMMON ,链接时确定其大小。
SYMBOL TABLE:
0000000000000000 l O .bss 0000000000000004 static_var2.3291
0000000000000004 l O .data 0000000000000004 static_var1.3290
0000000000000000 g O .data 0000000000000004 global_var1
0000000000000004 O *COM* 0000000000000004 global_var2
程序链接完成后,动态链接库及可执行文件中可以看到global_var2已存放到bss section中。
### 动态链接库
[root@centosgpt 10]# objdump -t ./libdynamicprocess.a |grep global
00000000000006e0 l F .text 0000000000000000 __do_global_dtors_aux
0000000000200e00 l O .fini_array 0000000000000000 __do_global_dtors_aux_fini_array_entry
0000000000201040 g O .bss 0000000000000004 global_var2
0000000000201030 g O .data 0000000000000004 global_var1
### 可执行文件中
[root@centosgpt 10]# objdump -t ./dynamiccreateprocess |grep global
00000000004005e0 l F .text 0000000000000000 __do_global_dtors_aux
0000000000600e08 l O .fini_array 0000000000000000 __do_global_dtors_aux_fini_array_entry
0000000000601038 g O .bss 0000000000000008 global_var2
弱符号产生及影响 :
如果全局变量未初始化,做为弱符号变量,在链接阶段才能确定其所占空间和大小。从程序输出可以看到存在同一名称的两个变量。
[root@centosgpt 10]# ./dynamiccreateprocess
createprocess.c 17 sizeof(global_var2)=8
process.c 18 sizeof(global_var2)=4
process.c 18 sizeof(global_var2)=4
[root@centosgpt 10]# total 32
total 32
-rw-r--r--. 1 root root 1664 Apr 29 2018 CentOS-Base.repo.bak
-rw-r--r--. 1 root root 1309 Apr 29 2018 CentOS-CR.repo
弱引用的应用 :
弱引用对于库文件十分有用。 通过弱引用判断当前程序是否连接到多线程Glibc库
#include <stdio.h>
#include <pthread.h>
int pthread_create(
pthread_t*,
const pthread_attr_t *,
void *(*)(void*),
void *) __attribute__((weak));
int main()
{
if (pthread_create){
printf("this is mutli-thread version\n");
}
else{
printf("this is single-thread version\n");
}
return 0;
}
[root@centosgpt 10]# ./pthread-single
this is single-thread version
[root@centosgpt 10]# ./pthread-multi
this is mutli-thread version
链接:
链接器将对应的目标文件合并组成更大的模块, 链接方式分为静态链接和动态链接。
为了验证重新调整了createprocess.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
extern int create_process(char *program, char **arg_list);
//double global_var2;
extern int global_var2;
int main()
{
char *arg_list[]={
"ls",
"-l",
"/etc/yum.repos.d",
NULL
};
printf("%s %d sizeof(global_var2)=%d\n",__FILE__,__LINE__, &global_var2);
//printf("%s %d sizeof(global_var2)=%d\n",__FILE__,__LINE__, sizeof(global_var2));
create_process("ls", arg_list);
//printf("%s %d sizeof(global_var2)=%d\n",__FILE__,__LINE__, sizeof(global_var2));
create_process("ls", arg_list);
return 0;
}
重定位:
主要包含两部分变量及函数 , 使用 objdump -S -d createprocess.o查看下目标文件中变量和函数的赋值, 由于在编译的时候并不知道外部变量和函数的实际地址, 所有编译时将外部的变量和函数赋值为相对地址。 后面链接时再进行重定位。
objdump -S -d createprocess.o
createprocess.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <main>:
extern int create_process(char *program, char **arg_list);
//double global_var2;
extern int global_var2;
int main()
{
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 48 83 ec 20 sub $0x20,%rsp
char *arg_list[]={
8: 48 8d 05 00 00 00 00 lea 0x0(%rip),%rax # f <main+0xf>
f: 48 89 45 e0 mov %rax,-0x20(%rbp)
13: 48 8d 05 00 00 00 00 lea 0x0(%rip),%rax # 1a <main+0x1a>
1a: 48 89 45 e8 mov %rax,-0x18(%rbp)
1e: 48 8d 05 00 00 00 00 lea 0x0(%rip),%rax # 25 <main+0x25>
25: 48 89 45 f0 mov %rax,-0x10(%rbp)
29: 48 c7 45 f8 00 00 00 movq $0x0,-0x8(%rbp)
30: 00
"ls",
"-l",
"/etc/yum.repos.d",
NULL
};
printf("%s %d sizeof(global_var2)=%d\n",__FILE__,__LINE__, global_var2);
31: 48 8b 05 00 00 00 00 mov 0x0(%rip),%rax # 38 <main+0x38>
38: 8b 00 mov (%rax),%eax
3a: 89 c1 mov %eax,%ecx
3c: ba 12 00 00 00 mov $0x12,%edx
41: 48 8d 35 00 00 00 00 lea 0x0(%rip),%rsi # 48 <main+0x48>
48: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 4f <main+0x4f>
4f: b8 00 00 00 00 mov $0x0,%eax
54: e8 00 00 00 00 callq 59 <main+0x59>
//printf("%s %d sizeof(global_var2)=%d\n",__FILE__,__LINE__, sizeof(global_var2));
create_process("ls", arg_list);
59: 48 8d 45 e0 lea -0x20(%rbp),%rax
5d: 48 89 c6 mov %rax,%rsi
60: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 67 <main+0x67>
67: e8 00 00 00 00 callq 6c <main+0x6c>
//printf("%s %d sizeof(global_var2)=%d\n",__FILE__,__LINE__, sizeof(global_var2));
create_process("ls", arg_list);
6c: 48 8d 45 e0 lea -0x20(%rbp),%rax
70: 48 89 c6 mov %rax,%rsi
73: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 7a <main+0x7a>
7a: e8 00 00 00 00 callq 7f <main+0x7f>
return 0;
7f: b8 00 00 00 00 mov $0x0,%eax
}
84: c9 leaveq
85: c3 retq
31: 48 8b 05 00 00 00 00 mov 0x0(%rip),%rax # 38 <main+0x38>
编译器将 global_var2地址用00 00 00 00占位
7a: e8 00 00 00 00 callq 7f <main+0x7f>
编译器将create_process地址用 00 00 00 00占位
符号解析:
readelf -s ./createprocess.o
其中 _GLOBAL_OFFSET_TABLE_, global_var2, printf, create_process
均为“UND” undefined项, 为重定位项目。
[root@centosgpt 10]# readelf -s ./createprocess.o
Symbol table '.symtab' contains 19 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS createprocess.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
6: 0000000000000000 0 SECTION LOCAL DEFAULT 6
7: 0000000000000000 0 SECTION LOCAL DEFAULT 8
8: 0000000000000000 0 SECTION LOCAL DEFAULT 9
9: 0000000000000000 0 SECTION LOCAL DEFAULT 11
10: 0000000000000000 0 SECTION LOCAL DEFAULT 13
11: 0000000000000000 0 SECTION LOCAL DEFAULT 15
12: 0000000000000000 0 SECTION LOCAL DEFAULT 16
13: 0000000000000000 0 SECTION LOCAL DEFAULT 14
14: 0000000000000000 133 FUNC GLOBAL DEFAULT 1 main
15: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _GLOBAL_OFFSET_TABLE_
16: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND global_var2
17: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND printf
18: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND create_process
重定位表:
主要用来确定那些位置的信息需要被重定位。
objdump -r createprocess.o
global_var2 使用 X86_64_GOTPCREL重定位类型
create_process 使用 X86_64_PLT32 重定位类型。
可以看到存在多种重定位类型。
[root@centosgpt 10]# objdump -r createprocess.o
createprocess.o: file format elf64-x86-64
RELOCATION RECORDS FOR [.text]:
OFFSET TYPE VALUE
000000000000000b R_X86_64_PC32 .rodata-0x0000000000000004
0000000000000016 R_X86_64_PC32 .rodata-0x0000000000000001
0000000000000021 R_X86_64_PC32 .rodata+0x0000000000000002
0000000000000034 R_X86_64_GOTPCREL global_var2-0x0000000000000004
0000000000000043 R_X86_64_PC32 .rodata+0x0000000000000013
000000000000004a R_X86_64_PC32 .rodata+0x0000000000000023
0000000000000054 R_X86_64_PLT32 printf-0x0000000000000004
0000000000000062 R_X86_64_PC32 .rodata-0x0000000000000004
0000000000000067 R_X86_64_PLT32 create_process-0x0000000000000004
0000000000000075 R_X86_64_PC32 .rodata-0x0000000000000004
000000000000007a R_X86_64_PLT32 create_process-0x0000000000000004
重定位类型:
重定位后根据不同CPU对于地址修正方式不同,通过下面的类型进行地址修正。
相关的重定位类型在 /arch/x86/include/asm/elf.h
/* x86-64 relocation types */
#define R_X86_64_NONE 0 /* No reloc */
#define R_X86_64_64 1 /* Direct 64 bit */
#define R_X86_64_PC32 2 /* PC relative 32 bit signed */
#define R_X86_64_GOT32 3 /* 32 bit GOT entry */
#define R_X86_64_PLT32 4 /* 32 bit PLT address */
#define R_X86_64_COPY 5 /* Copy symbol at runtime */
#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */
#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */
#define R_X86_64_RELATIVE 8 /* Adjust by program base */
#define R_X86_64_GOTPCREL 9 /* 32 bit signed pc relative
offset to GOT */
#define R_X86_64_32 10 /* Direct 32 bit zero extended */
#define R_X86_64_32S 11 /* Direct 32 bit sign extended */
#define R_X86_64_16 12 /* Direct 16 bit zero extended */
#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */
#define R_X86_64_8 14 /* Direct 8 bit sign extended */
#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */
#define R_X86_64_PC64 24 /* Place relative 64-bit signed */
Name | Value | Field | Calculation |
R_X86_64_NONE | 0 | none | none |
R_X86_64_64 | 1 | word64 | S + A |
R_X86_64_PC32 | 2 | word32 | S + A - P |
R_X86_64_GOT32 | 3 | word32 | G + A |
R_X86_64_PLT32 | 4 | word32 | L + A - P |
R_X86_64_COPY | 5 | none | none |
R_X86_64_GLOB_DAT | 6 | word64 | S |
R_X86_64_JUMP_SLOT | 7 | word64 | S |
R_X86_64_RELATIVE | 8 | word64 | B + A |
R_X86_64_GOTPCREL | 9 | word32 | G + GOT + A - P |
R_X86_64_32 | 10 | word32 | S + A |
R_X86_64_32S | 11 | word32 | S + A |
R_X86_64_16 | 12 | word16 | S + A |
R_X86_64_PC16 | 13 | word16 | S + A - P |
R_X86_64_8 | 14 | word8 | S + A |
R_X86_64_PC8 | 15 | word8 | S + A - P |
R_X86_64_DPTMOD64 | 16 | word64 | |
R_X86_64_DTPOFF64 | 17 | word64 | |
R_X86_64_TPOFF64 | 18 | word64 | |
R_X86_64_TLSGD | 19 | word32 | |
R_X86_64_TLSLD | 20 | word32 | |
R_X86_64_DTPOFF32 | 21 | word32 | |
R_X86_64_GOTTPOFF | 22 | word32 | |
R_X86_64_TPOFF32 | 23 | word32 |
A:(保存被修正的位置)
Represents the addend used to compute the value of the relocatable field.
B:
Represents the base address at which a shared object has been loaded into memory during execution. Generally, a shared object is built with a 0 base virtual address, but the execution address will be different.
G (GOT表中的offset)
Represents the offset into the global offset table at which the relocation entry’s symbol will reside during execution.
GOT
Represents the address of the global offset table.
L(PLT表中的地址)
Represents the place (section offset or address) of the Procedure Linkage Table entry for a symbol.
P
Represents the place (section offset or address) of the storage unit being relocated (computed using r_offset).
S
Represents the value of the symbol whose index resides in the relocation entry.
静态链接方式的重定位:
静态链接库相关变量和函数的重定位情况:
objdump -S -d ./staticcreateprocess
####main 的入口地址:00000000004005ed
00000000004005ed <main>:
extern int create_process(char *program, char **arg_list);
//double global_var2;
extern int global_var2;
int main()
#### global_var2
printf("%s %d sizeof(global_var2)=%d\n",__FILE__,__LINE__, &global_var2);
40061e: 48 8d 05 2f 0a 20 00 lea 0x200a2f(%rip),%rax # 601054 <global_var2>
400625: 48 89 c1 mov %rax,%rcx
400628: ba 12 00 00 00 mov $0x12,%edx
40062d: 48 8d 35 53 01 00 00 lea 0x153(%rip),%rsi # 400787 <__dso_handle+0x1f>
400634: 48 8d 3d 5c 01 00 00 lea 0x15c(%rip),%rdi # 400797 <__dso_handle+0x2f>
40063b: b8 00 00 00 00 mov $0x0,%eax
400640: e8 6b fe ff ff callq 4004b0 <printf@plt>
### create_process
create_process("ls", arg_list);
400658: 48 8d 45 e0 lea -0x20(%rbp),%rax
40065c: 48 89 c6 mov %rax,%rsi
40065f: 48 8d 3d 0a 01 00 00 lea 0x10a(%rip),%rdi # 400770 <__dso_handle+0x8>
400666: e8 07 00 00 00 callq 400672 <create_process>
global_var2:
重定位类型:R_X86_64_GOTPCREL
计算方式: G + GOT + A – P
G=
GOT=
GOT+G= 0000000000601054
A= 0000 0000
P=00000000004005ed+38
G + GOT + A – P=0x200a2f (00 20 0a 2f -> 2f 0a 20 00)
静态编译的情况下变量不存在GOT中: G+GOT 为global_var2的虚拟地址
可以通过符号表查询获得, 确实在GOT地址(000000600ff8 )附近,但是通过readelf 重定位信息无法查询。
[root@centosgpt 10]# readelf -s ./staticcreateprocess
Symbol table '.symtab' contains 78 entries:
Num: Value Size Type Bind Vis Ndx Name
59: 0000000000601054 4 OBJECT GLOBAL DEFAULT 26 global_var2
###未找到相关重定位信息,直接使用global_var2符号对应地址
[root@centosgpt 10]# readelf -r ./staticcreateprocess
Relocation section '.rela.dyn' at offset 0x3e0 contains 1 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000600ff8 000400000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0
Relocation section '.rela.plt' at offset 0x3f8 contains 5 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000601018 000100000007 R_X86_64_JUMP_SLO 0000000000000000 abort@GLIBC_2.2.5 + 0
000000601020 000200000007 R_X86_64_JUMP_SLO 0000000000000000 printf@GLIBC_2.2.5 + 0
000000601028 000300000007 R_X86_64_JUMP_SLO 0000000000000000 __libc_start_main@GLIBC_2.2.5 + 0
000000601030 000500000007 R_X86_64_JUMP_SLO 0000000000000000 execvp@GLIBC_2.2.5 + 0
000000601038 000600000007 R_X86_64_JUMP_SLO 0000000000000000 fork@GLIBC_2.2.5 + 0
create_process:
R_X86_64_PLT32
L=0000000000400672
A= 0000 0000
P=00000000004005ed+7e=40066B
L+A-P=0x00 00 00 07 (00 00 00 07 -> 07 00 00 00 )
静态变一下create_process不在PLT中的地址, L可以从符号表中获取地址信息,如下:
[root@centosgpt 10]# readelf -s ./staticcreateprocess
Symbol table '.symtab' contains 78 entries:
Num: Value Size Type Bind Vis Ndx Name
73: 0000000000400672 95 FUNC GLOBAL DEFAULT 14 create_process
动态链接方式的重定位:
动态链接库相关变量和函数的重定位情况:
objdump -S -d ./dynamiccreateprocess
global_var2 修正后地址 0x20093b
create_process修正后地址:0xfffffe75
#### main 函数的地址 000000000040067d
000000000040067d <main>:
extern int create_process(char *program, char **arg_list);
//double global_var2;
extern int global_var2;
int main()
.....
###global_var2
printf("%s %d sizeof(global_var2)=%d\n",__FILE__,__LINE__, &global_var2);
4006ae: 48 8b 05 3b 09 20 00 mov 0x20093b(%rip),%rax # 600ff0 <global_var2>
4006b5: 48 89 c1 mov %rax,%rcx
4006b8: ba 12 00 00 00 mov $0x12,%edx
4006bd: 48 8d 35 f3 00 00 00 lea 0xf3(%rip),%rsi # 4007b7 <__dso_handle+0x1f>
4006c4: 48 8d 3d fc 00 00 00 lea 0xfc(%rip),%rdi # 4007c7 <__dso_handle+0x2f>
4006cb: b8 00 00 00 00 mov $0x0,%eax
4006d0: e8 7b fe ff ff callq 400550 <printf@plt>
.......
###create_process
create_process("ls", arg_list);
4006e8: 48 8d 45 e0 lea -0x20(%rbp),%rax
4006ec: 48 89 c6 mov %rax,%rsi
4006ef: 48 8d 3d aa 00 00 00 lea 0xaa(%rip),%rdi # 4007a0 <__dso_handle+0x8>
4006f6: e8 75 fe ff ff callq 400570 <create_process@plt>
global_var2:
重定位类型:R_X86_64_GOTPCREL
计算方式: G + GOT + A – P
G= 0
GOT=0000000000600ff0
A= 0000 0000
P=000000000040067d+38=00000000004006b5
GOT+G 就是 global_var 在 .rela.dyn中的地址 000000600ff0
G + GOT + A – P=020093b ( 00 20 09 3b –> 3b 09 20 00 小端方式)
与静态编译不同,动态编译对应符号表中地址为0
###获取GOT地址和偏移信息
[root@centosgpt 10]# readelf -r ./dynamiccreateprocess
Relocation section '.rela.dyn' at offset 0x4a8 contains 2 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000600ff0 000100000006 R_X86_64_GLOB_DAT 0000000000000000 global_var2 + 0
000000600ff8 000400000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0
Relocation section '.rela.plt' at offset 0x4d8 contains 3 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000601018 000200000007 R_X86_64_JUMP_SLO 0000000000000000 printf@GLIBC_2.2.5 + 0
000000601020 000300000007 R_X86_64_JUMP_SLO 0000000000000000 __libc_start_main@GLIBC_2.2.5 + 0
000000601028 000500000007 R_X86_64_JUMP_SLO 0000000000000000 create_process + 0
###动态链接对应的符号表值未空, 未定义
[root@centosgpt 10]# readelf -s ./dynamiccreateprocess
...
55: 0000000000000000 0 OBJECT GLOBAL DEFAULT UND global_var2
...
create_process:
R_X86_64_PLT32
L=0000000000400570
A= 0000 0000
P=000000000040067d+7e=40066B
L+A-P=0xff ff fe 75 (ff ff fe 75 -> 75 fe ff ff )
与静态编译不同,动态编译对应符号表中地址为0
### 获取L的值 create_process 在 PLT的的地址 0000000000400570
[root@centosgpt 10]# objdump -S -j .plt ./dynamiccreateprocess
0000000000400570 <create_process@plt>:
400570: ff 25 b2 0a 20 00 jmpq *0x200ab2(%rip) # 601028 <create_process>
400576: 68 02 00 00 00 pushq $0x2
40057b: e9 c0 ff ff ff jmpq 400540 <.plt>
###动态链接对应的符号表值未空, 未定义
[root@centosgpt 10]# readelf -s ./dynamiccreateprocess
...
68: 0000000000000000 0 FUNC GLOBAL DEFAULT UND create_process
...
动态链接:
动态链接涉及的五个section:
- .plt: Procedure Linkage Table 在.got.plt中查找并跳转到对应位置, 或者调用连接器查询地址.
- .plt.got:
- .got:Global Offset Table ,链接器链接时实际填充的部分
- .got.plt:想当于.plt的全局偏移表。如果查到过使用外部地址, 否则跳回.plt
- .rela.plt:函数重定位表
动态库加载:
外部符号通过PLT来获取地址,PLT通过GOT获取实际地址并完成跳转, PLT是可执行权限,GOT才有写入权限。
准备
首先查看下存放GOT表的首地址 0x000000601000, PLT表的首地址:0000000000400540
运行objdump -dj .plt ./dynamiccreateprocess
[root@centosgpt 10]# objdump -dj .plt ./dynamiccreateprocess
./dynamiccreateprocess: file format elf64-x86-64
Disassembly of section .plt:
0000000000400540 <.plt>:
400540: ff 35 c2 0a 20 00 pushq 0x200ac2(%rip) # 601008 <_GLOBAL_OFFSET_TABLE_+0x8>
400546: ff 25 c4 0a 20 00 jmpq *0x200ac4(%rip) # 601010 <_GLOBAL_OFFSET_TABLE_+0x10>
40054c: 0f 1f 40 00 nopl 0x0(%rax)
0000000000400550 <printf@plt>:
400550: ff 25 c2 0a 20 00 jmpq *0x200ac2(%rip) # 601018 <printf@GLIBC_2.2.5>
400556: 68 00 00 00 00 pushq $0x0
40055b: e9 e0 ff ff ff jmpq 400540 <.plt>
0000000000400560 <__libc_start_main@plt>:
400560: ff 25 ba 0a 20 00 jmpq *0x200aba(%rip) # 601020 <__libc_start_main@GLIBC_2.2.5>
400566: 68 01 00 00 00 pushq $0x1
40056b: e9 d0 ff ff ff jmpq 400540 <.plt>
0000000000400570 <create_process@plt>:
400570: ff 25 b2 0a 20 00 jmpq *0x200ab2(%rip) # 601028 <create_process>
400576: 68 02 00 00 00 pushq $0x2
40057b: e9 c0 ff ff ff jmpq 400540 <.plt>
存放create_process函数地址的位置 0x000000601028
运行readelf -r ./dynamiccreateprocess
[root@centosgpt 10]# readelf -r ./dynamiccreateprocess
Relocation section '.rela.dyn' at offset 0x4a8 contains 2 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000600ff0 000100000006 R_X86_64_GLOB_DAT 0000000000000000 global_var2 + 0
000000600ff8 000400000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0
Relocation section '.rela.plt' at offset 0x4d8 contains 3 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000601018 000200000007 R_X86_64_JUMP_SLO 0000000000000000 printf@GLIBC_2.2.5 + 0
000000601020 000300000007 R_X86_64_JUMP_SLO 0000000000000000 __libc_start_main@GLIBC_2.2.5 + 0
000000601028 000500000007 R_X86_64_JUMP_SLO 0000000000000000 create_process + 0
调试
下面通过例子分析下动态链接库的加载步骤。
### gdb
### 在create_process 上设置断点
(gdb) b 20
Breakpoint 1 at 0x4006d5: file createprocess.c, line 20.
(gdb) b 22
Breakpoint 2 at 0x4006e8: file createprocess.c, line 22.
### 开始调试
(gdb) run
Starting program: /root/src/osgo/10/./dynamiccreateprocess
createprocess.c 18 sizeof(global_var2)=-136470456 -138592640
Breakpoint 1, main () at createprocess.c:20
20 create_process("ls", arg_list);
### 查看当前got表中的数据,查看6个地址信息,6项,看到了 create_process对应的地址为0x400576
(gdb) x/6a 0x000000601000
0x601000: 0x600e10 0x7ffff7ffe150
0x601010: 0x7ffff7df18a0 <_dl_runtime_resolve_xsave> 0x7ffff785f290 <__printf>
0x601020: 0x7ffff782e2e0 <__libc_start_main> 0x400576 <create_process@plt+6>
### 设置一下16进制显示
(gdb) set output-radix 16
Output radix now set to decimal 16, hex 10, octal 20.
### 监控 create_process地址变化
(gdb) watch *0x601028
(gdb) cont
Continuing.
Hardware watchpoint 3: *0x601028
Old value = 0x400576
New value = 0xf7bd9795
_dl_fixup (l=<optimized out>, reloc_arg=<optimized out>) at ../elf/dl-runtime.c:149
149 }
### 再次查看地址已经更新为绝对地址。
(gdb) x/6a 0x601000
0x601000: 0x600e10 0x7ffff7ffe150
0x601010: 0x7ffff7df18a0 <_dl_runtime_resolve_xsave> 0x7ffff785f290 <__printf>
0x601020: 0x7ffff782e2e0 <__libc_start_main> 0x7ffff7bd9795 <create_process>
### 这里是跳转到PLT[0], 对应GOT[2]
(gdb) x/2i 0x400540
0x400540: pushq 0x200ac2(%rip) # 0x601008
0x400546: jmpq *0x200ac4(%rip) # 0x601010
通过gdb 调试可以看到,
第一步, 首次调用create_process时, 通过 PLT[3]表项找到存放create_process函数地址的GOT[5](当然这个时候还没有加载不是实际地址),跳转到GOT[5]
第二步 ,发现GOT[5]存放的地址为 0x400576, PLT[3]表项有三条指令, 上面一步是第一步,0x400576为 PLT[3]第二条执行的地址, 于是继续执行PLT[3]中其余的两条指令,先把索引入栈, 这个索引就是.rel.plt表中的函数的索引
第三步, 指令跳转到 到PLT[0]. 而 PLT[0]指向的GOT[2]中存放的是_dl_runtime_resolve_xsave 地址, 通过上一步索引值载入create_process实际地址到GOT[5]
下面是section .rela.plt, 可以看到create_process所在位置。
[root@centosgpt 10]# readelf -r ./dynamiccreateprocess
Relocation section '.rela.dyn' at offset 0x4a8 contains 2 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000600ff0 000100000006 R_X86_64_GLOB_DAT 0000000000000000 global_var2 + 0
000000600ff8 000400000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0
Relocation section '.rela.plt' at offset 0x4d8 contains 3 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000601018 000200000007 R_X86_64_JUMP_SLO 0000000000000000 printf@GLIBC_2.2.5 + 0
000000601020 000300000007 R_X86_64_JUMP_SLO 0000000000000000 __libc_start_main@GLIBC_2.2.5 + 0
000000601028 000500000007 R_X86_64_JUMP_SLO 0000000000000000 create_process + 0
如果调用两次create_process,可以通过以下步骤看下动态链接的具体处理步骤:
##### 设置断点进行第一次调用
(gdb) b 20
Breakpoint 1 at 0x4006d5: file createprocess.c, line 20.
(gdb) b 22
Breakpoint 2 at 0x4006e8: file createprocess.c, line 22.
(gdb) run
Starting program: /root/src/osgo/10/./dynamiccreateprocess
createprocess.c 18 sizeof(global_var2)=-136470456 -138592640
Breakpoint 1, main () at createprocess.c:20
20 create_process("ls", arg_list);
(gdb)
###### 这次通过单步调试看下具体处理步骤
(gdb) disas
Dump of assembler code for function main:
。。。
=> 0x00000000004006ce <+97>: callq 0x400560 <create_process@plt>
。。。
End of assembler dump.
(gdb) si
0x00000000004006d9 20 create_process("ls", arg_list);
(gdb) si
0x00000000004006dc 20 create_process("ls", arg_list);
(gdb) si
0x00000000004006e3 20 create_process("ls", arg_list);
(gdb) si
0x0000000000400570 in create_process@plt ()
#### 这里显示了每个PLT执行的三个操作
(gdb) disas
Dump of assembler code for function create_process@plt:
=> 0x0000000000400570 <+0>: jmpq *0x200ab2(%rip) # 0x601028
0x0000000000400576 <+6>: pushq $0x2
0x000000000040057b <+11>: jmpq 0x400540
End of assembler dump.
#### 跳转到0x601028 可以看到 这个地址实际就是PLT[5]的第二条指令的位置,继续往下执行
(gdb) x 0x601028
0x601028: 0x00400576
(gdb) x *0x601028
0x400576 <create_process@plt+6>: 0x00000268
(gdb) x 0x00400576
0x400576 <create_process@plt+6>: 0x00000268
### 这里是跳转到PLT的
(gdb) x/2i 0x400540
0x400540: pushq 0x200ac2(%rip) # 0x601008
0x400546: jmpq *0x200ac4(%rip) # 0x601010
### 继续执行
(gdb) si
0x000000000040057b in create_process@plt ()
(gdb) si
0x0000000000400540 in ?? ()
(gdb) si
0x0000000000400546 in ?? ()
(gdb) si
_dl_runtime_resolve_xsave () at ../sysdeps/x86_64/dl-trampoline.h:71
71 pushq %rbx # push subtracts stack by 8.
#### _dl_runtime_resolve_xsave函数执行, 通过_dl_fixup 更新GOT[5]实际地址。
(gdb) disas
Dump of assembler code for function _dl_runtime_resolve_xsave:
=> 0x00007ffff7df18a0 <+0>: push %rbx
0x00007ffff7df18a1 <+1>: mov %rsp,%rbx
0x00007ffff7df18a4 <+4>: and $0xffffffffffffffc0,%rsp
0x00007ffff7df18a8 <+8>: sub 0x20b401(%rip),%rsp # 0x7ffff7ffccb0 <_rtld_local_ro+176>
0x00007ffff7df18af <+15>: mov %rax,(%rsp)
0x00007ffff7df18b3 <+19>: mov %rcx,0x8(%rsp)
0x00007ffff7df18b8 <+24>: mov %rdx,0x10(%rsp)
0x00007ffff7df18bd <+29>: mov %rsi,0x18(%rsp)
0x00007ffff7df18c2 <+34>: mov %rdi,0x20(%rsp)
0x00007ffff7df18c7 <+39>: mov %r8,0x28(%rsp)
0x00007ffff7df18cc <+44>: mov %r9,0x30(%rsp)
0x00007ffff7df18d1 <+49>: mov $0xee,%eax
0x00007ffff7df18d6 <+54>: xor %edx,%edx
0x00007ffff7df18d8 <+56>: mov %rdx,0x240(%rsp)
0x00007ffff7df18e0 <+64>: mov %rdx,0x248(%rsp)
0x00007ffff7df18e8 <+72>: mov %rdx,0x250(%rsp)
0x00007ffff7df18f0 <+80>: mov %rdx,0x258(%rsp)
0x00007ffff7df18f8 <+88>: mov %rdx,0x260(%rsp)
0x00007ffff7df1900 <+96>: mov %rdx,0x268(%rsp)
0x00007ffff7df1908 <+104>: mov %rdx,0x270(%rsp)
0x00007ffff7df1910 <+112>: mov %rdx,0x278(%rsp)
0x00007ffff7df1918 <+120>: xsave 0x40(%rsp)
0x00007ffff7df191d <+125>: mov 0x10(%rbx),%rsi
0x00007ffff7df1921 <+129>: mov 0x8(%rbx),%rdi
0x00007ffff7df1925 <+133>: callq 0x7ffff7de9c50 <_dl_fixup>
0x00007ffff7df192a <+138>: mov %rax,%r11
0x00007ffff7df192d <+141>: mov $0xee,%eax
0x00007ffff7df1932 <+146>: xor %edx,%edx
0x00007ffff7df1934 <+148>: xrstor 0x40(%rsp)
0x00007ffff7df1939 <+153>: mov 0x30(%rsp),%r9
0x00007ffff7df193e <+158>: mov 0x28(%rsp),%r8
0x00007ffff7df1943 <+163>: mov 0x20(%rsp),%rdi
0x00007ffff7df1948 <+168>: mov 0x18(%rsp),%rsi
0x00007ffff7df194d <+173>: mov 0x10(%rsp),%rdx
0x00007ffff7df1952 <+178>: mov 0x8(%rsp),%rcx
0x00007ffff7df1957 <+183>: mov (%rsp),%rax
0x00007ffff7df195b <+187>: mov %rbx,%rsp
0x00007ffff7df195e <+190>: mov (%rsp),%rbx
0x00007ffff7df1962 <+194>: add $0x18,%rsp
0x00007ffff7df1966 <+198>: bnd jmpq *%r11
(gdb) cont
Continuing.
process.c 18 sizeof(global_var2)=4
Detaching after fork from child process 20462.
Breakpoint 2, main () at createprocess.c:22
22 create_process("ls", arg_list);
(gdb) total 32
-rw-r--r--. 1 root root 1664 Apr 29 2018 CentOS-Base.repo.bak
-rw-r--r--. 1 root root 1309 Apr 29 2018 CentOS-CR.repo
-rw-r--r--. 1 root root 660 Jun 5 06:26 CentOS-Debuginfo.repo
-rw-r--r--. 1 root root 314 Apr 29 2018 CentOS-fasttrack.repo
-rw-r--r--. 1 root root 657 Dec 18 05:57 CentOS-Media.repo
-rw-r--r--. 1 root root 1331 Apr 29 2018 CentOS-Sources.repo
-rw-r--r--. 1 root root 4768 Apr 29 2018 CentOS-Vault.repo
#执行第二次调用后
(gdb) si
0x00000000004006ec 22 create_process("ls", arg_list);
(gdb) si
0x00000000004006ef 22 create_process("ls", arg_list);
(gdb) si
0x00000000004006f6 22 create_process("ls", arg_list);
(gdb) si
0x0000000000400570 in create_process@plt ()
(gdb) disas
Dump of assembler code for function create_process@plt:
=> 0x0000000000400570 <+0>: jmpq *0x200ab2(%rip) # 0x601028
0x0000000000400576 <+6>: pushq $0x2
0x000000000040057b <+11>: jmpq 0x400540
End of assembler dump.
(gdb) x 0x601028
0x601028: 0xf7bd9795
(gdb) disas 0xf7bd9795
No function contains specified address.
(gdb) disas 0x7ffff7bd9795
Dump of assembler code for function create_process:
0x00007ffff7bd9795 <+0>: push %rbp
0x00007ffff7bd9796 <+1>: mov %rsp,%rbp
0x00007ffff7bd9799 <+4>: sub $0x20,%rsp
0x00007ffff7bd979d <+8>: mov %rdi,-0x18(%rbp)
0x00007ffff7bd97a1 <+12>: mov %rsi,-0x20(%rbp)
0x00007ffff7bd97a5 <+16>: mov $0x4,%ecx
0x00007ffff7bd97aa <+21>: mov $0x12,%edx
0x00007ffff7bd97af <+26>: lea 0x47(%rip),%rsi # 0x7ffff7bd97fd
0x00007ffff7bd97b6 <+33>: lea 0x4a(%rip),%rdi # 0x7ffff7bd9807
0x00007ffff7bd97bd <+40>: mov $0x0,%eax
0x00007ffff7bd97c2 <+45>: callq 0x7ffff7bd9670 <printf@plt>
0x00007ffff7bd97c7 <+50>: callq 0x7ffff7bd9690 <fork@plt>
0x00007ffff7bd97cc <+55>: mov %eax,-0x4(%rbp)
0x00007ffff7bd97cf <+58>: cmpl $0x0,-0x4(%rbp)
0x00007ffff7bd97d3 <+62>: je 0x7ffff7bd97da <create_process+69>
0x00007ffff7bd97d5 <+64>: mov -0x4(%rbp),%eax
0x00007ffff7bd97d8 <+67>: jmp 0x7ffff7bd97f2 <create_process+93>
0x00007ffff7bd97da <+69>: mov -0x20(%rbp),%rdx
0x00007ffff7bd97de <+73>: mov -0x18(%rbp),%rax
0x00007ffff7bd97e2 <+77>: mov %rdx,%rsi
0x00007ffff7bd97e5 <+80>: mov %rax,%rdi
0x00007ffff7bd97e8 <+83>: callq 0x7ffff7bd9680 <execvp@plt>
0x00007ffff7bd97ed <+88>: callq 0x7ffff7bd9660 <abort@plt>
0x00007ffff7bd97f2 <+93>: leaveq
0x00007ffff7bd97f3 <+94>: retq
第一次调用create_process时, create_process在.plt的地址0x601028, 就是jmpq *0x200ac2(%rip)的下一条指令。
pushq $0x2 (.rel.plt 中对应函数索引, printf 为 0)
jmpq 0x400530
通过GOT[2] , _dl_runtime_resolve_xsave去查找符号, 通过_dl_fixup完成GOT[2]更新。
第二次调用是 0x601028 存放的是实际地址, 直接跳转到create_process地址
动态库安全问题:
涉及动态库的两个安全的两种攻击方式:ret2libc, ret2plt
参考:
Executable and Linkable Format
The ELF format – how programs look from the inside
Be First to Comment