diff options
Diffstat (limited to 'bin/syscallargs')
-rwxr-xr-x | bin/syscallargs | 125 |
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() + + |