eBPF初探

eBPF 源自经典 BPF(Berkeley Packet Filter),经典 BPF 最初主要用于数据链路层报文过滤;eBPF 在此基础上扩展为一个运行在内核中的通用可编程执行机制。它允许把一小段受 verifier 校验的程序动态加载到内核,在不修改内核源码、不编写传统内核模块的情况下,对网络、系统调用、内核函数、用户态函数等事件进行观测、过滤、聚合和上报

为什么 Android 逆向会用 eBPF

行为边界是敏感数据或行为最先出现的地方,java层边界通常是binder和系统服务交互,native层边界通常是syscall进入内核

检测手段(以root检测为例):libc调用函数open去扫描是否有su文件->内联libc自实现open->内联svc(从用户态陷入内核态的指令)发起syscall =>用户态hook越来越容易被检测

IDA等静态分析回答代码做什么;用户态frida等插桩回答进程正在做什么;eBPF回答系统层面真实发生了什么

eBPF 的核心组成

BPF Program:实际运行在内核中的 eBPF 程序,负责在指定事件发生时执行采集、过滤、统计或上报逻辑。它通常由C编写,再通过 clang/LLVM 编译成 eBPF 字节码

BPF Map:eBPF 程序保存状态和与用户态通信的数据结构,例如可以用 PID/TID 作为key记录 syscall 入口参数,再在 syscall exit 时取出并关联返回值

Loader:运行在用户态的加载器程序,负责把编译好的 eBPF 字节码加载进内核,并把 BPF Program 挂载到指定的 attach 点上

ring buffer:eBPF 中常用的事件输出机制,用于把内核态采集到的事件高效传递给用户态程序。相比 bpf_printk/trace_pipe,它更适合输出结构化事件;相比早期常用的 Perf Buffer,Ring Buffer 通常更节省内存,并且更容易保持跨 CPU 事件顺序

Verifier: 在程序加载进内核前检查 eBPF 程序是否安全,例如是否会越界访问、是否可能无限循环、是否非法访问内核内存;只有通过校验的程序才能被加载

Helper:由于 eBPF 程序不能随意调用普通内核函数,需要通过内核提供的 Helper 完成一些受控操作,例如获取当前 PID/TID、读取用户态或内核态内存、访问 BPF Map、向 Ring Buffer 输出事件等

常见 attach 点

kprobe/kretprobe:挂内核函数入口/返回,适合观察 VFS、进程创建、权限检查等内核路径;
uprobe/uretprobe:挂用户态 ELF 中的函数入口/返回,适合观察 libc、libart.so、目标 so 的关键函数;
tracepoint/raw_tracepoint:挂内核预埋事件点,逆向中常用 raw_syscalls/sys_enter 和 raw_syscalls/sys_exit 做全局 syscall 观察;
fentry/fexit:比 kprobe 更现代,参数访问更方便,但依赖内核支持,很多 Android 原厂内核未必可用;

eBPF 程序的加载与运行流程

从App到分析端的完整事件路径

eBPF参与过程:先把 eBPF C 代码编译成字节码,再通过 bpf() syscall 加载进内核,经 verifier 校验,attach 到 hook 点,之后从 ringbuf/perfbuf/map 读取结果

一个最小 eBPF 工程的文件分工

lesson.bpf.c 负责内核里发生的采集,过滤和上报,属于内核态程序;loader.c 负责把 eBPF 程序送进内核再把结果返回给用户态,属于用户态程序

常见 eBPF 开发库

libbpf 是 C 生态的主流库,负责加载、验证、attach BPF 对象,并支持 skeleton、CO-RE 等能力;cilium/ebpf 是 Go 生态的纯 Go eBPF 库,常配合 bpf2go 把 C 写的 BPF 程序编译并嵌入 Go 用户态程序,不是简单封装 libbpf

逆向实战场景

反调试与反注入行为分析

很多 App 会通过 ptrace、prctl、kill/tgkill、clone、seccomp、读取 /proc/self/status、读取 /proc/self/maps 等方式检测调试器、Frida、Xposed、Magisk 或异常运行环境

使用 eBPF 可以在 syscall 或内核函数层观察这些行为。例如通过 raw_syscalls/sys_enter 和 raw_syscalls/sys_exit 观察系统调用号、参数和返回值;通过 kprobe 观察内核文件打开路径;通过 uprobe 观察 libc 或目标 so 中的关键函数调用

Root 检测与环境检测分析

Root 检测通常会访问一些敏感路径或系统属性,例如 /system/bin/su、/system/xbin/su、/sbin/su、Magisk 相关路径、/proc/self/mounts、/proc/self/maps、/proc/self/status 等
eBPF 可以监控 openat、faccessat、readlinkat、newfstatat、getdents64 等文件相关 syscall,记录目标 App 是否访问了这些路径、访问结果是什么、返回值是否表示文件存在

文件访问行为追踪

文件访问是 Android 逆向中非常常见的行为面。App 可能会读取配置文件、证书文件、so 文件、dex 文件、缓存文件、沙箱目录、外部存储文件,也可能会扫描系统目录判断环境

eBPF 可以通过 syscall tracepoint、do_filp_open、security_file_open、vfs_open 等 attach 点观察文件打开行为。采集字段通常包括 PID、TID、UID、进程名、文件路径、打开 flags、返回 fd、返回错误码等

如果在 syscall enter 时记录参数,在 syscall exit 时记录返回值,再通过 PID/TID 或 fd 进行关联,就可以还原一次完整的文件访问行为

网络行为追踪

eBPF 也可以用于观察目标 App 的网络行为,例如连接了哪些 IP、访问了哪些端口、是否使用代理、是否存在命令控制通信等

常见观察点包括 connect、sendto、recvfrom、sendmsg、recvmsg 等 syscall,也可以结合 socket 相关 tracepoint 或 kprobe 观察更底层的网络事件

在逆向分析中,网络行为追踪可以帮助判断目标 App 的登录、风控、上报、反作弊、热更新、配置下发、恶意通信等行为链路

进程、线程与命令执行监控

App 或 native so 可能会通过创建子进程、执行命令、启动线程、发送信号等方式完成检测、解包、反调试或辅助执行

eBPF 可以监控 fork、clone、execve、do_execveat_common、sched_process_fork、sched_process_exec、kill、tgkill 等事件,记录进程创建、线程创建、命令执行和信号发送行为

so 加载与动态代码加载分析

Android App 常见的动态加载行为包括加载 native so、加载 dex、加载插件、释放加固壳代码、从匿名内存或 memfd 中执行代码等
eBPF 可以结合 uprobe 和 syscall 观察 dlopen、android_dlopen_ext、mmap、mprotect、memfd_create 等行为,判断目标 App 是否加载了新的 so、是否申请了可执行内存、是否把内存权限从可写改为可执行

权限与安全策略相关行为分析

一些 App 会检查 SELinux 状态、沙箱权限、文件权限、系统属性、挂载信息或安全策略差异

eBPF 可以观察文件权限检查、进程凭据、UID/GID、capability、部分 LSM/security hook 相关路径,辅助判断目标 App 是否在做环境识别或权限探测,例如可以关注 security_file_open、security_inode_permission、cap_capable、getuid、getgid、geteuid、stat、access 等相关行为。需要注意的是,LSM/security 相关 hook 点在不同 Android 内核上的可用性差异较大,实际使用前需要先确认符号是否存在、kprobe/fentry 是否可挂,以及当前设备是否允许加载对应类型的 eBPF 程序

辅助 dump dex

基本思路是用 uprobe 挂到libart.so 的关键函数,在 DexFile 被加载、验证或解释执行时捕获 DexFile 的内存地址和大小,再由用户态读取目标进程内存并落盘。它的优势是不需要像 Frida 那样注入目标进程,也不依赖 ptrace 长时间附加;限制是依赖 Android 版本、libart 符号/偏移、root 权限、SELinux/内核配置,并且对代码抽取、主动调用类脱壳不一定能完全替代传统方案

eBPF 限制

Android 上使用 eBPF 通常需要 root 权限,并且依赖内核配置是否开启 BTF、kprobe、tracepoint、fentry/fexit、ringbuf 等能力。不同 Android 版本、不同厂商内核支持情况差异较大,因此实际使用前需要先检查内核能力,例如是否存在 /sys/kernel/btf/vmlinux,以及目标 attach 点是否可用

另外,eBPF 更适合观察系统层行为,不一定能直接还原 Java 层方法名、业务参数语义。uprobe 虽然不需要注入目标进程,但也不应简单理解为完全无痕,面对强对抗场景仍然可能被检测

参考链接:

https://www.bilibili.com/video/BV17hBQBqEda

https://www.bilibili.com/video/BV1bQLt6KES1

https://matriy330.github.io/a976f0ae/


eBPF初探
https://j1ya-22.github.io/2026/06/08/eBPF初探/
作者
j1ya
发布于
2026年6月8日
许可协议