Fix the bug when showing the state of i386 GOT (#2017)

* Fix the bug when showing the state of i386 GOT

* Skip the test if binary can't be run
pull/2020/head
Alan Li 2 years ago committed by GitHub
parent df6b5a7ca3
commit 6a38ded24e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -136,15 +136,23 @@ def _got(path: str, accept_readonly: bool, symbol_filter: str) -> None:
# Parse the output of readelf line by line # Parse the output of readelf line by line
for category, lines in got_entry.items(): for category, lines in got_entry.items():
for line in lines: for line in lines:
# line might be something like: # There are 5 fields in the output of readelf:
# 00000000001ec018 0000000000000025 R_X86_64_IRELATIVE a0480 # "Offset", "Info", "Type", "Sym. Value", and "Symbol's Name"
# or something like: # We only care about "Offset", "Sym. Value" and "Symbol's Name" here
# 00000000001ec030 0000020a00000007 R_X86_64_JUMP_SLOT 000000000009ae80 realloc@@GLIBC_2.2.5 + 0 offset, _, _, *rest = line.split()[:5]
offset, _, rtype, *rest = line.split()[:5] if len(rest) < 2:
if len(rest) == 1: # "Sym. Value" or "Symbol's Name" are not present in this case
value = rest[0] # The output of readelf might look like this (missing both value and name):
# 00004e88 00000008 R_386_RELATIVE
# or something like this (only missing name):
# 00000000001ec018 0000000000000025 R_X86_64_IRELATIVE a0480
# TODO: Is it possible that we are missing the value but not the name?
value = rest[0] if rest else ""
name = "" name = ""
else: else:
# Every fields are present in this case
# The output of readelf might look like this:
# 00000000001ec030 0000020a00000007 R_X86_64_JUMP_SLOT 000000000009ae80 realloc@@GLIBC_2.2.5 + 0
value, name = rest value, name = rest
address = int(offset, 16) + bin_base_offset address = int(offset, 16) + bin_base_offset
# TODO/FIXME: This check might not work correctly if we failed to get the correct vmmap result # TODO/FIXME: This check might not work correctly if we failed to get the correct vmmap result

@ -38,7 +38,7 @@ GLIBC_2_33=$(PWD)/glibcs/2.33
.PHONY : all clean .PHONY : all clean
CUSTOM_TARGETS = reference_bin_pie.out reference_bin_nopie.out symbol_1600_and_752.out initialized_heap_x64.out initialized_heap_i386_big.out linked_lists.out CUSTOM_TARGETS = reference_bin_pie.out reference_bin_nopie.out reference_bin_nopie.i386.out symbol_1600_and_752.out initialized_heap_x64.out initialized_heap_i386_big.out linked_lists.out
all: $(LINKED) $(LINKED_ASM) $(COMPILED_GO) $(CUSTOM_TARGETS) all: $(LINKED) $(LINKED_ASM) $(COMPILED_GO) $(CUSTOM_TARGETS)
@ -146,5 +146,9 @@ reference_bin_nopie.out: reference-binary.c
@echo "[+] Building reference_bin_nopie.out" @echo "[+] Building reference_bin_nopie.out"
${ZIGCC} -fno-pie -o reference_bin_nopie.out reference-binary.c ${ZIGCC} -fno-pie -o reference_bin_nopie.out reference-binary.c
reference_bin_nopie.i386.out: reference-binary.c
@echo "[+] Building reference_bin_nopie.i386.out"
${ZIGCC} -fpie -target i386-linux-gnu -o reference_bin_nopie.i386.out reference-binary.c
symbol_1600_and_752.out: symbol_1600_and_752.cpp symbol_1600_and_752.out: symbol_1600_and_752.cpp
${CXX} -O0 -ggdb -Wno-pmf-conversions symbol_1600_and_752.cpp -o symbol_1600_and_752.out ${CXX} -O0 -ggdb -Wno-pmf-conversions symbol_1600_and_752.cpp -o symbol_1600_and_752.out

@ -11,6 +11,7 @@ import tests
NO_SECTS_BINARY = tests.binaries.get("gosample.x86") NO_SECTS_BINARY = tests.binaries.get("gosample.x86")
PIE_BINARY_WITH_PLT = "reference_bin_pie.out" PIE_BINARY_WITH_PLT = "reference_bin_pie.out"
NOPIE_BINARY_WITH_PLT = "reference_bin_nopie.out" NOPIE_BINARY_WITH_PLT = "reference_bin_nopie.out"
NOPIE_I386_BINARY_WITH_PLT = "reference_bin_nopie.i386.out"
def test_commands_plt_gotplt_got_when_no_sections(start_binary): def test_commands_plt_gotplt_got_when_no_sections(start_binary):
@ -105,12 +106,18 @@ def test_command_got_for_target_binary(binary_name, is_pie):
assert re.match(r"\[0x[0-9a-f]+\] puts@GLIBC_[0-9.]+ -> .*", out[4]) assert re.match(r"\[0x[0-9a-f]+\] puts@GLIBC_[0-9.]+ -> .*", out[4])
def test_command_got_for_target_binary_and_loaded_library(): @pytest.mark.parametrize(
binary = tests.binaries.get(NOPIE_BINARY_WITH_PLT) "binary_name", (NOPIE_BINARY_WITH_PLT, NOPIE_I386_BINARY_WITH_PLT), ids=["x86-64", "i386"]
)
def test_command_got_for_target_binary_and_loaded_library(binary_name):
binary = tests.binaries.get(binary_name)
gdb.execute(f"file {binary}") gdb.execute(f"file {binary}")
gdb.execute("break main") gdb.execute("break main")
gdb.execute("starti") try:
gdb.execute("starti")
except gdb.error:
pytest.skip("Test not supported on this platform.")
# Before loading libc, we can't find .got.plt of libc # Before loading libc, we can't find .got.plt of libc
out = gdb.execute("got -p libc", to_string=True).splitlines() out = gdb.execute("got -p libc", to_string=True).splitlines()
@ -120,7 +127,7 @@ def test_command_got_for_target_binary_and_loaded_library():
assert out[2] == "" assert out[2] == ""
assert out[3] == "No shared library matching the path filter found." assert out[3] == "No shared library matching the path filter found."
assert out[4] == "Available shared libraries:" assert out[4] == "Available shared libraries:"
assert out[5].endswith("/ld-linux-x86-64.so.2") assert out[5].endswith(("/ld-linux-x86-64.so.2", "/ld-linux.so.2"))
gdb.execute("continue") gdb.execute("continue")
@ -175,12 +182,12 @@ def test_command_got_for_target_binary_and_loaded_library():
assert re.match(r"\[0x[0-9a-f]+\] .*ABS.* -> .*", out[6 + i]) assert re.match(r"\[0x[0-9a-f]+\] .*ABS.* -> .*", out[6 + i])
# Try filtering out path with "l", which should match every library # Try filtering out path with "l", which should match every library
# First should be ld-linux-x86-64.so.2 # First should be ld-linux(-x86-64)?.so.2
out = gdb.execute("got -p l", to_string=True).splitlines() out = gdb.execute("got -p l", to_string=True).splitlines()
assert out[0] == "Filtering by lib/objfile path: l" assert out[0] == "Filtering by lib/objfile path: l"
assert out[1] == "Filtering out read-only entries (display them with -r or --show-readonly)" assert out[1] == "Filtering out read-only entries (display them with -r or --show-readonly)"
assert out[2] == "" assert out[2] == ""
assert re.match(r"State of the GOT of .*/ld-linux-x86-64.so.2:", out[3]) assert re.match(r"State of the GOT of .*/ld-linux(-x86-64)?.so.2:", out[3])
m = re.match( m = re.match(
r"GOT protection: (?:Partial|Full) RELRO \| Found (\d+) GOT entries passing the filter", r"GOT protection: (?:Partial|Full) RELRO \| Found (\d+) GOT entries passing the filter",
out[4], out[4],
@ -212,8 +219,8 @@ def test_command_got_for_target_binary_and_loaded_library():
assert out[4] == "" assert out[4] == ""
out = out[5:] out = out[5:]
# Second should be ld-linux-x86-64.so.2 # Second should be ld-linux(-x86-64)?.so.2
assert re.match(r"State of the GOT of .*/ld-linux-x86-64.so.2:", out[0]) assert re.match(r"State of the GOT of .*/ld-linux(-x86-64)?.so.2:", out[0])
m = re.match( m = re.match(
r"GOT protection: (?:Partial|Full) RELRO \| Found (\d+) GOT entries passing the filter", r"GOT protection: (?:Partial|Full) RELRO \| Found (\d+) GOT entries passing the filter",
out[1], out[1],
@ -243,7 +250,7 @@ def test_command_got_for_target_binary_and_loaded_library():
assert out[3] == "GOT protection: Full RELRO | Found 1 GOT entries passing the filter" assert out[3] == "GOT protection: Full RELRO | Found 1 GOT entries passing the filter"
assert re.match(r"\[0x[0-9a-f]+\] puts@GLIBC_[0-9.]+ -> .*", out[4]) assert re.match(r"\[0x[0-9a-f]+\] puts@GLIBC_[0-9.]+ -> .*", out[4])
assert out[5] == "" assert out[5] == ""
assert re.match(r"State of the GOT of .*/ld-linux-x86-64.so.2:", out[6]) assert re.match(r"State of the GOT of .*/ld-linux(-x86-64)?.so.2:", out[6])
assert re.match( assert re.match(
r"GOT protection: (?:Partial|Full) RELRO \| Found 0 GOT entries passing the filter", out[7] r"GOT protection: (?:Partial|Full) RELRO \| Found 0 GOT entries passing the filter", out[7]
) )

Loading…
Cancel
Save