当前位置: 首页 > news >正文

[ecapture]捕获TLS明文流量

说明:ecapture支持的能力更多,但我的需求只涉及这两种,所以个人文档中仅提及gotls、openssl

关键字:ebpfecapture

PR:
v1 PR: gojue/ecapture#959
master PR: gojue/ecapture#960

一、整体架构

┌─────────────────────────────────────────────────────────────┐ │ 用户空间 (User Space) │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ 上层服务&ecapture用户态 (用户态程序) │ │ │ │ - 加载 eBPF 程序到内核 │ │ │ │ - 从 perf event 读取数据 │ │ │ │ - 输出到文件/stdout/PCAP │ │ │ └───────────────────┬─────────────────────────────────┘ │ │ │ ▲ │ │ │ │ ⑥bpf_perf_event_output │ │ ▼ │ │ └──────────────────────┼─┼───────────────────────────────────┘ │ │ ┌──────────────────────┼─┼───────────────────────────────────┐ │ 内核空间 (Kernel Space) │ │ ┌──────────────────┴─┴─────────────────────────────────┐ │ │ │ eBPF 程序 (gotls_kern.c) │ │ │ │ │ │ │ │ ┌─────────────────────────────────────────────────┐ │ │ │ │ │ SEC("uprobe/gotls_write_register") │ │ │ │ │ │ int gotls_write_register(struct pt_regs *ctx) { │ │ │ │ │ │ // ①获取参数 │ │ │ │ │ │ // ②提取fd/IP/端口 │ │ │ │ │ │ // ③读取数据 │ │ │ │ │ │ // ④输出到 perf event │ │ │ │ │ │ } │ │ │ │ │ └─────────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ ┌─────────────────────────────────────────────────┐ │ │ │ │ │ eBPF Maps │ │ │ │ │ │ - events (perf event array) ←─────────────┐ │ │ │ │ │ │ - gte_context_gen (percpu array) │ │ │ │ │ │ └─────────────────────────────────────────────────┘ │ │ │ └───────────────────────────────────────────────────────┘ │ │ ▲ │ │ │ ⑤写入数据到 map │ │ ┌──────────────────────┴───────────────────────────────────┐ │ │ 目标 Go 程序 │ │ │ func (c *Conn) writeRecordLocked(...) { │ │ │ // ③函数执行时触发 uprobe │ │ │ // eBPF 程序在这里被调用 │ │ │ } │ │ └───────────────────────────────────────────────────────────┘

整个系统包含三个部分:

  1. 被监控的程序:如Nginx或这次测试使用的Caddy(Go程序)
  2. 内核空间:eBPF程序运行的地方
  3. 用户空间:上层服务接收并处理事件

二、eBPF的核心能力

eBPF提供了动态插桩能力,让我们能在不改变内核源码的基础上,注册自定义程序去hook内核函数或用户程序,实时监控系统行为。

  • hook内核函数:比如hook连接事件(__system_connect__system_accept4),获取连接的IP、端口等信息
  • hook用户程序:比如hook Go程序的tls.Conn下的writeRecordLockedread方法——这两个位置正好是数据解密后的明文位置

三、gotls的实现原理(以Caddy为例)

工作流程

  1. 挂载eBPF程序:通过uprobe技术,将eBPF程序挂载到Caddy的writeRecordLockedread方法上

  2. 触发捕获:当Caddy处理HTTPS请求时,会调用相应的方法——这些方法触发时,正好是数据已经被解密、准备发送或刚刚接收的时刻

  3. 抽取数据:在hook中从Go的tls.Conn结构体读取所需字段:

    • 文件描述符(fd)
    • 源IP、源端口
    • 目的IP、目的端口
    • 明文数据
  4. 上抛事件:通过perf event将数据发送到用户态的监听程序小A

  5. 输出结果:用户态的监听程序小A监听到事件后,得到系统期望的tuple五元组(协议、源IP、源端口、目的IP、目的端口)和明文内容

Go结构内存布局

tls.Conn (crypto/tls/conn.go) ├─ conn net.Conn (interface, offset 0, size 16) ← 接口占16字节 │ ├─ type pointer (offset 0, 8 bytes) ← 指向类型信息 │ └─ data pointer (offset 8, 8 bytes) → *net.TCPConn ← 指向具体TCP连接 │ └─ net.TCPConn (net/tcpsock.go) │ └─ conn (embedded, offset 0, size 8) ← 嵌入的net.conn结构体 │ └─ fd *netFD (offset 0, 8 bytes) → *net.netFD │ └─ net.netFD (net/fd_unix.go) │ ├─ pfd poll.FD (offset 0, size 56) │ │ └─ Sysfd int (offset 16, 8 bytes) ← 文件描述符 │ ├─ laddr Addr (offset 96, 16字节) ← 源IP、端口 │ └─ raddr Addr (offset 112, 16字节) ← 目的IP、端口

通过这个结构可以看到,可以直接从tls.Conn中抽取完整的网络连接信息,不需要依赖额外的连接事件。

四、方案特点

优势

  • 一次性拿到所有信息:fd、IP、端口、明文数据都在同一个hook中获取
  • 实现简单:不需要维护跨事件的map关联
  • 满足当前需求:适用于Go 1.20+、64位系统

依赖与限制

  • ⚠️强依赖内存布局

    • 64位和32位系统的内存对齐方案不同
    • Go版本变动可能修改tls.Conn字段结构
    • 遇到上述情况需要重新定制gotls_kern.c
  • 当前状态:Go 1.20+结构稳定,主流系统大多为64位,方案可用 // go1.20往前一些版本也行,需要确认go的文档,我没看

五、对比:OpenSSL的处理方式

gotls方案能直接抽取网络信息,但OpenSSL的TLS事件只有pid和fd句柄,没有IP、端口等。所以需要不同的处理方式:

  1. 先hook内核connect事件:在__system_connect__system_accept4中获取连接信息(IP、端口、fd、pid等),暂存在map中

  2. 再等TLS事件触发:当OpenSSL的TLS函数被调用时,拿到pid和fd,去map中查找对应的连接信息

  3. 拼凑完整tuple:将两部分信息合并,得到五元组

六、总结

通过eBPF的uprobe、kprobe技术,实现了对Go程序Caddy的TLS、nginx的openssl tls明文捕获:

  • 零侵入:无需修改Caddy|nginx源码

  • 无需解密:直接读取内存中的明文

  • 低性能损耗:eBPF程序轻量高效

七. ecapture 实现相关文章

文章都在ecapture专栏里

[eCapture] GoTLS Perf 事件有序下发
[ecapture]捕获TLS明文流量
[ecapture]Connect Events获取
[ecapture]go1.20 tls fd抽取
[ecapture] eBPF hook gotls 收包乱序根因分析
[ecapture] gotls:三种模式实现说明与上层应用职责

http://www.jsqmd.com/news/718723/

相关文章:

  • 压力传感器品牌排名重磅出炉!广东犸力凭硬核实力稳居前列,彰显国产标杆力量 - 速递信息
  • StructBERT中文情感分类在跨境电商落地:多语言评论统一情感映射方案
  • WarcraftHelper技术优化指南:解决魔兽争霸3在现代系统上的兼容性与性能瓶颈
  • 还在为AutoCAD字体缺失烦恼?这款智能插件让你彻底告别问号乱码!
  • 汽车行业适配的国产变频电源服务商推荐 - 奔跑123
  • Phi-mini-MoE-instruct模型原理精讲:深入理解混合专家(MoE)架构与稀疏激活
  • 2026标书AI工具推荐:解构云境标书AI的生产力架构
  • 围棋螺旋算子与全域周期精算模型—基于乖乖数学本源公理0/1/∞的弈道统一场
  • 传统OCR管道改造:LightOnOCR-2-1B替代Tesseract的迁移方案
  • ArcGIS Pro 2.8 实战:三调地类筛选器,手把手教你用SQL搞定农用地、建设用地一键分类
  • AI 大学堂:OpenClaw 实战训练营,从零上手,跑通你的第一个“养虾”项目
  • 终极指南:如何用Fan Control软件彻底解决电脑风扇噪音问题
  • FontCenter:如何彻底解决AutoCAD字体缺失问题的技术方案
  • Cursor Pro终极激活指南:3步快速解锁免费AI编程功能
  • 海外SAP项目已成标配:英语不是加分项,而是入场劵
  • 从数据碎片到数字记忆:用WeChatMsg永久保存你的微信对话时光
  • ChatGLM3-6B-128K部署详解:云服务器上运行最佳实践
  • 探索楚门的世界-三--象牙塔内外的做事情的差别
  • 探索楚门的世界-一-
  • 像素史诗·智识终端Dify低代码平台集成:快速构建AI工作流应用
  • 3分钟搞定TrollStore安装:TrollInstallerX让iOS越狱应用安装如此简单
  • 如何永久保存微信聊天记录:一个让你数据真正属于自己的完整指南
  • 如何快速让PS手柄在Windows上完美运行:终极兼容性解决方案
  • 如何永久保存QQ空间青春记忆?GetQzonehistory三步备份完整教程
  • 别再写死日期范围了!Element Plus el-date-picker 动态联动限制实战(附完整代码)
  • ARM CCN-502架构解析:缓存一致性网络与QoS机制
  • 从Git命令到可视化图表:5分钟学会用Mermaid gitGraph复盘你的Git操作历史
  • 逃离鸭科夫-这游戏做的不错-道具多的上天了
  • 别再只看电流电压了!用这5个关键参数,帮你搞定MOS管选型(附避坑清单)
  • Clawdbot备份与恢复:保障Qwen3-VL模型数据安全