GitHub万星项目进度70%,eCapture旁观者7K Stars达成 简述 是的,一年半时间,笔者的首个7000星的GitHub开源项目来了,它就是eCapture旁观者。未来一年,争取过万星。 项目背景 eCapture一个无需CA证书,无侵入的HTTPS/TLS明文抓包工具。可以在Linux 4.18以上版本使用,同时也支持Android arm64 5.5以上版本。 eCapture的中文名叫「旁观者」,即「当局者迷,旁观者清」,与其本身功能「旁路、观察」契合,且发音与英文有相似之处。此工具切入大家痛点,不需要CA证书,也不需要进程冲洗,就可以在Linux上分析生产环境的https网络包的问题,深受大家欢迎。 从2022年3月底创建,至今一年半时间,获得7000星。与知乎上较为出名的xmake-io/xmake开源项目相比,可谓神速。当然,个人的努力固然有,但主要是借助了历史的进程——eBPF技术兴起。 依赖技术 eBPF技术是Linux内核的子系统,用来做跟踪、抽样、网络优化、安全管控等。借助于eBPF的动态插桩能力,eCapture可以无需应用进程重启,无需CA证书,动态增加观测点,实现对进程的HTTPS/TLS加密通讯抽样诊断。 eCapture项目是用来做网络分析的,适合线上环境,不方便重启进程的场景。使用eBPF TC/uprobe HOOK技术,支持Linux/Android 系统,支持x86_64/aarch64两种CPU架构,工具大小不到10M。 支持保存为文本日志,以及pcap-NG格式文件,直接用wireshark打开分析。同时,还可以支持关联网络所属的进程名、进程PID等信息,在入侵对抗领域,特别需要后门网络包是哪个进程发出的,从而更好的定位后门进程。 迭代 从2022年3月起,至今发布了34个版本,160多个PR;整理了5篇文章,录制了3个视频。花费了100多瓶可乐,掉了几万根头发...... 时间与精力 大部份功能上,项目的规划、研发、测试、交付、运营对接,都是我一个人。时间上,几乎都是下班后的21点到凌晨1点,以及周末的大部分时间。也就是说,2022年的大部分休息时间都贡献给了eCapture这个开源项目。 一个人做开源,特别花时间、花体力,花精力,完全是用爱发电。 可以对比eCapture跟xmake两个项目的代码编写事件。 可能是因为eBPF技术比较偏内核底层,社区熟悉这部分技术的人不多,可以为项目贡献的人寥寥无几,整体参与度不高。在这里也非常感谢为eCapture做贡献的人。 展现在大家眼前的是一个软件,一个项目,帮你解决了一些问题。所需要代码行数并不多等,实际上,笔者需要花费大量时间、精力,研究学习背后的技术知识。 冰山的背后 比如Pcapng格式的支持,需要了解PcapNG File Format,了解里面Block的含义,如何解析、填充;了解Packet flow in Netfilter and General Networking,才能更好的选择HOOK点。深挖Decryption Secrets Block标准,并提交了google/gopacket PR # 1042,支持DSB格式,使得Wireshark可以直接分析eCapture捕获https加密包的明文内容。 比如Golang uretprobe,需要了解Golang在不同版本下的ABI规范,是基于寄存器,还是栈。符号表内,函数调用的参数传递顺序差异。函数调用栈与参数、返回值的内存布局等。只有了解这些,才能更好的实现函数入参、返回值的捕获,才能实现Golang的TLS类库密钥捕获,实现Golang TLS类库的加密通讯明文捕获。 比如Android系统的支持,需要了解AMD64、ARM64的兼容实现,了解Linux内核在Android上的调整点。探索在Linux上编译Android可运行的eBPF程序路线。翻阅对比Openssl类库封装成BoringSSL的差异,熟悉ELF文件装载、加载过程,定位合适的挂钩函数,确定内存偏移量,才能动态适配多种OpenSSL、BoringSSL类库,支持这些类库的TLS/SSL密钥捕获以及明文捕获。 除了技术实现,在推广运营上,笔者画了很多架构图、原理图,甚至自学视频制作,录制使用方法,学习视频剪辑、录音等等。虽然学习是痛苦的,但笔者的艺术细胞倒是提升不少。 你看到的只是海面上的成功部分,而实际上需要我付出的,却是冰川下大量的研发工作。 尽管如此,开源的路上依旧少不了质疑的人。 质疑 去年在知乎上有一偏讨论eBPF的评论,里面有一些臆测,认为eCapture就是一个木马后门,认为在敲击键盘那一刻,偷偷记录按键行为就可以实现。 还有人认为,作者是中国人,关注的公司也是腾讯、阿里、字节、清华大学这种中国的公司,脑海中确实又蠢又坏的猜忌。 人生的路上,少不了质疑与漫骂。对此我表示哈哈一笑。稍作回答:eBPF技术是正经技术,作者也是正经人。 展望 作为一个网络分析工具, eCapture的基本功能已经具备,在实际的分析场景中,希望能像tcpdump、Charles等工具一样,可以保留相同的参数习惯,以及结合这些工具一起使用。 为此,笔者规划两项功能,以及增强社区运营,推动eCapture进入到部分Linux发行版的软件源上。 Charles Proxy\Burp Suite可读的网络包文件 支持tcpdump的filter表达式 进入Linux发行版的软件源仓库 最初,项目是靠GitHub自己的推荐功能,吸引了很多关注者。之后为了让用户能快速上手,也写了很多文章,画各种原理图、架构图,制作各种使用的演示视频,这也特别花时间、精力。面向用户也是国内跟国外,语言是汉字与英语,所以还做了多语言的版本,对于英语渣的我来说,挑战也极大。 一路走来,感受颇多。项目传播速度,跟我精力的分配息息相关。从去年下半年起,笔者工作上特别忙,投入到这项目的时间逐渐减少。从热情澎湃时的每天晚上24点提交代码,到后来每周周末提交代码,甚至现在一个多月才提交几次代码。精力实在有限。 笔者期望,eCapture能为大家带来更大的帮助,期望大家也能一起参与进来。 一个人走得很快,但一群人走得更远。 当然,也不排除,未来捐献给类似CNCF之类的开源组织。但也要在笔者的这个万星目标之后考虑。万星项目,冲冲冲。 扩展阅读 为了方便新人上手,笔者曾录制过几篇视频,也整理过一些文章介绍,对eCapture不熟悉的同学可以读读。 eCapture旁观者:Android HTTPS明文抓包 eCapture:无需CA证书抓https网络明文通讯(Linux) eCapture支持bogingssl TLS 1.3明文捕获 Linux中基于eBPF的恶意利用与检测机制 Tetragon进程阻断原理 2023-08-28 0 0
eCapture旁观者支持Golang tls/https加密明文捕获 前言 云原生生态中,golang语言开发的项目越来越多,例如Docker和K8s、etcd等。作为SRE、RD,偶尔需要在生产环境抓网络通讯包,用来分析排查故障。很多时候,都是tls/https加密协议,如何在不重启业务保留现场,不改为自定义CA证书的情况下,分析明文通讯内容呢? 适用场景 eCapture 0.5.0版本在2023年3月12日发布,支持了go语言编写的软件的tls/https明文抓包。只需要root权限,即可捕获并保存为pcapng格式,使用wireshark即可打开查看。 使用方法 gotls模块的e参数用来设定golang编译的可执行文件路径,可以通过ecapture gotls -h来查看使用说明。 bin/ecapture gotls -h NAME: gotls - capture golang tls/https text content without CA cert for ELF compile by Golang toolchain USAGE: ecapture gotls [flags] DESCRIPTION: use eBPF uprobe/TC to capture process event data and network data. also support pcap-NG format. ecapture gotls ecapture gotls --elfpath=/home/cfc4n/go_https_client --hex --pid=3423 ecapture gotls --elfpath=/home/cfc4n/go_https_client -l save.log --pid=3423 ecapture gotls -w save_android.pcapng -i wlan0 --port 443 --elfpath=/home/cfc4n/go_https_client OPTIONS: -e, --elfpath="" ELF path to binary built with Go toolchain. -h, --help[=false] help for gotls -i, --ifname="" (TC Classifier) Interface name on which the probe will be attached. --port=443 port number to capture, default:443. -w, --write="" write the raw packets to file as pcapng format. GLOBAL OPTIONS: -d, --debug[=false] enable debug logging --hex[=false] print byte strings as hex encoded strings -l, --log-file="" -l save the packets to file --nosearch[=false] no lib search -p, --pid=0 if pid is 0 then we target all pids -u, --uid=0 if uid is 0 then we target all users 举个例子 比如/path/elf_filepath_compiled_by_go是一个go写的web服务,并且开启了https加密,代码如下: package main import ( "crypto/tls" "fmt" "io" "net/http" "os" ) func main() { b, e := GetHttp("https://github.com") if e == nil { fmt.Printf("response body: %s\n\n", b) } else { fmt.Printf("error :%v", e) } } func GetHttp(url string) (body []byte, err error) { // 开启TLS密钥记录,用于跟eCpature捕获的密钥对比。 f, err := os.OpenFile("/tmp/go_master_secret.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600) if err != nil { panic(err) } defer f.Close() c := &http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true, KeyLogWriter: f}, }} resp, e := c.Get(url) if e != nil { return nil, e } defer resp.Body.Close() body, err = io.ReadAll(resp.Body) return body, err } 可以使用如下命令,捕获明文通讯。 不用重启这个服务进程,也不需要做其他任何配置,就跟使用tcpdump一样。 ./ecapture gotls -e=/path/elf_filepath_compiled_by_go -w a.pcapng -i eth0 Wireshark打开网络包 下载地址 eCapture Github仓库:https://github.com/gojue/ecapture/releases/tag/v0.5.0 韩国GitHub镜像:https://ghproxy.com/https://github.com/gojue/ecapture/releases/tag/v0.5.0 以下内容,为功能实现原理,若你只是使用,可跳过。 技术原理 Probe参数获取 Golang的ABI不同于C,自定义了ABI机制。并且在go 1.17之前,使用的是栈方式传递调用参数;1.17以以后使用了寄存器方式传递调用参数。你可以阅读Register-based Go calling convention 了解更多知识。 这里有一张Golang的函数参数、返回值的寄存器传递布局,供参考。更多内容可以在线阅读《Go语言高级编程》中文版 eCapture的参数获取实现,可以阅读kern/go_argument.h Probe参数选择 笔者这里hook的是Golang源码目录下crypto/tls/common.go文件中的writeKeyLog函数。用来捕获tls的master secret的label类别、clientRandom、密钥值等。 Golang函数参数传递 有个需要注意的地方,比如writeKeyLog函数的第一个参数是string类型,第二、三个参数是slice类型。在Golang里,也都是一个结构体,如下代码: // runtime/string.go type stringStruct struct { str unsafe.Pointer len int } // runtime/slice.go type slice struct { array unsafe.Pointer len int cap int } 以string类型为例,在Go 的参数传递时,不光传递字符串的str unsafe.Pointer指针地址,也还会传递len int到寄存器上。所以,在获取参数时,需要注意参数所在位置。 eCapture的实现: lab_ptr = (void *)go_get_argument(ctx, is_register_abi, 2); lab_len_ptr = (void *)go_get_argument(ctx, is_register_abi, 3); cr_ptr = (void *)go_get_argument(ctx, is_register_abi, 4); cr_len_ptr = (void *)go_get_argument(ctx, is_register_abi, 5); secret_ptr = (void *)go_get_argument(ctx, is_register_abi, 7); secret_len_ptr = (void *)go_get_argument(ctx, is_register_abi, 8); bpf_probe_read_kernel(&lab_len, sizeof(lab_len), (void *)&lab_len_ptr); bpf_probe_read_kernel(&cr_len, sizeof(lab_len), (void *)&cr_len_ptr); bpf_probe_read_kernel(&secret_len, sizeof(lab_len), (void *)&secret_len_ptr); Golang uretprobe 在eCapture的文本模式中,需要在加密之前、解密之后拿到明文,对应的两个函数分别是crypto/tls.(*Conn).writeRecordLocked和 crypto/tls.(*Conn).Read。加密之前的获取只需要使用eBPF uprobe HOOK即可实现。而解密之后,则需要uretprobe,但Golang里,uretprobe的实现机制,会破坏他的堆栈,导致Golang程序进程崩溃。 这个问题,在iovisor/bcc社区也有讨论:BCC issue: Go crash with uretprobe #1320,包括火焰图、eBPF的领导者Brendan Gregg,对这个问题也没有太好的办法。Gianluca Borello给了间接的解决方案,相对来说还是比较繁琐的,也有一定的crash风险,有兴趣的同学可以去看看。 eCapture里,加密之前的uprobe 已经完成hook,实现https/tls的请求内容明文捕获。但解密后的内容,暂时无法实现。笔者也在尝试其他思路,比如找到Read返回值的调用函数,在哪里使用uprobe实现,但这点逻辑比较偏业务层,调用者比较多,不太方便过滤。如果你有更好的办法,也欢迎提出来。 扩展阅读 Hooking Go from Rust - Hitchhiker’s Guide to the Go-laxy BCC issue: Go crash with uretprobe #1320 Interface method calls with the Go register ABI eCapture旁观者官网 eCapture旁观者 Github仓库 2023-03-12 4 0