mirror of https://github.com/pwndbg/pwndbg
119 lines
3.0 KiB
Python
119 lines
3.0 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
Helpers for finding address mappings which are used as
|
|
a stack.
|
|
|
|
Generally not needed, except under qemu-user and for when
|
|
binaries do things to remap the stack (e.g. pwnies' postit).
|
|
"""
|
|
|
|
import gdb
|
|
import pwndbg.events
|
|
import pwndbg.memoize
|
|
import pwndbg.memory
|
|
|
|
# Dictionary of stack ranges.
|
|
# Key is the gdb thread ptid
|
|
# Value is a pwndbg.memory.Page object
|
|
stacks = {}
|
|
|
|
# Whether the stack is protected by NX.
|
|
# This is updated automatically by is_executable.
|
|
nx = False
|
|
|
|
def find(address):
|
|
"""
|
|
Returns a pwndbg.memory.Page object which corresponds to the
|
|
currently-loaded stack.
|
|
"""
|
|
if not stacks:
|
|
update()
|
|
|
|
for stack in stacks:
|
|
if address in stack:
|
|
return stack
|
|
|
|
def find_upper_stack_boundary(addr, max_pages=1024):
|
|
addr = pwndbg.memory.page_align(int(addr))
|
|
try:
|
|
for i in range(max_pages):
|
|
data = pwndbg.memory.read(addr, 4)
|
|
if b'\x7fELF' == pwndbg.memory.read(addr, 4):
|
|
break
|
|
addr += pwndbg.memory.PAGE_SIZE
|
|
except gdb.MemoryError:
|
|
pass
|
|
return addr
|
|
|
|
@pwndbg.events.stop
|
|
@pwndbg.memoize.reset_on_stop
|
|
def update():
|
|
"""
|
|
For each running thread, updates the known address range
|
|
for its stack.
|
|
"""
|
|
curr_thread = gdb.selected_thread()
|
|
try:
|
|
for thread in gdb.selected_inferior().threads():
|
|
thread.switch()
|
|
sp = pwndbg.regs.sp
|
|
|
|
sp_low = sp & ~(0xfff)
|
|
|
|
# If we don't already know about this thread, create
|
|
# a new Page mapping for it.
|
|
page = stacks.get(thread.ptid, None)
|
|
if page is None:
|
|
start = sp_low
|
|
stop = find_upper_stack_boundary(sp)
|
|
page = pwndbg.memory.Page(start, stop-start, 6 if not is_executable() else 7, 0, '[stack]')
|
|
stacks[thread.ptid] = page
|
|
continue
|
|
elif page.objfile is None:
|
|
page.objfile = '[stack]'
|
|
|
|
# If we *DO* already know about this thread, just
|
|
# update the lower boundary if it got any lower.
|
|
low = min(page.vaddr, sp_low)
|
|
if low != page.vaddr:
|
|
page.memsz += (page.vaddr - low)
|
|
page.vaddr = low
|
|
finally:
|
|
curr_thread.switch()
|
|
|
|
|
|
@pwndbg.memoize.reset_on_stop
|
|
def current():
|
|
"""
|
|
Returns the bounds for the stack for the current thread.
|
|
"""
|
|
return find(pwndbg.regs.sp)
|
|
|
|
@pwndbg.events.exit
|
|
def clear():
|
|
"""
|
|
Clears everything we know about any stack memory ranges.
|
|
|
|
Called when the target process exits.
|
|
"""
|
|
stacks.clear()
|
|
global nx
|
|
nx = False
|
|
|
|
@pwndbg.events.stop
|
|
@pwndbg.memoize.reset_on_exit
|
|
def is_executable():
|
|
global nx
|
|
nx = False
|
|
|
|
PT_GNU_STACK = 0x6474e551
|
|
ehdr = pwndbg.elf.exe()
|
|
|
|
for phdr in pwndbg.elf.iter_phdrs(ehdr):
|
|
p_type = int(phdr['p_type'])
|
|
if p_type == PT_GNU_STACK:
|
|
nx = True
|
|
|
|
return not nx
|