---
# System prepended metadata

title: ret2dl_resolve Sections 關係表
tags: [security]

---

---
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)
```