pwnlib.dynelf — 通过内存泄漏解析远程函数地址

解析加载到内存中的动态链接的 ELF 二进制文件中的符号 需要提供一个可以泄漏任意内存的函数 之后任何被动态加载的库的任何符号都可以被解析

例子

# 假设现在有一个进程或者远程连接
p = process('./pwnme')

# 声明一个函数, 这个函数只接收一个参数, 作为要被泄漏的内存地址
# 并且至少泄漏这个地址的一个字节的数据
def leak(address):
    data = p.read(address, 4)
    log.debug("%#x => %s" % (address, (data or '').encode('hex')))
    return data

# 为了便于说明这个例子
# 假设我们有任意多的指针
# 一个指针指向二进制程序的某一个符号的指针
# 另两个指针指向 libc
main   = 0xfeedf4ce
libc   = 0xdeadb000
system = 0xdeadbeef

# 使用我们的泄漏器, 和一个指向二进制程序的某一个符号的指针
# 我们就能泄漏这个内存地址的任意数据
# 事实上为了解析符号, 我们都不需要对目标二进制进行拷贝
d = DynELF(leak, main)
assert d.lookup(None,     'libc') == libc
assert d.lookup('system', 'libc') == system

# However, if we *do* have a copy of the target binary,

# 然而, 如果我们拷贝了这个二进制程序
# 这一步的速度就会快一点
d = DynELF(leak, main, elf=ELF('./pwnme'))
assert d.lookup(None,     'libc') == libc
assert d.lookup('system', 'libc') == system

# 我们也可以解析别的 lib 中的符号
# 然后返回这个符号的指针
d = DynELF(leak, libc + 0x1234)
assert d.lookup('system')      == system

DynELF

class pwnlib.dynelf.DynELF(leak, pointer=None, elf=None, libcdb=True)[源代码]

DynELF 知道如何在一个远程程序中通过信息泄漏或者内存泄漏来解析符号, 得益于 pwnlib.memleak.MemLeak 实现细节:

解析函数:

在所有那些导出自己的符号以便与被别的 lib 导入的二进制程序 (例如: libc.so )中, 有一系列用于保存符号名称的表, 并且会将这些符号进行哈希。 通过实现相同的哈希函数, 我们就可以知道符号名称 (例如: printf) 哈希的结果 然后我们就可以从哈希表中找到他们, 它在哈希表中位置会提供一个字符串表的索引(strtab)以及符号的地址(symtab)

Assuming we have the base address of libc.so, the way to resolve 假设我们已经得到了 libc.so 在虚拟内存中的基地址 为了进一步得到 printf, 我们需要定位 symtabstrtab 以及哈希表 "printf" 这个字符串已经被根据不同的方式(SYSV 或者 GNU)哈希过并且存储在哈希表中 然后只需要遍历哈希表直到我们找到匹配的入口 我们可以通过检查字符串表来验证这是绝对正确的, 检查字符串表, 然后就可以得到 symtablibc.so 中的偏移

解析 lib 地址:

如果我们有一个指向动态链接的可执行程序的指针, 我们就能得到一个叫做 ``link map``_ 的内部的链表结构 这个数据结构是一个链表, 并且它包含了所有被加载的 lib 的信息, 包括完整的路径以及内存地址

指向 lib map 的指针可以通过两种方法找到 两者都是从 DYNAMIC 数组中的条目引用的

  • 在没有开启 RELRO 的二进制程序中, 在 .got.plt 这个段会存在一个位置放置这个指针 那么问题就变成了在二进制程序中寻找 _DT_PLTGOT_ 了
  • 在所有的二进制文件中, 这个指针会保存在 _DT_DEBUG_ 的地方, 就算是被去除符号的二进制程序中也是这样

为了保持最大的灵活性, 这两种方式都会被彻底地使用

Instantiates an object which can resolve symbols in a running binary given a pwnlib.memleak.MemLeak leaker and a pointer inside the binary.

参数:
  • leak (MemLeak) – Instance of pwnlib.memleak.MemLeak for leaking memory
  • pointer (int) – A pointer into a loaded ELF file
  • elf (str,ELF) – Path to the ELF file on disk, or a loaded pwnlib.elf.ELF.
  • libcdb (bool) – Attempt to use libcdb to speed up libc lookups
bases()[源代码]

Resolve base addresses of all loaded libraries.

Return a dictionary mapping library path to its base address.

static find_base(leak, ptr)[源代码]

Given a pwnlib.memleak.MemLeak object and a pointer into a library, find its base address.

heap()[源代码]

Finds the beginning of the heap via __curbrk, which is an exported symbol in the linker, which points to the current brk.

lookup(symb = None, lib = None) → int[源代码]

Find the address of symbol, which is found in lib.

参数:
  • symb (str) – Named routine to look up If omitted, the base address of the library will be returned.
  • lib (str) – Substring to match for the library name. If omitted, the current library is searched. If set to 'libc', 'libc.so' is assumed.
返回:

Address of the named symbol, or None.

stack()[源代码]

Finds a pointer to the stack via __environ, which is an exported symbol in libc, which points to the environment block.

dynamic[源代码]

Returns – Pointer to the .DYNAMIC area.

elfclass[源代码]

32 or 64

elftype[源代码]

e_type from the elf header. In practice the value will almost always be ‘EXEC’ or ‘DYN’. If the value is architecture-specific (between ET_LOPROC and ET_HIPROC) or invalid, KeyError is raised.

libc[源代码]

Leak the Build ID of the remote libc.so, download the file, and load an ELF object with the correct base address.

返回:An ELF object, or None.

Pointer to the runtime link_map object

pwnlib.dynelf.gnu_hash(str) → int[源代码]

Function used to generated GNU-style hashes for strings. 该函数用来计算 GNU 风格的字符串哈希

pwnlib.dynelf.sysv_hash(str) → int[源代码]

该函数用来计算 SYSV 风格的字符串哈希