大家好,eCapture项目距上次新版发布也已经过去2-3个月了,在这期间,社区论坛里很多网友提了一些问题以及需求,等我来解决。这几个月博主工作特别忙,一直没时间更新eCapture。这个周末特地抽时间解决了社区中反应的一些bug、优化点等。至于新功能,主要的就是流量转发
功能,这个功能还是比较复杂的。
今天,博主先把发布一个修复bug的版本,等过段时间博主不是那么忙了,再构思流量转发
功能,分享给大家。
新版发布
功能介绍
- 增加了Linux 5.2一下内核的异常提示
- 移除了
curl/wget
等参数路径指定 - 恢复了关联数据包目标IP功能。
- 修复了Openssl FD总是为0的错误。
其中,第二条可能会有影响,移除curl\wget
路径参数,也顺带移除了自动搜索这两个软件所使用的Openssl
动态链接库版本,以及所属路径问题。意味着大家需要自己检查期望被捕获的程序所用的SSL类库版本,并手动使用--libssl
参数指定。
下载
下载地址:eCapture v0.6.2
流量转发
流量转发功能有很多使用场景,包括测试、渗透、研发、运维等,国内外都有用户提到过这个需求。
流量转发功能,必不可少的数据就是IP五元组,而这个问题,是eCapture最头疼的问题,拿不到远程地址的IP、端口数据。
eCapture实现介绍
- 基于TC的流量包获取,使用Uprob eBPF来完成捕获捕获。借助于wireshark的pcapng模式,进行解析展示。
- 基于uprobe hook,eBPF程序挂钩到
libssl
动态链接库文件中,针对SSL_write
、SSL_read
函数进行Hook,读取其参数、返回值。
uprobe hook也就是函数的调用hook,意味着只有这个函数触发时,才能执行eBPF的程序。并且是没法调用外部的SSL的外部函数的。
int SSL_write(SSL *s, const void *buf, int num)
{
// eBPF uprobe
return ret;
// eBPF uretprobe
}
int SSL_read(SSL *s, void *buf, int num)
{
// eBPF uprobe
return ret;
// eBPF uretprobe
}
比如,eBPF uprobe代码是插入到SSL_write
函数的入口点,可以读取SSL *s
、const void *buf
、int num
这三个参数。也就是说,这里并没有关于这个数据包所属IP、port等信息的参数。要想拿到IP等信息,必需从这三个参数里读取。
eCapture的数据包关联IP实现
eCapture的实现,是根据SSL *s
的内存地址,再根据结构体中的偏移地址进行二次定位、三次定位等,查找最终的目标数值。
但是,但是来了,SSL *s
对应ssl_st
结构体,里面并不存储IP\addr
等信息 。现在的实现是读取SSL *s
里的rbio\wbio
结构体,他们里面有个num字段,也就是FD
信息。 再hook connect
函数,
int __connect (int fd, __CONST_SOCKADDR_ARG addr, socklen_t len)
读取FD,以及addr
信息存储到 eBPF manp里。
eCapture再根据进程PID、fd来关联addr信息。 这样的实现有个缺点,就是__connect
函数一般都在libpthread.so
里,意味着除了hook libssl.so
,还需要再多hook一个动态链接库,而且在部分Linux发行版或者Android中,还不确定会被放到哪个链接库下。
缺陷
在SSL_read
时,是可以正常工作的。但在SSL_write
时,fd可能读到0,是因为SSL_write
函数它可能是一个异步的BIO实现。 这就导致发送的包拿不到目标IP信息。 未来做数据包转发时,目标代理软件就无法解析、无法关联到一个TCP会话中。
难点
只好另寻其他办法。 但从libssl.so这一个文件来看,不管是Read还是Write函数,第一个参数是SSL *结构体,最好能从这个结构体中找到存储了远程IP端口的信息。但我读了很久的Openssl类库源码,也没找到哪里存了这个数据。
取舍
如果说,Hook一个函数的方式实现不了,只能回到前面的hook两个函数的方向上了。但区别是说,仍要保持只hook openssl这一个动态链接库,否则兼容成本太大。 目前有一些思路,比如SSL_set_fd
函数等。
int SSL_set_fd(SSL *s, int fd)
int SSL_set_wfd(SSL *s, int fd)
int SSL_set_rfd(SSL *s, int fd)
需要确认的是,每个TCP链接,都必须要在绑定FD时,调用一次SSL_Set_fd
,或者SSL_set_wfd
、SSL_set_rfd
等函数,而这个是无法单从openssl的代码能确定的,而是取决于使用openssl类库的程序,可控性就很差。
耐心等待
是的,又到了你看不见工作量的需求上了,又需要博主拼命肝功能了,等我好消息。
CFC4N的博客 由 CFC4N 创作,采用 署名—非商业性使用—相同方式共享 4.0 进行许可。基于https://www.cnxct.com上的作品创作。转载请注明转自:eCapture支持流量转发的进展同步
博主你好,你的另一篇关于ecapture新特性的文章中提到由于消费ebpf map数据赶不上产生的速度,可能导致ecapture会丢掉部分报文,想请教一下cilium做网络转发/代理,或者你接下来准备做的这个流量转发功能,为什么不会担心丢包呢?是不通过map存嘛?
你好,抱歉回复晚了。博客的评论没有通知功能,建议你到微信公众号里留言。
是通过ebpf map来存,但map也有内存大小限制,一旦有这个限制,就会受到消费、生产的速度。
可以认为是代理功能吗?最近也在调研这个功能。大鹅项目好像是有类似的逻辑,dns配置 fallback 默认走代理。《https://github.com/daeuniverse/dae》