executing-plans
executing-plans
Use when executing an implementation plan in the current session as the implementer yourself — your human partner chose inline execution, or no subagent tool is available
re-ebpfeBPF 程序逆向与对抗分析:BPF-64 指令集、progs/maps 关联、bpftool 反汇编、跟踪取证/恶意样本/EDR 对抗三用途。 触发词:eBPF、BPF、bpftool、bcc、libbpf、xlated、BPF 指令、bpf hook、EDR 对抗、tracepoint、kprobe。
Independent GitHub snapshot · Automated classification · Original documentation governs
BPF_FUNC_* 数值不随内核版本重新编号),版本差异影响 helper 是否存在、是否允许用于特定 prog_type/context,以及内核结构布局;分析环境内核版本与样本目标版本尽量一致(见坑 3)所有工具先验证再使用。加载/运行 BPF 程序到内核需要 root(或 CAP_BPF/CAP_SYS_ADMIN);恶意样本(内核驻留/规避类)的动态分析默认在隔离虚拟机 + 快照中进行([[re-sandbox]],[[re-analyze/platform-tips]] 最高原则);仅只读查询(prog list/dump xlated)风险较低,但同样建议在可控环境核对内核版本一致性。
apt install bpftool(Debian bullseye 起有独立包;Ubuntu 19.10+ 亦可装 linux-tools-common + linux-tools-generic——前者只是 wrapper,真实二进制在 linux-tools-$(uname -r) 内核配套包里);旧版 Debian(buster 及更早)无独立包,从内核源码编译 tools/bpf/bpftool 或克隆 libbpf/bpftool 仓库构建dnf install bpftool(官方仓库);Arch: pacman -S bpftoolbpftool version;查询类命令加 sudo(bpftool prog list、bpftool prog dump xlated id N、bpftool feature probe、bpftool map show/dump id N、bpftool net show、bpftool btf dump file /sys/kernel/btf/vmlinux)apt install llvm;Fedora: dnf install llvm;Arch: pacman -S llvm;macOS: brew install llvm(llvm-objdump 在 /opt/homebrew/opt/llvm/bin/)llvm-objdump --version;反汇编: llvm-objdump -d prog.o(ELF e_machine=EM_BPF(247) 自动识别,无需指定目标);GNU objdump(binutils 2.29+ 带 bpf 目标)也可作备选apt install bpftrace;Fedora: dnf install bpftrace;Arch: pacman -S bpftracebpftrace --version;bpftrace -d -e 'probe { ... }' 输出其生成的 BPF 指令,用于对照 xlated dump 还原脚本行为pip install pyelftools(跨平台)python3 -c "import elftools; print(elftools.__version__)";用于批量提取 BPF ELF 的 .text/.maps/.BTF/.rel* 段apt install ghidra/pacman -S ghidra);macOS: brew install --cask ghidra;Windows: 官方 zippip install lief(新版内置 LLVM MC 驱动的 eBPF 反汇编器,lief.parse() 后按 section/地址反汇编)python3 -c "import lief; print(lief.__version__)"按顺序执行,每步结果存档;运行期查询需要 root,动态分析默认沙箱。
定位载体(样本是什么形态,决定后续手段):
file prog.o 显示 eBPF;readelf -S prog.o / llvm-readelf -S 查看 section——.text 与按 attach 点命名的 section(如 kprobe/do_sys_openat2、tracepoint/syscalls/sys_enter_openat、xdp、tc、cgroup_skb/ingress、fentry/<fn>)、.maps(BTF-defined map 定义)、.BTF(类型信息)、.BTF.ext(line info + CO-RE 重定位)、.rel*(重定位段);需要批量提取时用 pyelftoolsbpftool prog dump xlated 为主sudo bpftool prog list 列出全部已加载 prog(id/type/name/tag/map_ids/btf_id),核对样本特征(name/tag/加载时间)反汇编(静态与运行期双视角):
llvm-objdump -d prog.o —— 编译产物,符号与注释保留,利于快速通读逻辑sudo bpftool prog dump xlated id <id> opcodes(追加 linum 显示源码行信息)——verifier 处理后的真实执行形态序号: (opcode十六进制) 指令,例:0: (b7) r0 = 0、4: (18) r1 = map[id:2]、6: (85) call bpf_tail_call#12、7: (15) if r0 == 0x0 goto pc+18;opcodes 选项在每行下附原始字节call(0x85)统一编码——imm 为 helper 调用号,bpf2bpf 子程序调用则以相对偏移 + src_reg 标记区分;lddw(0x18)占两条指令(第二条存高 32 位),src_reg 区分 map 指针/map 值地址/子程序地址等用途progs/maps 关联还原:
prog list 的 map_ids 字段给出 prog 引用的 map;xlated 中 (18) r1 = map[id:N](需 kallsyms 可用,见坑 3)→ sudo bpftool map show id N(类型/key_size/value_size/max_entries/pinning)→ sudo bpftool map dump id N(部分类型支持;prog_array 可枚举尾调用目标链).maps section 的 BTF 结构还原 map 定义(type/key/value/max_entries/pinning/flags),.rel* 段记录 map 引用点;运行期实例与静态定义按 name/id 关联hook 点识别:
kprobe/xxx、tracepoint/syscalls/sys_enter_*、fentry/<函数>——fentry 系需要 vmlinux BTF)sudo bpftool net show(tc/xdp 及网卡)、sudo bpftool cgroup show(cgroup 系)、sudo bpftool link show(fentry/fexit/kprobe 链接)确认语义还原:
call <名称>#<调用号>,调用号按 Linux BPF UAPI 的固定 ID 映射解析(include/uapi/linux/bpf.h 的 ___BPF_FUNC_MAPPER 逐项显式编号,方法见坑 3);名称对照 bpftool 编译时表或目标内核;调用约定 r1-r5 参数、r0 返回值call <名>#<号> 的名称与手上工具显示对不上,或 bpftool 显示 call unknown#<号>(kptr_restrict=2 时显示 bpf_unspec#0);原因——helper ID 是 Linux BPF UAPI 的稳定编号,已有 BPF_FUNC_* 数值不随内核版本重新编号(___BPF_FUNC_MAPPER 逐项显式指定数值,正是为保证 backport 场景下 ID 可移植),版本差异影响的是 helper 是否存在、是否允许用于特定 prog_type/context、语义是否扩展——旧 bpftool 显示 call unknown#N 只是它的名称表不认识较新的 ID,不能反推 ID 漂移;名称来源两分:helper 名来自 bpftool 编译时表(与运行内核可能不同版本),call pc+X# 子程序名来自运行内核 kallsyms(需 bpf_jit_kallsyms=1、kptr_restrict=0,符号形如 bpf_prog__);对策——解析按 UAPI 固定 ID 映射:以 include/uapi/linux/bpf.h 的 ___BPF_FUNC_MAPPER 核对 ID→名称;可调用性用 sudo bpftool feature probe 列出该内核实际支持的 helper(按名,分 prog type)确认;kfunc 不属于该稳定 ABI;kallsyms 关联(/proc/sys/kernel/bpf_jit_kallsyms=1、kptr_restrict=0)影响名称显示,ID 本身以 UAPI 为准__htab_map_lookup_elem)、常量折叠、不可达代码删除;bpf2bpf 子程序显示为 call pc+X#<子程序符号名> 相对调用;尾调用目标不在本 prog(藏在 prog_array map 里,xlated 只见 bpf_tail_call);对策——以 xlated 为执行真相,静态反汇编仅作语义参考;用 linum 选项把源码行信息贴到 xlated 上对齐;尾调用链沿 prog_array map 的 fd 逐跳展开.BTF,map 布局、结构体偏移无从解析,Ghidra 里全是裸地址;原因——BTF 是类型信息的唯一权威来源(CO-RE 重定位也依赖它);对策——从 xlated 的 ldimm64 目标与 STX/ST 指令的偏移常量反推布局;有 .BTF 时用 bpftool btf dump file prog.o 导出类型,vmlinux 侧用 bpftool btf dump file /sys/kernel/btf/vmlinux 对照结构体bpftool map show + prog list 做全量快照,再逐 map dump;取证前把 pin 目录 /sys/fs/bpf 完整归档.BTF.ext 带重定位记录)的字段偏移是加载时由 libbpf 按目标内核 BTF 计算的,ELF 里的偏移只是占位;对策——结合目标内核版本的 vmlinux BTF 重算偏移,或直接以 xlated(已重定位后的最终偏移)为准