diff --git a/pwndbg/commands/__init__.py b/pwndbg/commands/__init__.py index 646de647d..e50d88b70 100644 --- a/pwndbg/commands/__init__.py +++ b/pwndbg/commands/__init__.py @@ -52,7 +52,11 @@ class Command(gdb.Command): builtin_override_whitelist = {"up", "down", "search", "pwd", "start", "ignore"} history = {} # type: Dict[int,str] - def __init__(self, function, prefix=False, command_name=None, shell=False): + def __init__( + self, function, prefix=False, command_name=None, shell=False, is_alias=False, aliases=[] + ): + self.is_alias = is_alias + self.aliases = aliases self.shell = shell if command_name is None: @@ -417,7 +421,7 @@ def OnlyWithResolvedHeapSyms(function): class _ArgparsedCommand(Command): - def __init__(self, parser, function, command_name=None, *a, **kw): + def __init__(self, parser, function, command_name=None, is_alias=False, aliases=[], *a, **kw): self.parser = parser if command_name is None: self.parser.prog = function.__name__ @@ -431,7 +435,9 @@ class _ArgparsedCommand(Command): # Note: function.__doc__ is used in the `pwndbg [filter]` command display function.__doc__ = self.parser.description.strip() - super(_ArgparsedCommand, self).__init__(function, command_name=command_name, *a, **kw) + super(_ArgparsedCommand, self).__init__( + function, command_name=command_name, is_alias=is_alias, aliases=aliases, *a, **kw + ) def split_args(self, argument): argv = gdb.string_to_argv(argument) @@ -464,8 +470,10 @@ class ArgparsedCommand: def __call__(self, function): for alias in self.aliases: - _ArgparsedCommand(self.parser, function, command_name=alias) - return _ArgparsedCommand(self.parser, function, command_name=self._command_name) + _ArgparsedCommand(self.parser, function, command_name=alias, is_alias=True) + return _ArgparsedCommand( + self.parser, function, command_name=self._command_name, aliases=self.aliases + ) # We use a 64-bit max value literal here instead of pwndbg.gdblib.arch.current diff --git a/pwndbg/commands/misc.py b/pwndbg/commands/misc.py index 07bade57f..f5c4a4032 100644 --- a/pwndbg/commands/misc.py +++ b/pwndbg/commands/misc.py @@ -4,6 +4,7 @@ import errno import gdb import pwndbg.auxv +import pwndbg.color as C import pwndbg.commands import pwndbg.gdblib.regs import pwndbg.gdblib.symbol @@ -84,8 +85,22 @@ def pwndbg_(filter_pattern, shell, all_): shell_cmds = False pwndbg_cmds = True - for name, docs in list_and_filter_commands(filter_pattern, pwndbg_cmds, shell_cmds): - print("%-20s %s" % (name, docs)) + from tabulate import tabulate + + table_data = [] + for name, aliases, docs in list_and_filter_commands(filter_pattern, pwndbg_cmds, shell_cmds): + alias_str = "" + aliases_len = 0 + if aliases: + aliases = map(C.blue, aliases) + alias_str = f" [{', '.join(aliases)}]" + + command_names = C.green(name) + alias_str + table_data.append((command_names, docs)) + + print( + tabulate(table_data, headers=[f"{C.green('Command')} [{C.blue('Aliases')}]", "Description"]) + ) parser = argparse.ArgumentParser(description="Print the distance between the two arguments.") @@ -125,6 +140,10 @@ def list_and_filter_commands(filter_str, pwndbg_cmds=True, shell_cmds=False): if not c.shell and not pwndbg_cmds: continue + # Don't print aliases + if c.is_alias: + continue + name = c.__name__ docs = c.__doc__ @@ -134,6 +153,6 @@ def list_and_filter_commands(filter_str, pwndbg_cmds=True, shell_cmds=False): docs = docs.splitlines()[0] if not filter_str or filter_str in name.lower() or (docs and filter_str in docs.lower()): - results.append((name, docs)) + results.append((name, c.aliases, docs)) return results diff --git a/pwndbg/gdblib/prompt.py b/pwndbg/gdblib/prompt.py index 2d3b30efa..fa62a2031 100644 --- a/pwndbg/gdblib/prompt.py +++ b/pwndbg/gdblib/prompt.py @@ -14,7 +14,9 @@ from pwndbg.lib.tips import get_tip_of_the_day funcs_list_str = ", ".join(message.notice("$" + f.name) for f in pwndbg.gdblib.functions.functions) -num_pwndbg_cmds = sum(1 for _ in filter(lambda c: not c.shell, pwndbg.commands.commands)) +num_pwndbg_cmds = sum( + 1 for _ in filter(lambda c: not (c.shell or c.is_alias), pwndbg.commands.commands) +) num_shell_cmds = sum(1 for _ in filter(lambda c: c.shell, pwndbg.commands.commands)) hint_lines = ( "loaded %i pwndbg commands and %i shell commands. Type %s for a list." diff --git a/tests/gdb-tests/tests/test_misc.py b/tests/gdb-tests/tests/test_misc.py index c5f7c0987..d96099c7f 100644 --- a/tests/gdb-tests/tests/test_misc.py +++ b/tests/gdb-tests/tests/test_misc.py @@ -1,13 +1,15 @@ +import pytest + import pwndbg.commands from pwndbg.commands.misc import list_and_filter_commands STACK_COMMANDS = [ - ("canary", "Print out the current stack canary."), - ("context", "Print out the current register, instruction, and stack context."), - ("down", "Select and print stack frame called by this one."), - ("retaddr", "Print out the stack addresses that contain return addresses."), - ("stack", "Dereferences on stack data with specified count and offset."), - ("up", "Select and print stack frame that called this one."), + ("canary", [], "Print out the current stack canary."), + ("context", ["ctx"], "Print out the current register, instruction, and stack context."), + ("down", [], "Select and print stack frame called by this one."), + ("retaddr", [], "Print out the stack addresses that contain return addresses."), + ("stack", [], "Dereferences on stack data with specified count and offset."), + ("up", [], "Select and print stack frame that called this one."), ] @@ -16,37 +18,20 @@ def test_list_and_filter_commands_filter(): assert cmd in list_and_filter_commands("stack") -def test_list_and_filter_commands_full_list(): - all_commands = list_and_filter_commands("", pwndbg_cmds=True, shell_cmds=True) - - def get_doc(c): - return c.__doc__.strip().splitlines()[0] if c.__doc__ else None - - cmd_name_docs = [(c.__name__, get_doc(c)) for c in pwndbg.commands.commands] - cmd_name_docs.sort() - - assert all_commands == cmd_name_docs - - -def test_list_and_filter_commands_shell(): - all_commands = list_and_filter_commands("", pwndbg_cmds=False, shell_cmds=True) +@pytest.mark.parametrize("pwndbg_cmds,shell_cmds", [(True, True), (False, True), (True, False)]) +def test_list_and_filter_commands_full_list(pwndbg_cmds, shell_cmds): + all_commands = list_and_filter_commands("", pwndbg_cmds=pwndbg_cmds, shell_cmds=shell_cmds) def get_doc(c): return c.__doc__.strip().splitlines()[0] if c.__doc__ else None - cmd_name_docs = [(c.__name__, get_doc(c)) for c in pwndbg.commands.commands if c.shell] - cmd_name_docs.sort() - - assert all_commands == cmd_name_docs - - -def test_list_and_filter_commands_pwndbg_cmds(): - all_commands = list_and_filter_commands("", pwndbg_cmds=True, shell_cmds=False) - - def get_doc(c): - return c.__doc__.strip().splitlines()[0] if c.__doc__ else None + commands = [] + if pwndbg_cmds: + commands.extend([c for c in pwndbg.commands.commands if not c.is_alias and not c.shell]) + if shell_cmds: + commands.extend([c for c in pwndbg.commands.commands if not c.is_alias and c.shell]) - cmd_name_docs = [(c.__name__, get_doc(c)) for c in pwndbg.commands.commands if not c.shell] + cmd_name_docs = [(c.__name__, c.aliases, get_doc(c)) for c in commands] cmd_name_docs.sort() assert all_commands == cmd_name_docs