Fix plt and gotplt commands (#1576)

* Fix plt and gotplt commands

* Add plt gotplt commands tests

* Fix got and plt commands and test them

* Revert accidental change

* Extend system path

* Hopefully fix PATH problems once and for all?

* fix import

* remove redundant part
pull/1586/head
Disconnect3d 3 years ago committed by GitHub
parent 871a440a50
commit ee832c80d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -38,11 +38,9 @@ RUN sed -i "s/^git submodule/#git submodule/" ./setup.sh && \
ADD ./setup-dev.sh /pwndbg/ ADD ./setup-dev.sh /pwndbg/
RUN ./setup-dev.sh RUN ./setup-dev.sh
RUN echo "source /pwndbg/gdbinit.py" >> ~/.gdbinit.py && \ RUN echo "source /pwndbg/gdbinit.py" >> ~/.gdbinit.py
echo "PYTHON_MINOR=$(python3 -c "import sys;print(sys.version_info.minor)")" >> /root/.bashrc && \
echo "PYTHON_PATH=\"/usr/local/lib/python3.${PYTHON_MINOR}/dist-packages/bin\"" >> /root/.bashrc && \
echo "export PATH=$PATH:$PYTHON_PATH" >> /root/.bashrc
ADD . /pwndbg/ ADD . /pwndbg/
RUN git submodule update --init --recursive RUN git submodule update --init --recursive

@ -1,11 +1,14 @@
import cProfile import cProfile
import glob import glob
import locale import locale
import os
import sys import sys
import time import time
from os import environ from os import environ
from os import path from os import path
import pkg_resources
_profiler = cProfile.Profile() _profiler = cProfile.Profile()
_start_time = None _start_time = None
@ -75,11 +78,15 @@ directory, file = path.split(__file__)
directory = path.expanduser(directory) directory = path.expanduser(directory)
directory = path.abspath(directory) directory = path.abspath(directory)
# Add gdb-pt-dump directory to sys.path so it can be imported
gdbpt = path.join(directory, "gdb-pt-dump") gdbpt = path.join(directory, "gdb-pt-dump")
sys.path.append(directory) sys.path.append(directory)
sys.path.append(gdbpt) sys.path.append(gdbpt)
# Add the dir where Pwntools binaries might be into PATH
pwntools_bin_dir = os.path.join(pkg_resources.get_distribution("pwntools").location, "bin")
os.environ["PATH"] = os.environ.get("PATH") + os.pathsep + pwntools_bin_dir
# warn if the user has different encoding than utf-8 # warn if the user has different encoding than utf-8
encoding = locale.getpreferredencoding() encoding = locale.getpreferredencoding()

@ -68,9 +68,22 @@ def print_symbols_in_section(section_name, filter_text="") -> None:
start, end = get_section_bounds(section_name) start, end = get_section_bounds(section_name)
if start is None: if start is None:
print(message.error("Could not find section")) print(message.error(f"Could not find section {section_name}"))
return return
elf_header = pwndbg.gdblib.elf.exe()
# If we started the binary and it has PIE, rebase it
if pwndbg.gdblib.proc.alive:
bin_base_addr = pwndbg.gdblib.proc.binary_base_addr
# Rebase the start and end addresses if needed
if start < bin_base_addr:
start += bin_base_addr
end += bin_base_addr
print(message.notice(f"Section {section_name} {start:#x}-{end:#x}:"))
symbols = get_symbols_in_region(start, end, filter_text) symbols = get_symbols_in_region(start, end, filter_text)
if not symbols: if not symbols:
@ -85,7 +98,7 @@ def get_symbols_in_region(start, end, filter_text=""):
ptr_size = pwndbg.gdblib.typeinfo.pvoid.sizeof ptr_size = pwndbg.gdblib.typeinfo.pvoid.sizeof
addr = start addr = start
while addr < end: while addr < end:
name = pwndbg.gdblib.symbol.get(addr) name = pwndbg.gdblib.symbol.get(addr, gdb_only=True)
if name != "" and "+" not in name and filter_text in name: if name != "" and "+" not in name and filter_text in name:
symbols.append((name, addr)) symbols.append((name, addr))
addr += ptr_size addr += ptr_size

@ -27,16 +27,14 @@ def got(name_filter="") -> None:
return return
if "PIE enabled" in pie_status: if "PIE enabled" in pie_status:
bin_base = pwndbg.gdblib.elf.exe().address bin_base = pwndbg.gdblib.proc.binary_base_addr
relro_color = message.off relro_color = message.off
if "Partial" in relro_status: if "Partial" in relro_status:
relro_color = message.warn relro_color = message.warn
elif "Full" in relro_status: elif "Full" in relro_status:
relro_color = message.on relro_color = message.on
print( print("GOT protection: %s | GOT functions: %d" % (relro_color(relro_status), len(jmpslots)))
"\nGOT protection: %s | GOT functions: %d\n " % (relro_color(relro_status), len(jmpslots))
)
for line in jmpslots: for line in jmpslots:
address, info, rtype, value, name = line.split()[:5] address, info, rtype, value, name = line.split()[:5]

@ -9,11 +9,13 @@ import sys
from types import ModuleType from types import ModuleType
from typing import Any from typing import Any
from typing import Callable from typing import Callable
from typing import Tuple
import gdb import gdb
import pwndbg.gdblib.qemu import pwndbg.gdblib.qemu
import pwndbg.lib.memoize import pwndbg.lib.memoize
import pwndbg.lib.memory
class module(ModuleType): class module(ModuleType):
@ -89,6 +91,18 @@ class module(ModuleType):
""" """
return gdb.current_progspace().filename return gdb.current_progspace().filename
@property
@pwndbg.lib.memoize.reset_on_start
@pwndbg.lib.memoize.reset_on_stop
def binary_base_addr(self) -> int:
return self.binary_vmmap[0].start
@property
@pwndbg.lib.memoize.reset_on_start
@pwndbg.lib.memoize.reset_on_stop
def binary_vmmap(self) -> Tuple[pwndbg.lib.memory.Page, ...]:
return tuple(p for p in pwndbg.gdblib.vmmap.get() if p.objfile == self.exe)
def OnlyWhenRunning(self, func): def OnlyWhenRunning(self, func):
@functools.wraps(func) @functools.wraps(func)
def wrapper(*a, **kw): def wrapper(*a, **kw):

@ -61,14 +61,15 @@ if "/usr/lib/debug" not in _get_debug_file_directory():
@pwndbg.lib.memoize.reset_on_objfile @pwndbg.lib.memoize.reset_on_objfile
def get(address: int, gdb_only=False) -> str: def get(address: int, gdb_only=False) -> str:
""" """
Retrieve the name for the symbol located at `address` Retrieve the name for the symbol located at `address` - either from GDB or from IDA sync
Passing `gdb_only=True`
""" """
# Fast path # Note: we do not return "" on `address < pwndbg.gdblib.memory.MMAP_MIN_ADDR`
if address < pwndbg.gdblib.memory.MMAP_MIN_ADDR or address >= ((1 << 64) - 1): # because this may be used to find out the symbol name on PIE binaries that weren't started yet
return "" # and then their symbol addresses can be found by GDB on their (non-rebased) offsets
# Don't look up stack addresses # Fast path: GDB's `info symbol` returns 'Numeric constant too large' here
if pwndbg.gdblib.stack.find(address): if address >= ((1 << 64) - 1):
return "" return ""
# This sucks, but there's not a GDB API for this. # This sucks, but there's not a GDB API for this.

@ -38,8 +38,9 @@ GLIBC_2_33=$(PWD)/glibcs/2.33
.PHONY : all clean .PHONY : all clean
all: $(LINKED) $(LINKED_ASM) $(COMPILED_GO) CUSTOM_TARGETS = reference_bin_pie.out reference_bin_nopie.out
all: $(LINKED) $(LINKED_ASM) $(COMPILED_GO) $(CUSTOM_TARGETS)
%.out : %.c %.out : %.c
@echo "[+] Building '$@'" @echo "[+] Building '$@'"
@ -97,4 +98,10 @@ clean :
@rm -f $(COMPILED) $(LINKED) $(COMPILED_ASM) $(LINKED_ASM) $(COMPILED_GO) @rm -f $(COMPILED) $(LINKED) $(COMPILED_ASM) $(LINKED_ASM) $(COMPILED_GO)
reference-binary.out: EXTRA_FLAGS := -Dexample=1 reference_bin_pie.out: reference-binary.c
@echo "[+] Building reference_bin_pie.out"
${ZIGCC} -fpie -o reference_bin_pie.out reference-binary.c
reference_bin_nopie.out: reference-binary.c
@echo "[+] Building reference_bin_nopie.out"
${ZIGCC} -fno-pie -o reference_bin_nopie.out reference-binary.c

@ -0,0 +1,70 @@
import re
import gdb
import pytest
import tests
NO_SECTS_BINARY = tests.binaries.get("gosample.x86")
PIE_BINARY_WITH_PLT = "reference_bin_pie.out"
NOPIE_BINARY_WITH_PLT = "reference_bin_nopie.out"
def test_commands_plt_gotplt_got_when_no_sections(start_binary):
start_binary(NO_SECTS_BINARY)
# elf.py commands
assert gdb.execute("plt", to_string=True) == "Could not find section .plt\n"
assert gdb.execute("gotplt", to_string=True) == "Could not find section .got.plt\n"
# got.py command
assert gdb.execute("got", to_string=True) == "NO JUMP_SLOT entries available in the GOT\n"
@pytest.mark.parametrize(
"binary_name,is_pie", ((PIE_BINARY_WITH_PLT, True), (NOPIE_BINARY_WITH_PLT, False))
)
def test_command_plt(binary_name, is_pie):
binary = tests.binaries.get(binary_name)
gdb.execute(f"file {binary}")
out = gdb.execute("plt", to_string=True).splitlines()
assert len(out) == 2
assert re.match(r"Section \.plt 0x[0-9a-f]+-0x[0-9a-f]+:", out[0])
assert re.match(r"0x[0-9a-f]+: puts@plt", out[1])
gdb.execute("starti")
out2 = gdb.execute("plt", to_string=True).splitlines()
if is_pie:
assert out != out2
else:
assert out == out2
assert len(out2) == 2
assert re.match(r"Section \.plt 0x[0-9a-f]+-0x[0-9a-f]+:", out2[0])
assert re.match(r"0x[0-9a-f]+: puts@plt", out2[1])
@pytest.mark.parametrize(
"binary_name,is_pie", ((PIE_BINARY_WITH_PLT, True), (NOPIE_BINARY_WITH_PLT, False))
)
def test_command_got(binary_name, is_pie):
binary = tests.binaries.get(binary_name)
gdb.execute(f"file {binary}")
out = gdb.execute("got", to_string=True).splitlines()
assert out == ["got: The program is not being run."]
gdb.execute("starti")
out2 = gdb.execute("got", to_string=True).splitlines()
assert out != out2
assert len(out2) == 2
assert out2[0] == "GOT protection: Full RELRO | GOT functions: 1"
assert re.match(r"\[0x[0-9a-f]+\] puts@GLIBC_[0-9.]+ -> .*", out2[1])
Loading…
Cancel
Save