eCapture v2 深度解析:当 AI Agent 写了 90% 的代码,开源开发进入「产品经理模式」
背景:一个 1.5 万 Star 的 eBPF 项目,被 AI 重写了
2026年4月,一个爆炸性的消息在开源圈传开:eCapture v2 发布,而这次版本更新中,90% 的代码是由 GitHub Copilot AI Agent 完成的。
这不是噱头,不是营销,而是一次真实的生产级实验——作者 CFC4N(网名)在博客中公开了完整的贡献数据:
| 贡献者 | 新增行数 | 删除行数 | 新增占比 |
|---|---|---|---|
| @Copilot(AI Agent) | 28,426 | 10,392 | ~90% |
| @cfc4n(作者本人) | 2,097 | 781 | ~7% |
| 社区贡献者 | 1,082 | 96 | ~3% |
更关键的是,这不是简单的「AI 帮忙写了几行代码」,而是一次完整的架构重构——8 个 Probe 全部标准化重写,user/ 目录彻底删除,E2E 测试覆盖 72+ 场景。作者的角色从「写代码的人」变成了「写需求的人和审 PR 的人」。
这标志着开源开发进入了一个新范式:产品经理模式。
eCapture 是什么:用 eBPF 撕开 TLS 加密的黑盒
在深入 v2 的变革之前,先理解 eCapture 解决的核心问题。
传统 TLS 抓包的痛点
网络调试、安全分析、流量审计——这些场景下,抓包是基本功。但 TLS/SSL 加密的普及,让「抓包」变成了「抓乱码」:
传统方案:
1. 中间人代理(MITM):需要生成 CA 证书,客户端安装信任,配置复杂
2. 服务端 keylog:需要修改应用代码,导出会话密钥,生产环境不现实
3. 服务端配置:修改 SSL 库配置,重启服务,影响可用性
每一种方案都有明显的短板。而 eCapture 的思路完全不同——不碰证书,不改配置,不重启服务。
eBPF 的「降维打击」
eBPF(Extended Berkeley Packet Filter)是 Linux 内核的可编程扩展。它允许你在内核态插入安全、高效的沙箱代码,无需修改内核源码或加载模块。
eCapture 的核心原理:
┌─────────────────────────────────────────────────────────┐
│ 用户态(User Space) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ OpenSSL │ │ GnuTLS │ │ GoTLS │ ... │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ SSL_write/SSL_read │
│ ▼ │
├─────────────────────────────────────────────────────────┤
│ 内核态(Kernel Space) │
│ ┌─────────────────────────────────────────────┐ │
│ │ eBPF Probe(uprobe/tracepoint) │ │
│ │ - Hook SSL_write/SSL_read │ │
│ │ - 捕获明文参数(plaintext buffer) │ │
│ │ - 通过 perf buffer 发送到用户态 │ │
│ └─────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
关键点:
- uprobe 机制:在用户态函数入口插入探针,捕获函数参数
- 明文捕获:SSL_write/SSL_read 的参数本身就是明文,在加密前/解密后截获
- 零侵入:不需要 CA 证书,不需要修改应用,不需要重启服务
支持的协议:
| Probe | 协议/库 | 用途 |
|---|---|---|
| OpenSSL | openssl | 最广泛,覆盖 Nginx、Apache、curl 等 |
| GnuTLS | gnutls | 部分服务端应用 |
| GoTLS | Go crypto/tls | Go 语言应用 |
| NSPR | NSS/NSPR | Firefox、部分 Java 应用 |
| MySQL | MySQL 协议 | 数据库明文查询 |
| Postgres | PostgreSQL 协议 | 数据库明文查询 |
| Bash/Zsh | Shell 命令 | 命令行审计 |
一行命令,抓取 HTTPS 明文
# 抓取所有 OpenSSL 流量的明文
sudo ecapture tls
# 指定 PID,只抓特定进程
sudo ecapture tls -p 12345
# 输出为 pcap 格式,可用 Wireshark 打开
sudo ecapture tls -w capture.pcap
# 只抓特定域名(通过 SNI 过滤)
sudo ecapture tls --host="api.example.com"
这就是 eCapture 的价值:把网络调试从「配置地狱」变成「一行命令」。
v2 的变革:从「能用」到「可以持续演进」
v1 时代的 eCapture 已经有 1.5 万 Star,功能完备,但内部架构却越来越难以维护。作者在博客中坦言:
老实说,v1 时代的 user/ 目录,笔者自己看着都头疼。
v2 的核心变化,可以用一句话概括:把整个项目从「能用」变成了「可以持续演进」。
架构重构:user/ 目录彻底消失
v1 时代,所有 Probe 的代码都堆在 user/ 目录下:
user/
├── bash.go
├── mysql.go
├── openssl.go
├── gnutls.go
├── gotls.go
├── nspr.go
├── postgres.go
└── zsh.go
随着支持的协议越来越多,这个目录越来越乱——命名不统一、接口不一致、职责边界模糊。
v2.0.0 用 7 个 Phase 完成了完整的迁移:
internal/probe/
├── base/
│ ├── config.go # 通用配置
│ ├── probe.go # BaseProbe 实现
│ └── event.go # 事件基类
├── openssl/
│ ├── config.go # 嵌入 BaseConfig
│ ├── event.go # 实现 DecodeFromBytes()
│ ├── register.go # init() 自注册
│ └── openssl_probe.go # 嵌入 BaseProbe
├── gotls/
│ └── ...
└── ...
每个 Probe 现在都有统一的文件结构,职责清晰,接口一致。
三大设计模式:工厂、模板方法、观察者
v2 引入了三个设计模式,彻底解耦了各个组件:
1. 工厂模式(Factory Pattern)
// register.go - 每个 Probe 自注册
func init() {
factory.RegisterProbe("openssl", NewOpenSSLProbe)
}
// factory.go - 统一创建入口
func NewProbe(name string, cfg config.Config) (Probe, error) {
creator, ok := probes[name]
if !ok {
return nil, fmt.Errorf("unknown probe: %s", name)
}
return creator(cfg)
}
好处:
- 新增 Probe 只需实现接口,在 init() 中自注册
- CLI 命令通过工厂创建,互不依赖
- 符合开闭原则:扩展开放,修改关闭
2. 模板方法模式(Template Method Pattern)
// base/probe.go - 公共流程
type BaseProbe struct {
name string
config config.Config
}
func (p *BaseProbe) Run() error {
// 1. 初始化(公共逻辑)
if err := p.Initialize(); err != nil {
return err
}
// 2. 启动(子类实现)
if err := p.Start(); err != nil {
return err
}
// 3. 事件循环(公共逻辑)
p.eventLoop()
// 4. 停止(子类实现)
return p.Stop()
}
// openssl/openssl_probe.go - 差异化逻辑
func (p *OpenSSLProbe) Start() error {
// OpenSSL 特有的启动逻辑
return p.attachOpenSSL()
}
好处:
- 公共流程在 BaseProbe 中实现一次
- 子 Probe 只需覆写差异化逻辑
- 减少重复代码,降低维护成本
3. 观察者模式(Observer Pattern)
// 事件分发器
type EventDispatcher struct {
subscribers map[string][]Subscriber
}
func (d *EventDispatcher) Dispatch(event Event) {
for _, sub := range d.subscribers[event.Type()] {
sub.OnEvent(event)
}
}
// 订阅者:文件写入、pcap 写入、WebSocket 推送
type FileWriter struct {}
func (w *FileWriter) OnEvent(event Event) {
w.file.Write(event.Data())
}
type PcapWriter struct {}
func (w *PcapWriter) OnEvent(event Event) {
w.writePcapRecord(event)
}
type WebSocketWriter struct {}
func (w *WebSocketWriter) OnEvent(event Event) {
w.conn.WriteMessage(event.JSON())
}
好处:
- 事件源和消费者完全解耦
- 新增输出格式只需实现 Subscriber 接口
- 支持多路输出(同时写文件 + 推送远端)
事件流转全链路
从 eBPF 内核态抓到数据,到最终写入文件或推送到远端,完整路径如下:
┌─────────────────────────────────────────────────────────────┐
│ 内核态 │
│ ┌──────────────┐ │
│ │ eBPF Program │ uprobe/tracepoint │
│ │ (C code) │ ──────────────────┐ │
│ └──────────────┘ │ │
│ │ perf buffer │ │
└─────────│───────────────────────────│─────────────────────┘
▼ │
┌─────────────────────────────────────│─────────────────────┐
│ 用户态 │ │
│ ┌──────────────┐ │ │
│ │ EventDecoder │ ◄────────────────┘ │
│ └──────┬───────┘ │
│ │ Event │
│ ▼ │
│ ┌──────────────────┐ │
│ │ EventDispatcher │ │
│ └────────┬─────────┘ │
│ │ Dispatch │
│ ▼ │
│ ┌─────────┐ ┌─────────┐ ┌─────────────┐ │
│ │FileWriter│ │PcapWriter│ │WebSocketWriter│ │
│ └─────────┘ └─────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
这个链路在 v2 里被完整地标准化了。之前 v1 里部分 Probe 是直接写文件,绕过了 Dispatcher,导致远端推送(ecaptureQ 模式)下数据丢失——这个 Bug 在 v2.1.0 的 #964 里修掉了。
面向用户的改进:GoTLS 四元组、pcap 可靠写入、Android 成熟
架构重构是「内功」,用户能感知到的是「外功」。
1. GoTLS 终于有四元组了
很多同学用 eCapture 抓 Go 应用的 TLS 流量,一直有个痛点:抓到的明文没有连接信息,不知道这条流量来自哪个 IP、哪个端口。
# v1 输出:只有明文,没有连接信息
GET /api/users HTTP/1.1
Host: api.example.com
...
# v2 输出:带连接四元组
[192.168.1.100:54321 -> 10.0.0.1:443]
GET /api/users HTTP/1.1
Host: api.example.com
...
v2.0.1 和 v2.1.0 里,贡献者 @zenyanle 接连贡献了两个 PR(#947, #960),给 GoTLS Probe 加上了从 tls.Conn 里提取 fd、进而获取连接四元组的能力。
对于要做流量审计或安全分析的同学,这个功能很关键。
2. pcap 写入更可靠了
v2.0.0 引入了缓冲 pcapng 写入,带上了接口元数据;v2.2.0 修掉了一个 Close() 里的竞态条件——之前在高并发场景下,DSB keylog 写入会出现数据乱序或丢失的情况。
// v2 的 pcap 写入流程
type PcapWriter struct {
buf *bufio.Writer
mu sync.Mutex
closed bool
}
func (w *PcapWriter) Write(record PcapRecord) error {
w.mu.Lock()
defer w.mu.Unlock()
if w.closed {
return ErrClosed
}
// 缓冲写入,批量 flush
return w.buf.Write(record.Bytes())
}
func (w *PcapWriter) Close() error {
w.mu.Lock()
defer w.mu.Unlock()
if w.closed {
return nil
}
// 先 flush 缓冲区,再关闭
if err := w.buf.Flush(); err != nil {
return err
}
w.closed = true
return w.file.Close()
}
现在写 pcap 文件,序列化保证了,关闭时也不会丢尾巴了。
3. Android 支持更成熟
v2 系列对 Android 的支持大幅提升:
- BoringSSL Android 16 的 offset 更新(#885)
- Android 模拟器的 DNS 解析问题修复(#957)
- Android e2e PCAP 模式自动探测活跃网络接口(#976)
- Build tag 从
androidgki统一改名为ecap_android(#930)
对于在 Android 上用 eCapture 做流量分析的同学,v2 系列的稳定性比 v1 强了不止一个量级。
4. 全覆盖的 E2E 测试体系
v2.0.0 里加入了 72+ 个 E2E 测试场景,覆盖了 bash、tls、gnutls、gotls 全部模块,以及 pcap/keylog/text 三种抓包模式。
# E2E 测试示例
func TestOpenSSL_KeyLog(t *testing.T) {
// 1. 启动测试 HTTPS 服务
server := startTLSServer(t)
defer server.Close()
// 2. 启动 eCapture
ec := startECapture(t, "tls", "-w", "capture.keylog")
defer ec.Stop()
// 3. 发送测试请求
resp, err := http.Get(server.URL)
require.NoError(t, err)
resp.Body.Close()
// 4. 验证 keylog 文件
keylog := parseKeylog(t, "capture.keylog")
require.NotEmpty(t, keylog)
require.Equal(t, server.TLSVersion(), keylog.Version)
}
这意味着每次发版前,核心路径都会被自动验证一遍。用户升级时踩到「老功能突然坏了」这种坑的概率,大幅降低。
AI Agent 的真实参与:从「辅助」到「主导」
现在回到最核心的问题:AI Agent 到底做了什么?
数据说话:90% 的代码,35% 的 PR
从 v2.0.0 到 v2.2.1,GitHub 上合并了约 37 个 PR,分布如下:
| 贡献者 | PR 数量 | 占比 |
|---|---|---|
| @Copilot(GitHub Copilot AI Agent) | 13 | ~35% |
| @cfc4n(作者本人) | 21 | ~57% |
| 社区贡献者(@zenyanle、@Carl Chen 等) | 3 | ~8% |
v2.0.0 那次大重构是 AI 参与度最高的版本:8 个 Probe 的标准化迁移、E2E 测试框架搭建,10 个 PR 直接由 @Copilot 提交,占当版 PR 总量的近 45%。
再看代码行数:
| 贡献者 | 新增行数 | 删除行数 | 新增占比 |
|---|---|---|---|
| @Copilot(AI Agent) | 28,426 | 10,392 | ~90% |
| @cfc4n(作者本人) | 2,097 | 781 | ~7% |
| 社区贡献者 | 1,082 | 96 | ~3% |
90% 的代码是 AI 写的,作者的角色更像产品经理加 Code Reviewer。
AI 是怎么知道该做什么的?
答案是:每一个 AI PR 背后,都对应着作者写的一个详细 Issue。
# Issue 示例:重构 OpenSSL Probe
## 目标
将 user/openssl.go 迁移到 internal/probe/openssl/,符合 v2 架构规范。
## 要求
1. 创建 internal/probe/openssl/ 目录
2. 实现 config.go,嵌入 BaseConfig
3. 实现 event.go,实现 DecodeFromBytes() 接口
4. 实现 register.go,在 init() 中调用 factory.RegisterProbe()
5. 实现 openssl_probe.go,嵌入 BaseProbe,覆写 Initialize/Start/Stop/Close
6. 保持原有功能不变,通过 E2E 测试
## 参考
- 已完成的 Bash Probe:internal/probe/bash/
- 架构设计文档:docs/architecture.md
任务描述写得越清楚,AI 出活越靠谱;描述含糊的,烂 PR 照样打回去重写。
那段时间的工作流,基本是「作者当产品经理 + Code Reviewer,AI 当执行工程师」。
AI Agent 擅长什么?不擅长什么?
擅长:模式清晰、重复度高的任务
比如「按照这个标准模板,把剩余 6 个 Probe 全部重构一遍」。
// AI 擅长的模式化任务
// 给定 Bash Probe 的模板,生成 OpenSSL Probe
// bash/bash_probe.go(模板)
type BashProbe struct {
base.BaseProbe
config *BashConfig
}
func (p *BashProbe) Initialize() error {
return p.loadBPFProgram()
}
func (p *BashProbe) Start() error {
return p.attachUprobe()
}
// AI 生成的 openssl/openssl_probe.go
type OpenSSLProbe struct {
base.BaseProbe
config *OpenSSLConfig
}
func (p *OpenSSLProbe) Initialize() error {
return p.loadBPFProgram()
}
func (p *OpenSSLProbe) Start() error {
return p.attachUprobe()
}
这种活交给 AI 效率极高。
不擅长:涉及 eBPF 内核行为的细节、并发竞态的根因分析
// AI 不擅长的问题
// 为什么在高并发场景下,DSB keylog 写入会出现数据乱序?
// 这需要理解:
// 1. eBPF perf buffer 的读取语义
// 2. Go 的 goroutine 调度
// 3. pcapng 格式的序列化要求
// 4. 文件系统的写入缓冲
// 这类问题还是得靠人来兜底
v2 实践的启示:AI Agent 不是替代,是解放
v2 这次实践下来,作者的感受是:
AI Agent 不是替代开发者,而是把开发者从重复劳动里解放出来,让人能把精力放在真正需要判断力的地方。
这标志着开源开发进入了一个新范式:
| 传统模式 | 产品经理模式 |
|---|---|
| 开发者写代码 | 开发者写需求(Issue) |
| 开发者做 Code Review | AI 写代码,开发者做 Code Review |
| 重复劳动消耗精力 | 精力集中在架构决策、Bug 分析 |
| 一个人干多个人的活 | 一个人 + AI = 一个团队 |
破坏性变更备忘:从 v1 升级到 v2
如果你有基于 eCapture 源码开发的二次开发代码,升级 v2 需要注意以下几点:
| 变更项 | v1 | v2 |
|---|---|---|
| Probe 代码位置 | user/ | internal/probe/ |
| Build tag(Android) | androidgki | ecap_android |
| eBPF bytecode 目录 | 项目根 | ebpfassets/ |
| Probe 注册方式 | 手动初始化 | init() 自注册 + Factory |
实战:用 eCapture 抓包的真实场景
场景 1:调试微服务 HTTPS 调用
# 找到目标进程
ps aux | grep my-service
# 抓取该进程的 TLS 明文
sudo ecapture tls -p $(pgrep -f my-service) -w debug.pcap
# 用 Wireshark 打开
wireshark debug.pcap
场景 2:审计数据库查询
# 抓取 MySQL 明文查询
sudo ecapture mysql -w mysql.pcap
# 抓取 PostgreSQL 明文查询
sudo ecapture postgres -w postgres.pcap
场景 3:安全分析——检测敏感数据泄露
# 抓取所有 TLS 流量,输出为 text
sudo ecapture tls -o sensitive.log
# 搜索敏感关键词
grep -E 'password|token|secret|credit_card' sensitive.log
场景 4:Android 应用流量分析
# 在 Android 设备上运行
adb shell su -c "ecapture tls -w /sdcard/capture.pcap"
# 拉取到本地
adb pull /sdcard/capture.pcap
# 用 Wireshark 分析
wireshark capture.pcap
技术深度:eBPF uprobe 的实现原理
uprobe 的工作机制
uprobe 是 Linux 内核提供的用户态探针机制,允许在用户态函数入口/出口插入断点。
┌─────────────────────────────────────────────────────────┐
│ 用户态进程 │
│ ┌────────────────────────────────────────────┐ │
│ │ SSL_write@plt │ │
│ │ jmp *SSL_write@got │ │
│ └────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────┐ │
│ │ SSL_write(libssl.so) │ │
│ │ 0x7f12345678: push rbp │ ◄── uprobe 断点
│ │ 0x7f12345679: mov rbp, rsp │ │
│ │ ... │ │
│ └────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
│
▼ 内核态信号
┌─────────────────────────────────────────────────────────┐
│ eBPF 程序 │
│ SEC("uprobe/SSL_write") │
│ int probe_ssl_write(struct pt_regs *ctx) { │
│ // 从寄存器中提取参数 │
│ const void *buf = (const void *)PT_REGS_PARM2(ctx);│
│ size_t count = PT_REGS_PARM3(ctx); │
│ │
│ // 读取明文数据 │
│ bpf_probe_read(&data, count, buf); │
│ │
│ // 发送到用户态 │
│ bpf_perf_event_output(ctx, &events, ..., &data); │
│ return 0; │
│ } │
└─────────────────────────────────────────────────────────┘
关键技术点
- 符号解析:通过
/proc/pid/maps和 ELF 解析,找到目标函数地址 - 断点注入:通过
perf_event_open+ioctl(PERF_EVENT_IOC_SET_BPF)注入 eBPF 程序 - 参数提取:通过
pt_regs结构从寄存器中提取函数参数 - 数据传输:通过 perf buffer 或 ring buffer 将数据发送到用户态
与竞品对比:eCapture vs tcpdump vs mitmproxy
| 特性 | eCapture | tcpdump | mitmproxy |
|---|---|---|---|
| TLS 明文捕获 | ✅ 无需配置 | ❌ 只能抓密文 | ✅ 需要配置 CA |
| 侵入性 | 零侵入 | 零侵入 | 需要安装 CA |
| 性能影响 | 低(eBPF) | 低 | 中(代理转发) |
| 平台支持 | Linux/Android | 全平台 | 全平台 |
| 数据库支持 | ✅ MySQL/PG | ❌ | ❌ |
| 实时推送 | ✅ WebSocket | ❌ | ✅ |
总结:开源开发的「产品经理模式」来了
eCapture v2 的发布,不仅是一个技术工具的升级,更是一次开源开发范式的实验。
技术层面:
- 架构重构:工厂模式 + 模板方法 + 观察者,彻底解耦
- 功能增强:GoTLS 四元组、pcap 可靠写入、Android 成熟
- 质量保障:72+ E2E 测试,核心路径全覆盖
范式层面:
- AI Agent 写了 90% 的代码,35% 的 PR
- 作者角色从「写代码」变成「写需求 + 审 PR」
- 开源开发进入「产品经理模式」
启示:
- AI Agent 擅长模式清晰、重复度高的任务
- 人负责架构决策、Bug 分析、需求定义
- AI 不是替代,是解放——把人从重复劳动中解放出来
eCapture 的作者在博客最后写道:
v2 是一次迟到很久的重构。老实说,v1 时代的 user/ 目录,笔者自己看着都头疼。这次借着 AI Coding Agent 的帮助,总算把欠的技术债还掉了大半。
这或许就是 AI Agent 时代开源开发的新常态:技术债不再是负担,因为 AI 可以帮你还。
项目地址:https://github.com/gojue/ecapture(15,097 ⭐)
官网:https://ecapture.cc
作者博客:https://www.cnxct.com/ecapture-v2/