Some improvements to configuration

- Modify `get_set_string()` to match GDB's builtin behaviour
- Make `gcc-compiler-path`'s and `cymbol-editor`'s `set_show_doc` first strings to lowercase
- Change `gcc-compiler-path` and `cymbol-editor` to `gdb.PARAM_OPTIONAL_FILENAME`
- Add resolve_heap_via_heuristic as a gdb.PARAM_ENUM config with options:
  - auto: pwndbg will try to use heuristics if debug symbols are missing
  - force: pwndbg will always try to use heuristics, even if debug symbols are available
  - never: pwndbg will never use heuristics to resolve the heap
- Move some hints to `resolve_heap_via_heuristic`'s `help_docstring`
pull/1437/head
lebr0nli 3 years ago committed by Disconnect3d
parent d28f25c345
commit 0b394a440d

@ -34,11 +34,15 @@ from pwndbg.color import message
gcc_compiler_path = pwndbg.gdblib.config.add_param( gcc_compiler_path = pwndbg.gdblib.config.add_param(
"gcc-compiler-path", "gcc-compiler-path",
"", "",
"Path to the gcc/g++ toolchain for generating imported symbols", "path to the gcc/g++ toolchain for generating imported symbols",
param_class=gdb.PARAM_OPTIONAL_FILENAME,
) )
cymbol_editor = pwndbg.gdblib.config.add_param( cymbol_editor = pwndbg.gdblib.config.add_param(
"cymbol-editor", "", "Path to the editor for editing custom structures" "cymbol-editor",
"",
"path to the editor for editing custom structures",
param_class=gdb.PARAM_OPTIONAL_FILENAME,
) )
# Remeber loaded symbols. This would be useful for 'remove-symbol-file'. # Remeber loaded symbols. This would be useful for 'remove-symbol-file'.

@ -1179,7 +1179,7 @@ parser.add_argument(
@pwndbg.commands.ArgparsedCommand(parser) @pwndbg.commands.ArgparsedCommand(parser)
def heap_config(filter_pattern): def heap_config(filter_pattern):
display_config(filter_pattern, "heap") display_config(filter_pattern, "heap", has_file_command=False)
print( print(
message.hint( message.hint(

@ -29,9 +29,9 @@ class Parameter(gdb.Parameter):
# `set_doc`, `show_doc`, and `__doc__` must be set before `gdb.Parameter.__init__`. # `set_doc`, `show_doc`, and `__doc__` must be set before `gdb.Parameter.__init__`.
# They will be used for `help set <param>` and `help show <param>`, # They will be used for `help set <param>` and `help show <param>`,
# respectively # respectively
self.set_doc = "Set " + param.set_show_doc self.set_doc = "Set " + param.set_show_doc + "."
self.show_doc = "Show " + param.set_show_doc self.show_doc = "Show " + param.set_show_doc + "."
self.__doc__ = param.help_docstring self.__doc__ = param.help_docstring or None
if param.param_class == gdb.PARAM_ENUM: if param.param_class == gdb.PARAM_ENUM:
super().__init__( super().__init__(
@ -76,13 +76,13 @@ class Parameter(gdb.Parameter):
if not pwndbg.decorators.first_prompt: if not pwndbg.decorators.first_prompt:
return "" return ""
return "Set %s to %r" % (self.param.set_show_doc, self.native_value) return "Set %s to %r." % (self.param.set_show_doc, self.native_value)
def get_show_string(self, svalue): def get_show_string(self, svalue):
"""Handles the GDB `show <param>` command""" """Handles the GDB `show <param>` command"""
return "The current value of %r is %r" % ( return "%s is %r." % (
self.param.name, self.param.set_show_doc.capitalize(),
Parameter._value_to_gdb_native(svalue, self.param.param_class), svalue,
) )
@staticmethod @staticmethod

@ -1,3 +1,5 @@
import gdb
import pwndbg.color.message as message import pwndbg.color.message as message
import pwndbg.gdblib.config import pwndbg.gdblib.config
import pwndbg.gdblib.symbol import pwndbg.gdblib.symbol
@ -7,7 +9,7 @@ current = None
def add_heap_param( def add_heap_param(
name, default, set_show_doc, *, help_docstring=None, param_class=None, enum_sequence=None name, default, set_show_doc, *, help_docstring="", param_class=None, enum_sequence=None
): ):
return pwndbg.gdblib.config.add_param( return pwndbg.gdblib.config.add_param(
name, name,
@ -35,7 +37,36 @@ symbol_list = [main_arena, thread_arena, mp_, tcache, global_max_fast]
heap_chain_limit = add_heap_param("heap-dereference-limit", 8, "number of bins to dereference") heap_chain_limit = add_heap_param("heap-dereference-limit", 8, "number of bins to dereference")
resolve_heap_via_heuristic = add_heap_param( resolve_heap_via_heuristic = add_heap_param(
"resolve-heap-via-heuristic", False, "Resolve missing heap related symbols via heuristics" "resolve-heap-via-heuristic",
"auto",
"the strategy to resolve heap via heuristic",
help_docstring="""If pwndbg fails to use the debug symbols to resolve the heap, it can try to resolve the heap via heuristics.
This configuration sets the strategy for resolving the heap.
There are three strategies:
auto == pwndbg will try to use heuristics if debug symbols are missing
force == pwndbg will always try to use heuristics, even if debug symbols are available
never == pwndbg will never use heuristics to resolve the heap
If the output of the heap related command produces errors with heuristics, you can try manually setting the libc symbol addresses.
For this, see the `heap_config` command output and set the `main_arena`, `mp_`, `global_max_fast`, `tcache` and `thread_arena` addresses.
Note: pwndbg will generate more reliable results with proper debug symbols.
Therefore, when debug symbols are missing, you should try to install them first if you haven't already.
They can probably be installed via the package manager of your choice.
See also: https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
E.g. on Ubuntu/Debian you might need to do the following steps (for 64-bit and 32-bit binaries):
sudo apt-get install libc6-dbg
sudo dpkg --add-architecture i386
sudo apt-get install libc-dbg:i386
If you used setup.sh on Arch based distro you'll need to do a power cycle or set environment variable manually like this: export DEBUGINFOD_URLS=https://debuginfod.archlinux.org
""",
param_class=gdb.PARAM_ENUM,
enum_sequence=["auto", "force", "never"],
) )
@ -59,7 +90,7 @@ def resolve_heap(is_first_run=False):
import pwndbg.heap.ptmalloc import pwndbg.heap.ptmalloc
global current global current
if resolve_heap_via_heuristic: if resolve_heap_via_heuristic == "force":
current = pwndbg.heap.ptmalloc.HeuristicHeap() current = pwndbg.heap.ptmalloc.HeuristicHeap()
if not is_first_run and pwndbg.gdblib.proc.alive and current.libc_has_debug_syms(): if not is_first_run and pwndbg.gdblib.proc.alive and current.libc_has_debug_syms():
print( print(
@ -68,11 +99,5 @@ def resolve_heap(is_first_run=False):
" This is not recommended!" " This is not recommended!"
) )
) )
else:
print(
message.warn(
"You are going to resolve the heap via heuristic. This might not work in all cases."
)
)
else: else:
current = pwndbg.heap.ptmalloc.DebugSymsHeap() current = pwndbg.heap.ptmalloc.DebugSymsHeap()

@ -26,13 +26,18 @@ class Parameter:
default, default,
set_show_doc, set_show_doc,
*, *,
help_docstring=None, help_docstring="",
param_class=None, param_class=None,
enum_sequence=None, enum_sequence=None,
scope="config", scope="config",
): ):
# Note: `set_show_doc` should be a noun phrase, e.g. "the value of the foo"
# The `set_doc` will be "Set the value of the foo."
# The `show_doc` will be "Show the value of the foo."
# `get_set_string()` will return "Set the value of the foo to VALUE."
# `get_show_string()` will return "Show the value of the foo."
self.set_show_doc = set_show_doc.strip() self.set_show_doc = set_show_doc.strip()
self.help_docstring = help_docstring.strip() if help_docstring else None self.help_docstring = help_docstring.strip()
self.name = name self.name = name
self.default = default self.default = default
self.value = default self.value = default
@ -129,7 +134,7 @@ class Config:
default, default,
set_show_doc, set_show_doc,
*, *,
help_docstring=None, help_docstring="",
param_class=None, param_class=None,
enum_sequence=None, enum_sequence=None,
scope="config", scope="config",

@ -123,7 +123,7 @@ def test_malloc_chunk_command(start_binary):
def test_malloc_chunk_command_heuristic(start_binary): def test_malloc_chunk_command_heuristic(start_binary):
start_binary(HEAP_MALLOC_CHUNK) start_binary(HEAP_MALLOC_CHUNK)
gdb.execute("set resolve-heap-via-heuristic on") gdb.execute("set resolve-heap-via-heuristic force")
gdb.execute("break break_here") gdb.execute("break break_here")
gdb.execute("continue") gdb.execute("continue")
@ -225,7 +225,7 @@ class mock_for_heuristic:
def test_main_arena_heuristic(start_binary): def test_main_arena_heuristic(start_binary):
start_binary(HEAP_MALLOC_CHUNK) start_binary(HEAP_MALLOC_CHUNK)
gdb.execute("set resolve-heap-via-heuristic on") gdb.execute("set resolve-heap-via-heuristic force")
gdb.execute("break break_here") gdb.execute("break break_here")
gdb.execute("continue") gdb.execute("continue")
@ -267,7 +267,7 @@ def test_main_arena_heuristic(start_binary):
def test_mp_heuristic(start_binary): def test_mp_heuristic(start_binary):
start_binary(HEAP_MALLOC_CHUNK) start_binary(HEAP_MALLOC_CHUNK)
gdb.execute("set resolve-heap-via-heuristic on") gdb.execute("set resolve-heap-via-heuristic force")
gdb.execute("break break_here") gdb.execute("break break_here")
gdb.execute("continue") gdb.execute("continue")
@ -303,7 +303,7 @@ def test_mp_heuristic(start_binary):
def test_global_max_fast_heuristic(start_binary): def test_global_max_fast_heuristic(start_binary):
# TODO: Support other architectures or different libc versions # TODO: Support other architectures or different libc versions
start_binary(HEAP_MALLOC_CHUNK) start_binary(HEAP_MALLOC_CHUNK)
gdb.execute("set resolve-heap-via-heuristic on") gdb.execute("set resolve-heap-via-heuristic force")
gdb.execute("break break_here") gdb.execute("break break_here")
gdb.execute("continue") gdb.execute("continue")
@ -331,7 +331,7 @@ def test_global_max_fast_heuristic(start_binary):
def test_thread_cache_heuristic(start_binary): def test_thread_cache_heuristic(start_binary):
# TODO: Support other architectures or different libc versions # TODO: Support other architectures or different libc versions
start_binary(HEAP_MALLOC_CHUNK) start_binary(HEAP_MALLOC_CHUNK)
gdb.execute("set resolve-heap-via-heuristic on") gdb.execute("set resolve-heap-via-heuristic force")
gdb.execute("break break_here") gdb.execute("break break_here")
gdb.execute("continue") gdb.execute("continue")
@ -366,7 +366,7 @@ def test_thread_cache_heuristic(start_binary):
def test_thread_arena_heuristic(start_binary): def test_thread_arena_heuristic(start_binary):
# TODO: Support other architectures or different libc versions # TODO: Support other architectures or different libc versions
start_binary(HEAP_MALLOC_CHUNK) start_binary(HEAP_MALLOC_CHUNK)
gdb.execute("set resolve-heap-via-heuristic on") gdb.execute("set resolve-heap-via-heuristic force")
gdb.execute("break break_here") gdb.execute("break break_here")
gdb.execute("continue") gdb.execute("continue")
@ -395,7 +395,7 @@ def test_thread_arena_heuristic(start_binary):
def test_heuristic_fail_gracefully(start_binary): def test_heuristic_fail_gracefully(start_binary):
# TODO: Support other architectures or different libc versions # TODO: Support other architectures or different libc versions
start_binary(HEAP_MALLOC_CHUNK) start_binary(HEAP_MALLOC_CHUNK)
gdb.execute("set resolve-heap-via-heuristic on") gdb.execute("set resolve-heap-via-heuristic force")
gdb.execute("break break_here") gdb.execute("break break_here")
gdb.execute("continue") gdb.execute("continue")

@ -35,10 +35,12 @@ def test_gdb_parameter_default_value_works(start_binary, params):
param_name = f"test-param-{name_suffix}" param_name = f"test-param-{name_suffix}"
help_docstring = f"Help docstring for {param_name}" help_docstring = f"Help docstring for {param_name}"
set_show_doc = "the value of the foo"
param = pwndbg.gdblib.config.add_param( param = pwndbg.gdblib.config.add_param(
param_name, param_name,
default_value, default_value,
"some show string", set_show_doc,
help_docstring=help_docstring, help_docstring=help_docstring,
**optional_kwargs, **optional_kwargs,
) )
@ -47,7 +49,7 @@ def test_gdb_parameter_default_value_works(start_binary, params):
pwndbg.gdblib.config_mod.Parameter(param) pwndbg.gdblib.config_mod.Parameter(param)
out = gdb.execute(f"show {param_name}", to_string=True) out = gdb.execute(f"show {param_name}", to_string=True)
assert out == f"The current value of {param_name!r} is {displayed_value!r}\n" assert out == f"{set_show_doc.capitalize()} is {displayed_value!r}.\n"
if ( if (
optional_kwargs.get("param_class") in (gdb.PARAM_UINTEGER, gdb.PARAM_INTEGER) optional_kwargs.get("param_class") in (gdb.PARAM_UINTEGER, gdb.PARAM_INTEGER)
and default_value == 0 and default_value == 0
@ -62,10 +64,10 @@ def test_gdb_parameter_default_value_works(start_binary, params):
assert param.value == default_value assert param.value == default_value
out = gdb.execute(f"help show {param_name}", to_string=True) out = gdb.execute(f"help show {param_name}", to_string=True)
assert out == f"Show some show string\n{help_docstring}\n" assert out == f"Show {set_show_doc}.\n{help_docstring}\n"
assert ( assert (
gdb.execute(f"help set {param_name}", to_string=True) gdb.execute(f"help set {param_name}", to_string=True)
== f"Set some show string\n{help_docstring}\n" == f"Set {set_show_doc}.\n{help_docstring}\n"
) )
# TODO/FIXME: Is there a way to unregister a GDB parameter defined in Python? # TODO/FIXME: Is there a way to unregister a GDB parameter defined in Python?

@ -30,11 +30,15 @@ def test_triggers():
set_show(param_name, 0) set_show(param_name, 0)
set_show(param_name, 1) set_show(param_name, 1)
set_show(param_name, -1) set_show(param_name, -1)
elif isinstance(p.value, str): elif isinstance(p.value, str) and p.param_class != gdb.PARAM_ENUM:
set_show(param_name, "") set_show(param_name, "")
set_show(param_name, "some invalid text") set_show(param_name, "some invalid text")
set_show(param_name, "red") set_show(param_name, "red")
set_show(param_name, "bold,yellow") set_show(param_name, "bold,yellow")
elif isinstance(p.value, str) and p.param_class == gdb.PARAM_ENUM:
# Only valid values are allowed, invalid values will cause an error
for enum in p.enum_sequence:
set_show(param_name, enum)
else: else:
print(p.value, type(p.value)) print(p.value, type(p.value))
assert False assert False

Loading…
Cancel
Save