[ctf wiki pwn] stackoverflow:ret2dlresolve系列2(_dl_runtime_resolve glibc源码解析实践)

Posted 漫小牛

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[ctf wiki pwn] stackoverflow:ret2dlresolve系列2(_dl_runtime_resolve glibc源码解析实践)相关的知识,希望对你有一定的参考价值。

1 _dl_runtime_resolve入口

_dl_runtime_resolve在glibc中是采用汇编实现的,其中32位的入口点在/sysdeps/i386/dl-trampoline.S,64位的入口点在/sysdeps/x86_64/dl-trampoline.S。本文主要分析32位源码,版本为2.23
从glibc在线源码网站https://elixir.bootlin.com/glibc/glibc-2.23/source/sysdeps/i386/dl-trampoline.S可看到该文件的汇编代码:

	.text
	.globl _dl_runtime_resolve
	.type _dl_runtime_resolve, @function
	cfi_startproc
	.align 16
_dl_runtime_resolve:
	cfi_adjust_cfa_offset (8)
	pushl %eax		# Preserve registers otherwise clobbered.
	cfi_adjust_cfa_offset (4)
	pushl %ecx
	cfi_adjust_cfa_offset (4)
	pushl %edx
	cfi_adjust_cfa_offset (4)
	movl 16(%esp), %edx	# Copy args pushed by PLT in register.  Note
	movl 12(%esp), %eax	# that `fixup' takes its parameters in regs.
	call _dl_fixup		# Call resolver.
	popl %edx		# Get register content back.
	cfi_adjust_cfa_offset (-4)
	movl (%esp), %ecx
	movl %eax, (%esp)	# Store the function address.
	movl 4(%esp), %eax
	ret $12			# Jump to function address.
	cfi_endproc
	.size _dl_runtime_resolve, .-_dl_runtime_resolve

汇编代码中通过L16的call _dl_fixup执行主要的resolver功能。在/sysdeps/i386/dl-machine.h可查看函数_dl_fixup的原型:

extern ElfW(Addr) _dl_fixup (struct link_map *l,
			     ElfW(Word) reloc_offset)

该函数共有两个参数,对应L14和L15的两条movl指令,一个是eax寄存器中存储的link_map,edx是GOT表中关于PLT重定位的索引值。

2 _dl_fixup函数

2.1 函数源码

该函数的位置在/elf/dl-runtime.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)
{
  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(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];
  void *const rel_addr = (void *)(l->l_addr + reloc->r_offset);
  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,
				    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,
				   sym ? (LOOKUP_VALUE_ADDRESS (result)
					  + sym->st_value) : 0);
    }
  else
    {
      /* We already found the symbol.  The module (and therefore its load
	 address) is also known.  */
      value = DL_FIXUP_MAKE_VALUE (l, l->l_addr + sym->st_value);
      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;

  return elf_machine_fixup_plt (l, result, reloc, rel_addr, value);
}

这个函数开始有一段儿注释:

/* 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. */

大致解读一下:

在每个PLT入口第一次被调用时,会通过一种**trampoline机制(got表指向plt的下一条指令,push plt的index后,转向公共的plt表项)**来调用这个函数_dl_fixup。这个时候,要做的就是针对给定共享库(如libc.so)的重定位操作,并返回共享库中的真实地址,程序使用重定位后的地址并执行库中original调用。以后再调用该函数时,就会直接定位到共享库中的位置。

不管是32位还是64位都会通过这个函数实现查找和重定位操作,具体通过一些宏定义操作来区分位宽。可通过第二个参数的宏定义的相关代码看到这一点:

/* 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)

2.2 _dl_fixup的两个参数

接下来,来看一下该函数对应的两个参数,第一个是struct link_map *l,第二个是ElfW(Word) reloc_arg。

2.2.1 参数1 link_map

第一个参数在/include/link.h

/* Structure describing a loaded shared object.  The `l_next' and `l_prev'
   members form a chain of all the shared objects loaded at startup.

   These data structures exist in space used by the run-time dynamic linker;
   modifying them may have disastrous results.

   This data structure might change in future, if necessary.  User-level
   programs must avoid defining objects of this type.  */

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.  */

    /* Array of DT_NEEDED dependencies and their dependencies, in
       dependency order for symbol lookup (with and without
       duplicates).  There is no entry before the dependencies have
       been loaded.  */
    struct r_scope_elem l_searchlist;

    /* We need a special searchlist to process objects marked with
       DT_SYMBOLIC.  */
    struct r_scope_elem l_symbolic_searchlist;
    
    ...

  };

篇幅有限省略了部分代码,下面简单解读一下:

  • link_map是一个用于描述可加载共享目标文件(.so)的结构,l_next和l_prev用于连接程序启动时加载的所有.so,该结构是一个双向链表,主要用于运行时动态链接器(run-time dynamic linker)。说得直白一点儿,一个link_map就对应一个.so,当加载多个.so时,可通过指针使之成为链表。
  • l_info是.dynamic seciton的首地址,可通过该结构获取.dynsym节、.dynstr节、.rel.plt节等的地址,也可取得一些与动态链接相关的一些值。

2.2.2 参数2 reloc_arg

第二个参数是reloc_arg,即重定位索引,函数中使用了reloc_offset,从如下的宏定义可看出两者的值相同:

#ifndef reloc_offset
# define reloc_offset reloc_arg
# define reloc_index  reloc_arg / sizeof (PLTREL)
#endif

这个值也是二进制程序中,plt节中相应plt表项push进去的一个索引值:
在这里插入图片描述

2.3 _dl_fixup的执行流程

_dl_fixup函数的流程图为:
在这里插入图片描述
从图中可知,实施查找的核心函数是_dl_lookup_symbol_x,执行该函数前是在准备所需的数据结构,这些数据结构有link_map的符号表dynsym(symtab)、字符串表dynstr(strtab)、重定位表rel.plt(reloc),以及重定位索引reloc_offset(reloc_arg)。
由于_dl_lookup_symbol_x是在这些数据结构的基础上进行的查找,因此,有必要深入分析这些数据结构和数据结构之间的关系。

3 主要数据结构及关系解析(从实例中分析)

3.1 主要数据结构

以wiki中ret2dlresolve中no-relro 32位程序(2015-xdctf-pwn200)进行分析。
使用readelf的-d选项查看.dynamic动态节区(.dynamic是动态链接可执行文件所特有的,包含了动态链接器所必须的一些信息)的内容:

giantbranch@ubuntu:~/Desktop/ret2dlresolve/2015-xdctf-pwn200/32/no-relro$ readelf -d main_no_relro_32 

Dynamic section at offset 0x7c4 contains 24 entries:
  Tag        Type                         Name/Value
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]
 0x0000000c (INIT)                       0x804832c
 0x0000000d (FINI)                       0x8048634
 0x00000019 (INIT_ARRAY)                 0x80497bc
 0x0000001b (INIT_ARRAYSZ)               4 (bytes)
 0x0000001a (FINI_ARRAY)                 0x80497c0
 0x0000001c (FINI_ARRAYSZ)               4 (bytes)
 0x6ffffef5 (GNU_HASH)                   0x804818c
 0x00000005 (STRTAB)                     0x804824c
 0x00000006 (SYMTAB)                     0x80481ac
 0x0000000a (STRSZ)                      107 (bytes)
 0x0000000b (SYMENT)                     16 (bytes)
 0x00000015 (DEBUG)                      0x0
 0x00000003 (PLTGOT)                     0x80498b8
 0x00000002 (PLTRELSZ)                   40 (bytes)
 0x00000014 (PLTREL)                     REL
 0x00000017 (JMPREL)                     0x8048304
 0x00000011 (REL)                        0x80482ec
 0x00000012 (RELSZ)                      24 (bytes)
 0x00000013 (RELENT)                     8 (bytes)
 0x6ffffffe (VERNEED)                    0x80482cc
 0x6fffffff (VERNEEDNUM)                 1
 0x6ffffff0 (VERSYM)                     0x80482b8
 0x00000000 (NULL)                       0x0

其中的JMPREL segment,对应于.rel.plt section,是用来保存运行时重定位表的。

ps:ELF中的section主要提供给Linker使用, 而segment提供给Loader用,Linker需要关心.text, .rel.text, .data, .rodata等等,关键是Linker需要做relocation。而Loader只需要知道Read/Write/Execute的属性。

.rel.plt与.rel.dyn类似,只不过.rel.plt是用于函数重定位,.rel.dyn是用于变量重定位。下面使用readelf的-r选项查看与重定位相关的信息:

giantbranch@ubuntu:~/Desktop/ret2dlresolve/2015-xdctf-pwn200/32/no-relro$ readelf -r main_no_relro_32 

Relocation section '.rel.dyn' at offset 0x2ec contains 3 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
080498ac  00000306 R_386_GLOB_DAT    00000000   __gmon_start__
080498b0  00000706 R_386_GLOB_DAT    00000000   stdin@GLIBC_2.0
080498b4  00000806 R_386_GLOB_DAT    00000000   stdout@GLIBC_2.0

Relocation section '.rel.plt' at offset 0x304 contains 5 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
080498c4  00000107 R_386_JUMP_SLOT   00000000   setbuf@GLIBC_2.0
080498c8  00000207 R_386_JUMP_SLOT   00000000   read@GLIBC_2.0
080498cc  00000407 R_386_JUMP_SLOT   00000000   strlen@GLIBC_2.0
080498d0  00000507 R_386_JUMP_SLOT   00000000   __libc_start_main@GLIBC_2.0
080498d4  00000607 R_386_JUMP_SLOT   00000000   write@GLIBC_2.0

可以看到,.rel.plt里包含5个条目。在.dynamic section中的PLTRELSZ的值为40字节,即.rel.plt的总大小;PLTREL则指明这些条目的类型为REL;RELENT指明了每个REL类型条目的大小,8bytes。于是40/8=5即为条目个数。
从.plt section也可以看到,.plt与.rel.plt相对应:

Disassembly of section .plt:

08048350 <setbuf@plt-0x10>:
 8048350:       ff 35 bc 98 04 08       push   DWORD PTR ds:0x80498bc
 8048356:       ff 25 c0 98 04 08       jmp    DWORD PTR ds:0x80498c0
 804835c:       00 00                   add    BYTE PTR [eax],al
        ...

08048360 <setbuf@plt>:
 8048360:       ff 25 c4 98 04 08       jmp    DWORD PTR ds:0x80498c4
 8048366:       68 00 00 00 00          push   0x0
 804836b:       e9 e0 ff ff ff          jmp    8048350 <_init+0x24>

08048370 <read@plt>:
 8048370:       ff 25 c8 98 04 08       jmp    DWORD PTR ds:0x80498c8
 8048376:       68 08 00 00 00          push   0x8
 804837b:       e9 d0 ff ff ff          jmp    8048350 <_init+0x24>

08048380 <strlen@plt>:
 8048380:       ff 25 cc 98 04 08       jmp    DWORD PTR ds:0x80498cc
 8048386:       68 10 00 00 00          push   0x10
 804838b:       e9 c0 ff ff ff          jmp    8048350 <_init+0x24>

08048390 <__libc_start_main@plt>:
 8048390:       ff 25 d0 98 04 08       jmp    DWORD PTR ds:0x80498d0
 8048396:       68 18 00 00 00          push   0x18
 804839b:       e9 b0 ff ff ff          jmp    8048350 <_init+0x24>

080483a0 <write@plt>:
 80483a0:       ff 25 d4 98 04 08       jmp    DWORD PTR ds:0x80498d4
 80483a6:       68 20 00 00 00          push   0x20
 80483ab:       e9 a0 ff ff ff          jmp    8048350 <_init+0x24>

这些条目的类型是Elf32_Rel,其定义如下

typedef uint32_t Elf32_Addr;
typedef uint32_t Elf32_Word;
typedef struct
{
  Elf32_Addr    r_offset;               /* Address */
  Elf32_Word    r_info;                 /* Relocation type and symbol index */
} Elf32_Rel;
#define ELF32_R_SYM(val) ((val) >> 8) #define ELF32_R_TYPE(val) ((val) & 0xff)

我们以.rel.plt第二条,即read的条目为例,来对比调试器显示的结果。.rel.plt的首地址为0x8048304,第二条为0x8048304+8=0x804830c,gdb打印出该处的read条目:

gdb-peda$ x/2x 0x804830c
0x804830c:	0x080498c8	0x00000207

可看到,前4个字节的0x080498c8刚好是read entry的offset(Elf32_Rel结构中的r_offset);0x00000207为Info(Elf32_Rel结构中的r_info),r_info的最后一个字节表示类型(0x07,对应R_386_JUMP_SLOT),前三个字节为符号的索引symbol index(0x000002),也就是第二个符号(第一个是setbuf)。
看到这里,有些人可能会提出一个疑问:readelf -r中的每个条目是8个字节,已经被前两个字段占满了,那么为什么会显示出Sym.Value和Sym. Name两项多余的信息呢?
解答:这些信息正是通过symbol index找到的,具体的,.dynamic section中的SYMTAB,即.dynsym section,保存的便是相关的符号信息。每一条symbol信息的大小在SYMENT中体现,为16 bytes。

符号表的定义为:

/* Symbol table entry.  */

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;

可通过readelf -s来打印出这些信息:

giantbranch@ubuntu:~/Desktop/ret2dlresolve/2015-xdctf-pwn200/32/no-relro$ readelf -s main_no_relro_32 

Symbol table '.dynsym' contains 10 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 00000000     0 FUNC    GLOBAL DEFAULT  UND setbuf@GLIBC_2.0 (2)
     2: 00000000     0 FUNC    GLOBAL DEFAULT  UND read@GLIBC_2.0 (2)
     3: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     4: 00000000     0 FUNC    GLOBAL DEFAULT  UND strlen@GLIBC_2.0 (2)
     5: 00000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.0 (2)
     6: 00000000     0 FUNC    GLOBAL DEFAULT  UND write@GLIBC_2.0 (2)
     7: 00000000     0 OBJECT  GLOBAL DEFAULT  UND stdin@GLIBC_2.0 (2)
     8: 00000000     0 OBJECT  GLOBAL DEFAULT  UND stdout@GLIBC_2.0 (2)
     9: 0804864c     4 OBJECT  GLOBAL DEFAULT   16 _IO_stdin_used

Symbol table '.symtab' contains 70 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
...

ps:
1、注意我们这里只看.dynsym,因为它是运行时所需的。诸如export/import的符号信息全在这里。而.symtab是编译时的符号信息,这部分在strip之后会被删除掉。
2、.dynsym节保存了从共享库导入的动态符号信息,该节保存在text段中,节类型被标记为SHT_DYNSYM。

可以看到,之前所说的read函数的符号信息条目index确实为2。我们通过调试器来看看其实际内容。.dynsym节的地址为0x80481ac,index值为2的地址为0x80481ac+0x10*2 = 0x80481cc。gdb打印的结果为:

gdb-peda$ x/4x 0x80481cc
0x80481d8:	0x00000012	0x0000005c	0x00000000	0x00000000

对比Elf32_Sym的定义,可以得到这些值:

st_name = 0x00000027;
st_value = 0x00000000;
st_size = 0x00000000;
st_info = 0x00;
st_other = 0x00;
st_shndx = 0x0012; 

st_shndx 是定义该符号的 section 在 section header table 中的索引 (section number),该值为0x0012,目前存疑,待解决。

st_name保存的是该符号在STRTAB,即.dynstr中的地址,STRTAB section的首地址为0x804824c,gdb中打印的结果为:

gdb-peda$ x/s 0x804824c+0x27
0x8048273:	"read"

3.2 穿针引线(_dl_runtime_resolve/_dl_fixup)

在搞清楚上面一系列数据结构后,利用_dl_runtime_resolve/_dl_fixup的过程把这些内容串起来。
在这里插入图片描述

请读者按1-7的流程自己走一遍。

4 通过.dynamic查找特定节的地址

.dynamic的首地址可通过readelf -S查找,确定首地址后,其他各个部分的地址通过索引来查找。
link_map结构中引用.dynamic各个位置的定义的代码为:

    /* 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];

l_info[0]的地址就是.dynamic的首地址,我们所关注的.dynstr,.dynsym,.rel.plt均在DT_NUM以内,数组的每个元素为如下的Elf32_Dyn结构:

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;

Elf32_Dyn结构占用8个字节,前四个字节为类型,后四个字节为整数值或地址。不同的section具有不同的索引:

/* Legal values for d_tag (dynamic entry type).  */

#define DT_NULL		0		/* Marks end of dynamic section */
#define DT_NEEDED	1		/* Name of needed library */
#define DT_PLTRELSZ	2		/* Size in bytes of PLT relocs */
#define DT_PLTGOT	3		/* Processor defined value */
#define DT_HASH		4		/* Address of symbol hash table */
#define DT_STRTAB	5		/* Address of string table */
#define DT_SYMTAB	6		/* Address of symbol table */
#define DT_RELA		7		/* Address of Rela relocs */
#define DT_RELASZ	8		/* Total size of Rela relocs */
#define DT_RELAENT	9		/* Size of one Rela reloc */
#define DT_STRSZ	10		/* Size of string table */
#define DT_SYMENT	11		/* Size of one symbol table entry */
#define DT_INIT		12		/* Address of init function */
#define DT_FINI		13		/* Address of termination function */
#define DT_SONAME	14		/* Name of shared object */
#define DT_RPATH	15		/* Library search path (deprecated) */
#define DT_SYMBOLIC	16		/* Start symbol search here */
#define DT_REL		17		/* Address of Rel relocs */
#define DT_RELSZ	18		/* Total size of Rel relocs */
#define DT_RELENT	19		/* Size of one Rel reloc */
#define DT_PLTREL	20		/* Type of reloc in PLT */
#define DT_DEBUG	21		/* For debugging; unspecified */
#define DT_TEXTREL	22		/* Reloc might modify .text */
#define DT_JMPREL	23		/* Address of PLT relocs */
#define	DT_BIND_NOW	24		/* Process relocations of object */
#define	DT_INIT_ARRAY	25		/* Array with addresses of init fct */
#define	DT_FINI_ARRAY	26		/* Array with addresses of fini fct */
#define	DT_INIT_ARRAYSZ	27		/* Size in bytes of DT_INIT_ARRAY */
#define	DT_FINI_ARRAYSZ	28		/* Size in bytes of DT_FINI_ARRAY */
#define DT_RUNPATH	29		/* Library search path */
#define DT_FLAGS	30		/* Flags for the object being loaded */
#define DT_ENCODING	32		/* Start of encoded range */
#define DT_PREINIT_ARRAY 32		/* Array with addresses of preinit fct*/
#define DT_PREINIT_ARRAYSZ 33		/* size in bytes of DT_PREINIT_ARRAY */
#define	DT_NUM		34		/* Number used */
...

以查找.dynstr section地址保存在.dynamic段的位置为例,若.dynamic的首地址为a,由于.dynstr的索引为5,因此.dynstr的地址保存在.dynamic段的位置为a+5*8+4=a+0x2c。

以上是关于[ctf wiki pwn] stackoverflow:ret2dlresolve系列2(_dl_runtime_resolve glibc源码解析实践)的主要内容,如果未能解决你的问题,请参考以下文章

[ctf wiki pwn] stackoverflow:hctf2016-brop wp

[ctf wiki pwn] stackoverflow:hctf2016-brop wp

[CTF Wiki Pwn]Stackoverflow Lab002: ret2shellcode

[CTF Wiki Pwn]Stackoverflow Lab003: ret2syscall

[CTF Wiki Pwn]Stackoverflow Lab001: ret2text

[CTF Wiki Pwn]Stackoverflow: ret2reg