---
title: ret2dl_resolve Sections 關係表
description: 簡單紀錄在利用 ret2dl_resolve 時,相關 Sections 之間的關係
tags: security
lang: zh_tw
---
[TOC]
# ret2dl_resolve Sections 關係表
## 32bits
```
.rel.plt:
base(.rel.plt) + reloc_arg
Elf32_Rel <804A010h, 507h> ; R_386_JMP_SLOT write
r_offset (4): 804A010h
r_info (4): 507h
.dynsym:
base(.dynsym) + (r_info >> 8) * 0x10
Elf32_Sym <offset aWrite - offset byte_8048268, 0, 0, 12h, 0, 0> ; "write"
st_name (4): 0x54
st_value (4): 0
st_size (4): 0
st_info (1): 0x12
st_other (1): 0
st_shndx (2): 0
.dynstr:
base(.dynstr) + st_name
"write"
```
## 64bits
```
.rela.plt:
base(.rela.plt) + reloc_arg * 0x18
Elf64_Rela <601018h, 100000007h, 0> ; R_X86_64_JUMP_SLOT printf
r_offset (8): 0x601018
r_info (8): 0x100000007
r_addend (8): 0
.dynsym:
base(.dynsym) + (r_info >> 32) * 0x18
Elf64_Sym <offset aPrintf - offset byte_400330, 12h, 0, 0, 0, 0> ; "printf"
st_name (4): 0xb
st_info (1): 0x12
st_other (1): 0
st_shndx (2): 0
st_value (8): 0
st_size (8): 0
.dynstr:
base(.dynstr) + st_name
"printf"
```
## description
- `st_info` 可以為 0
- `r_info` 低位必須是 `07`
- 解析出的 function 真正位址會放回 `r_offset`
- `r_offset` 指向的就是 `GOT`
* 參考: http://blog.eonew.cn/archives/934
> 漏洞利用方式
> 1.控制eip为PLT[0]的地址,只需传递一个index_arg参数
> 2.控制index_arg的大小,使reloc的位置落在可控地址内
> 3.伪造reloc的内容,使sym落在可控地址内
> 4.伪造sym的内容,使name落在可控地址内
> 5.伪造name为任意库函数,如system
> 绕过
在执行ret2dl-solve时,还有一点必须绕过,那就是.gnu.version+ELF32_R_SYM(rel_entry->r_info) * 2必须是一个较小的值,否则下面的代码就会执行错误。
* Source Code: https://elixir.bootlin.com/glibc/glibc-2.23/source/elf/dl-runtime.c#L215
* Assembly 截圖:
![](https://i.imgur.com/5BA1B9C.png)
# Source code reading
* https://elixir.bootlin.com/glibc/glibc-2.31/source/sysdeps/x86_64/dl-trampoline.h#L60
```c=
.globl _dl_runtime_resolve
.hidden _dl_runtime_resolve
.type _dl_runtime_resolve, @function
.align 16
cfi_startproc
_dl_runtime_resolve:
cfi_adjust_cfa_offset(16) # Incorporate PLT
_CET_ENDBR
# if DL_RUNTIME_RESOLVE_REALIGN_STACK
# if LOCAL_STORAGE_AREA != 8
# error LOCAL_STORAGE_AREA must be 8
# endif
pushq %rbx # push subtracts stack by 8.
cfi_adjust_cfa_offset(8)
cfi_rel_offset(%rbx, 0)
mov %RSP_LP, %RBX_LP
cfi_def_cfa_register(%rbx)
and $-STATE_SAVE_ALIGNMENT, %RSP_LP
# endif
# ifdef REGISTER_SAVE_AREA
sub $REGISTER_SAVE_AREA, %RSP_LP
# if !DL_RUNTIME_RESOLVE_REALIGN_STACK
cfi_adjust_cfa_offset(REGISTER_SAVE_AREA)
# endif
# else
# Allocate stack space of the required size to save the state.
# if IS_IN (rtld)
sub _rtld_local_ro+RTLD_GLOBAL_RO_DL_X86_CPU_FEATURES_OFFSET+XSAVE_STATE_SIZE_OFFSET(%rip), %RSP_LP
# else
sub _dl_x86_cpu_features+XSAVE_STATE_SIZE_OFFSET(%rip), %RSP_LP
# endif
# endif
# Preserve registers otherwise clobbered.
movq %rax, REGISTER_SAVE_RAX(%rsp)
movq %rcx, REGISTER_SAVE_RCX(%rsp)
movq %rdx, REGISTER_SAVE_RDX(%rsp)
movq %rsi, REGISTER_SAVE_RSI(%rsp)
movq %rdi, REGISTER_SAVE_RDI(%rsp)
movq %r8, REGISTER_SAVE_R8(%rsp)
movq %r9, REGISTER_SAVE_R9(%rsp)
# ifdef USE_FXSAVE
fxsave STATE_SAVE_OFFSET(%rsp)
# else
movl $STATE_SAVE_MASK, %eax
xorl %edx, %edx
# Clear the XSAVE Header.
# ifdef USE_XSAVE
movq %rdx, (STATE_SAVE_OFFSET + 512)(%rsp)
movq %rdx, (STATE_SAVE_OFFSET + 512 + 8)(%rsp)
# endif
movq %rdx, (STATE_SAVE_OFFSET + 512 + 8 * 2)(%rsp)
movq %rdx, (STATE_SAVE_OFFSET + 512 + 8 * 3)(%rsp)
movq %rdx, (STATE_SAVE_OFFSET + 512 + 8 * 4)(%rsp)
movq %rdx, (STATE_SAVE_OFFSET + 512 + 8 * 5)(%rsp)
movq %rdx, (STATE_SAVE_OFFSET + 512 + 8 * 6)(%rsp)
movq %rdx, (STATE_SAVE_OFFSET + 512 + 8 * 7)(%rsp)
# ifdef USE_XSAVE
xsave STATE_SAVE_OFFSET(%rsp)
# else
xsavec STATE_SAVE_OFFSET(%rsp)
# endif
# endif
# Copy args pushed by PLT in register.
# %rdi: link_map, %rsi: reloc_index
mov (LOCAL_STORAGE_AREA + 8)(%BASE), %RSI_LP
mov LOCAL_STORAGE_AREA(%BASE), %RDI_LP
call _dl_fixup # Call resolver.
```
* 呼叫 `_dl_fixup`, 為解析器
## \_dl_fixup
* https://elixir.bootlin.com/glibc/glibc-2.31/source/elf/dl-runtime.c#L59
```c=
DL_FIXUP_VALUE_TYPE
attribute_hidden __attribute ((noinline)) 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)
{
// 取得 symbol table 位址 (.dynsym)
const ElfW(Sym) *const symtab
= (const void *) D_PTR (l, l_info[DT_SYMTAB]);
// 取得 string table 位址 (.dynstr)
const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]);
// reloc_offset 從 reloc_arg 得來,
// 取得 PLTREL, 為 rel 或 rela
// 詳細請看尾部補充
const PLTREL *const reloc
= (const void *) (D_PTR (l, l_info[DT_JMPREL]) + reloc_offset);
// 取得對應 Sym, 從 .dynsym 和 r_info 推算出來
// ELFW(R_SYM) 最終展開成 ELF32_R_SYM 或 ELF64_R_SYM
// ELF32_R_SYM(i) 展開為 ((i) >> 8)
// ELF64_R_SYM(i) 展開為 ((i) >> 32)
const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];
const ElfW(Sym) *refsym = sym;
// GOT 位址, 解析完成後會將結果放回來
void *const rel_addr = (void *)(l->l_addr + reloc->r_offset);
lookup_t result;
DL_FIXUP_VALUE_TYPE value;
// ELFW(R_TYPE) 最終展開成 ELF32_R_TYPE 或 ELF64_R_TYPE
// ELF32_R_TYPE(i) 展開為 ((i) & 0xff)
// ELF64_R_TYPE(i) 展開為 ((i) & 0xffffffff)
/* Sanity check that we're really looking at a PLT relocation. */
assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT);
// st_other 我們設為 0, 步入 if
/* 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
// 解析 symbol name
// strtab + sym->st_name
result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym, l->l_scope,
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,
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));
/* Finally, fix up the plt itself. */
if (__glibc_unlikely (GLRO(dl_bind_not)))
return value;
// 最終將解析結果放回 GOT
return elf_machine_fixup_plt (l, result, refsym, sym, reloc, rel_addr, value);
}
```
* 裡面頻繁使用到各種 Macro 例如 `ElfW`, `D_PTR`
* PLTREL 選擇是用 Rela 或 Rel:
* https://elixir.bootlin.com/glibc/glibc-2.31/source/elf/dl-runtime.c#L34
```c=
#if (!ELF_MACHINE_NO_RELA && !defined ELF_MACHINE_PLT_REL) \
|| ELF_MACHINE_NO_REL
# define PLTREL ElfW(Rela)
#else
# define PLTREL ElfW(Rel)
#endif
```
## Macro reloc_offset
* x64:
* https://elixir.bootlin.com/glibc/glibc-2.31/source/sysdeps/x86_64/dl-runtime.c#L6
```c=
#define reloc_offset reloc_arg * sizeof (PLTREL)
```
* x86:
* https://elixir.bootlin.com/glibc/glibc-2.31/source/elf/dl-runtime.c#L45
```c=
#ifndef reloc_offset
# define reloc_offset reloc_arg
# define reloc_index reloc_arg / sizeof (PLTREL)
#endif
```
## Macro ElfW
* https://elixir.bootlin.com/glibc/glibc-2.31/source/elf/link.h#L28
```c=
/* We use this macro to refer to ELF types independent of the native wordsize.
`ElfW(TYPE)' is used in place of `Elf32_TYPE' or `Elf64_TYPE'. */
#define ElfW(type) _ElfW (Elf, __ELF_NATIVE_CLASS, type)
#define _ElfW(e,w,t) _ElfW_1 (e, w, _##t)
#define _ElfW_1(e,w,t) e##w##t
```
* 另外有大寫版本:
* https://elixir.bootlin.com/glibc/glibc-2.31/source/include/link.h#L368
```c=
/* We use this macro to refer to ELF macros independent of the native
wordsize. `ELFW(R_TYPE)' is used in place of `ELF32_R_TYPE' or
`ELF64_R_TYPE'. */
#define ELFW(type) _ElfW (ELF, __ELF_NATIVE_CLASS, type)
```
* 幾個範例:
```c=
ElfW(Word) reloc_arg;
// Elf__ELF_NATIVE_CLASS_Word reloc_arg;
```
* 接著看
* https://elixir.bootlin.com/glibc/glibc-2.31/source/bits/elfclass.h
```c=
#define __ELF_NATIVE_CLASS __WORDSIZE
```
* 於是乎 macro 繼續展開:
```c=
ElfW(Word) reloc_arg;
// Elf__ELF_NATIVE_CLASS_Word reloc_arg;
// Elf__WORDSIZE_Word reloc_arg;
```
* 接著每個 architecture 就能自定義 `__WORDSIZE` 多大
* https://elixir.bootlin.com/glibc/glibc-2.31/source/sysdeps/x86/bits/wordsize.h#L4
```c=
/* Determine the wordsize from the preprocessor defines. */
#if defined __x86_64__ && !defined __ILP32__
# define __WORDSIZE 64
#else
# define __WORDSIZE 32
#define __WORDSIZE32_SIZE_ULONG 0
#define __WORDSIZE32_PTRDIFF_LONG 0
#endif
#ifdef __x86_64__
# define __WORDSIZE_TIME64_COMPAT32 1
/* Both x86-64 and x32 use the 64-bit system call interface. */
# define __SYSCALL_WORDSIZE 64
#else
# define __WORDSIZE_TIME64_COMPAT32 0
#endif
```
* 假設在 64 bit, macro 展開變成以下
```c=
ElfW(Word) reloc_arg;
// Elf__ELF_NATIVE_CLASS_Word reloc_arg;
// Elf__WORDSIZE_Word reloc_arg;
// Elf64_Word reloc_arg;
```
* 而這些類型都有基本定義
* https://elixir.bootlin.com/glibc/glibc-2.31/source/elf/elf.h
```c=
/* Type for a 16-bit quantity. */
typedef uint16_t Elf32_Half;
typedef uint16_t Elf64_Half;
/* Types for signed and unsigned 32-bit quantities. */
typedef uint32_t Elf32_Word;
typedef int32_t Elf32_Sword;
typedef uint32_t Elf64_Word;
typedef int32_t Elf64_Sword;
/* Types for signed and unsigned 64-bit quantities. */
typedef uint64_t Elf32_Xword;
typedef int64_t Elf32_Sxword;
typedef uint64_t Elf64_Xword;
typedef int64_t Elf64_Sxword;
/* Type of addresses. */
typedef uint32_t Elf32_Addr;
typedef uint64_t Elf64_Addr;
/* Type of file offsets. */
typedef uint32_t Elf32_Off;
typedef uint64_t Elf64_Off;
/* Type for section indices, which are 16-bit quantities. */
typedef uint16_t Elf32_Section;
typedef uint16_t Elf64_Section;
/* Type for version symbol information. */
typedef Elf32_Half Elf32_Versym;
typedef Elf64_Half Elf64_Versym;
```
* 但大寫的結局就不太相同:
```c=
ELFW(R_SYM) (reloc->r_info);
// ELF__ELF_NATIVE_CLASS_R_SYM
// ELF__WORDSIZE_R_SYM
// ELF64_R_SYM
```
* https://elixir.bootlin.com/glibc/glibc-2.31/source/elf/elf.h#L673
```c=
/* 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))
...
/* How to extract and insert information held in the st_other field. */
#define ELF32_ST_VISIBILITY(o) ((o) & 0x03)
/* For ELF64 the definitions are the same. */
#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o)
```
## Macro D_PTR
* https://elixir.bootlin.com/glibc/glibc-2.31/source/sysdeps/generic/ldsodefs.h#L78
```c=
/* 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. */
#ifdef DL_RO_DYN_SECTION
# define D_PTR(map, i) ((map)->i->d_un.d_ptr + (map)->l_addr)
#else
# define D_PTR(map, i) (map)->i->d_un.d_ptr
#endif
```
* 幾個在 \_dl_fixup 用到的地方:
```c=
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 PLTREL *const reloc
= (const void *) (D_PTR (l, l_info[DT_JMPREL]) + reloc_offset);
const ElfW(Half) *vernum =
(const void *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]);
```
* 展開後:
```c=
const Elf64_Sym *const symtab
= (const void *) (l)->l_info[DT_SYMTAB]->d_un.d_ptr;
const char *strtab = (const void *) (l)->l_info[DT_STRTAB]->d_un.d_ptr;
const PLTREL *const reloc
= (const void *) ((l)->l_info[DT_JMPREL]->d_un.d_ptr + reloc_offset);
const Elf64_Half *vernum =
(const void *) (l)->l_info[VERSYMIDX (DT_VERSYM)]->d_un.d_ptr;
```
* l_info 定義如下:
```c=
ElfW(Dyn) *l_info[DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM
+ DT_EXTRANUM + DT_VALNUM + DT_ADDRNUM];
```
* ElfW(Dyn) 展開後如下:
```c=
/* Dynamic section entry. */
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;
```
* 至於 index 的那些 Macro:
```c=
#define DT_STRTAB 5 /* Address of string table */
#define DT_SYMTAB 6 /* Address of symbol table */
#define DT_JMPREL 23 /* Address of PLT relocs */
#define DT_NUM 35 /* Number used */
#define DT_VERSYM 0x6ffffff0
```
* Macro VERSYMIDX:
```c=
#define VERSYMIDX(sym) (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX (sym))
#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */
#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */
#define DT_THISPROCNUM 0
```
* 此 macro 能處理 relocation 問題
## Macro DL_FIXUP_MAKE_VALUE
* https://elixir.bootlin.com/glibc/glibc-2.31/source/sysdeps/generic/dl-lookupcfg.h#L23
```c=
/* Construct a value of type DL_FIXUP_VALUE_TYPE from a code address
and a link map. */
#define DL_FIXUP_MAKE_VALUE(map, addr) (addr)
```
## Macro SYMBOL_ADDRESS
* https://elixir.bootlin.com/glibc/glibc-2.31/source/sysdeps/generic/ldsodefs.h#L90
```c=
#define LOOKUP_VALUE_ADDRESS(map, set) ((set) || (map) ? (map)->l_addr : 0)
#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)
```