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 # elfsections
```text ```text
usage: elfsections [-h] usage: elfsections [-h] [-R]
``` ```
Prints the section mappings contained in the ELF header. 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 ### Optional arguments
|Short|Long|Help| |Short|Long|Help|
| :--- | :--- | :--- | | :--- | :--- | :--- |
|-h|--help|show this help message and exit| |-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. --> <!-- 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------------ --> <!-- ------------\>8---- ----\>8---- ----\>8------------ -->

@ -1,39 +1,114 @@
from __future__ import annotations from __future__ import annotations
import argparse
from typing import List from typing import List
from typing import Tuple from typing import Tuple
from elftools.elf.elffile import ELFFile from elftools.elf.elffile import ELFFile
import pwndbg.aglib
import pwndbg.aglib.proc
import pwndbg.color.memory as M
import pwndbg.commands import pwndbg.commands
from pwndbg.color import message from pwndbg.color import message
from pwndbg.commands import CommandCategory 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( Examples:
"Prints the section mappings contained in the ELF header.", category=CommandCategory.LINUX 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 @pwndbg.commands.OnlyWithFile
def elfsections() -> None: def elfsections(no_rebase: bool) -> None:
local_path = pwndbg.aglib.file.get_proc_exe_file() 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: with open(local_path, "rb") as f:
elffile = ELFFile(f) elffile = ELFFile(f)
sections = [] sections = []
for section in elffile.iter_sections(): for section in elffile.iter_sections():
start = section["sh_addr"] start = section["sh_addr"]
privilege = section["sh_flags"]
# Don't print sections that aren't mapped into memory # 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 continue
size = section["sh_size"] 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() sections.sort()
for start, end, name in sections: # print legend
print(f"{start:#x} - {end:#x} ", name) 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( @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] r"GOT protection: (?:Partial|Full) RELRO \| Found 0 GOT entries passing the filter", out[10]
) )
assert len(out) == 11 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