Fix tcache and support it on targets w/o -lpthread (#552)

* Fix tcache and support it on targets w/o -lpthread

Short summary:
* fixes tcache having wrong address
* adds heuristic to retrieve tcache address when binary is compiled w/o
-lpthread (may not work on glibc's other than 2.27)
* fixes `pwndbg.symbol.address` as it could return offsets instead of
an address

---

Long description below.

This commit fixes tcache: we used the address of &tcache instead of
tcache for dereferencing the struct. This can be observed with:
```
pwndbg> p *tcache
$8 = {
  counts = '\000' <repeats 63 times>,
  entries = {0x0 <repeats 64 times>}
}
pwndbg> tcache
{
  counts = "\020`uUUU\000\000\000\000\000\000\000\000\000\000"...,
  entries = {0x0, 0x0, 0x7ffff7fd7740, 0x7ffff7fd80a0, 0x7ffff7fd7740, 0x1, 0x0, 0x7025de0aec8a0300, 0x236a7550e4a6104e, 0x0 <repeats 55 times>}
}
```

It also adds possibility to retrieve tcache information from targets
that are compiled without -lpthread [-pthread].

**NOTE: This is experimental and may not work across different glibc
versions. It was tested on Ubuntu 18.04 on 2.27 glibc.**

This is because we get tcache pointer by making an assumption that it
will lie 0x10 bytes before one of the addresses that points to
&main_arena.

It also fixes `pwndbg.symbol.address`'s `info address` path when it
returned addresses that were out of memory maps due to the fact GDB may
return a string containing an offset instead of an address. E.g.:
```
pwndbg> info address tcache
Symbol "tcache" is a thread-local variable at offset 0x40 in the thread-local storage for `/lib/x86_64-linux-gnu/libc.so.6'.
```

* Fix tcache retrieval heuristic
pull/554/head
Disconnect3d 7 years ago committed by GitHub
parent 631c932731
commit 8f33ec480f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -138,11 +138,33 @@ class Heap(pwndbg.heap.heap.BaseHeap):
def has_tcache(self):
return (self.mp and 'tcache_bins' in self.mp.type.keys() and self.mp['tcache_bins'])
def _fetch_tcache_addr(self):
"""
As of Ubuntu 18.04 and glibc 2.27 the tcache_perthread_struct* tcache
is located 0x10 bytes after the heap page, so we just return it here.
pwndbg> p tcache
$1 = (tcache_perthread_struct *) 0x555555756010
pwndbg> vmmap 0x555555756010
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
0x555555756000 0x555555777000 rw-p 21000 0 [heap]
"""
return self.get_heap_boundaries().vaddr + 0x10
@property
def thread_cache(self):
tcache_addr = pwndbg.symbol.address('tcache')
# The symbol.address returns ptr to ptr to tcache struct, as in:
# pwndbg> p &tcache
# $1 = (tcache_perthread_struct **) 0x7ffff7fd76f0
# so we need to dereference it
if tcache_addr is not None:
tcache_addr = pwndbg.memory.pvoid(tcache_addr)
if tcache_addr is None:
tcache_addr = self._fetch_tcache_addr()
if tcache_addr is not None:
try:
self._thread_cache = pwndbg.memory.poi(self.tcache_perthread_struct, tcache_addr)

@ -211,8 +211,18 @@ def address(symbol):
try:
result = gdb.execute('info address %s' % symbol, to_string=True, from_tty=False)
address = re.search('0x[0-9a-fA-F]+', result).group()
return int(address, 0)
address = int(re.search('0x[0-9a-fA-F]+', result).group(), 0)
# The address found should lie in one of the memory maps
# There are cases when GDB shows offsets e.g.:
# pwndbg> info address tcache
# Symbol "tcache" is a thread-local variable at offset 0x40
# in the thread-local storage for `/lib/x86_64-linux-gnu/libc.so.6'.
if not pwndbg.vmmap.find(address):
return None
return address
except gdb.error:
return None

Loading…
Cancel
Save