Refactor cymbol command to use subcommands (#3160)

* Add --show-all flag to cymbol to list all custom structure names

* Fix: Apply ruff formatting to cymbol.py

* Fix: Regenerate cymbol docs for --show-all flag

* Refactor cymbol command to use subcommands

* fixed Lint/lint prolem

* fixed Lint/lint problem

* fixed Lint/lint problem

* fixed Lint/lint problem

* added --flag

* Fix: remove --show-all causing argparse conflict

* fixed code

* Update pwndbg/commands/cymbol.py

Co-authored-by: k4lizen <124312252+k4lizen@users.noreply.github.com>

* Update docs/commands/misc/cymbol.md

---------

Co-authored-by: parrot <parrot@localhost.localdomain>
Co-authored-by: An0nAN4N7 <143926241+An0nAN4N7@users.noreply.github.com>
Co-authored-by: Disconnect3d <dominik.b.czarnota@gmail.com>
Co-authored-by: k4lizen <124312252+k4lizen@users.noreply.github.com>
pull/3167/head
sh4dowkey 5 months ago committed by GitHub
parent 7e7c47f209
commit 7d92563ab4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -147,7 +147,7 @@
- [checksec](misc/checksec.md) - Prints out the binary security settings using `checksec`.
- [comm](misc/comm.md) - Put comments in assembly code.
- [cyclic](misc/cyclic.md) - Cyclic pattern creator/finder.
- [cymbol](misc/cymbol.md) - Add, show, load, edit, or delete custom structures in plain C.
- [cymbol](misc/cymbol.md) - Manage custom C structures in pwndbg. Supports project-specific auto-loading from .gdbinit.
- [down](misc/down.md) - Select and print stack frame called by this one.
- [dt](misc/dt.md) - Dump out information on a type (e.g. ucontext_t).
- [dumpargs](misc/dumpargs.md) - Prints determined arguments for call/syscall instruction.

@ -3,24 +3,41 @@
<small style="color: lightgray;">(only in GDB)</small>
```text
usage: cymbol [-h] [-a name] [-f filepath] [-r name] [-e name] [-l name]
[-s name] [--show-all]
usage: cymbol [-h] {add,remove,edit,load,show,file,show-all} ...
```
Add, show, load, edit, or delete custom structures in plain C.
Manage custom C structures in pwndbg. Supports project-specific auto-loading from .gdbinit.
### Positional arguments
|Positional Argument|Help|
| :--- | :--- |
|subcommand|Available subcommands|
### Optional arguments
|Short|Long|Help|
| :--- | :--- | :--- |
|-h|--help|show this help message and exit|
|-a|--add|Add a new custom structure|
|-f|--file|Add a new custom structure from header file|
|-r|--remove|Remove an existing custom structure|
|-e|--edit|Edit an existing custom structure|
|-l|--load|Load an existing custom structure|
|-s|--show|Show the source code of an existing custom structure|
||--show-all|Show names of all available custom structures|
### Notes
The `cymbol` command loads custom C structs and symbols into the debugger using GCC under the hood.
Usage Example:
`cymbol file --force ./structs.h`
--force:
Use this flag to force symbol reloading, even if symbols with the same name already exist.
Warning:
If a loaded structure defines a symbol that already exists, the debugger may prefer the original
symbol or behave unexpectedly. Its recommended to use unique struct names to avoid
symbol conflicts.
Tip:
You can add this command to your `.gdbinit` file for automatic loading:
`cymbol file --force ./path/to/structs.h`
<!-- 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,7 +8,7 @@ is advised to configure the 'gcc-config-path' config parameter to your own cross
gnu gcc compiled toolchain for your target architecture.
You are advised to configure the 'cymbol-editor' config parameter to the path of your
favorite text editor. Otherwise cymbol exapnds $EDITOR and $VISUAL environment variables
favorite text editor. Otherwise cymbol expands $EDITOR and $VISUAL environment variables
to find the path to the default text editor.
"""
@ -126,10 +126,10 @@ def generate_debug_symbols(
return pwndbg_debug_symbols_output_file
def add_custom_structure(custom_structure_name: str) -> None:
def add_custom_structure(custom_structure_name: str, force=False):
pwndbg_custom_structure_path = os.path.join(pwndbg_cachedir, custom_structure_name) + ".c"
if os.path.exists(pwndbg_custom_structure_path):
if os.path.exists(pwndbg_custom_structure_path) and not force:
option = input(
message.notice(
"A custom structure was found with the given name, would you like to overwrite it? [y/n] "
@ -154,8 +154,9 @@ def add_custom_structure(custom_structure_name: str) -> None:
load_custom_structure.__wrapped__(custom_structure_name, pwndbg_custom_structure_path)
def add_structure_from_header(header_file: str, custom_structure_name: str = None) -> None:
# Properly handle the provided or default name for the custom structure
def add_structure_from_header(
header_file: str, custom_structure_name: str = None, force: bool = False
) -> None:
custom_structure_name = (
custom_structure_name.strip()
if custom_structure_name
@ -169,11 +170,21 @@ def add_structure_from_header(header_file: str, custom_structure_name: str = Non
pwndbg_custom_structure_path = os.path.join(pwndbg_cachedir, custom_structure_name) + ".c"
if os.path.exists(pwndbg_custom_structure_path):
option = input(
message.notice(f"Structure '{custom_structure_name}' already exists. Overwrite? [y/n] ")
)
if option != "y":
return
if not force:
option = input(
message.notice(
f"Structure '{custom_structure_name}' already exists. Overwrite? [y/n] "
)
)
if option.lower() != "y":
print(message.notice("Aborted by user."))
return
else:
print(
message.warn(
f"Overwriting existing structure '{custom_structure_name}' due to --force flag."
)
)
try:
with open(header_file, "r") as src, open(pwndbg_custom_structure_path, "w") as f:
@ -186,7 +197,6 @@ def add_structure_from_header(header_file: str, custom_structure_name: str = Non
print(message.error(f"Failed to process header file: {e}"))
return
# Avoid checking for file existance. Call the decorator wrapper directly.
load_custom_structure.__wrapped__(custom_structure_name, pwndbg_custom_structure_path)
@ -251,84 +261,86 @@ def show_custom_structure(custom_structure_name: str, custom_structure_path: str
parser = argparse.ArgumentParser(
description="Add, show, load, edit, or delete custom structures in plain C."
)
parser.add_argument(
"-a",
"--add",
metavar="name",
help="Add a new custom structure",
default=None,
type=str,
)
parser.add_argument(
"-f",
"--file",
metavar="filepath",
help="Add a new custom structure from header file",
default=None,
type=str,
)
parser.add_argument(
"-r",
"--remove",
metavar="name",
help="Remove an existing custom structure",
default=None,
type=str,
description="Manage custom C structures in pwndbg. Supports project-specific auto-loading from .gdbinit."
)
parser.add_argument(
"-e",
"--edit",
metavar="name",
help="Edit an existing custom structure",
default=None,
type=str,
)
parser.add_argument(
"-l",
"--load",
metavar="name",
help="Load an existing custom structure",
default=None,
type=str,
)
parser.add_argument(
"-s",
"--show",
metavar="name",
help="Show the source code of an existing custom structure",
default=None,
type=str,
)
parser.add_argument(
"--show-all",
help="Show names of all available custom structures",
action="store_true",
subparsers = parser.add_subparsers(dest="subcommand", help="Available subcommands")
add_parser = subparsers.add_parser("add", help="Add a custom structure")
add_parser.add_argument("name", help="Name of custom structure")
add_parser.add_argument(
"--force", action="store_true", help="Overwrite if structure already exists"
)
remove_parser = subparsers.add_parser("remove", help="Remove a custom structure")
remove_parser.add_argument("name", help="Name of custom structure")
edit_parser = subparsers.add_parser("edit", help="Edit a custom structure")
edit_parser.add_argument("name", help="Name of custom structure")
load_parser = subparsers.add_parser("load", help="Load a custom structure")
load_parser.add_argument("name", help="Name of custom structure")
show_parser = subparsers.add_parser("show", help="Show a custom structure")
show_parser.add_argument("name", help="Name of custom structure")
file_parser = subparsers.add_parser("file", help="Add a structure from a header file")
file_parser.add_argument("path", help="Path to header file")
file_parser.add_argument("--name", help="Optional structure name")
file_parser.add_argument("--force", action="store_true", help="Overwrite if exists")
show_all_parser = subparsers.add_parser("show-all", help="Show all stored structure")
@pwndbg.commands.Command(
parser,
category=CommandCategory.MISC,
notes="""
@pwndbg.commands.Command(parser, category=CommandCategory.MISC)
The `cymbol` command loads custom C structs and symbols into the debugger using GCC under the hood.
Usage Example:
`cymbol file --force ./structs.h`
--force:
Use this flag to force symbol reloading, even if symbols with the same name already exist.
Warning:
If a loaded structure defines a symbol that already exists, the debugger may prefer the original
symbol or behave unexpectedly. Its recommended to use unique struct names to avoid
symbol conflicts.
Tip:
You can add this command to your `.gdbinit` file for automatic loading:
`cymbol file --force ./path/to/structs.h`
""",
)
def cymbol(
add: str, file: str, remove: str, edit: str, load: str, show: str, show_all: bool
) -> None:
if add:
add_custom_structure(add)
elif file:
add_structure_from_header(file)
elif remove:
remove_custom_structure(remove)
elif edit:
edit_custom_structure(edit)
elif load:
load_custom_structure(load)
elif show:
show_custom_structure(show)
elif show_all:
print(message.notice("Available custom structure names:\n"))
for file in os.listdir(pwndbg_cachedir):
if file.endswith(".c"):
name = os.path.splitext(file)[0]
print(f" - {name}")
else:
parser.print_help()
subcommand: str = None,
name: str = None,
path: str = None,
force=False,
):
match subcommand:
case "add":
add_custom_structure(name, force=force)
case "remove":
remove_custom_structure(name)
case "edit":
edit_custom_structure(name)
case "load":
load_custom_structure(name)
case "file":
add_structure_from_header(path, name, force=force)
case "show":
show_custom_structure(name)
case "show-all":
print(message.notice("Available custom structure names:\n"))
for file in os.listdir(pwndbg_cachedir):
if file.endswith(".c"):
name = os.path.splitext(file)[0]
print(f" - {name}")
case _:
parser.print_help()

Loading…
Cancel
Save