搬砖
ebpf in android
1.ebpf原理
收集资料
https://www.ebpf.top/post/ebpf-overview-part-3/
我当前阶段理解的重点是: 前端,后端,加载器, 数据结构
2.ebpf学习经历
收集资料
ebpf是linux内核提供的hook api
看了很多大佬博客,前期在Android上运行bpf程序遇到问题很多,重点是内核版本,Android api变化, 编译环境方面问题很多
总结了下bpf快速上手路线,我的手机小米12,应使用kenelSu root了
多看理解早期基本用法 bpf on Android
Android上体验bcc脚本
环境准备
安装magiskSSH模块,它会帮我创建好linux在安卓上的环境,还会准备好ssh, 注意这里用的frp内网穿透提供ssh登录能力,如果不需要做内网穿透,直接ssh链接内网手机就可以了。当然我是用frp内网穿透链接云手机,只需要将我自己电脑上的公钥上传到跳板机器和内网手机1
2
3
4
5
6
7
8
9
10Host jumpserver2
User root
HostName *****
Port 22
Host phone
User root
Port 22900
HostName localhost
ProxyJump jumpserver2发现没有,不需要额外配置 IdentityFile,直接ssh phone
本来打算用 vscode ssh 开发的,折腾了半天vscode无法打开Android目录,因为在ssh链接时候用了bash 命令,然而Android上没有bash命令,报解析错误,后改为在手机端启动code tunnel 来远程开发
体验ebpf
没体验赶紧体验,不懂就问chatgpt,比如1
2
3
4
5BPF Maps
BPF_PERCPU_ARRAY(ssl_data, struct probe_SSL_data_t, 1); 声明了一个 per-CPU 数组来存储追踪事件的数据。// 也就是初始化1个数据结构probe_SSL_data_t 放在 ssl_data
BPF_PERF_OUTPUT(perf_SSL_rw); 和 BPF_PERF_OUTPUT(perf_SSL_do_handshake); 创建了用于将追踪数据发送到用户空间的性能输出数组。// 也就是创建的输出管道,内核里不断perf_submit 提交数据到管道,python前端注册好管道监听就能获取到相关数据
BPF_HASH(start_ns, u32); 和 BPF_HASH(bufs, u32, u64); 声明了哈希表来存储事件开始的时间戳和缓冲区地址。// 这个就不用解释了,hash
eCapture 编译
复习下osi七层协议,重点是ssl/tls
1
2
3
4
5
6
7
8
9
10
11
12
13应用层 (第7层) | ← 应用程序(如 Web 浏览器、电子邮件客户端)
表示层 (第6层) | ← 数据格式化、数据加密、数据压缩
会话层 (第5层) | ← 会话管理、身份验证
-------------------------| ← SSL/TLS 加密层 (对tcp加密;提供了身份验证、密钥交换、加密和解密)
传输层 (第4层) | ← 可靠的数据传输(如 TCP 协议)
网络层 (第3层) | ← 路由、IP 数据报传输、地址解析
数据链路层 (第2层) | ← 物理寻址、错误检测和修正
物理层 (第1层) | ← 物理介质传输(如电缆、光纤)
socket是抽象概念,跨越了多个层次。
应用层协议(如 HTTP、FTP、SMTP)使用 Socket API 来实现网络通信功能。(应用层)
Socket定义了端口和传输协议(如 TCP 或 UDP)(传输层)
Socket本身不包含会话层和表示层的全部功能在 Android 系统中,绝大多数与 TLS 相关的操作都是通过位于 /apex/com.android.conscrypt/lib64/libssl.so(对于 64 位系统)这一路径下的库来执行的。这个库是一个由 BoringSSL 提供支持的 Conscrypt 库的一部分,BoringSSL 本身是 Google 维护的 OpenSSL 的分支版本。
Conscrypt 作为 Android 平台上的一个安全提供者,为高级应用程序编程接口(API)实现了底层的加密协议。应用程序和开发者通常使用 Android SDK 中提供的更高级别的 API,例如 javax.net.ssl.SSLContext 和 HttpsURLConnection,以及流行的网络库如 OkHttp,它们在内部都沟通与 Conscrypt 提供的功能以建立和维护安全的网络连接。
除非开发者在 Android 应用中明确地引入了自定义的 TLS 库(例如使用一个独立的 OpenSSL 版本或其他的 SSL/TLS 实现),否则默认情况下,安全通信都是通过系统提供的 Conscrypt 实现的 TLS 功能来处理的。通过 Security 类和 Provider 类的 API,Conscrypt 被集成和注册到 Java 安全框架中。这意味着不仅系统本身的网络通信依赖 Conscrypt,第三方应用程序除非另外指定,都会默认使用它来处理相关的 SSL/TLS 连接。
因此,如果你正在处理安全连接、证书验证或其他与 TLS 相关的安全特性,只要不是使用的自定义的加密库,你可以认为所有的操作最终都是通过 Android 系统提供的 Conscrypt 实现,进而调用 /apex/com.android.conscrypt/lib64/libssl.so 库的函数来完成的。
编译eCapture
我的编译过程- 使用gnirehtet将手机网络请求转发到电脑,电脑可翻墙加速下载
- 配置go,按照上面文章就能可以搞定,最后缺什么环境变量补什么
- 编译 make
eCapture使用
先看下eCapture项目博客的相关介绍
复习下bcc项目 只看“Network and Sockets Tools”
- [x]tools/gethostlatency
- [x]tools/bindsnoop
- [x]tools/netqtop tools/netqtop.c
- [x]tools/sofdsnoop
- [x]tools/solisten
- [x]tools/sslsniff “推荐 python sslsniff.py -p pid -l –handshake -g -n”
- [x]tools/tcpaccept
- [x]tools/tcpconnect
- [x]tools/tcpconnlat
- [x]tools/tcpdrop
- [x]tools/tcplife
- [x]tools/tcpretrans
- [x]tools/tcprtt
- [x]tools/tcpstates
- [x]tools/tcpsubnet
- [x]tools/tcpsynbl
- [x]tools/tcptop
- [x]tools/tcptracer
- [x]tools/tcpcong
ecapture编译
我的手机需要引入header文件
ANDROID=1 make nocore使用 ./ecapture tls -h
vscode remote 远程调试ecapture
- 在手机端启动 ./code tunnel (我这里用的是vscode 隧道)
- vscode 安装Golang插件,自动补全go代码
- 插件装好后配置launch.json这个代码运行配置文件,重点是args 这个参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14{
"version": "0.2.0",
"configurations": [
{
"name": "Launch ecapture",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${workspaceFolder}", // 确保这里的路径指向正确的可执行文件或包
"args": ["tls"], // 替换为你的实际参数
"logOutput": "dap"
}
]
} - debug
- 这时你会遇到 libpcap 没有安装问题,找到/lib/libpcap目录,./configure && make && make install
代码分析
对比分析下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// 来自ecapture
struct {
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
__uint(key_size, sizeof(u32));
__uint(value_size, sizeof(u32));
__uint(max_entries, 1024);
} tls_events SEC(".maps");
// 来自bcc sslsniff.py
struct bpf_map_def SEC("maps") ssl_data = {
.type = BPF_MAP_TYPE_PERCPU_ARRAY,
.key_size = sizeof(u32),
.value_size = sizeof(struct probe_SSL_data_t),
.max_entries = 1
};1
2
3
4
5
6
7
8
9
10
11映射类型:
tls_events 是一个 BPF_MAP_TYPE_PERF_EVENT_ARRAY 类型,用于发送事件数据到用户空间。
ssl_data 是一个 BPF_MAP_TYPE_PERCPU_ARRAY 类型,用于每个 CPU 存储特定的数据。
使用场景:
tls_events 用于与用户空间通信,如监控或跟踪 SSL/TLS 事件。
ssl_data 用于存储每个 CPU 相关数据,例如用于性能监控或者调试目的。
映射大小:
tls_events 可以根据系统上的 CPU 数量进行扩展,最多支持 1024 个 CPU。
ssl_data 为每个 CPU 提供了一个数组元素。tls_events ssl_data 可以理解为不通作用的管道么?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18可以这样理解,tls_events 和 ssl_data 在 eBPF 程序中的确类似于不同作用的“管道”,其中每个管道都有不同的用途和行为。这里的“管道”是指它们各自如何在内核和用户空间之间传输数据的方式。
tls_events 映射(管道)
tls_events 映射是一个 BPF_MAP_TYPE_PERF_EVENT_ARRAY 类型的 eBPF 映射。它的主要作用是作为一个通道将数据从内核空间(eBPF 程序)发送到用户空间(分析工具或者监听程序)。这种映射允许 eBPF 程序通过 perf event 基础设施高效地传输数据,该数据通常与监控或跟踪事件有关。
当 eBPF 程序用 bpf_perf_event_output 函数把数据发送到 tls_events 映射时,用户空间监听程序可以从关联的 perf event 文件描述符读取这些事件数据。
ssl_data 映射(管道)
ssl_data 映射是一个 BPF_MAP_TYPE_PERCPU_ARRAY 类型的 eBPF 映射。这个映射像是为每个 CPU 专门创建的一个本地存储区域,使得 eBPF 程序可以避免跨 CPU 的数据共享和潜在的竞争状态。它通常用于存储每个 CPU 核心特定的上下文信息或用于临时存储计算数据。
由于是 per-CPU 映射,ssl_data 并不直接涉及到用户空间的数据传输。它更像是内核空间内部的一个局部存储区,用于 eBPF 程序的内部数据管理。
总结
tls_events 和 ssl_data 虽然都是 eBPF 程序中的数据结构,但它们的“管道”作用是不同的:
tls_events 类似于一个将数据从 eBPF 程序发送到用户空间的出口管道。
ssl_data 类似于一个在内核中为每个 CPU 提供数据存储的内部管道。
根据需要传输数据的方向(内核到用户空间,还是内核内部)和用途(事件传输,还是数据存储),你会选择合适的映射类型来实现你的目标。-
- ecapature/utils/ 下介绍了如何读取ssl结构体相关的参数信息
定制bcc/ebpf在android平台上实现基于dwarf的用户态栈回溯
-
- [x]直接ndk交叉编译 ./build.sh
编译问题: https://github.com/SeeFlowerX/unwinddaemon/issues/2 - [x]基于aosp编译
编译问题: https://blog.csdn.net/Chris_1994/article/details/135017508
1
2
3
4
5
6
7
81. 直接拉取对应分支
repo init -u https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/platform/manifest -b android14-release
repo sync -j8
2. 交换内存不够的处理办法
sudo dd if=/dev/zero of=/swapfile bs=1M count=20480
sudo mkswap /swapfile
sudo swapon /swapfile
unwinddaemon编译问题:
1
2
3system/extras/unwinddaemon/lib.cpp:454:85: error: unused parameter 'stack_buf' [-Werror,-Wunused-parameter]
const char* UnwindCallChainV2(int pid, UnwindOption* opt, uint64_t* regs_buf, void* stack_buf)
注: 对于警告问题直接注释,(void)stack_buf; // 显式声明 stack_buf 为未使用
- [x]直接ndk交叉编译 ./build.sh
unwindbcc 主要是在注册事件前,事件回调后修改相关代码。实现跨进程通讯打印
- open_perf_buffer(BPF.cc)->open_all_cpu->open_on_cpu->bpf_open_perf_buffer_opts(libbpf.c)->__NR_perf_event_open
- perf_reader_poll(perf_reader.c)->perf_reader_event_read->parse_sw->print_frame_info
-
stackplz
注:go环境和ndk交叉编译, android端debian没有对于的cpu架构ndk