编译运行ebpf代码的流水账
接上篇博客
在写上篇博客的时候,了解到 ebpf 相当于一个内核中的虚拟机,也许可以绕过 netfilter 内核编程时不同版本函数差异过大的限制。于是就去搜索了一下,因为如果可以做一些安全小工具在内核层面运行的话,会减少在用户层面留下的操作痕迹,剧透一下最后没有成功,但是也踩了一些坑学到了一些有趣的东西,这篇博客就是我实验、思考 ebpf 相关问题的一篇流水账
一、源码构建bcc
搜索 ebpf 首先发现了很多关于 bcc 的介绍,bcc 是基于 linux ebpf 特性的内核跟踪及调试工具,它提供了一个名为 bcc 的 python 库,简化了 ebpf 应用的开发过程,可以直接在 python 里面嵌入通过 c 语言写的 bpf 程序,当前 vps 环境是 ubuntu 18.04,内核版本 4.15
安装依赖的工具
sudo apt-get -y install bison build-essential cmake flex git libedit-dev \ libllvm6.0 llvm-6.0-dev libclang-6.0-dev python zlib1g-dev libelf-dev
安装 python3 的包
sudo apt-get install python3-distutils sudo apt-get install python3-pip sudo apt-get install python3-setuptools
下载 bcc-src-with-submodule.tar.gz 源码,地址:https://github.com/iovisor/bcc/releases
新建一个 bcc 源码文件夹,把源码拖进去解压,执行如下命令即可安装成功
mkdir ebpf; cd ebpf tar -zxvf bcc-src-with-submodule.tar.gz mkdir bcc/build; cd bcc/build cmake .. make sudo make install cmake -DPYTHON_CMD=python3 .. # build python3 binding pushd src/python/ make sudo make install popd
bcc tools 的默认安装目录为 /usr/share/bcc/tools,/root/ebpf/bcc/examples 路径下也有一些样例,如图为 python3 hello_world.py 的输出,但是这样的 py 脚本和 bcc tools 路径下编译好的二进制文件都需要事先安装环境,无法满足渗透小工具需要上传到目标机运行的需求
二、bpf环境配置
当前 vps 环境是 ubuntu 20.04,内核版本 5.4,安装依赖的工具
apt install libncurses5-dev flex bison libelf-dev binutils-dev libssl-dev apt install clang apt install llvm
apt install bc
下载内核源代码并解压
apt-get source linux cd /usr/src/linux-source-5.4.0 tar -jxvf /usr/src/linux-source-5.4.0/linux-source-5.4.0.tar.bz2
切换到内核源代码的根目录,生成内核编译时的头文件,把本机的 .config 文件复制过来,再使用 make 命令编译 samples/bpf/ 目录下的所有示例代码即可生成 bpf 的可执行文件,还可以用 make menuconfig 命令可视化选择想为内核添加的内核模块,我就直接复制本机的 .config 过来了,执行这个命令时如果 vps 终端太小了会报错,全屏就可以解决
make headers_install cp -a /boot/config-$(uname -r) ./.config make M=samples/bpf/
编写一个简单的 bpf 程序,需要在 samples/bpf/ 文件夹中编写 hello_kern.c,hello_user.c ,修改 Makefile,最后在源代码根目录下执行 make M=samples/bpf 编译生成可执行文件
hello_kern.c
#include <linux/bpf.h> #include "bpf_helpers.h" #define SEC(NAME) __attribute__((section(NAME), used)) SEC("tracepoint/syscalls/sys_enter_execve") int bpf_prog(void *ctx){ char msg[] = "hello world\n"; bpf_trace_printk(msg, sizeof(msg)); return 0; } char _license[] SEC("license") = "GPL";
hello_user.c
#include <stdio.h> #include "bpf_load.h" int main(int argc, char **argv){ if(load_bpf_file("hello_kern.o")!=0){ printf("The kernel didn't load BPF program\n"); return -1; } read_trace_pipe(); return 0; }
修改 Makefile 文件的三个地方,分别在对应的位置添加 hostprogs-y += hellohostprogs-y += hello,hello-objs := bpf_load.o hello_user.o,always += hello_kern.o,修改后完整的 Makefile 文件如下
# SPDX-License-Identifier: GPL-2.0 BPF_SAMPLES_PATH ?= $(abspath $(srctree)/$(src)) TOOLS_PATH := $(BPF_SAMPLES_PATH)/../../tools # List of programs to build hostprogs-y := test_lru_dist hostprogs-y += sock_example hostprogs-y += fds_example hostprogs-y += sockex1 hostprogs-y += sockex2 hostprogs-y += sockex3 hostprogs-y += tracex1 hostprogs-y += tracex2 hostprogs-y += tracex3 hostprogs-y += tracex4 hostprogs-y += tracex5 hostprogs-y += tracex6 hostprogs-y += tracex7 hostprogs-y += test_probe_write_user hostprogs-y += trace_output hostprogs-y += lathist hostprogs-y += offwaketime hostprogs-y += spintest hostprogs-y += map_perf_test hostprogs-y += test_overhead hostprogs-y += test_cgrp2_array_pin hostprogs-y += test_cgrp2_attach hostprogs-y += test_cgrp2_sock hostprogs-y += test_cgrp2_sock2 hostprogs-y += xdp1 hostprogs-y += xdp2 hostprogs-y += xdp_router_ipv4 hostprogs-y += test_current_task_under_cgroup hostprogs-y += trace_event hostprogs-y += sampleip hostprogs-y += tc_l2_redirect hostprogs-y += lwt_len_hist hostprogs-y += xdp_tx_iptunnel hostprogs-y += test_map_in_map hostprogs-y += per_socket_stats_example hostprogs-y += xdp_redirect hostprogs-y += xdp_redirect_map hostprogs-y += xdp_redirect_cpu hostprogs-y += xdp_monitor hostprogs-y += xdp_rxq_info hostprogs-y += syscall_tp hostprogs-y += cpustat hostprogs-y += xdp_adjust_tail hostprogs-y += xdpsock hostprogs-y += xdp_fwd hostprogs-y += task_fd_query hostprogs-y += xdp_sample_pkts hostprogs-y += ibumad hostprogs-y += hbm hostprogs-y += hello # Libbpf dependencies LIBBPF = $(TOOLS_PATH)/lib/bpf/libbpf.a CGROUP_HELPERS := ../../tools/testing/selftests/bpf/cgroup_helpers.o TRACE_HELPERS := ../../tools/testing/selftests/bpf/trace_helpers.o fds_example-objs := fds_example.o sockex1-objs := sockex1_user.o sockex2-objs := sockex2_user.o sockex3-objs := bpf_load.o sockex3_user.o tracex1-objs := bpf_load.o tracex1_user.o tracex2-objs := bpf_load.o tracex2_user.o tracex3-objs := bpf_load.o tracex3_user.o tracex4-objs := bpf_load.o tracex4_user.o tracex5-objs := bpf_load.o tracex5_user.o tracex6-objs := bpf_load.o tracex6_user.o tracex7-objs := bpf_load.o tracex7_user.o test_probe_write_user-objs := bpf_load.o test_probe_write_user_user.o trace_output-objs := bpf_load.o trace_output_user.o $(TRACE_HELPERS) lathist-objs := bpf_load.o lathist_user.o offwaketime-objs := bpf_load.o offwaketime_user.o $(TRACE_HELPERS) spintest-objs := bpf_load.o spintest_user.o $(TRACE_HELPERS) map_perf_test-objs := bpf_load.o map_perf_test_user.o test_overhead-objs := bpf_load.o test_overhead_user.o test_cgrp2_array_pin-objs := test_cgrp2_array_pin.o test_cgrp2_attach-objs := test_cgrp2_attach.o test_cgrp2_sock-objs := test_cgrp2_sock.o test_cgrp2_sock2-objs := bpf_load.o test_cgrp2_sock2.o xdp1-objs := xdp1_user.o # reuse xdp1 source intentionally xdp2-objs := xdp1_user.o xdp_router_ipv4-objs := xdp_router_ipv4_user.o test_current_task_under_cgroup-objs := bpf_load.o $(CGROUP_HELPERS) \ test_current_task_under_cgroup_user.o trace_event-objs := bpf_load.o trace_event_user.o $(TRACE_HELPERS) sampleip-objs := bpf_load.o sampleip_user.o $(TRACE_HELPERS) tc_l2_redirect-objs := bpf_load.o tc_l2_redirect_user.o lwt_len_hist-objs := bpf_load.o lwt_len_hist_user.o xdp_tx_iptunnel-objs := xdp_tx_iptunnel_user.o test_map_in_map-objs := bpf_load.o test_map_in_map_user.o per_socket_stats_example-objs := cookie_uid_helper_example.o xdp_redirect-objs := xdp_redirect_user.o xdp_redirect_map-objs := xdp_redirect_map_user.o xdp_redirect_cpu-objs := bpf_load.o xdp_redirect_cpu_user.o xdp_monitor-objs := bpf_load.o xdp_monitor_user.o xdp_rxq_info-objs := xdp_rxq_info_user.o syscall_tp-objs := bpf_load.o syscall_tp_user.o cpustat-objs := bpf_load.o cpustat_user.o xdp_adjust_tail-objs := xdp_adjust_tail_user.o xdpsock-objs := xdpsock_user.o xdp_fwd-objs := xdp_fwd_user.o task_fd_query-objs := bpf_load.o task_fd_query_user.o $(TRACE_HELPERS) xdp_sample_pkts-objs := xdp_sample_pkts_user.o $(TRACE_HELPERS) ibumad-objs := bpf_load.o ibumad_user.o $(TRACE_HELPERS) hbm-objs := bpf_load.o hbm.o $(CGROUP_HELPERS) hello-objs := bpf_load.o hello_user.o # Tell kbuild to always build the programs always := $(hostprogs-y) always += sockex1_kern.o always += sockex2_kern.o always += sockex3_kern.o always += tracex1_kern.o always += tracex2_kern.o always += tracex3_kern.o always += tracex4_kern.o always += tracex5_kern.o always += tracex6_kern.o always += tracex7_kern.o always += sock_flags_kern.o always += test_probe_write_user_kern.o always += trace_output_kern.o always += tcbpf1_kern.o always += tc_l2_redirect_kern.o always += lathist_kern.o always += offwaketime_kern.o always += spintest_kern.o always += map_perf_test_kern.o always += test_overhead_tp_kern.o always += test_overhead_raw_tp_kern.o always += test_overhead_kprobe_kern.o always += parse_varlen.o parse_simple.o parse_ldabs.o always += test_cgrp2_tc_kern.o always += xdp1_kern.o always += xdp2_kern.o always += xdp_router_ipv4_kern.o always += test_current_task_under_cgroup_kern.o always += trace_event_kern.o always += sampleip_kern.o always += lwt_len_hist_kern.o always += xdp_tx_iptunnel_kern.o always += test_map_in_map_kern.o always += cookie_uid_helper_example.o always += tcp_synrto_kern.o always += tcp_rwnd_kern.o always += tcp_bufs_kern.o always += tcp_cong_kern.o always += tcp_iw_kern.o always += tcp_clamp_kern.o always += tcp_basertt_kern.o always += tcp_tos_reflect_kern.o always += tcp_dumpstats_kern.o always += xdp_redirect_kern.o always += xdp_redirect_map_kern.o always += xdp_redirect_cpu_kern.o always += xdp_monitor_kern.o always += xdp_rxq_info_kern.o always += xdp2skb_meta_kern.o always += syscall_tp_kern.o always += cpustat_kern.o always += xdp_adjust_tail_kern.o always += xdp_fwd_kern.o always += task_fd_query_kern.o always += xdp_sample_pkts_kern.o always += ibumad_kern.o always += hbm_out_kern.o always += hbm_edt_kern.o always += hello_kern.o KBUILD_HOSTCFLAGS += -I$(objtree)/usr/include KBUILD_HOSTCFLAGS += -I$(srctree)/tools/lib/bpf/ KBUILD_HOSTCFLAGS += -I$(srctree)/tools/testing/selftests/bpf/ KBUILD_HOSTCFLAGS += -I$(srctree)/tools/lib/ -I$(srctree)/tools/include KBUILD_HOSTCFLAGS += -I$(srctree)/tools/perf KBUILD_HOSTCFLAGS += -DHAVE_ATTR_TEST=0 HOSTCFLAGS_bpf_load.o += -I$(objtree)/usr/include -Wno-unused-variable KBUILD_HOSTLDLIBS += $(LIBBPF) -lelf HOSTLDLIBS_tracex4 += -lrt HOSTLDLIBS_trace_output += -lrt HOSTLDLIBS_map_perf_test += -lrt HOSTLDLIBS_test_overhead += -lrt HOSTLDLIBS_xdpsock += -pthread # Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline: # make samples/bpf/ LLC=~/git/llvm/build/bin/llc CLANG=~/git/llvm/build/bin/clang LLC ?= llc CLANG ?= clang LLVM_OBJCOPY ?= llvm-objcopy BTF_PAHOLE ?= pahole # Detect that we're cross compiling and use the cross compiler ifdef CROSS_COMPILE HOSTCC = $(CROSS_COMPILE)gcc CLANG_ARCH_ARGS = -target $(ARCH) endif # Don't evaluate probes and warnings if we need to run make recursively ifneq ($(src),) HDR_PROBE := $(shell echo "\#include <linux/types.h>\n struct list_head { int a; }; int main() { return 0; }" | \ $(HOSTCC) $(KBUILD_HOSTCFLAGS) -x c - -o /dev/null 2>/dev/null && \ echo okay) ifeq ($(HDR_PROBE),) $(warning WARNING: Detected possible issues with include path.) $(warning WARNING: Please install kernel headers locally (make headers_install).) endif BTF_LLC_PROBE := $(shell $(LLC) -march=bpf -mattr=help 2>&1 | grep dwarfris) BTF_PAHOLE_PROBE := $(shell $(BTF_PAHOLE) --help 2>&1 | grep BTF) BTF_OBJCOPY_PROBE := $(shell $(LLVM_OBJCOPY) --help 2>&1 | grep -i 'usage.*llvm') BTF_LLVM_PROBE := $(shell echo "int main() { return 0; }" | \ $(CLANG) -target bpf -O2 -g -c -x c - -o ./llvm_btf_verify.o; \ readelf -S ./llvm_btf_verify.o | grep BTF; \ /bin/rm -f ./llvm_btf_verify.o) BPF_EXTRA_CFLAGS += -fno-stack-protector ifneq ($(BTF_LLVM_PROBE),) EXTRA_CFLAGS += -g else ifneq ($(and $(BTF_LLC_PROBE),$(BTF_PAHOLE_PROBE),$(BTF_OBJCOPY_PROBE)),) EXTRA_CFLAGS += -g LLC_FLAGS += -mattr=dwarfris DWARF2BTF = y endif endif endif # Trick to allow make to be run from this directory all: $(MAKE) -C ../../ $(CURDIR)/ BPF_SAMPLES_PATH=$(CURDIR) clean: $(MAKE) -C ../../ M=$(CURDIR) clean @find $(CURDIR) -type f -name '*~' -delete $(LIBBPF): FORCE # Fix up variables inherited from Kbuild that tools/ build system won't like $(MAKE) -C $(dir $@) RM='rm -rf' LDFLAGS= srctree=$(BPF_SAMPLES_PATH)/../../ O= $(obj)/syscall_nrs.h: $(obj)/syscall_nrs.s FORCE $(call filechk,offsets,__SYSCALL_NRS_H__) targets += syscall_nrs.s clean-files += syscall_nrs.h FORCE: # Verify LLVM compiler tools are available and bpf target is supported by llc .PHONY: verify_cmds verify_target_bpf $(CLANG) $(LLC) verify_cmds: $(CLANG) $(LLC) @for TOOL in $^ ; do \ if ! (which -- "$${TOOL}" > /dev/null 2>&1); then \ echo "*** ERROR: Cannot find LLVM tool $${TOOL}" ;\ exit 1; \ else true; fi; \ done verify_target_bpf: verify_cmds @if ! (${LLC} -march=bpf -mattr=help > /dev/null 2>&1); then \ echo "*** ERROR: LLVM (${LLC}) does not support 'bpf' target" ;\ echo " NOTICE: LLVM version >= 3.7.1 required" ;\ exit 2; \ else true; fi $(BPF_SAMPLES_PATH)/*.c: verify_target_bpf $(LIBBPF) $(src)/*.c: verify_target_bpf $(LIBBPF) $(obj)/tracex5_kern.o: $(obj)/syscall_nrs.h $(obj)/hbm_out_kern.o: $(src)/hbm.h $(src)/hbm_kern.h $(obj)/hbm.o: $(src)/hbm.h $(obj)/hbm_edt_kern.o: $(src)/hbm.h $(src)/hbm_kern.h # asm/sysreg.h - inline assembly used by it is incompatible with llvm. # But, there is no easy way to fix it, so just exclude it since it is # useless for BPF samples. $(obj)/%.o: $(src)/%.c @echo " CLANG-bpf " $@ $(Q)$(CLANG) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) $(EXTRA_CFLAGS) -I$(obj) \ -I$(srctree)/tools/testing/selftests/bpf/ \ -D__KERNEL__ -D__BPF_TRACING__ -Wno-unused-value -Wno-pointer-sign \ -D__TARGET_ARCH_$(SRCARCH) -Wno-compare-distinct-pointer-types \ -Wno-gnu-variable-sized-type-not-at-end \ -Wno-address-of-packed-member -Wno-tautological-compare \ -Wno-unknown-warning-option $(CLANG_ARCH_ARGS) \ -I$(srctree)/samples/bpf/ -include asm_goto_workaround.h \ -O2 -emit-llvm -c $< -o -| $(LLC) -march=bpf $(LLC_FLAGS) -filetype=obj -o $@ ifeq ($(DWARF2BTF),y) $(BTF_PAHOLE) -J $@ endif
执行二进制文件 ./hello
但是实验时才发现编译生成的二进制文件还是不能在其他环境运行,暂时不知道怎么解决工具的依赖问题,于是打算去 github 逛逛,看看有没有 ebpf 写的渗透工具,学习一下思路,发现了大佬的工具 https://github.com/Gui774ume/ebpfkit 也是要一些依赖的,暂时我找不到不需要依赖运行 ebpf 程序的方式了,所以最后没有成功哈哈
参考文章:
https://blog.csdn.net/weixin_44395686/article/details/106712543
https://cloudnative.to/blog/compile-bpf-examples/
https://blog.csdn.net/qq_34258344/article/details/108932912