eCapture 介绍
用法介绍
- 编写一个golang的简单http客户端,请求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
}
- 编译成可自行文件 main
go build -o main main.go - 分别执行ecapture程序和main程序
/home/wisteria/tls-decrypt/main
./ecapture gotls -e=/home/wisteria/tls-decrypt/main -m pcap -w a2.pcap -i eth0 - 用wireshark查看保存的a2.pcap文件,可以看到解密后的报文
gotls卸载分析
-
ebpf uprobe
核心原理是利用ebpf uprobe技术 hook 到ssl相关的函数,下面以writeKeyLog函数为例- 用户态程序注册函数
const ( GoTlsReadFunc = "crypto/tls.(*Conn).Read" GoTlsWriteFunc = "crypto/tls.(*Conn).writeRecordLocked" GoTlsMasterSecretFunc = "crypto/tls.(*Config).writeKeyLog" ) if g.isRegisterABI { sec = "uprobe/gotls_mastersecret_register" fn = "gotls_mastersecret_register" } else { sec = "uprobe/gotls_mastersecret_stack" fn = "gotls_mastersecret_stack" } g.bpfManager = &manager.Manager{ Probes: []*manager.Probe{ { Section: "classifier/egress", EbpfFuncName: tcFuncNameEgress, Ifname: g.ifName, NetworkDirection: manager.Egress, }, { Section: "classifier/ingress", EbpfFuncName: tcFuncNameIngress, Ifname: g.ifName, NetworkDirection: manager.Ingress, }, // -------------------------------------------------- // gotls master secrets { Section: sec, EbpfFuncName: fn, AttachToFuncName: config.GoTlsMasterSecretFunc, BinaryPath: g.path, UID: "uprobe_gotls_master_secret", UAddress: g.conf.(*config.GoTLSConfig).GoTlsMasterSecretAddr, }, },- 内核ebpf挂载程序
SEC("uprobe/gotls_mastersecret_register") int gotls_mastersecret_register(struct pt_regs *ctx) { return gotls_mastersecret(ctx, true); }* crypto/tls/common.go * func (c *Config) writeKeyLog(label string, clientRandom, secret []byte) error */ static __always_inline int gotls_mastersecret(struct pt_regs *ctx, bool is_register_abi) { // const char *label, *clientrandom, *secret; void *lab_ptr, *cr_ptr, *secret_ptr; void *lab_len_ptr, *cr_len_ptr, *secret_len_ptr; s32 lab_len, cr_len, secret_len; /* * * in golang struct, slice header like this * type slice struct { * array unsafe.Pointer * len int * cap int * } * so, arument index are in the order one by one * */ 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); if (lab_len <= 0 || cr_len <= 0 || secret_len <= 0) { return 0; } debug_bpf_printk( "gotls_mastersecret read params length success, lab_len:%d, cr_len:%d, " "secret_len:%d\n", lab_len, cr_len, secret_len); struct mastersecret_gotls_t mastersecret_gotls = {}; mastersecret_gotls.labellen = lab_len; mastersecret_gotls.client_random_len = cr_len; mastersecret_gotls.secret_len = secret_len; int ret = bpf_probe_read_user_str(&mastersecret_gotls.label, sizeof(mastersecret_gotls.label), (void *)lab_ptr); if (ret < 0) { debug_bpf_printk( "gotls_mastersecret read mastersecret label failed, ret:%d, " "lab_ptr:%p\n", ret, lab_ptr); return 0; } debug_bpf_printk("gotls_mastersecret read mastersecret label:%s\n", mastersecret_gotls.label); ret = bpf_probe_read_user_str(&mastersecret_gotls.client_random, sizeof(mastersecret_gotls.client_random), (void *)cr_ptr); if (ret < 0) { debug_bpf_printk( "gotls_mastersecret read mastersecret client_random failed, " "ret:%d, cr_ptr:%p\n", ret, cr_ptr); return 0; } ret = bpf_probe_read_user_str(&mastersecret_gotls.secret_, sizeof(mastersecret_gotls.secret_), (void *)secret_ptr); if (ret < 0) { debug_bpf_printk( "gotls_mastersecret read mastersecret secret_ failed, ret:%d, " "secret_ptr:%p\n", ret, secret_ptr); return 0; } bpf_perf_event_output(ctx, &mastersecret_go_events, BPF_F_CURRENT_CPU, &mastersecret_gotls, sizeof(struct mastersecret_gotls_t)); return 0; }