[ctf wiki pwn] stackoverflow: ret2dlresolve Series 1 (_dl_runtime_resolve glibc source code analysis and practice)

Posted by tnkannan on Sun, 16 Jan 2022 00:33:45 +0100

1 _dl_runtime_resolve entry

_ dl_runtime_resolve is implemented by assembly in glibc, in which the 32-bit entry point is / sysdeps / i386 / dl trampoline S. 64 bit entry point in / sysps_ 64/dl-trampoline. S. This paper mainly analyzes the 32-bit source code, version 2.23.
From glibc online source website https://elixir.bootlin.com/glibc/glibc-2.23/source/sysdeps/i386/dl-trampoline.S You can see the assembly code of the file:

	.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

Call through L16 in assembly code_ dl_fixup performs the main resolver functions. At / sysdeps / i386 / dl machine H to view functions_ dl_ Prototype of fixup:

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

The function has two parameters, corresponding to the two movl instructions of L14 and L15. One is the link stored in the eax register_ Map, edx is the index value about PLT relocation in the GOT table.

2 _dl_fixup function

2.1 function source code

The location of this function is in / 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 begins with a comment:

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

A general interpretation:

When each PLT entry is called for the first time, this function will be called through a * * trampoline mechanism (get table points to the next instruction of PLT, push plt index, and turn to the public PLT table entry) * *_ dl_fixup. At this time, all you need to do is to relocate a given shared library (such as libc.so) and return the real address in the shared library. The program uses the relocated address and executes the original call in the library. When this function is called later, it will be directly located in the shared library.

Whether 32-bit or 64 bit, this function will be used to realize the search and relocation operations. Specifically, some macro definition operations are used to distinguish the bit width. This can be seen through the relevant code of the macro definition of the second parameter:

/* 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_ Two parameters of fixup

Next, let's take a look at the two parameters corresponding to the function. The first is struct link_map *l, the second is ElfW(Word) reloc_arg.

2.2.1 parameter 1 link_map

The first parameter is in / 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;
    
    ...

  };

Due to the limited space, some codes are omitted. The following is a brief interpretation:

  • link_map is a structure used to describe a loadable shared target file (. so), l_next and l_prev is used to connect all the data loaded when the program starts so, the structure is a bidirectional linked list, which is mainly used for run-time dynamic linker. To put it bluntly, a link_map corresponds to one so, when multiple are loaded so, you can make it a linked list through the pointer.
  • l_ Yes The first address of dynamic security, which can be obtained through this structure dynsym section dynstr section rel. The address of PLT section can also obtain some values related to dynamic links.

2.2.2 parameter 2 reloc_arg

The second parameter is reloc_arg, i.e. relocation index. Reloc is used in the function_ Offset. It can be seen from the following macro definition that the two values are the same:

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

This value is also an index value push ed into the corresponding plt table entry in the plt section in the binary program:

2.3 _ dl_ Implementation process of fixup

link_map is mainly used to obtain the location of the symbol table in the dynamic link library_ A member in the map points to a dynamic segment, which contains the information required for various dynamic links.

Get symbol table symtab

  const ElfW(Sym) *const symtab
    = (const void *) D_PTR (l, l_info[DT_SYMTAB]);

_ dl_ The flow chart of fixup function is as follows:

As can be seen from the figure, the core function of implementing search is_ dl_lookup_symbol_x. Before executing this function, you are preparing the required data structures, which have links_ Map's symbol table dynsym (symtab), string table dynstr (strtab), relocation table rel PLT (reloc), and relocation index reloc_offset(reloc_arg).
Because_ dl_lookup_symbol_x is based on these data structures. Therefore, it is necessary to deeply analyze the relationship between these data structures and data structures.

3 main data structure and relationship analysis (analysis from examples)

3.1 main data structure

It is analyzed with the no relro 32-bit program (2015 xdctf pwn200) in ret2dlresolve in wiki.
Use the - d option of readelf to view The contents of the dynamic section area (. Dynamic is unique to the dynamic link executable file and contains some information necessary for the dynamic linker):

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

Jmrel segment corresponds to rel.plt section is used to save the runtime relocation table.

ps: the section in ELF is mainly used by Linker, while the segment is used by Loader, which Linker needs to care about text, .rel.text, .data, .rodata, etc. the key is that Linker needs to relocate. The Loader only needs to know the properties of Read/Write/Execute.

.rel.plt and rel.dyn is similar, except rel.plt is used for function relocation rel.dyn is used for variable relocation. Next, use the - r option of readelf to view information related to relocation:

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

As you can see rel.plt contains five entries. Yes The value of PLTRELSZ in dynamic section is 40 bytes, i.e REL. Total size of PLT; PLTREL indicates that the type of these entries is REL; RELENT indicates the size of each REL type entry, 8 bytes. So 40 / 8 = 5 is the number of entries.
From plt section can also be seen plt and rel.plt corresponds to:

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>

The type of these entries is Elf32_Rel, as defined below

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)

We're talking about it rel.plt the second item, the read entry, as an example, to compare the results displayed by the debugger rel. The first address of PLT is 0x8048304, and the second one is 0x8048304+8=0x804830c. gdb prints the read entry here:

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

It can be seen that 0x080498c8 of the first four bytes is just the offset of read entry (r_offset in Elf32_Rel structure); 0x00000207 is Info (r_info in Elf32_Rel structure), R_ The last byte of Info indicates the type (0x07, corresponding to R_386_JUMP_SLOT). The first three bytes are the symbol index(0x000002), that is, the second symbol (the first is setbuf).
Seeing this, some people may ask a question: each entry in readelf -r is 8 bytes and has been occupied by the first two fields, so why is sym displayed Value and sym What about the two superfluous information?
Answer: this information is found through the symbol index. Specifically SYMTAB in dynamic section, i.e dynsym section saves the relevant symbol information. The size of each symbol message is reflected in symbol, which is 16 bytes.
The symbol table is defined as:

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

This information can be printed by 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. Note that we only look here dynsym, because it is required by the runtime. Symbol information such as export/import is all here. And symtab is the symbol information at compile time, which will be deleted after strip.
2,. The dynsym section stores the dynamic symbol information imported from the shared library. The section is saved in the text section, and the section type is marked as SHT_DYNSYM.

You can see that the symbol information entry index of the read function mentioned earlier is indeed 2. Let's look at the actual content through the debugger The address of dynsym section is 0x80481ac, and the address of index value 2 is 0x80481ac+0x10*2 = 0x80481cc. The result printed by gdb is:

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

Compare Elf32_ These values can be obtained from the definition of sym:

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

st_shndx is the index (section number) of the section defining the symbol in the section header table. The value is 0x0012. It is in doubt and needs to be solved.

st_name saves the symbol in STRTAB, that is The address in dynstr, the first address of STRTAB section is 0x804824c, and the result printed in gdb is:

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

3.2 threading (_dl_runtime_resolve/_dl_fixup)

After understanding the above series of data structures, use_ dl_ runtime_ resolve/_ dl_ The fixup process concatenates these contents.

Please follow the process of 1-7.

4. Passed dynamic finds the address of a specific section

The first address of. dynamic can be found through readelf -S. after determining the first address, the addresses of other parts can be found through index.
link_ Reference in. Map structure The codes defined for dynamic locations are:

    /* 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_ The address of info [0] is The first address of dynamic is what we are concerned about dynstr,. dynsym,. rel.plt is in DT_ Within num, each element of the array is Elf32 as follows_ Dyn structure:

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 structure occupies 8 bytes. The first four bytes are of type and the last four bytes are integer values or addresses. Different section s have different indexes:

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

To find The dynstr section address is saved in Take the position of dynamic segment as an example, if The first address of dynamic is a, because Dynstr has an index of 5, so The address of dynstr is saved in The position of dynamic section is a+5*8+4=a+0x2c.

Topics: CTF pwn