动态链接
使用动态链接时,程序按模块拆分为多个独立部分,在程序运行时才链接在一起。linux中ELF动态链接文件称为动态共享对象(Dynamic shared objects),以.so为拓展名。常用的C语言运行库glibc保存为libc.so。
链接的操作由动态链接器ld完成。运行时动态链接器与普通共享对象一同被映射到进程的地址空间,程序在运行时首先运行ld,完成所有动态链接工作后再转交主程序。
如果程序中存在跨模块的数据访问,由于目标变量的地址要在装载时才能确定,需要使得程序中的代码与地址无关。于是把跟地址相关的代码放在数据段里,即全局偏移表(Global Offset Table)。至于模块间的调用、跳转,也用GOT表实现,但是出于效率考虑,需要引入延迟绑定机制。
延迟绑定(Lazy Binding)的思想是在程序第一次用到时才绑定(符号查找,重定位),使用PLT表(Procedure Linkage Table)实现,实现伪代码如下:
PLT0:
push *(GOT + 4)
jump *(GOT + 8)
...
bar@plt:
jmp *(bar@GOT)
label:
push n
jump PLT0
...
bar@GOT:
jmp label
其中bar@GOT
中初始保存bar@plt
的下一条指令地址(label),随后push的n是bar在重定位表项中的序号。GOT + 4
中保存一个名为link_map的结构体的地址,它保存了本模块动态链接的相关信息,GOT + 8
中保存_dl_runtime_resolve()
的地址,该函数的作用便是解析link_map,计算出bar函数的真正地址,并将其填入bar@GOT中。
理解延迟绑定相关实现,与后面利用密切相关。
相关结构
.interp段
一个字符串,动态连接器的路径
.dynamic段
readelf -d Lib.so
查看
保存了动态连接器所需的基本信息,结构如下:
typedef struct
{
Elf32_Sword d_tag; /* Dynamic entry type */
union
{
Elf32_Word d_val; /* Integer value */
Elf32_Addr d_ptr; /* Address value */
} d_un;
} Elf32_Dyn;
typedef struct
{
Elf64_Sxword d_tag; /* Dynamic entry type */
union
{
Elf64_Xword d_val; /* Integer value */
Elf64_Addr d_ptr; /* Address value */
} d_un;
} Elf64_Dyn;
在elf.h中定义了不同d_tag的值与对应类型,后面比较常用的有:
- DT_REL 动态链接重定位表地址
- DT_SYMTAB 动态链接符号表地址
- DT_STRTAB 动态链接字符串表地址
- DT_INIT 初始化代码地址
- DT_FINI 结束代码地址
IDA查看如下:
LOAD:0000000000005090 Elf64_Dyn <5, 51F0h> ; DT_STRTAB
LOAD:00000000000050A0 Elf64_Dyn <6, 5368h> ; DT_SYMTAB
LOAD:00000000000050B0 Elf64_Dyn <0Ah, 171h> ; DT_STRSZ
LOAD:00000000000050C0 Elf64_Dyn <0Bh, 18h> ; DT_SYMENT
LOAD:00000000000050D0 Elf64_Dyn <15h, 0> ; DT_DEBUG
LOAD:00000000000050E0 Elf64_Dyn <3, 4000h> ; DT_PLTGOT
LOAD:00000000000050F0 Elf64_Dyn <2, 198h> ; DT_PLTRELSZ
LOAD:0000000000005100 Elf64_Dyn <14h, 7> ; DT_PLTREL
LOAD:0000000000005110 Elf64_Dyn <17h, 948h> ; DT_JMPREL
LOAD:0000000000005120 Elf64_Dyn <7, 828h> ; DT_RELA
DT_REL动态链接重定位表
readelf -r Lib.so查看
共享对象的重定位在装载时完成,重定位表分为.rel.dyn和.rel.plt。前者修正数据引用,即.got和.data;后者修正.got.plt
typedef struct
{
Elf32_Addr r_offset; /* Address */
Elf32_Word r_info; /* Relocation type and symbol index */
} Elf32_Rel;
typedef struct
{
Elf64_Addr r_offset; /* Address */
Elf64_Xword r_info; /* Relocation type and symbol index */
} Elf64_Rel;
/* How to extract and insert information held in the r_info field. */
#define ELF32_R_SYM(val) ((val) >> 8)
#define ELF32_R_TYPE(val) ((val) & 0xff)
#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff))
#define ELF64_R_SYM(i) ((i) >> 32)
#define ELF64_R_TYPE(i) ((i) & 0xffffffff)
#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type))
r_offset
表示需要修正的地址,r_info
高位表示该符号在符号表中的序号,低位表示符号类型
IDA中如下:
LOAD:0000000000000828 ; ELF RELA Relocation Table
LOAD:0000000000000828 Elf64_Rela <3DE0h, 8, 1340h> ; R_X86_64_RELATIVE +1340h
LOAD:0000000000000840 Elf64_Rela <3DE8h, 8, 15B5h> ; R_X86_64_RELATIVE +15B5h
LOAD:0000000000000858 Elf64_Rela <3DF0h, 8, 1300h> ; R_X86_64_RELATIVE +1300h
LOAD:0000000000000870 Elf64_Rela <40A8h, 8, 40A8h> ; R_X86_64_RELATIVE +40A8h
LOAD:0000000000000888 Elf64_Rela <3FD8h, 200000006h, 0> ; R_X86_64_GLOB_DAT __libc_start_main
LOAD:00000000000008A0 Elf64_Rela <3FE0h, 400000006h, 0> ; R_X86_64_GLOB_DAT _ITM_deregisterTMCloneTable
LOAD:00000000000008B8 Elf64_Rela <3FE8h, 1000000006h, 0> ; R_X86_64_GLOB_DAT __gmon_start__
LOAD:00000000000008D0 Elf64_Rela <3FF0h, 1500000006h, 0> ; R_X86_64_GLOB_DAT _ITM_registerTMCloneTable
LOAD:00000000000008E8 Elf64_Rela <3FF8h, 1700000006h, 0> ; R_X86_64_GLOB_DAT __cxa_finalize
LOAD:0000000000000900 Elf64_Rela <4100h, 1600000005h, 0> ; R_X86_64_COPY stdout
LOAD:0000000000000918 Elf64_Rela <4110h, 1800000005h, 0> ; R_X86_64_COPY stdin
LOAD:0000000000000930 Elf64_Rela <4120h, 1A00000005h, 0> ; R_X86_64_COPY stderr
LOAD:0000000000000948 ; ELF JMPREL Relocation Table
LOAD:0000000000000948 Elf64_Rela <4018h, 100000007h, 0> ; R_X86_64_JUMP_SLOT free
LOAD:0000000000000960 Elf64_Rela <4020h, 300000007h, 0> ; R_X86_64_JUMP_SLOT strncpy
LOAD:0000000000000978 Elf64_Rela <4028h, 500000007h, 0> ; R_X86_64_JUMP_SLOT _exit
LOAD:0000000000000990 Elf64_Rela <4030h, 600000007h, 0> ; R_X86_64_JUMP_SLOT puts
LOAD:00000000000009A8 Elf64_Rela <4038h, 700000007h, 0> ; R_X86_64_JUMP_SLOT write
LOAD:00000000000009C0 Elf64_Rela <4040h, 800000007h, 0> ; R_X86_64_JUMP_SLOT strlen
LOAD:00000000000009D8 Elf64_Rela <4048h, 900000007h, 0> ; R_X86_64_JUMP_SLOT __stack_chk_fail
LOAD:00000000000009F0 Elf64_Rela <4050h, 0A00000007h, 0> ; R_X86_64_JUMP_SLOT mmap
LOAD:0000000000000A08 Elf64_Rela <4058h, 0B00000007h, 0> ; R_X86_64_JUMP_SLOT printf
LOAD:0000000000000A20 Elf64_Rela <4060h, 0C00000007h, 0> ; R_X86_64_JUMP_SLOT memset
LOAD:0000000000000A38 Elf64_Rela <4068h, 0D00000007h, 0> ; R_X86_64_JUMP_SLOT alarm
LOAD:0000000000000A50 Elf64_Rela <4070h, 0E00000007h, 0> ; R_X86_64_JUMP_SLOT read
LOAD:0000000000000A68 Elf64_Rela <4078h, 0F00000007h, 0> ; R_X86_64_JUMP_SLOT memcmp
LOAD:0000000000000A80 Elf64_Rela <4080h, 1100000007h, 0> ; R_X86_64_JUMP_SLOT malloc
LOAD:0000000000000A98 Elf64_Rela <4088h, 1200000007h, 0> ; R_X86_64_JUMP_SLOT setvbuf
LOAD:0000000000000AB0 Elf64_Rela <4090h, 1300000007h, 0> ; R_X86_64_JUMP_SLOT __isoc99_scanf
LOAD:0000000000000AC8 Elf64_Rela <4098h, 1400000007h, 0> ; R_X86_64_JUMP_SLOT exit
DT_SYMTAB 动态链接符号表
readelf -sD Lib.so查看
通常保存在段.dynsym
typedef struct
{
Elf32_Word st_name; /* Symbol name (string tbl index) */
Elf32_Addr st_value; /* Symbol value */
Elf32_Word st_size; /* Symbol size */
unsigned char st_info; /* Symbol type and binding */
unsigned char st_other; /* Symbol visibility */
Elf32_Section st_shndx; /* Section index */
} Elf32_Sym;
typedef struct
{
Elf64_Word st_name; /* Symbol name (string tbl index) */
unsigned char st_info; /* Symbol type and binding */
unsigned char st_other; /* Symbol visibility */
Elf64_Section st_shndx; /* Section index */
Elf64_Addr st_value; /* Symbol value */
Elf64_Xword st_size; /* Symbol size */
} Elf64_Sym;
/* How to extract and insert information held in the st_info field. */
#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4)
#define ELF32_ST_TYPE(val) ((val) & 0xf)
#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf))
/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */
#define ELF64_ST_BIND(val) ELF32_ST_BIND (val)
#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val)
#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type))
DT_STRTAB 动态链接字符串表
.dynstr中
一个字符串表,索引[0]永远为0,获取的时候是取相对[0]处的地址作为偏移来取字符串的。
三个表关系
三个表有这样的查找关系:
- 先根据
.rel.plt
重定位表的r_info
字段r_info>>8
确定在.dynsym
表中的偏移。 - 根据在
.dynsym
中的偏移得到在中.dynstr
的字符串地址。 - 之后由动态链接库解析字符串拿到地址。
link_map
一个结构体,保存动态链接相关信息
struct link_map
{
/* These first few members are part of the protocol with the debugger.
This is the same format used in SVR4. */
ElfW(Addr) l_addr; /* Difference between the address in the ELF
file and the addresses in memory. */
char *l_name; /* Absolute file name object was found in. */
ElfW(Dyn) *l_ld; /* Dynamic section of the shared object. */
struct link_map *l_next, *l_prev; /* Chain of loaded objects. */
/* All following members are internal to the dynamic linker.
They may change without notice. */
/* This is an element which is only ever different from a pointer to
the very same copy of this type for ld.so when it is used in more
than one namespace. */
struct link_map *l_real;
/* Number of the namespace this link map belongs to. */
Lmid_t l_ns;
struct libname_list *l_libname;
/* Indexed pointers to dynamic section.
[0,DT_NUM) are indexed by the processor-independent tags.
[DT_NUM,DT_NUM+DT_THISPROCNUM) are indexed by the tag minus DT_LOPROC.
[DT_NUM+DT_THISPROCNUM,DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM) are
indexed by DT_VERSIONTAGIDX(tagvalue).
[DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM,
DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM+DT_EXTRANUM) are indexed by
DT_EXTRATAGIDX(tagvalue).
[DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM+DT_EXTRANUM,
DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM+DT_EXTRANUM+DT_VALNUM) are
indexed by DT_VALTAGIDX(tagvalue) and
[DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM+DT_EXTRANUM+DT_VALNUM,
DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM+DT_EXTRANUM+DT_VALNUM+DT_ADDRNUM)
are indexed by DT_ADDRTAGIDX(tagvalue), see <elf.h>. */
ElfW(Dyn) *l_info[DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM
+ DT_EXTRANUM + DT_VALNUM + DT_ADDRNUM];
const ElfW(Phdr) *l_phdr; /* Pointer to program header table in core. */
ElfW(Addr) l_entry; /* Entry point location. */
ElfW(Half) l_phnum; /* Number of program header entries. */
ElfW(Half) l_ldnum; /* Number of dynamic segment entries. */
...
}
有两个比较重要的部分:
l_addr
程序的基址l_name
程序的名称l_info[]
一个数组,保存了多个重定位项的地址
总结
调用_dl_runtime_resolve时,传参link_map和rel_offset,查找流程如下
_dl_runtime_resolve(link_map, rel_offset)
+
+-----------+ |
| Elf32_Rel | <--------------+
+-----------+
+--+ | r_offset | +-----------+
| | r_info | +----> | Elf32_Sym |
| +-----------+ +-----------+ +----------+
| .rel.plt | st_name | +--> | system\0 |
| | | +----------+
v +-----------+ .dynstr
+----+-----+ .dynsym
| <system> |
+----------+
.got.plt
ret2dlresolve
原理
_dl_runtime_resolve实际调用的是_dl_fixup(link_map, reloc_arg),代码如下:
/* This function is called through a special trampoline from the PLT the
first time each PLT entry is called. We must perform the relocation
specified in the PLT of the given shared object, and return the resolved
function address to the trampoline, which will restart the original call
to that address. Future calls will bounce directly from the PLT to the
function. */
DL_FIXUP_VALUE_TYPE
attribute_hidden __attribute ((noinline)) DL_ARCH_FIXUP_ATTRIBUTE
_dl_fixup (
# ifdef ELF_MACHINE_RUNTIME_FIXUP_ARGS
ELF_MACHINE_RUNTIME_FIXUP_ARGS,
# endif
struct link_map *l, ElfW(Word) reloc_arg)
{
const ElfW(Sym) *const symtab
= (const void *) D_PTR (l, l_info[DT_SYMTAB]);
const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]);
const uintptr_t pltgot = (uintptr_t) D_PTR (l, l_info[DT_PLTGOT]);
const PLTREL *const reloc // 通过重定位表+偏移reloc_arg获取要解析函数的重定位信息
= (const void *) (D_PTR (l, l_info[DT_JMPREL])
+ reloc_offset (pltgot, reloc_arg));
const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];
const ElfW(Sym) *refsym = sym;
void *const rel_addr = (void *)(l->l_addr + reloc->r_offset); //rel_addr为要解析函数的重定位地址,即对应GOT表项地址
lookup_t result;
DL_FIXUP_VALUE_TYPE value;
/* Sanity check that we're really looking at a PLT relocation. */
assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT);
/* Look up the target symbol. If the normal lookup rules are not
used don't look in the global scope. */
if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0)
{
const struct r_found_version *version = NULL;
if (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL)
{
const ElfW(Half) *vernum =
(const void *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]);
ElfW(Half) ndx = vernum[ELFW(R_SYM) (reloc->r_info)] & 0x7fff;
version = &l->l_versions[ndx];
if (version->hash == 0)
version = NULL;
}
/* We need to keep the scope around so do some locking. This is
not necessary for objects which cannot be unloaded or when
we are not using any threads (yet). */
int flags = DL_LOOKUP_ADD_DEPENDENCY;
if (!RTLD_SINGLE_THREAD_P)
{
THREAD_GSCOPE_SET_FLAG ();
flags |= DL_LOOKUP_GSCOPE_LOCK;
}
#ifdef RTLD_ENABLE_FOREIGN_CALL
RTLD_ENABLE_FOREIGN_CALL;
#endif
result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym, l->l_scope, //_dl_lookup_symbol_x 通过函数名解析得到对应函数符号表信息sym,返回对应的linkmap
version, ELF_RTYPE_CLASS_PLT, flags, NULL);
/* We are done with the global scope. */
if (!RTLD_SINGLE_THREAD_P)
THREAD_GSCOPE_RESET_FLAG ();
#ifdef RTLD_FINALIZE_FOREIGN_CALL
RTLD_FINALIZE_FOREIGN_CALL;
#endif
/* Currently result contains the base load address (or link map)
of the object that defines sym. Now add in the symbol
offset. */
value = DL_FIXUP_MAKE_VALUE (result, // 通过result和sym确定函数真实加载地址
SYMBOL_ADDRESS (result, sym, false));
}
else
{
/* We already found the symbol. The module (and therefore its load
address) is also known. */
value = DL_FIXUP_MAKE_VALUE (l, SYMBOL_ADDRESS (l, sym, true));
result = l;
}
/* And now perhaps the relocation addend. */
value = elf_machine_plt_value (l, reloc, value);
if (sym != NULL
&& __builtin_expect (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC, 0))
value = elf_ifunc_invoke (DL_FIXUP_VALUE_ADDR (value));
#ifdef SHARED
/* Auditing checkpoint: we have a new binding. Provide the auditing
libraries the possibility to change the value and tell us whether further
auditing is wanted.
The l_reloc_result is only allocated if there is an audit module which
provides a la_symbind. */
if (l->l_reloc_result != NULL)
{
/* This is the address in the array where we store the result of previous
relocations. */
struct reloc_result *reloc_result
= &l->l_reloc_result[reloc_index (pltgot, reloc_arg, sizeof (PLTREL))];
unsigned int init = atomic_load_acquire (&reloc_result->init);
if (init == 0)
{
_dl_audit_symbind (l, reloc_result, sym, &value, result);
/* Store the result for later runs. */
if (__glibc_likely (! GLRO(dl_bind_not)))
{
reloc_result->addr = value;
/* Guarantee all previous writes complete before init is
updated. See CONCURRENCY NOTES below. */
atomic_store_release (&reloc_result->init, 1);
}
}
else
value = reloc_result->addr;
}
#endif
/* Finally, fix up the plt itself. */
if (__glibc_unlikely (GLRO(dl_bind_not)))
return value;
return elf_machine_fixup_plt (l, result, refsym, sym, reloc, rel_addr, value);
}
/* Return true if dynamic section in the shared library L should be
relocated. */
static inline bool
dl_relocate_ld (const struct link_map *l)
{
/* Don't relocate dynamic section if it is readonly */
return !(l->l_ld_readonly || DL_RO_DYN_SECTION);
}
/* All references to the value of l_info[DT_PLTGOT],
l_info[DT_STRTAB], l_info[DT_SYMTAB], l_info[DT_RELA],
l_info[DT_REL], l_info[DT_JMPREL], and l_info[VERSYMIDX (DT_VERSYM)]
have to be accessed via the D_PTR macro. The macro is needed since for
most architectures the entry is already relocated - but for some not
and we need to relocate at access time. */
#define D_PTR(map, i) \
((map)->i->d_un.d_ptr + (dl_relocate_ld (map) ? 0 : (map)->l_addr))
该函数查找函数对应的重定位表项通过:
const PLTREL *const reloc
= (const void *) (D_PTR (l, l_info[DT_JMPREL])
+ reloc_offset (pltgot, reloc_arg));
即通过link_map中的l_info数组中的DT_JMPREL项找到.rel.plt地址,加上先前传入参数reloc_arg的偏移,确定要解析的函数(如alarm)的重定位表项信息reloc;
然后result = _dl_lookup_symbol_x
通过函数名解析得到对应函数(alarm)符号表信息sym,返回对应的linkmap结构地址;gdb调试界面如下:
► 0x7ffff7fd5f6c <_dl_fixup+252> call _dl_lookup_symbol_x <_dl_lookup_symbol_x>
rdi: 0x555555559205 ◂— 0x616d006d72616c61 /* 'alarm' */
rsi: 0x7ffff7ffe2e0 —▸ 0x555555554000 ◂— 0x10102464c457f
rdx: 0x7fffffffd5c0 —▸ 0x555555559490 ◂— 0x1200000015
rcx: 0x7ffff7ffe650 —▸ 0x7ffff7ffe5a0 —▸ 0x7ffff7fbb7f0 —▸ 0x7ffff7ffe2e0 —▸ 0x555555554000 ◂— ...
r8: 0x7ffff7fbb840 —▸ 0x5555555592ce ◂— 'GLIBC_2.2.5'
r9: 0x1
arg[6]: 0x1
arg[7]: 0x0
最后value = DL_FIXUP_MAKE_VALUE
通过result->l_addr + (sym)->st_value 确定函数真实加载地址
函数结束会将 elf_machine_fixup_plt (l, result, refsym, sym, reloc, rel_addr, value)
会将解析的真实地址value写入到rel_addr,即对应GOT表项。
以上就是_dl_runtime_resolve解析函数地址的流程。
32/64区别
存在以下区别:
- 在32位中,reloc_arg作为偏移量,而在64位中作为.rel.plt的数组下标
- 结构体均升级为64位版本
- version问题
- version问题如下
if (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL)
{
const ElfW(Half) *vernum =
(const void *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]);
ElfW(Half) ndx = vernum[ELFW(R_SYM) (reloc->r_info)] & 0x7fff;
version = &l->l_versions[ndx];
if (version->hash == 0)
version = NULL;
}
此处把(reloc->r_info)>>32
作为下标取值vernum,由于我们伪造的(reloc->r_info)>>32
很大,导致容易取到不可读区域。
解决方法之一是避免进入该循环,即使得l->l_info[VERSYMIDX (DT_VERSYM)]
为0,地址为link_map+0x1c8。然而这需要泄露ld地址,都有地址了还打ret2dl就不礼貌了。
另一种解决方案是选择不进入if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0)
的大循环,而是走else分支
else
{
/* We already found the symbol. The module (and therefore its load
address) is also known. */
value = DL_FIXUP_MAKE_VALUE (l, SYMBOL_ADDRESS (l, sym, true));
result = l;
}
#define LOOKUP_VALUE_ADDRESS(map, set) ((set) || (map) ? (map)->l_addr : 0)
/* Calculate the address of symbol REF using the base address from map MAP,
if non-NULL. Don't check for NULL map if MAP_SET is TRUE. */
#define SYMBOL_ADDRESS(map, ref, map_set) \
((ref) == NULL ? 0 \
: (__glibc_unlikely ((ref)->st_shndx == SHN_ABS) ? 0 \
: LOOKUP_VALUE_ADDRESS (map, map_set)) + (ref)->st_value)
DL_FIXUP_MAKE_VALUE
用于计算函数真实值。我们只需将sym->st_value指向某个已解析函数的got表,l->addr指向目标函数和已解析函数的偏移。
在不泄露ld的情况下伪造l->addr,我们需要伪造link_map,一般需要满足:
- link_map中的DT_STRTAB、DT_SYMTAB、DT_JMPREL可读
- DT_SYMTAB结构体中的d_ptr即sym,*(sym+5) & 0x03 != 0
- (reloc->r_info)&0xff == 7
- rel_addr = l->addr + reloc->r_offset即原先需要修改的got表地址有可写权限
- l->l_addr + sym->st_value 为system的地址
利用
通过以上原理解析,我们知道了动态库解析一个函数地址的流程,那么我们根据该流程有以下几个攻击思路:
- 利用传入错误的reloc_arg使其查找到我们伪造好的.rel.plt项,实现对重定位项的劫持,解析为我们想要的函数,由延迟绑定的机制可知,我们需要先压栈我们需要的reloc_arg,随后调用plt0。并在对应地址上伪造好.rel.plt,dynsym,dynstr。在dynstr中写入想要解析的任意函数即可。
- 控制linkmap的l_addr(需要泄露地址),使其错误的解析到libc中其他的函数地址
具体利用参考Adcanced_ROP利用手法。