summaryrefslogtreecommitdiffstats
path: root/bin/syscallargs
diff options
context:
space:
mode:
Diffstat (limited to 'bin/syscallargs')
-rwxr-xr-xbin/syscallargs125
1 files changed, 125 insertions, 0 deletions
diff --git a/bin/syscallargs b/bin/syscallargs
new file mode 100755
index 0000000..ecc6571
--- /dev/null
+++ b/bin/syscallargs
@@ -0,0 +1,125 @@
+#!/usr/bin/env python3
+
+# syscallargs -- List Linux system calls and their arguments, by Tanel Poder [https://0x.tools]
+# Copyright 2024 Tanel Poder
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# NOTES
+# Basic documentation is in my blog entry:
+# https://tanelpoder.com/posts/list-linux-system-call-arguments-with-syscallargs/
+
+# You can list system call numbers using:
+# ausyscall --dump
+#
+# Or check psnproc.py to see how psn reads it from /usr/include/.../unistd.h files
+
+import os, sys, argparse, signal
+
+__version__ = "1.0.1"
+__author__ = "Tanel Poder"
+__date__ = "2024-06-11"
+__description__ = "List system call arguments from debugfs"
+__url__ = "https://0x.tools"
+
+DEFAULT_PATH='/sys/kernel/debug/tracing/events/syscalls'
+
+
+def parse_syscall_format(file_path):
+ syscall_info = {}
+ args_found = False
+ with open(file_path, 'r') as file:
+ lines = file.readlines()
+ for line in lines:
+ line = line.strip()
+ if line.startswith('name:'):
+ # split line to fields, also remove leading "sys_enter_" with [10:]
+ syscall_name = line.split(':')[1].strip()[10:]
+ syscall_info['name'] = syscall_name
+ elif line.startswith('field:'):
+ # syscall arguments come immediately after "__syscall_nr" line
+ # saving syscall_nr to populate dict as not all syscalls have arguments
+ if line.startswith('field:int __syscall_nr'):
+ args_found = True
+ if args_found:
+ # example: field:char * buf; offset:24; size:8; signed:0;
+ field_info = line.split(';')[0].replace(':',' ').split()
+ if len(field_info) >= 3:
+ field_type = ' '.join(field_info[1:-1])
+ field_name = field_info[-1]
+ if 'args' not in syscall_info:
+ syscall_info['args'] = []
+ syscall_info['args'].append((field_type, field_name))
+
+ return syscall_info
+
+def list_syscalls(syscalls_path):
+ syscalls = []
+ for root, dirs, files in os.walk(syscalls_path):
+ dirs[:] = [d for d in dirs if d.startswith("sys_enter_")]
+ for file in files:
+ if file == 'format':
+ file_path = os.path.join(root, file)
+ syscall_info = parse_syscall_format(file_path)
+ syscalls.append(syscall_info)
+
+ if not syscalls:
+ if os.geteuid() != 0:
+ print("Error: No syscalls found. Please run this as root or mount the debugfs with proper permissions.", file=sys.stderr)
+ else:
+ print(f"Error: No syscalls found in the specified path {syscalls_path}", file=sys.stderr)
+ sys.exit(1)
+
+ return syscalls
+
+
+def main():
+ signal.signal(signal.SIGPIPE, signal.SIG_DFL) # for things like: ./syscallargs | head
+
+ parser = argparse.ArgumentParser(description='List kernel system calls and their arguments from Linux debugfs.')
+ parser.add_argument('-l', '--newlines', action='store_true', help='Print each system call and its arguments on a new line')
+ parser.add_argument('-t', '--typeinfo', action='store_true', help='Include type information for syscall arguments in the output')
+ parser.add_argument('-V', '--version', action='version', version=f"%(prog)s {__version__} by {__author__} [{__url__}]", help='Show the program version and exit')
+ parser.add_argument('--path', type=str, default=DEFAULT_PATH, help=f'Path to the debugfs syscalls directory: {DEFAULT_PATH}')
+
+ args = parser.parse_args()
+ syscalls_path = args.path
+ syscalls = list_syscalls(syscalls_path)
+
+ for syscall in syscalls:
+ print(f"{syscall['name']}", end="\n" if args.newlines else "(")
+
+ args_list = []
+ for index, (arg_type, arg_name) in enumerate(syscall['args'][1:]):
+ if args.typeinfo:
+ argout = f"({arg_type}) {arg_name}"
+ else:
+ argout = arg_name
+ if args.newlines:
+ print(f" {index}: {argout}")
+ else:
+ args_list.append(argout)
+ if not args.newlines:
+ print(", ".join(args_list), end="")
+
+
+ print("" if args.newlines else ");")
+
+if __name__ == "__main__":
+ main()
+
+