Skip to content

Commit

Permalink
elf.py: optimize get_ehdr
Browse files Browse the repository at this point in the history
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...)
  • Loading branch information
disconnect3d committed Aug 21, 2021
1 parent 66d5d6c commit dc0e1f4
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 54 deletions.
65 changes: 20 additions & 45 deletions pwndbg/elf.py
Expand Up @@ -228,57 +228,32 @@ def reset_ehdr_type_loaded():
ehdr_type_loaded = 0


@pwndbg.abi.LinuxOnly()
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
def get_ehdr(pointer):
"""
addr = pwndbg.memory.page_align(pointer)
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
Returns an ehdr object for the ELF pointer points into.
# Return the address if found ELF header
if data == b'\x7FELF':
return addr

addr += step
We expect the `pointer` to be an address from the binary.
"""
vmmap = pwndbg.vmmap.find(pointer)
base = None

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):
"""Returns an ehdr object for the ELF pointer points into.
"""
# Align down to a page boundary, and scan until we find
# the ELF header.
base = pwndbg.memory.page_align(pointer)
if pwndbg.memory.read(vmmap.start, 4) == b'\x7fELF':
base = vmmap.start

# 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:
# For non linux ABI, the ELF header may not exist at all
if pwndbg.abi.linux:
print("ERROR: Could not find ELF base!")
return None, None
Expand All @@ -287,7 +262,7 @@ def get_ehdr(pointer):
ei_class = pwndbg.memory.byte(base+4)

# Find out where the section headers start
Elfhdr = read(Ehdr, base)
Elfhdr = read(Ehdr, base)
return ei_class, Elfhdr


Expand Down
11 changes: 9 additions & 2 deletions pwndbg/memory.py
Expand Up @@ -333,8 +333,12 @@ def find_upper_boundary(addr, max_pages=1024):
# import sys
# sys.stdout.write(hex(addr) + '\n')
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:
break
return pwndbg.arch.ptrmask
except gdb.MemoryError:
pass
return addr
Expand All @@ -352,8 +356,11 @@ def find_lower_boundary(addr, max_pages=1024):
for i in range(max_pages):
pwndbg.memory.read(addr, 1)
addr -= pwndbg.memory.PAGE_SIZE

# Sanity check (see comment in find_upper_boundary)
if addr < 0:
break
return 0

except gdb.MemoryError:
addr += pwndbg.memory.PAGE_SIZE
return addr
Expand Down
11 changes: 4 additions & 7 deletions pwndbg/stack.py
Expand Up @@ -38,18 +38,15 @@ def find(address):
return stack


def find_upper_stack_boundary(addr, max_pages=1024):
addr = pwndbg.memory.page_align(int(addr))
def find_upper_stack_boundary(stack_ptr, max_pages=1024):
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,
# so we return current page as a walkaround.
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
# 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)
return pwndbg.memory.find_upper_boundary(stack_ptr, max_pages)


@pwndbg.events.stop
Expand Down

0 comments on commit dc0e1f4

Please sign in to comment.