通常找到function加载地址的方法是通过同一个libc中另一个function的offset计算出来,这需要远程的libc和我们的libc版本相同。或者我们也可以通过libcdb.com找到远程libc的方法,虽然这个方法有时候会失败。

DynELF

如果程序有办法leak memory我们就可以使用DynELF。正如文档介绍的那样,DynELF使用了两种技术:首先找到libc的base address,然后通过Symbol Table Section和the String Table Section遍历直到找出我们需要的function

下面我们就来看他是如何实现的

找到libc的Base Address

首先我们从进程中找出libc空间内的一个地址,我们可以从已经解析过的Global Offset Table中找到, Next we can parse through that address space using the leak with decrements of the memory page size (0x1000)直到我们找到\x7fELF字符串常量,它代表了载入的base address. 代码如下所示:

# leak func returns n number of bytes from given address
def findLibcBase(ptr):
   ptr &= 0xfffffffffffff000
   while leak(ptr, 4) != "\x7fELF":
      ptr -= 0x1000
   return ptr

找到Program Header

program header包括了一系列的Elf32_Phdr/Elf64_Phdr结构,每一个结构包含一个segments

通过0x1c for 32bit or 0x20 for 64bit的偏移,从ELF header中找到Program Header的位置,progrtam header的结构Elf32_Phdr包含了如下元素

typedef struct {
        Elf32_Word      p_type;
        Elf32_Off       p_offset;
        Elf32_Addr      p_vaddr;
        Elf32_Addr      p_paddr;
        Elf32_Word      p_filesz;
        Elf32_Word      p_memsz;
        Elf32_Word      p_flags;
        Elf32_Word      p_align;
} Elf32_Phdr;

代码如下所示

# addr argument is the module's base address or rather the beginning of ELF Header
def findPhdr(addr):
   if bits == 32:
      e_phoff = u32(leak(addr + 0x1c, wordSz).ljust(4, '\0'))
   else:
      e_phoff = u64(leak(addr + 0x20, wordSz).ljust(8, '\0'))
   return e_phoff + addr

找到DYNAMIC Section

下一步我们要从Elf32_Phdr结构体中找到DYNAMIC Section,我们可以通过Elf32_Phdr->p_type == 2来找到它,其中Elf32_Phdr->p_vaddr就是DYNAMIC Section的地址

查找DYNAMIC Section的代码如下

def findDynamic(Elf32_Phdr, bitSz):
   if bitSz == 32:
      i = -32
      p_type = 0
      while p_type != 2:
         i += 32
         p_type = u32(leak(Elf32_Phdr + i, wordSz).ljust(4, '\0'))
      return u32(leak(Elf32_Phdr + i + 8, wordSz).ljust(4, '\0'))    # + PIE
   else:
      i = -56
      p_type = 0
      while p_type != 2:
         i += 56
         p_type = u64(leak(Elf32_Phdr + i, hwordSz).ljust(8, '\0'))
      return u64(leak(Elf32_Phdr + i + 16, wordSz).ljust(8, '\0'))   # + PIE

对于PIE (Possition Independent Executable) ,p_vaddr是一个offset,对于non-PIE binaries,p_vaddr是一个虚拟绝对地址

DYNAMIC Section包含了许多个Elf32_Dyn/Elf64_Dyn结构,Each structure contains information about section tables participating in the dynamic linking process. Some of them include DT_GOTPLT, DT_HASH, DT_STRTAB, DT_SYMTAB, DT_DEBUG any many more.

对于这个dynamic section tables,我们感兴趣的是DT_SYMTAB 或者称作 Symbol Table 和 DT_STRTAB 或者称作 String Table.

找出DT_SYMTAB 和 DT_STRTAB

Symbol Table包含了许多Elf32_Sym/Elf64_Sym这个结构,function的地址可以从每个Elf32_Sym->st_value中找到

找到Function Addresses