ELF 文件格式分析

定义:

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结构定义:

    /include/uapi/linux/elf.h,

#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结构定义:

/include/uapi/linux/elf.h

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定义:

/include/uapi/linux/elf.h

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 */
Table: Relocation Types
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

参考:

Understanding the ELF

Executable and Linkable Format

The ELF format – how programs look from the inside

再议 PLT 与 GOT

Acronyms relevant to Executable and Linkable Format (ELF)

深入了解GOT,PLT和动态链接

PLT与GOT

Relocation

How to make gdb print out all values in hexadecimal mode?

Be First to Comment

发表回复