elf.py: optimize get_ehdr

Before this commit the `pwndbg.elf.get_ehdr(pointer)` function searched for the ELF header by iterating through memory pages (with a step of -4kB) until the ELF magic is found (b'\x7fELF' value).

This was most likely redundant and this commit optimized this logic to look at the begining of the page and if it doesn't have the ELF magic, to look at the first page of the given objfile. Actually, maybe there was one compelling argument to do it this way which is bare metal debugging where we don't have all vmmap info. However! This situation is likely broken anyway, so let's skip doing more work than needed.

Additionally, we refactor the `pwndbg.stack.find_upper_stack_boundary` function to use the `pwndbg.memory.find_upper_boundary` function instead of the `pwndbg.elf.find_elf_magic` function, as the last one was removed in this commit. (NOTE: this actually fixes a potential bug that we could have incorrectly set the upper stack boundary if it contained an ELF magic on the beginning of one of its 4kB pages...)
pull/951/head
Disconnect3d 4 years ago
parent 66d5d6cc51
commit dc0e1f419a

@ -228,57 +228,32 @@ def reset_ehdr_type_loaded():
ehdr_type_loaded = 0 ehdr_type_loaded = 0
@pwndbg.abi.LinuxOnly() def get_ehdr(pointer):
def find_elf_magic(pointer, max_pages=1024, search_down=False, ret_addr_anyway=False):
"""Search the nearest page which contains the ELF headers
by comparing the ELF magic with first 4 bytes.
Parameter:
search_down: change the search direction
to search over the lower address.
That is, decreasing the page pointer instead of increasing.
(default: False)
Returns:
An integer address of ELF page base
None if not found within the page limit
""" """
addr = pwndbg.memory.page_align(pointer) Returns an ehdr object for the ELF pointer points into.
step = pwndbg.memory.PAGE_SIZE
if search_down:
step = -step
max_addr = pwndbg.arch.ptrmask
for i in range(max_pages):
# Make sure address within valid range or gdb will raise Overflow exception
if addr < 0 or addr > max_addr:
return None
try:
data = pwndbg.memory.read(addr, 4)
except gdb.MemoryError:
return addr if ret_addr_anyway else None
# Return the address if found ELF header We expect the `pointer` to be an address from the binary.
if data == b'\x7FELF': """
return addr vmmap = pwndbg.vmmap.find(pointer)
base = None
addr += step
return addr if ret_addr_anyway else None # We first check if the begining of the page contains the ELF magic
if pwndbg.memory.read(vmmap.start, 4) == b'\x7fELF':
base = vmmap.start
# The page did not have ELF magic; it may be that .text and binary start are split
# into two pages, so let's get the first page from the pointer's page objfile
else:
for v in pwndbg.vmmap.get():
if v.objfile == vmmap.objfile:
vmmap = v
break
def get_ehdr(pointer): if pwndbg.memory.read(vmmap.start, 4) == b'\x7fELF':
"""Returns an ehdr object for the ELF pointer points into. base = vmmap.start
"""
# Align down to a page boundary, and scan until we find
# the ELF header.
base = pwndbg.memory.page_align(pointer)
# For non linux ABI, the ELF header may not be found in memory.
# This will hang the gdb when using the remote gdbserver to scan 1024 pages
base = find_elf_magic(pointer, search_down=True)
if base is None: if base is None:
# For non linux ABI, the ELF header may not exist at all
if pwndbg.abi.linux: if pwndbg.abi.linux:
print("ERROR: Could not find ELF base!") print("ERROR: Could not find ELF base!")
return None, None return None, None
@ -287,7 +262,7 @@ def get_ehdr(pointer):
ei_class = pwndbg.memory.byte(base+4) ei_class = pwndbg.memory.byte(base+4)
# Find out where the section headers start # Find out where the section headers start
Elfhdr = read(Ehdr, base) Elfhdr = read(Ehdr, base)
return ei_class, Elfhdr return ei_class, Elfhdr

@ -333,8 +333,12 @@ def find_upper_boundary(addr, max_pages=1024):
# import sys # import sys
# sys.stdout.write(hex(addr) + '\n') # sys.stdout.write(hex(addr) + '\n')
addr += pwndbg.memory.PAGE_SIZE addr += pwndbg.memory.PAGE_SIZE
# Sanity check in case a custom GDB server/stub
# incorrectly returns a result from read
# (this is most likely redundant, but its ok to keep it?)
if addr > pwndbg.arch.ptrmask: if addr > pwndbg.arch.ptrmask:
break return pwndbg.arch.ptrmask
except gdb.MemoryError: except gdb.MemoryError:
pass pass
return addr return addr
@ -352,8 +356,11 @@ def find_lower_boundary(addr, max_pages=1024):
for i in range(max_pages): for i in range(max_pages):
pwndbg.memory.read(addr, 1) pwndbg.memory.read(addr, 1)
addr -= pwndbg.memory.PAGE_SIZE addr -= pwndbg.memory.PAGE_SIZE
# Sanity check (see comment in find_upper_boundary)
if addr < 0: if addr < 0:
break return 0
except gdb.MemoryError: except gdb.MemoryError:
addr += pwndbg.memory.PAGE_SIZE addr += pwndbg.memory.PAGE_SIZE
return addr return addr

@ -38,18 +38,15 @@ def find(address):
return stack return stack
def find_upper_stack_boundary(addr, max_pages=1024): def find_upper_stack_boundary(stack_ptr, max_pages=1024):
addr = pwndbg.memory.page_align(int(addr)) stack_ptr = pwndbg.memory.page_align(int(stack_ptr))
# We can't get the stack size from stack layout and page fault on bare metal mode, # We can't get the stack size from stack layout and page fault on bare metal mode,
# so we return current page as a walkaround. # so we return current page as a walkaround.
if not pwndbg.abi.linux: if not pwndbg.abi.linux:
return addr + pwndbg.memory.PAGE_SIZE return stack_ptr + pwndbg.memory.PAGE_SIZE
# We don't want to find ELF magic here. We reuse the find_elf_magic func return pwndbg.memory.find_upper_boundary(stack_ptr, max_pages)
# as it traverses 4kB pages and can find the boundary for us
# (in other words, we expect the ELF magic to not be present on the stack)
return pwndbg.elf.find_elf_magic(addr, max_pages=max_pages, ret_addr_anyway=True)
@pwndbg.events.stop @pwndbg.events.stop

Loading…
Cancel
Save