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;
}
////////////////////////////////////////////////////////////////////