ELF重定位分析

1. 变量访问

/////////////////////////////////////////////////////////////
//ml_mainpic_data.c
//gcc -fPIC -shared -g -o libmlpicdata.so ml_mainpic_data.c
int myglob = 42;

int ml_func(int a, int b) {
    myglob += a;
    return b + myglob;
}
/////////////////////////////////////////////////////////////

# objdump -d -Mintel libmlpicdata.so

00000000000006b8 <ml_func>:
 6b8:   55                      push   rbp
 6b9:   48 89 e5                mov    rbp,rsp
 6bc:   89 7d fc                mov    DWORD PTR [rbp-0x4],edi
 6bf:   89 75 f8                mov    DWORD PTR [rbp-0x8],esi
 6c2:   48 8b 05 0f 09 20 00    mov    rax,QWORD PTR [rip+0x20090f]        # 200fd8 <_DYNAMIC+0x1c8> 
 6c9:   8b 10                   mov    edx,DWORD PTR [rax]
 6cb:   8b 45 fc                mov    eax,DWORD PTR [rbp-0x4]
 6ce:   01 c2                   add    edx,eax
 6d0:   48 8b 05 01 09 20 00    mov    rax,QWORD PTR [rip+0x200901]        # 200fd8 <_DYNAMIC+0x1c8>
 6d7:   89 10                   mov    DWORD PTR [rax],edx
 6d9:   48 8b 05 f8 08 20 00    mov    rax,QWORD PTR [rip+0x2008f8]        # 200fd8 <_DYNAMIC+0x1c8>
 6e0:   8b 10                   mov    edx,DWORD PTR [rax]
 6e2:   8b 45 f8                mov    eax,DWORD PTR [rbp-0x8]
 6e5:   01 d0                   add    eax,edx
 6e7:   5d                      pop    rbp
 6e8:   c3                      ret 
 
 6c2:   48 8b 05 0f 09 20 00    mov    rax,QWORD PTR [rip+0x20090f] //获取GOT中myglob变量的地址并保存到rax
    该地址在程序正式执行前,在动态连接阶段由动态连接器生成
    rip指针指向下一条指令的地址,上面代码中保存的值0x6c9
    查看.rela.dyn节中,变量地址修改偏移0x200fd8,
    myglob变量与RIP中值的偏移值:0x200fd8-0x6c9=0x20090f
    
 6c9:   8b 10                   mov    edx,DWORD PTR [rax]    //获取myglob变量的值
    
动态连接器执行:
1. 查看可执行文件节头表,定位.dynamic节,确定需要加载的动态库,加载动态库(.dynamic)
# eu-readelf -d driver

Dynamic segment contains 31 entries:
 Addr: 0x0000000000600e08  Offset: 0x000e08  Link to section: [ 6] '.dynstr'
  Type              Value
  NEEDED            Shared library: [libmlpicdata.so]

2. 通过查看libmlpicdata.so节头表,定位.dynamic节,确定重定位信息
# eu-readelf -S libmlpicdata.so 
There are 33 section headers, starting at offset 0x1368:

Section Headers:
[Nr] Name                 Type         Addr             Off      Size     ES Flags Lk Inf Al
[ 0]                      NULL         0000000000000000 00000000 00000000  0        0   0  0
...
[ 3] .dynsym              DYNSYM       0000000000000230 00000230 00000150 24 A      4   2  8
...
[ 7] .rela.dyn            RELA         0000000000000470 00000470 000000d8 24 A      3   0  8
...
[19] .dynamic             DYNAMIC      0000000000200e10 00000e10 000001c0 16 WA     4   0  8
[20] .got                 PROGBITS     0000000000200fd0 00000fd0 00000030  8 WA     0   0  8
[21] .got.plt             PROGBITS     0000000000201000 00001000 00000028  8 WA     0   0  8
[22] .data                PROGBITS     0000000000201028 00001028 00000004  0 WA     0   0  4
...

3. 查看需要重定位的信息, RELA=0x0000000000000470
# eu-readelf -d libmlpicdata.so 

Dynamic segment contains 28 entries:
 Addr: 0x0000000000200e10  Offset: 0x000e10  Link to section: [ 4] '.dynstr'
  Type              Value
  ...
  PLTGOT            0x0000000000201000
  PLTRELSZ          48 (bytes)
  PLTREL            RELA
  JMPREL            0x0000000000000548
  RELA              0x0000000000000470
  RELASZ            216 (bytes)
  RELAENT           24 (bytes)
  ...
  
4. 通过查看libmlpicdata.so节头表, 定位.rela.dyn节, 变量myglob地址修改偏移0x200fd8
# readelf -r libmlpicdata.so 

重定位节 '.rela.dyn' 位于偏移量 0x470 含有 9 个条目:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
  ...
000000200fd8  000700000006 R_X86_64_GLOB_DAT 0000000000201028 myglob + 0
  ... 
  
#define ELF64_R_SYM(info) ((info)>>32)
#define ELF64_R_TYPE(info) ((Elf64_Word)(info))
#define ELF64_R_INFO(sym, type) (((Elf64_Xword)(sym)<<32)+(Elf64_Xword)(type))

Sym. Value = (Info)>>32 = 000700000006 >> 32 = 0x7 (.dynsym index) = 0000000000201028 (symbol virtual address)
Type = (unsigned ing)000700000006 = 0x6 = R_X86_64_GLOB_DAT

5. 通过查看libmlpicdata.so节头表, 定位.dynsym节,获得myglob变量的偏移0x2010128
eu-readelf -s libmlpicdata.so 

Symbol table [ 3] '.dynsym' contains 14 entries:
 2 local symbols  String table: [ 4] '.dynstr'
  Num:            Value   Size Type    Bind   Vis          Ndx Name
    ...
    7: 0000000000201028      4 OBJECT  GLOBAL DEFAULT       22 myglob  

6. gdb driver

name=./libmlpicdata.so (7 segments), address=0x7ffff7bd9000

(gdb) x/4xg 0x7ffff7dd9fd0
0x7ffff7dd9fd0: 0x0000000000000000      0x00007ffff7dda028
0x7ffff7dd9fe0: 0x0000000000000000      0x0000000000000000

(gdb) p &myglob
$1 = (int *) 0x7ffff7dda028 <myglob>

myglob地址重定位后保存位置绝对地址 = 加载地址(0x7ffff7bd9000) + 重定位改写地址(0x200fd8) = 0x7ffff7dd9fd8
myglob重定位后绝对地址 = 加载地址(0x7ffff7bd9000) + 变量虚拟地址偏移(0x201028) = 0x7ffff7dda028
2. 函数调用

///////////////////////////////////////////////////////
//ml_mainpic.c
//gcc -fPIC -shared -g -o libmlpic.so ml_mainpic.c
int myglob = 42;

int ml_util_func(int a) {
  return a + 1;
}

int ml_func(int a, int b) {
  int c = b + ml_util_func(a);
  myglob += c;
  return b + myglob; 
}
//////////////////////////////////////////////////////

x64: 过程链接表示例
.PLT0:
    pushq GOT+8(%rip) # GOT[1]
    jmp *GOT+16(%rip) # GOT[2]
    nop; nop
    nop; nop
.PLT1:
    jmp *name1@GOTPCREL(%rip) # 16 bytes from .PLT0
    pushq $index1
    jmp .PLT0
.PLT2:
    jmp *name2@GOTPCREL(%rip) # 16 bytes from .PLT1
    pushl $index2
    jmp .PLT0
    
以下步骤介绍了运行时链接程序和程序如何通过过程链接表和全局偏移表来协作解析符号引用。
1. 初始创建程序的内存映像时,运行时链接程序会将全局偏移表中的第二项和第三项设置为特殊值。以下步骤说明了这些值。
2. 进程映像中的每个共享目标文件都有各自的过程链接表,并且控制权仅转移给位于同一目标文件内的过程链接表项。
3. 例如,该程序会调用 name1,以将控制权转移给标签 .PLT1。
4. 第一条指令会跳至全局偏移表项中对应于 name1 的地址。最初,全局偏移表保存下一条 pushq 指令的地址,而不是 name1 的实际地址。
5. 该程序将在栈中推送一个重定位索引 (index1)。该重定位索引是重定位表中一个32位的非负索引。重定位表由动态节(.dynamic)中的 JMPREL 项标识。
指定的重定位项的类型为R_X86_64_JMP_SLOT,其偏移指定了前面的jmp指令中使用的全局偏移表项。该重定位项还包含符号表索引,以供运行时链接程序用于获取引用的符号 name1。
6. 推送该重定位索引后,程序将跳至过程链接表中的第一项 .PLT0。
pushq 指令会在栈中推送全局偏移表的第二项 (GOT+8) 的值,从而为运行时链接程序提供一个字的标识信息。
然后,程序将跳至第三个全局偏移表项 (GOT+16) 中的地址,以继续跳至运行时链接程序。
7. 运行时链接程序将展开栈、检查指定的重定位项、获取符号的值、在全局偏移项表中存储 name1 的实际地址并跳至目标。
8. 过程链接表项的后续执行结果会直接传输给 name1,而不会再次调用运行时链接程序。位于 .PLT1 的 jmp 指令将跳至 name1,而不是对 pushq 指令调用

每一个PLT项都有一个与之对应的GOT slot。
启动时,动态连接器会使用与GOT对应的PLT第二条语句(pushq $index*)的地址填充GOT slot。

静态分析:
# objdump -d libmlpic.so

libmlpic.so:     文件格式 elf64-x86-64
... 

Disassembly of section .plt:
00000000000005e0 <__gmon_start__@plt-0x10>:
 5e0:   ff 35 22 0a 20 00       pushq  0x200a22(%rip)        # 201008 <_GLOBAL_OFFSET_TABLE_+0x8>
 5e6:   ff 25 24 0a 20 00       jmpq   *0x200a24(%rip)        # 201010 <_GLOBAL_OFFSET_TABLE_+0x10>
 5ec:   0f 1f 40 00             nopl   0x0(%rax)

00000000000005f0 <__gmon_start__@plt>:
 5f0:   ff 25 22 0a 20 00       jmpq   *0x200a22(%rip)        # 201018 <_GLOBAL_OFFSET_TABLE_+0x18>
 5f6:   68 00 00 00 00          pushq  $0x0
 5fb:   e9 e0 ff ff ff          jmpq   5e0 <_init+0x20>

0000000000000600 <ml_util_func@plt>:
 600:   ff 25 1a 0a 20 00       jmpq   *0x200a1a(%rip)        # 201020 <_GLOBAL_OFFSET_TABLE_+0x20>
 606:   68 01 00 00 00          pushq  $0x1
 60b:   e9 d0 ff ff ff          jmpq   5e0 <_init+0x20>

Disassembly of section .text:
0000000000000708 <ml_util_func>:
 708:   55                      push   %rbp
 709:   48 89 e5                mov    %rsp,%rbp
 70c:   89 7d fc                mov    %edi,-0x4(%rbp)
 70f:   8b 45 fc                mov    -0x4(%rbp),%eax
 712:   83 c0 01                add    $0x1,%eax
 715:   5d                      pop    %rbp
 716:   c3                      retq   

0000000000000717 <ml_func>:
 717:   55                      push   %rbp
 718:   48 89 e5                mov    %rsp,%rbp
 71b:   48 83 ec 20             sub    $0x20,%rsp
 71f:   89 7d ec                mov    %edi,-0x14(%rbp)
 722:   89 75 e8                mov    %esi,-0x18(%rbp)
 725:   8b 45 ec                mov    -0x14(%rbp),%eax
 728:   89 c7                   mov    %eax,%edi
 72a:   e8 d1 fe ff ff          callq  600 <ml_util_func@plt>
 72f:   8b 55 e8                mov    -0x18(%rbp),%edx
 732:   01 d0                   add    %edx,%eax

# eu-readelf -S libmlpic.so
There are 33 section headers, starting at offset 0x13e0:

Section Headers:
[Nr] Name                 Type         Addr             Off      Size     ES Flags Lk Inf Al
[ 0]                      NULL         0000000000000000 00000000 00000000  0        0   0  0
...
[ 3] .dynsym              DYNSYM       0000000000000238 00000238 00000168 24 A      4   2  8
...
[ 8] .rela.plt            RELA         0000000000000578 00000578 00000048 24 A      3  10  8
...
[10] .plt                 PROGBITS     00000000000005e0 000005e0 00000040 16 AX     0   0 16
...
[19] .dynamic             DYNAMIC      0000000000200e10 00000e10 000001c0 16 WA     4   0  8
[20] .got                 PROGBITS     0000000000200fd0 00000fd0 00000030  8 WA     0   0  8
[21] .got.plt             PROGBITS     0000000000201000 00001000 00000030  8 WA     0   0  8
...

# eu-readelf -r libmlpic.so

Relocation section [ 7] '.rela.dyn' at offset 0x4a0 contains 9 entries:
  Offset              Type            Value               Addend Name
  0x0000000000200df0  X86_64_RELATIVE 000000000000000000   +1744 
  ...

Relocation section [ 8] '.rela.plt' for section [10] '.plt' at offset 0x578 contains 3 entries:
  Offset              Type            Value               Addend Name
  0x0000000000201018  X86_64_JUMP_SLOT 000000000000000000      +0 __gmon_start__
  0x0000000000201020  X86_64_JUMP_SLOT 0x0000000000000708      +0 ml_util_func
  0x0000000000201028  X86_64_JUMP_SLOT 000000000000000000      +0 __cxa_finalize
  

动态分析:
name=./libmlpic.so (7 segments), address=0x7ffff7bd9000

PLT绝对地址 = 加载地址(0x7ffff7bd9000) + 虚拟地址偏移(0x5e0) = 0x7ffff7bd95e0

(gdb) x/8xg 0x7ffff7bd95e0
0x7ffff7bd95e0 <__gmon_start__@plt-0x10>:     0x25ff00200a2235ff      0x00401f0f00200a24
0x7ffff7bd95f0 <__gmon_start__@plt>:        0x006800200a2225ff      0xffffffe0e9000000
0x7ffff7bd9600 <ml_util_func@plt>:          0x016800200a1a25ff      0xffffffd0e9000000
0x7ffff7bd9610 <__cxa_finalize@plt>:        0x026800200a1225ff      0xffffffc0e9000000

0x7ffff7bd9600 <ml_util_func@plt>:      0x016800200a1a25ff      0xffffffd0e9000000
 7ffff7bd9600:   ff 25 1a 0a 20 00       jmpq   *0x200a1a(%rip)        # 201020 <_GLOBAL_OFFSET_TABLE_+0x20>
 7ffff7bd9606:   68 01 00 00 00          pushq  $0x1
 7ffff7bd960b:   e9 d0 ff ff ff          jmpq   5e0 <_init+0x20>

.got节用于保存变量重定位地址,.got.plt节用于保存函数重定位地址。

函数使用的got(.got.plt)绝对地址 = 加载地址(0x7ffff7bd9000) + 虚拟地址偏移(0x201000) = 0x7ffff7dda000
调用ml_util_func之前got内容:
(gdb) x/6xg 0x7ffff7dda000
0x7ffff7dda000: 0x0000000000200e10      0x00007ffff7ff96b0
0x7ffff7dda010: 0x00007ffff7df18a0      0x00007ffff7bd95f6
0x7ffff7dda020: 0x00007ffff7bd9606      0x00007ffff7bd9616

got中第一项为指向.dynamic节的偏移地址,与节信息中的[19] .dynamic对应
got中第二、三项为特殊值,用于调用ld-linux*.so
ml_util_func地址重定位后保存位置绝对地址 = 加载地址(0x7ffff7bd9606) + 偏移(0x200a1a) = 0x7ffff7dda020,其保存的地址为0x00007ffff7bd9606, 即为pushq  $0x1的地址

调用ml_util_func之后got内容:
(gdb) n
9               myglob += c;
(gdb) x/6xg 0x7ffff7dda000
0x7ffff7dda000: 0x0000000000200e10      0x00007ffff7ff96b0
0x7ffff7dda010: 0x00007ffff7df18a0      0x00007ffff7bd95f6
0x7ffff7dda020: 0x00007ffff7bd9708      0x00007ffff7bd9616
(gdb) p &ml_util_func
$1 = (int (*)(int)) 0x7ffff7bd9708 <ml_util_func>

执行函数调用后,ml_util_func地址重定位后保存位置绝对地址 = 0x7ffff7dda020,其保存的地址为0x00007ffff7bd9708, 即为ml_util_func的地址

3. 驱动代码

////////////////////////////////////////////////////////////////////
//driver.c
//gcc -g -std=c99 -Wl,-rpath,. -o driver_data driver.c -L. -lmlpicdata
//gcc -g -std=c99 -Wl,-rpath,. -o driver_func driver.c -L. -lmlpic
#define _GNU_SOURCE

#include <link.h>
#include <stdlib.h>
#include <stdio.h>

static int header_handler(struct dl_phdr_info* info, size_t size, void* data) { 
  printf("name=%s (%d segments), address=%p\n",
    info->dlpi_name, info->dlpi_phnum, (void *)info->dlpi_addr);

  for (int i=0; i<info->dlpi_phnum; ++i) {
    printf("\t\theader %02d: address=%10p\n", i,
      (void *)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr));
    printf("\t\t\ttype=%u, flags=0x%x\n",
      info->dlpi_phdr[i].p_type, info->dlpi_phdr[i].p_flags);
  }
  printf("\n");
  return 0;
}

extern int ml_func(int, int);

int main(int argc, char* argv[]) {
  dl_iterate_phdr(header_handler, NULL);

  int t = ml_func(argc, argc);
  return t;
}
////////////////////////////////////////////////////////////////////

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值