通过分析各种不同elf文件的elf和反汇编信息来学习重定向。这里使用x86架构。
通过make来编译可重定向目标文件、可执行文件和共享库。并调用readelf和objdump将elf信息和反汇编信息输出到相应文件。
Makefile文件如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | CC := cc READELF := readelf OBJDUMP := objdump CFLAGS := -m32 srcs := exe.c exe_lib.c lib.c lib_lib.c objs = $(srcs:%.c=obj/%.o) objs_pic = $(filter obj/lib%.o,$(srcs:%.c=obj/%_pic.o)) objs_pie = $(filter obj/exe%,$(srcs:%.c=obj/%_pie.o)) bins_name := liblib.so liblib_pic.so exe exe_pie exe_lib exe_lib_pie \ liblib_lib.so liblib_lib_pic.so bins = $(bins_name:%=obj/%) elfs := $(objs) $(objs_pic) $(objs_pie) $(bins) elfdumps := $(elfs:obj/%=readelf/%.readelf) objdumps := $(elfs:obj/%=objdump/%.objdump) dirs := obj readelf objdump all: $(dirs) $(objs) $(objs_pic) $(objs_pie) $(bins) $(elfdumps) $(objdumps) .PHONY: $(dirs) $(dirs): [ -d $@ ] || mkdir $@ $(objs):obj/%.o:%.c $(CC) $(CFLAGS) -c -o $@ $< $(objs_pic):obj/%_pic.o:%.c $(CC) $(CFLAGS) -fpic -c -o $@ $< $(objs_pie):obj/%_pie.o:%.c $(CC) $(CFLAGS) -fpie -c -o $@ $< obj/exe: obj/exe.o $(CC) $(CFLAGS) -nostdlib -o $@ $< obj/exe_pie: obj/exe_pie.o $(CC) $(CFLAGS) -fpie -nostdlib -o $@ $< obj/exe_lib: obj/exe_lib.o $(CC) $(CFLAGS) -nostdlib -o $@ $< -Lobj -llib obj/exe_lib_pie: obj/exe_lib_pie.o $(CC) $(CFLAGS) -fpie -nostdlib -o $@ $< -Lobj -llib obj/liblib.so: obj/lib.o $(CC) $(CFLAGS) -shared -o $@ $< obj/liblib_pic.so: obj/lib_pic.o $(CC) $(CFLAGS) -shared -fpic -o $@ $< obj/liblib_lib.so: obj/lib_lib.o $(CC) $(CFLAGS) -shared -o $@ $< -Lobj -llib obj/liblib_lib_pic.so: obj/lib_lib_pic.o $(CC) $(CFLAGS) -shared -fpic -o $@ $< -Lobj -llib $(elfdumps):readelf/%.readelf:obj/% $(READELF) -a $< > $@ $(objdumps):objdump/%.objdump:obj/% $(OBJDUMP) -d $< > $@ clean: rm -fr $(dirs) |
分析下面几种类型,所有的编译如上述Makefile文件所示。
gcc手册如是说:
-fpic
Generate position-independent code (PIC) suitable for use in a shared library, if supported for the target machine. Such code accesses all constant addresses through a global offset table (GOT). The dynamic loader resolves the GOT entries when the program starts (the dynamic loader is not part of GCC; it is part of the operating system). If the GOT size for the linked executable exceeds a machine-specific maximum size, you get an error message from the linker indicating that ‘-fpic’ does not work; in that case, recompile with ‘-fPIC’ instead. (These maximums are 8k on the SPARC, 28k on AArch64 and 32k on the m68k and RS/6000. The x86 has no such limit.) Position-independent code requires special support, and therefore works only on certain machines. For the x86, GCC supports PIC for System V but not for the Sun 386i. Code generated for the IBM RS/6000 is always position-independent. When this flag is set, the macros __pic__ and __PIC__ are defined to 1.
-fPIC
If supported for the target machine, emit position-independent code, suitable for dynamic linking and avoiding any limit on the size of the global offset table. This option makes a difference on AArch64, m68k, PowerPC and SPARC. Position-independent code requires special support, and therefore works only on certain machines. When this flag is set, the macros __pic__ and __PIC__ are defined to 2.
-fpie
-fPIE
These options are similar to ‘-fpic’ and ‘-fPIC’, but generated position independent code can be only linked into executables. Usually these options are used when ‘-pie’ GCC option is used during linking. ‘-fpie’ and ‘-fPIE’ both define the macros __pie__ and __PIE__. The macros have the value 1 for ‘-fpie’ and 2 for ‘-fPIE’
简单的说,-fpic用于共享库,-fpie用于可执行文件,在写下面的内容时,错误的使用-fpic创建可执行文件,发现有部分代码是位置相关的。
访问本地静态变量、本地静态函数、本地全局变量、本地全局函数,查看可重定向情况。
exe.c
1 2 3 4 5 6 7 8 9 10 11 12 | int g_c = 1; int g_d() { return 1; } static int c = 1; static int d() { return 1; } int _start() { return c + d() + g_c + g_d(); } |
先来看可重定向目标文件exe.o的elf信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .text PROGBITS 00000000 000034 000038 00 AX 0 0 1 [ 2] .rel.text REL 00000000 000210 000018 08 I 10 1 4 [ 3] .data PROGBITS 00000000 00006c 000008 00 WA 0 0 4 Relocation section '.rel.text' at offset 0x210 contains 3 entries: Offset Info Type Sym.Value Sym. Name 00000020 00000301 R_386_32 00000000 .data 00000027 00000a01 R_386_32 00000004 g_c 0000002f 00000b02 R_386_PC32 0000000a g_d Symbol table '.symtab' contains 13 entries: Num: Value Size Type Bind Vis Ndx Name 0: 00000000 0 NOTYPE LOCAL DEFAULT UND 1: 00000000 0 FILE LOCAL DEFAULT ABS exe.c 2: 00000000 0 SECTION LOCAL DEFAULT 1 3: 00000000 0 SECTION LOCAL DEFAULT 3 4: 00000000 0 SECTION LOCAL DEFAULT 4 5: 00000004 4 OBJECT LOCAL DEFAULT 3 c 6: 0000000a 10 FUNC LOCAL DEFAULT 1 d 7: 00000000 0 SECTION LOCAL DEFAULT 6 8: 00000000 0 SECTION LOCAL DEFAULT 7 9: 00000000 0 SECTION LOCAL DEFAULT 5 10: 00000000 4 OBJECT GLOBAL DEFAULT 3 g_c 11: 00000000 10 FUNC GLOBAL DEFAULT 1 g_d 12: 00000014 36 FUNC GLOBAL DEFAULT 1 _start |
重定向section为.rel.text,是.text section的重定向信息。
静态变量的重定向
第一条是R_386_32,表示是绝对地址。符号是.data,这是.data section的地址,静态变量使用这种重定向,静态本地变量c存储在.data section,但是它是本地的,可以通过.data的偏移来访问。但是在链接阶段,该目标文件中的.text和.data的相对位置是可能变化的,通过.data的地址来进行重定向。
看c在符号表中的条目:"5: 00000004 4 OBJECT LOCAL DEFAULT 3 c"。它位于section 3,即.data section中,偏移为value值0x4。
在来看.text偏移0x20处的反汇编信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | obj/exe.o: file format elf32-i386 Disassembly of section .text: 00000000 <g_d>: 0: 55 push %ebp 1: 89 e5 mov %esp,%ebp 3: b8 01 00 00 00 mov $0x1,%eax 8: 5d pop %ebp 9: c3 ret 0000000a <d>: a: 55 push %ebp b: 89 e5 mov %esp,%ebp d: b8 01 00 00 00 mov $0x1,%eax 12: 5d pop %ebp 13: c3 ret 00000014 <_start>: 14: 55 push %ebp 15: 89 e5 mov %esp,%ebp 17: 53 push %ebx 18: e8 ed ff ff ff call a <d> 1d: 89 c2 mov %eax,%edx 1f: a1 04 00 00 00 mov 0x4,%eax 24: 01 c2 add %eax,%edx 26: a1 00 00 00 00 mov 0x0,%eax 2b: 8d 1c 02 lea (%edx,%eax,1),%ebx 2e: e8 fc ff ff ff call 2f <_start+0x1b> 33: 01 d8 add %ebx,%eax 35: 5b pop %ebx 36: 5d pop %ebp 37: c3 ret |
汇编代码为:"1f: a1 04 00 00 00 mov 0x4,%eax",根据ELF文档Intel Arch节中的内容,R_386_32计算地址的方法为S+A。也即c在.data中的偏移存储在指令中,为0x4。而.data的地址,通过链接阶段确定。最终的地址为S+A。
静态函数和调用者在同一个.text section,通过PC相对地址来访问,因此不需要重定向信息。
全局变量
重定向类型位R_386_32,在汇编:"26: a1 00 00 00 00 mov 0x0,%eax"中,A为0,而S则通过符号表确定,链接器通过符号表确定符号在链接阶段的绝对地址。
全局函数
因为i386指令集,函数调用是PC相对指令,因此重定向类型为:R_386_PC32。和R_386_32不同之处,其计算重定向值的方法为:S+A-P。
最终可执行文件没有可重定向section:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .note.gnu.build-i NOTE 080480d4 0000d4 000024 00 A 0 0 4 [ 2] .text PROGBITS 080480f8 0000f8 000038 00 AX 0 0 1 [ 3] .eh_frame_hdr PROGBITS 08048130 000130 000024 00 A 0 0 4 [ 4] .eh_frame PROGBITS 08048154 000154 00007c 00 A 0 0 4 [ 5] .data PROGBITS 0804a000 001000 000008 00 WA 0 0 4 [ 6] .comment PROGBITS 00000000 001008 000034 01 MS 0 0 1 [ 7] .shstrtab STRTAB 00000000 001196 00005b 00 0 0 1 [ 8] .symtab SYMTAB 00000000 00103c 000120 10 9 12 4 [ 9] .strtab STRTAB 00000000 00115c 00003a 00 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings) I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown) O (extra OS processing required) o (OS specific), p (processor specific) There are no section groups in this file. Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align LOAD 0x000000 0x08048000 0x08048000 0x001d0 0x001d0 R E 0x1000 LOAD 0x001000 0x0804a000 0x0804a000 0x00008 0x00008 RW 0x1000 NOTE 0x0000d4 0x080480d4 0x080480d4 0x00024 0x00024 R 0x4 GNU_EH_FRAME 0x000130 0x08048130 0x08048130 0x00024 0x00024 R 0x4 GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x10 Section to Segment mapping: Segment Sections... 00 .note.gnu.build-id .text .eh_frame_hdr .eh_frame 01 .data 02 .note.gnu.build-id 03 .eh_frame_hdr 04 |
使用-fpie选项,编译目标文件和链接可执行文件。
思考:位置无关代码依赖于PC相对位置,因为如果使用绝对位置的话,.text段总是要被重定向的,绕不过去。对于PC相对位置函数调用通常不同架构都是支持的,但是有些架构不支持访问PC相对变量的指令,x86就是如此,因此需要插入一段代码获取PC寄存器的值。而x64架构支持访问PC相对变量的指令。
来看exe_pie.o的elf信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .group GROUP 00000000 000034 000008 04 14 16 4 [ 2] .group GROUP 00000000 00003c 000008 04 14 19 4 [ 3] .text PROGBITS 00000000 000044 000059 00 AX 0 0 1 [ 4] .rel.text REL 00000000 000324 000048 08 I 14 3 4 [ 5] .data PROGBITS 00000000 0000a0 000008 00 WA 0 0 4 [ 6] .bss NOBITS 00000000 0000a8 000000 00 WA 0 0 1 [ 7] .text.__x86.get_p PROGBITS 00000000 0000a8 000004 00 AXG 0 0 1 [ 8] .text.__x86.get_p PROGBITS 00000000 0000ac 000004 00 AXG 0 0 1 [ 9] .comment PROGBITS 00000000 0000b0 000035 01 MS 0 0 1 [10] .note.GNU-stack PROGBITS 00000000 0000e5 000000 00 0 0 1 [11] .eh_frame PROGBITS 00000000 0000e8 0000a4 00 A 0 0 4 [12] .rel.eh_frame REL 00000000 00036c 000028 08 I 14 11 4 [13] .shstrtab STRTAB 00000000 000394 000096 00 0 0 1 [14] .symtab SYMTAB 00000000 00018c 000140 10 15 14 4 [15] .strtab STRTAB 00000000 0002cc 000058 00 0 0 1 Relocation section '.rel.text' at offset 0x324 contains 9 entries: Offset Info Type Sym.Value Sym. Name 00000004 00001002 R_386_PC32 00000000 __x86.get_pc_thunk.ax 00000009 0000110a R_386_GOTPC 00000000 _GLOBAL_OFFSET_TABLE_ 00000018 00001002 R_386_PC32 00000000 __x86.get_pc_thunk.ax 0000001d 0000110a R_386_GOTPC 00000000 _GLOBAL_OFFSET_TABLE_ 0000002d 00001302 R_386_PC32 00000000 __x86.get_pc_thunk.bx 00000033 0000110a R_386_GOTPC 00000000 _GLOBAL_OFFSET_TABLE_ 00000040 00000309 R_386_GOTOFF 00000000 .data 00000048 00000e09 R_386_GOTOFF 00000000 g_c 00000050 00000f02 R_386_PC32 00000000 g_d Symbol table '.symtab' contains 20 entries: Num: Value Size Type Bind Vis Ndx Name 0: 00000000 0 NOTYPE LOCAL DEFAULT UND 1: 00000000 0 FILE LOCAL DEFAULT ABS exe.c 2: 00000000 0 SECTION LOCAL DEFAULT 3 3: 00000000 0 SECTION LOCAL DEFAULT 5 4: 00000000 0 SECTION LOCAL DEFAULT 6 5: 00000004 4 OBJECT LOCAL DEFAULT 5 c 6: 00000014 20 FUNC LOCAL DEFAULT 3 d 7: 00000000 0 SECTION LOCAL DEFAULT 7 8: 00000000 0 SECTION LOCAL DEFAULT 8 9: 00000000 0 SECTION LOCAL DEFAULT 10 10: 00000000 0 SECTION LOCAL DEFAULT 11 11: 00000000 0 SECTION LOCAL DEFAULT 9 12: 00000000 0 SECTION LOCAL DEFAULT 1 13: 00000000 0 SECTION LOCAL DEFAULT 2 14: 00000000 4 OBJECT GLOBAL DEFAULT 5 g_c 15: 00000000 20 FUNC GLOBAL DEFAULT 3 g_d 16: 00000000 0 FUNC GLOBAL HIDDEN 7 __x86.get_pc_thunk.ax 17: 00000000 0 NOTYPE GLOBAL DEFAULT UND _GLOBAL_OFFSET_TABLE_ 18: 00000028 49 FUNC GLOBAL DEFAULT 3 _start 19: 00000000 0 FUNC GLOBAL HIDDEN 8 __x86.get_pc_thunk.bx |
分析:
多出两个Section:.text.__x86.get_pc_thunk.ax和.text.__x86.get_pc_thunk.bx,分别对应函数__x86.get_pc_thunk.ax和__x86.get_pc_thunk.bx,x86架构不能直接访问PC寄存器的值,这两个函数用来将PC寄存器的值读入eax和ebx。
.rel.text包含了.text section的重定向信息,可以发现没有R_386_32(绝对地址)类型。
来看exe_pie.o的反汇编信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | obj/exe_pie.o: file format elf32-i386 Disassembly of section .text: 00000000 <g_d>: 0: 55 push %ebp 1: 89 e5 mov %esp,%ebp 3: e8 fc ff ff ff call 4 <g_d+0x4> 8: 05 01 00 00 00 add $0x1,%eax d: b8 01 00 00 00 mov $0x1,%eax 12: 5d pop %ebp 13: c3 ret 00000014 <d>: 14: 55 push %ebp 15: 89 e5 mov %esp,%ebp 17: e8 fc ff ff ff call 18 <d+0x4> 1c: 05 01 00 00 00 add $0x1,%eax 21: b8 01 00 00 00 mov $0x1,%eax 26: 5d pop %ebp 27: c3 ret 00000028 <_start>: 28: 55 push %ebp 29: 89 e5 mov %esp,%ebp 2b: 53 push %ebx 2c: e8 fc ff ff ff call 2d <_start+0x5> 31: 81 c3 02 00 00 00 add $0x2,%ebx 37: e8 d8 ff ff ff call 14 <d> 3c: 89 c2 mov %eax,%edx 3e: 8b 83 04 00 00 00 mov 0x4(%ebx),%eax 44: 01 c2 add %eax,%edx 46: 8b 83 00 00 00 00 mov 0x0(%ebx),%eax 4c: 8d 1c 02 lea (%edx,%eax,1),%ebx 4f: e8 fc ff ff ff call 50 <_start+0x28> 54: 01 d8 add %ebx,%eax 56: 5b pop %ebx 57: 5d pop %ebp 58: c3 ret Disassembly of section .text.__x86.get_pc_thunk.ax: 00000000 <__x86.get_pc_thunk.ax>: 0: 8b 04 24 mov (%esp),%eax 3: c3 ret Disassembly of section .text.__x86.get_pc_thunk.bx: 00000000 <__x86.get_pc_thunk.bx>: 0: 8b 1c 24 mov (%esp),%ebx 3: c3 ret |
结合.rel.text中的重定向条目来分析,g_d()和d()中也有重定向条目,没啥用,这里不分析。
_start():
2c: e8 fc ff ff ff call 2d <_start+0x5>
8048124: e8 2c 00 00 00 call 8048155 <__x86.get_pc_thunk.bx>
[5] 偏移0x2d,类型R_386_PC32,符号__x86.get_pc_thunk.bx,将PC寄存器的值读入ebx。
PC指向下一条指令,所以0x8048155-0x8048129=0x2c,重定向结果为0x2c。执行后ebx的值PC地址。
注意:实际的计算方法是S+A-P,这里的P是重定向条目指定的偏移,而不是PC寄存器的值,链接器不处理架构相关的细节,它不清除当前指令多长,不同的架构PC也由不同的含义,相关信息由编译器生成合适的加数A。所以实际的计算是:0x8048155-0x8048125+0xfffffffc(-4)=0x2c
31: 81 c3 02 00 00 00 add $0x2,%ebx
8048129: 81 c3 d7 1e 00 00 add $0x1ed7,%ebx
[6] 偏移0x33,类型R_386_GOTPC,符号_GLOBAL_OFFSET_TABLE_,重定向为GOT表相对PC的地址。
在exe_pie的符号表中,
13: 0804a000 0 OBJECT LOCAL DEFAULT 5 _GLOBAL_OFFSET_TABLE_
所以实际的重定向为GOT+A-P:0x804a000-0x804812b+0x2=0x1ed7。
执行后ebx的值为GOT的地址。
37: e8 d8 ff ff ff call 14 <d>
3c: 89 c2 mov %eax,%edx
这样edx为d()。
3e: 8b 83 04 00 00 00 mov 0x4(%ebx),%eax
8048136: 8b 83 10 00 00 00 mov 0x10(%ebx),%eax
[7] 偏移0x40,类型R_386_GOTOFF,符号.data,重定向为.data section相对于GOT的偏移。
S+A-GOT,如上,GOT为0x804a000,.data可以通过section头确定为0x804a00c
[ 6] .data PROGBITS 0804a00c 00100c 000008 00 WA 0 0 4
所以重定向值为:0x804a00c-0x804a000+0x4=0x10。
这里A存储的是c是.data中的偏移,S-GOT为.data相对GOT的偏移,ebx存储为GOT的绝对地址。
因此ebx+A+S-GOT就是静态变量c的地址。这样eax为c的值。
44: 01 c2 add %eax,%edx
这样edx的值为d()+c
46: 8b 83 00 00 00 00 mov 0x0(%ebx),%eax
804813e: 8b 83 0c 00 00 00 mov 0xc(%ebx),%eax
[8] 偏移0x48,类型R_386_GOTOFF,符号g_c,重定向为g_c相对于GOT表的偏移。
由于ebx已经存储了GOT的绝对地址,这样就获得g_c的地址。这样就可将g_c的值拷贝到%eax。
4c: 8d 1c 02 lea (%edx,%eax,1),%ebx
这样ebx的值为d()+c+g_c,只差一个g_d()了。
4f: e8 fc ff ff ff call 50 <_start+0x28>
8048147: e8 ac ff ff ff call 80480f8 <g_d>
9 偏移0x50,类型R_386_PC32,符号g_d,重定向为g_d()相对PC的偏移,通过call相对位置函数调用。
思考:
这里是编译为可执行文件,因此使用R_386_PC32是可以的,可执行文件这个偏移是固定的。但是使用-fpic用于动态链接库时就不可以了,因此全局函数可能在共享库中,加载位置是变化的,因此偏移是变化的,产生的重定向条目为R_386_PLT32。通过PLT条目访问。
54: 01 d8 add %ebx,%eax
eax为最终的值。
思考:
可重定向目标文件只有.rel.text重定向信息,没有GOT和PLT,只有在链接为可执行文件或共享库时,链接器才根据重定向类型创建GOT和PLT。
pie编译的可执行文件是不需要.got的,只需要GOT的地址,用来做相对地址引用。objdump -s exe_pie,查看,.got.plt section只有保留的3个条目,每个条目全0。
1 2 | Contents of section .got.plt: 804a000 00000000 00000000 00000000 ............ |
如上,调用本地全局函数,都是通过R_386_PC32重定向实现的,那么访问函数指针是啥情况来,exe.c添加相关代码:
1 2 3 4 5 6 7 8 9 10 | #if 1 void *static_func_ptr() { return ( void *)d; } void *global_func_ptr() { return ( void *)g_d; } #endif |
exe.o的elf信息,多出两个重定向条目
1 2 | 0000003c 00000201 R_386_32 00000000 .text 00000046 00000b01 R_386_32 00000000 g_d |
exe.o的反汇编:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 00000038 <static_func_ptr>: 38: 55 push %ebp 39: 89 e5 mov %esp,%ebp 3b: b8 0a 00 00 00 mov $0xa,%eax 40: 5d pop %ebp 41: c3 ret 00000042 <global_func_ptr>: 42: 55 push %ebp 43: 89 e5 mov %esp,%ebp 45: b8 00 00 00 00 mov $0x0,%eax 4a: 5d pop %ebp 4b: c3 ret |
非常简单,静态函数取.text的绝对地址,然后加数为静态函数在.text中的偏移。全局函数直接重定向为全局函数的地址。
exe_pie.o的elf信息,多出几个重定向条目
1 2 3 4 5 6 | 0000005d 00001002 R_386_PC32 00000000 __x86.get_pc_thunk.ax 00000062 0000110a R_386_GOTPC 00000000 _GLOBAL_OFFSET_TABLE_ 00000068 00000209 R_386_GOTOFF 00000000 .text 00000072 00001002 R_386_PC32 00000000 __x86.get_pc_thunk.ax 00000077 0000110a R_386_GOTPC 00000000 _GLOBAL_OFFSET_TABLE_ 0000007d 00000f09 R_386_GOTOFF 00000000 g_d |
exe_pie.o相关反汇编信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 0000000a <d>: 。。。 00000059 <static_func_ptr>: 59: 55 push %ebp 5a: 89 e5 mov %esp,%ebp 5c: e8 fc ff ff ff call 5d <static_func_ptr+0x4> 61: 05 01 00 00 00 add $0x1,%eax 66: 8d 80 14 00 00 00 lea 0x14(%eax),%eax 6c: 5d pop %ebp 6d: c3 ret 0000006e <global_func_ptr>: 6e: 55 push %ebp 6f: 89 e5 mov %esp,%ebp 71: e8 fc ff ff ff call 72 <global_func_ptr+0x4> 76: 05 01 00 00 00 add $0x1,%eax 7b: 8d 80 00 00 00 00 lea 0x0(%eax),%eax 81: 5d pop %ebp 82: c3 ret |
可执行文件exe_pie的反汇编信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 08048151 <static_func_ptr>: 8048151: 55 push %ebp 8048152: 89 e5 mov %esp,%ebp 8048154: e8 22 00 00 00 call 804817b <__x86.get_pc_thunk.ax> 8048159: 05 a7 1e 00 00 add $0x1ea7,%eax 804815e: 8d 80 0c e1 ff ff lea -0x1ef4(%eax),%eax 8048164: 5d pop %ebp 8048165: c3 ret 08048166 <global_func_ptr>: 8048166: 55 push %ebp 8048167: 89 e5 mov %esp,%ebp 8048169: e8 0d 00 00 00 call 804817b <__x86.get_pc_thunk.ax> 804816e: 05 92 1e 00 00 add $0x1e92,%eax 8048173: 8d 80 f8 e0 ff ff lea -0x1f08(%eax),%eax 8048179: 5d pop %ebp 804817a: c3 ret |
获取静态函数指针的套路:
获取PC地址,获取GOT相对PC的地址,获取.text相对GOT的偏移。这样就能获取.text的绝对地址,而静态函数在.text中的偏移是固定的,在编译.o时就确定了,这个偏移就是加数A。这样就得到了静态函数的地址。
获取全局函数指针的套路:
获取PC地址,获取GOT相对PC的地址,获取全局函数地址相对GOT的偏移,这就获取到全局函数的地址。
参考
ELF:Intel architecture. https://insidelinuxdev.net/article/a00c0b.html
ELF:Intel architecture and System V Release 4 Dependencies. https://insidelinuxdev.net/article/a032be.html
The Intel386 psABI version draft for clarifying R_386_GOT32 and R_386_GOT32X relocations. https://github.com/hjl-tools/x86-psABI/wiki/intel386-psABI-draft.pdf