From 26db4533aa08d77c4bbc359b4760a0944e0c6b23 Mon Sep 17 00:00:00 2001 From: Allen Chang <57740058+AC01010@users.noreply.github.com> Date: Tue, 5 Aug 2025 08:10:51 -0400 Subject: [PATCH] Add command to display information about kernel syscalls (#3103) * merge conflicts * Fix lint errors * ksyscalls * Update pwndbg/commands/ksyscalls.py * Requested changes * docs * merge fix * Minor change * Minor change in test * Check sys_call_table symbol --------- Co-authored-by: Disconnect3d --- docs/commands/index.md | 1 + docs/commands/kernel/ksyscalls.md | 23 +++++++ pwndbg/commands/__init__.py | 1 + pwndbg/commands/ksyscalls.py | 67 +++++++++++++++++++ .../qemu_system/tests/test_commands_kernel.py | 10 +++ 5 files changed, 102 insertions(+) create mode 100644 docs/commands/kernel/ksyscalls.md diff --git a/docs/commands/index.md b/docs/commands/index.md index 0f9483c98..dfba16710 100644 --- a/docs/commands/index.md +++ b/docs/commands/index.md @@ -86,6 +86,7 @@ - [knft-list-rules](kernel/knft-list-rules.md) - Dump netfilter rules form a specific chain - [knft-list-sets](kernel/knft-list-sets.md) - Dump netfilter sets from a specific table - [knft-list-tables](kernel/knft-list-tables.md) - Dump netfliter tables from a specific network namespace +- [ksyscalls](kernel/ksyscalls.md) - Displays Linux syscall table, including names and addresses of syscalls. - [ktask](kernel/ktask.md) - Displays information about kernel tasks. - [kversion](kernel/kversion.md) - Outputs the kernel version (/proc/version). - [msr](kernel/msr.md) - Read or write to Model Specific Register (MSR) diff --git a/docs/commands/kernel/ksyscalls.md b/docs/commands/kernel/ksyscalls.md new file mode 100644 index 000000000..5a75a2a2d --- /dev/null +++ b/docs/commands/kernel/ksyscalls.md @@ -0,0 +1,23 @@ + +# ksyscalls + +```text +usage: ksyscalls [-h] [syscall_name] + +``` + +Displays Linux syscall table, including names and addresses of syscalls. +### Positional arguments + +|Positional Argument|Help| +| :--- | :--- | +|syscall_name|A syscall name to search for| + +### Optional arguments + +|Short|Long|Help| +| :--- | :--- | :--- | +|-h|--help|show this help message and exit| + + + diff --git a/pwndbg/commands/__init__.py b/pwndbg/commands/__init__.py index f8d583290..7b125ac79 100644 --- a/pwndbg/commands/__init__.py +++ b/pwndbg/commands/__init__.py @@ -925,6 +925,7 @@ def load_commands() -> None: import pwndbg.commands.klookup import pwndbg.commands.kmod import pwndbg.commands.knft + import pwndbg.commands.ksyscalls import pwndbg.commands.ktask import pwndbg.commands.kversion import pwndbg.commands.leakfind diff --git a/pwndbg/commands/ksyscalls.py b/pwndbg/commands/ksyscalls.py index 9d48db4f9..a56f7294d 100644 --- a/pwndbg/commands/ksyscalls.py +++ b/pwndbg/commands/ksyscalls.py @@ -1 +1,68 @@ +""" +Displays the syscall table for kernel debugging. +""" + from __future__ import annotations + +import argparse + +import pwndbg.color.message as message +import pwndbg.commands + +parser = argparse.ArgumentParser( + description="Displays Linux syscall table, including names and addresses of syscalls." +) + +parser.add_argument("syscall_name", nargs="?", type=str, help="A syscall name to search for") + + +@pwndbg.commands.Command(parser, category=pwndbg.commands.CommandCategory.KERNEL) +@pwndbg.commands.OnlyWhenQemuKernel +@pwndbg.commands.OnlyWhenPagingEnabled +@pwndbg.commands.OnlyWithKernelDebugSymbols +def ksyscalls(syscall_name=None) -> None: + # Look up the address of the sys_call_table symbol. + table_addr = pwndbg.aglib.symbol.lookup_symbol_addr("sys_call_table") + if table_addr is None: + print( + "The sys_call_table symbol was not found. This may indicate that the symbol is not available in the current build." + ) + return + + try: + # Compute number of syscalls in the table. + sc_count = int( + pwndbg.dbg.selected_frame().evaluate_expression( + "sizeof(sys_call_table) / sizeof(void *)" + ) + ) + except pwndbg.dbg_mod.Error: + print( + "The sys_call_table symbol was not found. This may indicate that the symbol is not available in the current build." + ) + return + + try: + print(f"Syscall table address with {sc_count} entries found at {table_addr:#x}.\n") + + size_ptr = pwndbg.aglib.arch.ptrsize + + print(f"{'':>4} {'Address':>18} {'Symbol'}") + + # Iterate through the syscall table entries. + + for i in range(sc_count): + sc_addr = pwndbg.aglib.memory.read_pointer_width(table_addr + i * size_ptr) + + symbol = pwndbg.aglib.symbol.resolve_addr(sc_addr) + + if syscall_name is not None: + if symbol is None or syscall_name not in symbol: + continue + + print_entry = lambda: print(f"{i:>4} {hex(sc_addr):>18} {symbol or ''}") + print_entry() + + except pwndbg.dbg_mod.Error as e: + print(message.error(f"ERROR: {e}")) + return diff --git a/tests/library/qemu_system/tests/test_commands_kernel.py b/tests/library/qemu_system/tests/test_commands_kernel.py index 62daa91c7..1caf04075 100644 --- a/tests/library/qemu_system/tests/test_commands_kernel.py +++ b/tests/library/qemu_system/tests/test_commands_kernel.py @@ -58,6 +58,16 @@ def test_command_kmod(): assert "Kernel modules address found at" in res or "The modules symbol was not found." in res +def test_command_ksyscalls(): + if not pwndbg.aglib.kernel.has_debug_symbols(): + res = gdb.execute("ksyscalls", to_string=True) + assert "may only be run when debugging a Linux kernel with debug" in res + return + + res = gdb.execute("ksyscalls", to_string=True) + assert "entries found at" in res or "sys_call_table symbol was not found" in res + + def test_command_ktask(): if not pwndbg.aglib.kernel.has_debug_info(): res = gdb.execute("ktask", to_string=True)