Fix #2958: Rebase addresses in elfsections command (#2999)

* Add the rebase address for elf command.

* linting

* linting

* Add file offset and rel address in memory. Build a table for display

* Modify the display information table. Add the coloring based output

* generate docs
pull/3013/head
1anp3sk 7 months ago committed by GitHub
parent 9a01ac32b5
commit dda55bfe8e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -2,16 +2,22 @@
# elfsections
```text
usage: elfsections [-h]
usage: elfsections [-h] [-R]
```
Prints the section mappings contained in the ELF header.
If binary not start or use --no-rebase, the section permission based on section flags.
Examples:
elfsections
elfsections --no-rebase
### Optional arguments
|Short|Long|Help|
| :--- | :--- | :--- |
|-h|--help|show this help message and exit|
|-R|--no-rebase|Print the non-rebased section address. |
<!-- END OF AUTOGENERATED PART. Do not modify this line or the line below, they mark the end of the auto-generated part of the file. If you want to extend the documentation in a way which cannot easily be done by adding to the command help description, write below the following line. -->
<!-- ------------\>8---- ----\>8---- ----\>8------------ -->

@ -1,39 +1,114 @@
from __future__ import annotations
import argparse
from typing import List
from typing import Tuple
from elftools.elf.elffile import ELFFile
import pwndbg.aglib
import pwndbg.aglib.proc
import pwndbg.color.memory as M
import pwndbg.commands
from pwndbg.color import message
from pwndbg.commands import CommandCategory
parser = argparse.ArgumentParser(
description="""Prints the section mappings contained in the ELF header.
If binary not start or use --no-rebase, the section permission based on section flags.
@pwndbg.commands.Command(
"Prints the section mappings contained in the ELF header.", category=CommandCategory.LINUX
Examples:
elfsections
elfsections --no-rebase
""",
)
parser.add_argument(
"-R",
"--no-rebase",
help="Print the non-rebased section address. ",
action="store_true",
default=False,
dest="no_rebase",
)
@pwndbg.commands.Command(parser, category=CommandCategory.LINUX)
@pwndbg.commands.OnlyWithFile
def elfsections() -> None:
def elfsections(no_rebase: bool) -> None:
local_path = pwndbg.aglib.file.get_proc_exe_file()
bin_base_addr = 0
# Get the binary base address, for rebase the section address if we need.
if pwndbg.aglib.proc.alive:
bin_base_addr = pwndbg.aglib.proc.binary_base_addr
__SH_WRITE = 1 << 0
__SH_ALLOC = 1 << 1
__SH_EXEC = 1 << 2
with open(local_path, "rb") as f:
elffile = ELFFile(f)
sections = []
for section in elffile.iter_sections():
start = section["sh_addr"]
privilege = section["sh_flags"]
# Don't print sections that aren't mapped into memory
if start == 0:
# test in go sample: "gosample.x64/86", some sections have the address but don't have the SH_ALLOC flags
if not (privilege & __SH_ALLOC):
continue
size = section["sh_size"]
sections.append((start, start + size, section.name))
# rebase the address if we need
if not no_rebase:
start = bin_base_addr + start if start < bin_base_addr else start
sections.append([start, start + size, size, section.name, privilege])
sections.sort()
for start, end, name in sections:
print(f"{start:#x} - {end:#x} ", name)
# print legend
print(M.legend())
# table header
print(f"{'Start':>18} {'End':>18} {'Perm':>8} {'Size':>10} {'Name':<}")
# if the binary is started, use the memory permission for the coloring
if pwndbg.aglib.proc.alive and not no_rebase:
for start, end, size, name, privilege in sections:
page = pwndbg.aglib.vmmap.find(start)
privilege_str = "R" if page.read else "-"
privilege_str += "W" if page.write else "-"
privilege_str += "X" if page.execute else "-"
print(
M.get(
start,
text=f"{start:>#18x} {end:>#18x} {privilege_str:>8} {size:>#10x} {name:<}",
)
)
else:
# if the binary is not start, use the section flags for the coloring.
for start, end, size, name, privilege in sections:
color = M.c.rodata
privilege_str = "R"
if privilege & __SH_WRITE:
privilege_str += "W"
color = M.c.data
else:
privilege_str += "-"
if privilege & __SH_EXEC:
privilege_str += "X"
color = M.c.code
else:
privilege_str += "-"
print(color(f"{start:>#18x} {end:>#18x} {privilege_str:>8} {size:>#10x} {name:<}"))
@pwndbg.commands.Command(

@ -260,3 +260,36 @@ def test_command_got_for_target_binary_and_loaded_library(binary_name):
r"GOT protection: (?:Partial|Full) RELRO \| Found 0 GOT entries passing the filter", out[10]
)
assert len(out) == 11
@pytest.mark.parametrize(
"binary_name,is_pie", ((NOPIE_BINARY_WITH_PLT, False), (PIE_BINARY_WITH_PLT, True))
)
def test_command_elf(binary_name, is_pie):
binary = tests.binaries.get(binary_name)
gdb.execute(f"file {binary}")
gdb.execute("starti")
out = gdb.execute("elf", to_string=True).splitlines()
assert len(out) == 25
# test for default
for section in out[2:]:
assert re.match(
r"^\s*0x[\da-fA-F]+\s+0x[\da-fA-F]+\s+(?:[RWX-]{3})\s+0x[\da-fA-F]+\s+\.([^\s]+)$",
section,
)
if is_pie:
address = section.split()
assert address[0].startswith("0x55555555")
# if this is a pie binary, test for --no-rebase
if is_pie:
out = gdb.execute("elf -R", to_string=True).splitlines()
assert len(out) == 25
for section in out[2:]:
assert re.match(
r"^\s*0x[\da-fA-F]+\s+0x[\da-fA-F]+\s+(?:[RWX-]{3})\s+0x[\da-fA-F]+\s+\.([^\s]+)$",
section,
)

Loading…
Cancel
Save