From 1af2a9f9b2e4b1581fd191305f7ab5e7af408b0b Mon Sep 17 00:00:00 2001 From: yuweizzz Date: Wed, 30 Oct 2024 11:59:24 +0800 Subject: [PATCH 01/11] basic kernel --- kern/gnutls_kern.c | 2 + kern/gnutls_masterkey.h | 322 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 324 insertions(+) create mode 100644 kern/gnutls_masterkey.h diff --git a/kern/gnutls_kern.c b/kern/gnutls_kern.c index cb57e7276..70bcf1b75 100644 --- a/kern/gnutls_kern.c +++ b/kern/gnutls_kern.c @@ -217,3 +217,5 @@ int probe_ret_SSL_read(struct pt_regs* ctx) { bpf_map_delete_elem(&active_ssl_read_args_map, ¤t_pid_tgid); return 0; } + +#include "gnutls_masterkey.h" diff --git a/kern/gnutls_masterkey.h b/kern/gnutls_masterkey.h new file mode 100644 index 000000000..b8b0e74ef --- /dev/null +++ b/kern/gnutls_masterkey.h @@ -0,0 +1,322 @@ +#define GNUTLS_RANDOM_SIZE 32 +#define GNUTLS_MASTER_SIZE 48 +#define MAX_HASH_SIZE 64 + +// Ref: +// https://github.com/gnutls/gnutls/blob/3.7.9/lib/gnutls_int.h +// +// typedef struct gnutls_session_int *gnutls_session_t; +// struct gnutls_session_int { +// security_parameters_st security_parameters; +// record_parameters_st *record_parameters[MAX_EPOCH_INDEX]; +// internals_st internals; +// gnutls_key_st key; +// }; +// +// gnutls_session_int --> security_parameters_st +// struct security_parameters_st { +// // ignore +// // ... +// const mac_entry_st *prf; +// uint8_t master_secret[GNUTLS_MASTER_SIZE]; +// uint8_t client_random[GNUTLS_RANDOM_SIZE]; +// // ignore +// // ... +// const version_entry_st *pversion; +// }; +// +// gnutls_session_int --> security_parameters_st --> mac_entry_st +// typedef struct mac_entry_st { +// // ignore +// // ... +// gnutls_mac_algorithm_t id; +// // ignore +// // ... +// } mac_entry_st; +// +// gnutls_session_int --> security_parameters_st -> version_entry_st +// typedef struct { +// // ignore +// // ... +// gnutls_protocol_t id; /* gnutls internal version number */ +// // ignore +// // ... +// } version_entry_st; +// +// gnutls_session_int --> gnutls_key_st +// struct gnutls_key_st { +// // ignore +// // ... +// union { +// struct { +// // ignore +// // ... +// uint8_t hs_ckey[MAX_HASH_SIZE]; /* client_hs_traffic_secret */ +// uint8_t hs_skey[MAX_HASH_SIZE]; /* server_hs_traffic_secret */ +// uint8_t ap_ckey[MAX_HASH_SIZE]; /* client_ap_traffic_secret */ +// uint8_t ap_skey[MAX_HASH_SIZE]; /* server_ap_traffic_secret */ +// uint8_t ap_expkey[MAX_HASH_SIZE]; /* {early_,}exporter_master_secret */ +// // ignore +// // ... +// } tls13; /* tls1.3 */ +// +// /* Follow the SSL3.0 and TLS1.2 key exchanges */ +// struct { +// // ignore +// // ... +// } tls12; /* from ssl3.0 to tls12 */ +// } proto; +// // ignore +// // ... +// }; +// + +struct gnutls_mastersecret_st { + u32 version; + /* from ssl3.0 to tls12 */ + u8 client_random[GNUTLS_RANDOM_SIZE]; + u8 master_secret[GNUTLS_MASTER_SIZE]; + + /* tls1.3 */ + u32 cipher_id; + u8 client_handshake_secret[MAX_HASH_SIZE]; + u8 server_handshake_secret[MAX_HASH_SIZE]; + u8 client_traffic_secret[MAX_HASH_SIZE]; + u8 server_traffic_secret[MAX_HASH_SIZE]; + u8 exporter_master_secret[MAX_HASH_SIZE]; +}; + +// gnutls_session_int->security_parameters +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS 0x0 + +// gnutls_session_int->security_parameters.prf +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_PRF 0x18 + +// mac_entry_st->id +#define MAC_ENTRY_ST_ID 0x18 + +// security_parameters_st->client_random +#define SECURITY_PARAMETERS_ST_CLIENT_RANDOM 0x50 + +// security_parameters_st->master_secret +#define SECURITY_PARAMETERS_ST_MASTER_SECRET 0x20 + +// security_parameters_st->pversion +#define SECURITY_PARAMETERS_ST_PVERSION 0xf8 + +// version_entry_st->id +#define VERSION_ENTRY_ST_ID 0x8 + +// gnutls_session_int->key.proto.tls13.hs_ckey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_HS_CKEY 0x1794 + +// gnutls_session_int->key.proto.tls13.hs_skey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_HS_SKEY 0x17d4 + +// gnutls_session_int->key.proto.tls13.ap_ckey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_CKEY 0x1814 + +// gnutls_session_int->key.proto.tls13.ap_skey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_SKEY 0x1854 + +// gnutls_session_int->key.proto.tls13.ap_expkey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_EXPKEY 0x1894 + +/////////////////////////BPF MAPS //////////////////////////////// + +// bpf map +struct { + __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); + __uint(key_size, sizeof(u32)); + __uint(value_size, sizeof(u32)); + __uint(max_entries, 1024); +} mastersecret_gnutls_events SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __type(key, u64); + __type(value, u64); + __uint(max_entries, 1024); +} gnutls_session_maps SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __type(key, u64); + __type(value, struct gnutls_mastersecret_st); + __uint(max_entries, 2048); +} bpf_context SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __type(key, u32); + __type(value, struct gnutls_mastersecret_st); + __uint(max_entries, 1); +} bpf_context_gen SEC(".maps"); + +/////////////////////////COMMON FUNCTIONS //////////////////////////////// +// 这个函数用来规避512字节栈空间限制,通过在堆上创建内存的方式,避开限制 +static __always_inline struct gnutls_mastersecret_st *make_event() { + u32 key_gen = 0; + struct gnutls_mastersecret_st *bpf_ctx = bpf_map_lookup_elem(&bpf_context_gen, &key_gen); + if (!bpf_ctx) return 0; + u64 id = bpf_get_current_pid_tgid(); + bpf_map_update_elem(&bpf_context, &id, bpf_ctx, BPF_ANY); + return bpf_map_lookup_elem(&bpf_context, &id); +} + +/////////////////////////BPF FUNCTIONS //////////////////////////////// +SEC("uprobe/gnutls_handshake") +int uprobe_gnutls_master_key(struct pt_regs *ctx) { + u64 current_pid_tgid = bpf_get_current_pid_tgid(); + u32 pid = current_pid_tgid >> 32; + u64 current_uid_gid = bpf_get_current_uid_gid(); + u32 uid = current_uid_gid; +#ifndef KERNEL_LESS_5_2 + // if target_ppid is 0 then we target all pids + if (target_pid != 0 && target_pid != pid) { + return 0; + } + if (target_uid != 0 && target_uid != uid) { + return 0; + } +#endif + u64 gnutls_session_addr = (u64)PT_REGS_PARM1(ctx); + bpf_map_update_elem(&gnutls_session_maps, ¤t_pid_tgid, &gnutls_session_addr, BPF_ANY); + debug_bpf_printk("gnutls uprobe/gnutls_handshake PID: %d, gnutls_session_addr: %d\n", pid, gnutls_session_addr); + return 0; +} + +SEC("uretprobe/gnutls_handshake") +int uretprobe_gnutls_master_key(struct pt_regs *ctx) { + u64 current_pid_tgid = bpf_get_current_pid_tgid(); + u32 pid = current_pid_tgid >> 32; + u64 current_uid_gid = bpf_get_current_uid_gid(); + u32 uid = current_uid_gid; + +#ifndef KERNEL_LESS_5_2 + // if target_ppid is 0 then we target all pids + if (target_pid != 0 && target_pid != pid) { + return 0; + } + if (target_uid != 0 && target_uid != uid) { + return 0; + } +#endif + + u8 handshake_return = (u8)PT_REGS_RC(ctx); + if (handshake_return != 0) { + // handshake failed + debug_bpf_printk("gnutls uretprobe/gnutls_handshake PID: %d, handshake failed, ret: %d\n", pid, handshake_return); + return 0; + } + debug_bpf_printk("gnutls uretprobe/gnutls_handshake PID: %d\n", pid); + + u64 *gnutls_session_addr_ptr = bpf_map_lookup_elem(&gnutls_session_maps, ¤t_pid_tgid); + if (!gnutls_session_addr_ptr) { + debug_bpf_printk("gnutls uretprobe/gnutls_handshake, lookup for gnutls_session_addr failed\n"); + return 0; + } + + u64 gnutls_session_addr = (u64) *gnutls_session_addr_ptr; + debug_bpf_printk("gnutls uretprobe/gnutls_handshake, gnutls_session_addr: %d\n", gnutls_session_addr); + + // ssl_version + u64 pversion_addr; + int ret = bpf_probe_read_user(&pversion_addr, sizeof(pversion_addr), + (void *)(gnutls_session_addr + GNUTLS_SESSION_INT_SECURITY_PARAMETERS + SECURITY_PARAMETERS_ST_PVERSION)); + if (ret) { + debug_bpf_printk("gnutls uretprobe/gnutls_handshake, get pversion_addr failed, ret: %d\n", ret); + return 0; + } + int ssl_version; + ret = bpf_probe_read_user(&ssl_version, sizeof(ssl_version), (void *)(pversion_addr + VERSION_ENTRY_ST_ID)); + if (ret) { + debug_bpf_printk("gnutls uretprobe/gnutls_handshake, get ssl_version failed, ret: %d\n", ret); + return 0; + } + debug_bpf_printk("ssl_version: %d\n", ssl_version); + + struct gnutls_mastersecret_st *mastersecret = make_event(); + if (!mastersecret) { + debug_bpf_printk("gnutls uretprobe/gnutls_handshake, mastersecret is null\n"); + return 0; + } + + // tls 1.2 + if (ssl_version == 4) { + mastersecret->version = ssl_version; + ret = bpf_probe_read_user(&mastersecret->client_random, sizeof(mastersecret->client_random), + (void *)(gnutls_session_addr + SECURITY_PARAMETERS_ST_CLIENT_RANDOM)); + if (ret) { + debug_bpf_printk("gnutls uretprobe/gnutls_handshake, get client_random failed, ret: %d\n", ret); + return 0; + } + ret = bpf_probe_read_user(&mastersecret->master_secret, sizeof(mastersecret->master_secret), + (void *)(gnutls_session_addr + SECURITY_PARAMETERS_ST_MASTER_SECRET)); + if (ret) { + debug_bpf_printk("gnutls uretprobe/gnutls_handshake, get master_secret failed, ret: %d\n", ret); + return 0; + } + bpf_perf_event_output(ctx, &mastersecret_gnutls_events, BPF_F_CURRENT_CPU, mastersecret, sizeof(struct gnutls_mastersecret_st)); + } + + // tls 1.3 + if (ssl_version == 5) { + mastersecret->version = ssl_version; + // mac cipher id + u64 prf_addr; + ret = bpf_probe_read_user(&prf_addr, sizeof(prf_addr), + (void *)(gnutls_session_addr + GNUTLS_SESSION_INT_SECURITY_PARAMETERS_PRF)); + if (ret) { + debug_bpf_printk("gnutls uretprobe/gnutls_handshake, get prf failed, ret: %d\n", ret); + return 0; + } + int mac_cipher_id; + ret = bpf_probe_read_user(&mac_cipher_id, sizeof(mac_cipher_id), (void *)(prf_addr + MAC_ENTRY_ST_ID)); + if (ret) { + debug_bpf_printk("gnutls uretprobe/gnutls_handshake, get mac_cipher_id failed, ret: %d\n", ret); + return 0; + } + debug_bpf_printk("gnutls uretprobe/gnutls_handshake, get mac_cipher_id ret: %d\n", mac_cipher_id); + mastersecret->cipher_id = mac_cipher_id; + ret = bpf_probe_read_user(&mastersecret->client_random, sizeof(mastersecret->client_random), + (void *)(gnutls_session_addr + SECURITY_PARAMETERS_ST_CLIENT_RANDOM)); + if (ret) { + debug_bpf_printk("gnutls uretprobe/gnutls_handshake, get client_random failed, ret: %d\n", ret); + return 0; + } + ret = bpf_probe_read_user(&mastersecret->client_handshake_secret, sizeof(mastersecret->client_handshake_secret), + (void *)(gnutls_session_addr + GNUTLS_SESSION_INT_KEY_PROTO_TLS13_HS_CKEY)); + if (ret) { + debug_bpf_printk("gnutls uretprobe/gnutls_handshake, get client_handshake_secret failed, ret: %d\n", ret); + return 0; + } + ret = bpf_probe_read_user(&mastersecret->server_handshake_secret, sizeof(mastersecret->server_handshake_secret), + (void *)(gnutls_session_addr + GNUTLS_SESSION_INT_KEY_PROTO_TLS13_HS_SKEY)); + if (ret) { + debug_bpf_printk("gnutls uretprobe/gnutls_handshake, get server_handshake_secret failed, ret: %d\n", ret); + return 0; + } + ret = bpf_probe_read_user(&mastersecret->client_traffic_secret, sizeof(mastersecret->client_traffic_secret), + (void *)(gnutls_session_addr + GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_CKEY)); + if (ret) { + debug_bpf_printk("gnutls uretprobe/gnutls_handshake, get client_traffic_secret failed, ret: %d\n", ret); + return 0; + } + ret = bpf_probe_read_user(&mastersecret->server_traffic_secret, sizeof(mastersecret->server_traffic_secret), + (void *)(gnutls_session_addr + GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_SKEY)); + if (ret) { + debug_bpf_printk("gnutls uretprobe/gnutls_handshake, get server_traffic_secret failed, ret: %d\n", ret); + return 0; + } + ret = bpf_probe_read_user(&mastersecret->exporter_master_secret, sizeof(mastersecret->exporter_master_secret), + (void *)(gnutls_session_addr + GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_EXPKEY)); + if (ret) { + debug_bpf_printk("gnutls uretprobe/gnutls_handshake, get exporter_master_secret failed, ret: %d\n", ret); + return 0; + } + bpf_perf_event_output(ctx, &mastersecret_gnutls_events, BPF_F_CURRENT_CPU, mastersecret, sizeof(struct gnutls_mastersecret_st)); + } + + return 0; +} From 3934b854ec5fe163585879bf4446c803990290fc Mon Sep 17 00:00:00 2001 From: yuweizzz Date: Wed, 30 Oct 2024 14:30:55 +0800 Subject: [PATCH 02/11] config --- cli/cmd/gnutls.go | 11 +++++++++++ user/config/config_gnutls.go | 22 ++++++++++++++++++++-- user/config/config_gnutls_linux.go | 2 +- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/cli/cmd/gnutls.go b/cli/cmd/gnutls.go index cf3bac48e..b7291dffb 100644 --- a/cli/cmd/gnutls.go +++ b/cli/cmd/gnutls.go @@ -18,6 +18,8 @@ package cmd import ( + "strings" + "github.com/gojue/ecapture/user/config" "github.com/gojue/ecapture/user/module" "github.com/spf13/cobra" @@ -35,6 +37,8 @@ ecapture gnutls ecapture gnutls --hex --pid=3423 ecapture gnutls -l save.log --pid=3423 ecapture gnutls --gnutls=/lib/x86_64-linux-gnu/libgnutls.so +ecapture gnutls -m keylog -k ecapture_gnutls_key.og +ecapture gnutls -m pcap --pcapfile save.pcapng -i eth0 --gnutls=/lib/x86_64-linux-gnu/libgnutls.so tcp port 443 `, Run: gnuTlsCommandFunc, } @@ -42,10 +46,17 @@ ecapture gnutls --gnutls=/lib/x86_64-linux-gnu/libgnutls.so func init() { //opensslCmd.PersistentFlags().StringVar(&gc.Curlpath, "wget", "", "wget file path, default: /usr/bin/wget. (Deprecated)") gnutlsCmd.PersistentFlags().StringVar(&gc.Gnutls, "gnutls", "", "libgnutls.so file path, will automatically find it from curl default.") + gnutlsCmd.PersistentFlags().StringVarP(&gc.Model, "model", "m", "text", "capture model, such as : text, pcap/pcapng, key/keylog") + gnutlsCmd.PersistentFlags().StringVarP(&gc.KeylogFile, "keylogfile", "k", "ecapture_gnutls_key.og", "The file stores SSL/TLS keys, and eCapture captures these keys during encrypted traffic communication and saves them to the file.") + gnutlsCmd.PersistentFlags().StringVarP(&gc.PcapFile, "pcapfile", "w", "save.pcapng", "write the raw packets to file as pcapng format.") + gnutlsCmd.PersistentFlags().StringVarP(&gc.Ifname, "ifname", "i", "", "(TC Classifier) Interface name on which the probe will be attached.") rootCmd.AddCommand(gnutlsCmd) } // gnuTlsCommandFunc executes the "bash" command. func gnuTlsCommandFunc(command *cobra.Command, args []string) { + if gc.PcapFilter == "" && len(args) != 0 { + gc.PcapFilter = strings.Join(args, " ") + } runModule(module.ModuleNameGnutls, gc) } diff --git a/user/config/config_gnutls.go b/user/config/config_gnutls.go index ded7bd7fd..589b9f89e 100644 --- a/user/config/config_gnutls.go +++ b/user/config/config_gnutls.go @@ -20,8 +20,13 @@ import "encoding/json" type GnutlsConfig struct { BaseConfig //Curl path string `json:"curlpath"` //curl的文件路径 - Gnutls string `json:"gnutls"` - ElfType uint8 // + Gnutls string `json:"gnutls"` + Model string `json:"model"` + PcapFile string `json:"pcapfile"` + KeylogFile string `json:"keylog"` + Ifname string `json:"ifname"` + PcapFilter string `json:"pcapfilter"` + ElfType uint8 } func NewGnutlsConfig() *GnutlsConfig { @@ -30,6 +35,19 @@ func NewGnutlsConfig() *GnutlsConfig { return config } +func (gc *GnutlsConfig) checkModel() string { + var m string + switch gc.Model { + case TlsCaptureModelKeylog, TlsCaptureModelKey: + m = TlsCaptureModelKey + case TlsCaptureModelPcap, TlsCaptureModelPcapng: + m = TlsCaptureModelPcap + default: + m = TlsCaptureModelText + } + return m +} + func (gc *GnutlsConfig) Bytes() []byte { b, e := json.Marshal(gc) if e != nil { diff --git a/user/config/config_gnutls_linux.go b/user/config/config_gnutls_linux.go index 8290509c6..9ec5ffbe5 100644 --- a/user/config/config_gnutls_linux.go +++ b/user/config/config_gnutls_linux.go @@ -58,6 +58,6 @@ func (gc *GnutlsConfig) Check() error { } gc.ElfType = ElfTypeSo - + gc.Model = gc.checkModel() return nil } From 24aa0924b178deba9c18fb5bd8ead93e36388b2e Mon Sep 17 00:00:00 2001 From: yuweizzz Date: Wed, 30 Oct 2024 17:43:56 +0800 Subject: [PATCH 03/11] user --- kern/gnutls_kern.c | 1 + user/event/event_mastersecret_gnutls.go | 107 ++++++++++ user/module/probe_gnutls.go | 101 +++++++++- user/module/probe_gnutls_keylog.go | 105 ++++++++++ user/module/probe_gnutls_pcap.go | 251 ++++++++++++++++++++++++ 5 files changed, 555 insertions(+), 10 deletions(-) create mode 100644 user/event/event_mastersecret_gnutls.go create mode 100644 user/module/probe_gnutls_keylog.go create mode 100644 user/module/probe_gnutls_pcap.go diff --git a/kern/gnutls_kern.c b/kern/gnutls_kern.c index 70bcf1b75..ac7fb0049 100644 --- a/kern/gnutls_kern.c +++ b/kern/gnutls_kern.c @@ -13,6 +13,7 @@ // limitations under the License. #include "ecapture.h" +#include "tc.h" enum ssl_data_event_type { kSSLRead, kSSLWrite }; diff --git a/user/event/event_mastersecret_gnutls.go b/user/event/event_mastersecret_gnutls.go new file mode 100644 index 000000000..88fdfe3ba --- /dev/null +++ b/user/event/event_mastersecret_gnutls.go @@ -0,0 +1,107 @@ +// Author: yuweizzz . +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package event + +import ( + "bytes" + "encoding/binary" + "fmt" +) + +const ( + GnutlsMasterSize = 48 + GnutlsRandomSize = 32 + GnutlsMaxHashSize = 64 +) + +// mastersecret_gnutls_events +type MasterSecretGnutlsEvent struct { + eventType EventType + Version int32 `json:"version"` + ClientRandom [GnutlsRandomSize]byte `json:"clientRandom"` + MasterSecret [GnutlsMasterSize]byte `json:"masterSecret"` + CipherId int32 `json:"cipherId"` // PRF MAC + ClientHandshakeSecret [GnutlsMaxHashSize]byte `json:"clientHandshakeSecret"` + ServerHandshakeSecret [GnutlsMaxHashSize]byte `json:"serverHandshakeSecret"` + ClientTrafficSecret [GnutlsMaxHashSize]byte `json:"clientTrafficSecret"` + ServerTrafficSecret [GnutlsMaxHashSize]byte `json:"serverTrafficSecret"` + ExporterMasterSecret [GnutlsMaxHashSize]byte `json:"exporterMasterSecret"` + payload string +} + +func (mse *MasterSecretGnutlsEvent) Decode(payload []byte) (err error) { + buf := bytes.NewBuffer(payload) + if err = binary.Read(buf, binary.LittleEndian, &mse.Version); err != nil { + return + } + if err = binary.Read(buf, binary.LittleEndian, &mse.ClientRandom); err != nil { + return + } + if err = binary.Read(buf, binary.LittleEndian, &mse.MasterSecret); err != nil { + return + } + if err = binary.Read(buf, binary.LittleEndian, &mse.CipherId); err != nil { + return + } + if err = binary.Read(buf, binary.LittleEndian, &mse.ClientHandshakeSecret); err != nil { + return + } + if err = binary.Read(buf, binary.LittleEndian, &mse.ServerHandshakeSecret); err != nil { + return + } + if err = binary.Read(buf, binary.LittleEndian, &mse.ClientTrafficSecret); err != nil { + return + } + if err = binary.Read(buf, binary.LittleEndian, &mse.ServerTrafficSecret); err != nil { + return + } + if err = binary.Read(buf, binary.LittleEndian, &mse.ExporterMasterSecret); err != nil { + return + } + mse.payload = fmt.Sprintf("CLIENT_RANDOM %02x %02x", mse.ClientRandom, mse.MasterSecret) + return nil +} + +func (mse *MasterSecretGnutlsEvent) StringHex() string { + s := fmt.Sprintf("ClientRandom: %02x, MasterSecret: %02x", mse.ClientRandom[0:GnutlsRandomSize], mse.MasterSecret[0:GnutlsMasterSize]) + return s +} + +func (mse *MasterSecretGnutlsEvent) String() string { + s := fmt.Sprintf("ClientRandom: %02x, MasterSecret: %02x", mse.ClientRandom[0:GnutlsRandomSize], mse.MasterSecret[0:GnutlsMasterSize]) + return s +} + +func (mse *MasterSecretGnutlsEvent) Clone() IEventStruct { + event := new(MasterSecretGnutlsEvent) + event.eventType = EventTypeModuleData + return event +} + +func (mse *MasterSecretGnutlsEvent) EventType() EventType { + return mse.eventType +} + +func (mse *MasterSecretGnutlsEvent) GetUUID() string { + return fmt.Sprintf("%02X", mse.ClientRandom) +} + +func (mse *MasterSecretGnutlsEvent) Payload() []byte { + return []byte(mse.payload) +} + +func (mse *MasterSecretGnutlsEvent) PayloadLen() int { + return len(mse.payload) +} diff --git a/user/module/probe_gnutls.go b/user/module/probe_gnutls.go index 1befdc425..ca8b120ca 100644 --- a/user/module/probe_gnutls.go +++ b/user/module/probe_gnutls.go @@ -19,6 +19,14 @@ import ( "context" "errors" "fmt" + "io" + "math" + "os" + "path" + "path/filepath" + "sync" + "time" + "github.com/cilium/ebpf" manager "github.com/gojue/ebpfmanager" "github.com/gojue/ecapture/assets" @@ -26,18 +34,22 @@ import ( "github.com/gojue/ecapture/user/event" "github.com/rs/zerolog" "golang.org/x/sys/unix" - "io" - "math" - "os" - "path" ) type MGnutlsProbe struct { - Module + MTCProbe bpfManager *manager.Manager bpfManagerOptions manager.Options eventFuncMaps map[*ebpf.Map]event.IEventStruct eventMaps []*ebpf.Map + + pidConns map[uint32]map[uint32]string + pidLocker sync.Locker + + keyloggerFilename string + keylogger *os.File + masterKeys map[string]bool + eBPFProgramType TlsCaptureModelType } // 对象初始化 @@ -50,6 +62,51 @@ func (g *MGnutlsProbe) Init(ctx context.Context, logger *zerolog.Logger, conf co g.Module.SetChild(g) g.eventMaps = make([]*ebpf.Map, 0, 2) g.eventFuncMaps = make(map[*ebpf.Map]event.IEventStruct) + g.pidConns = make(map[uint32]map[uint32]string) + g.pidLocker = new(sync.Mutex) + g.masterKeys = make(map[string]bool) + model := g.conf.(*config.GnutlsConfig).Model + switch model { + case config.TlsCaptureModelKey, config.TlsCaptureModelKeylog: + g.eBPFProgramType = TlsCaptureModelTypeKeylog + g.keyloggerFilename = g.conf.(*config.GnutlsConfig).KeylogFile + file, err := os.OpenFile(g.keyloggerFilename, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0o600) + if err != nil { + return err + } + g.keylogger = file + case config.TlsCaptureModelPcap, config.TlsCaptureModelPcapng: + g.eBPFProgramType = TlsCaptureModelTypePcap + pcapFile := g.conf.(*config.GnutlsConfig).PcapFile + fileInfo, err := filepath.Abs(pcapFile) + if err != nil { + g.logger.Warn().Err(err).Str("pcapFile", pcapFile).Str("eBPFProgramType", g.eBPFProgramType.String()).Msg("pcapFile not found") + return err + } + g.tcPacketsChan = make(chan *TcPacket, 2048) + g.tcPackets = make([]*TcPacket, 0, 256) + g.pcapngFilename = fileInfo + case config.TlsCaptureModelText: + fallthrough + default: + g.eBPFProgramType = TlsCaptureModelTypeText + } + + var ts unix.Timespec + err = unix.ClockGettime(unix.CLOCK_MONOTONIC, &ts) + if err != nil { + return err + } + startTime := ts.Nano() + bootTime := time.Now().UnixNano() - startTime + + g.startTime = uint64(startTime) + g.bootTime = uint64(bootTime) + + g.tcPacketLocker = &sync.Mutex{} + g.masterKeyBuffer = bytes.NewBuffer([]byte{}) + + g.logger.Info().Str("model", g.eBPFProgramType.String()).Str("eBPFProgramType", g.eBPFProgramType.String()).Msg("GnuTlsProbe init") return nil } @@ -61,9 +118,9 @@ func (g *MGnutlsProbe) Start() error { } func (g *MGnutlsProbe) start() error { - // fetch ebpf assets - var bpfFileName = g.geteBPFName("user/bytecode/gnutls_kern.o") + var err error + bpfFileName := g.geteBPFName("user/bytecode/gnutls_kern.o") g.logger.Info().Str("bytecode filename", bpfFileName).Msg("BPF bytecode loaded") byteBuf, err := assets.Asset(bpfFileName) if err != nil { @@ -72,7 +129,22 @@ func (g *MGnutlsProbe) start() error { } // setup the managers - err = g.setupManagers() + switch g.eBPFProgramType { + case TlsCaptureModelTypeKeylog: + err = g.setupManagersKeylog() + case TlsCaptureModelTypePcap: + err = g.setupManagersPcap() + pcapFilter := g.conf.(*config.GnutlsConfig).PcapFilter + if g.eBPFProgramType == TlsCaptureModelTypePcap && pcapFilter != "" { + ebpfFuncs := []string{tcFuncNameIngress, tcFuncNameEgress} + g.bpfManager.InstructionPatchers = prepareInsnPatchers(g.bpfManager, + ebpfFuncs, pcapFilter) + } + case TlsCaptureModelTypeText: + fallthrough + default: + err = g.setupManagers() + } if err != nil { return fmt.Errorf("tls(gnutls) module couldn't find binPath %v", err) } @@ -88,7 +160,16 @@ func (g *MGnutlsProbe) start() error { } // 加载map信息,map对应events decode表。 - err = g.initDecodeFun() + switch g.eBPFProgramType { + case TlsCaptureModelTypeKeylog: + err = g.initDecodeFunKeylog() + case TlsCaptureModelTypePcap: + err = g.initDecodeFunPcap() + case TlsCaptureModelTypeText: + fallthrough + default: + err = g.initDecodeFunText() + } if err != nil { return err } @@ -198,7 +279,7 @@ func (g *MGnutlsProbe) DecodeFun(em *ebpf.Map) (event.IEventStruct, bool) { return fun, found } -func (g *MGnutlsProbe) initDecodeFun() error { +func (g *MGnutlsProbe) initDecodeFunText() error { //GnutlsEventsMap 与解码函数映射 GnutlsEventsMap, found, err := g.bpfManager.GetMap("gnutls_events") if err != nil { diff --git a/user/module/probe_gnutls_keylog.go b/user/module/probe_gnutls_keylog.go new file mode 100644 index 000000000..eb1b9855f --- /dev/null +++ b/user/module/probe_gnutls_keylog.go @@ -0,0 +1,105 @@ +// Author: yuweizzz . +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package module + +import ( + "errors" + "math" + "os" + "path" + + "github.com/cilium/ebpf" + manager "github.com/gojue/ebpfmanager" + "github.com/gojue/ecapture/user/config" + "github.com/gojue/ecapture/user/event" + "golang.org/x/sys/unix" +) + +func (g *MGnutlsProbe) setupManagersKeylog() error { + var binaryPath string + switch g.conf.(*config.GnutlsConfig).ElfType { + case config.ElfTypeSo: + binaryPath = g.conf.(*config.GnutlsConfig).Gnutls + default: + //如果没找到 "/lib/x86_64-linux-gnu/libgnutls.so.30" + binaryPath = path.Join(defaultSoPath, "libgnutls.so.30") + } + _, err := os.Stat(binaryPath) + if err != nil { + return err + } + + g.logger.Info().Str("binaryPath", binaryPath).Uint8("elfType", g.conf.(*config.GnutlsConfig).ElfType).Msg("gnutls binary path") + g.bpfManager = &manager.Manager{ + Probes: []*manager.Probe{ + { + Section: "uprobe/gnutls_handshake", + EbpfFuncName: "uprobe_gnutls_master_key", + AttachToFuncName: "gnutls_handshake", + BinaryPath: binaryPath, + }, + { + Section: "uretprobe/gnutls_handshake", + EbpfFuncName: "uretprobe_gnutls_master_key", + AttachToFuncName: "gnutls_handshake", + BinaryPath: binaryPath, + }, + }, + + Maps: []*manager.Map{ + { + Name: "mastersecret_gnutls_events", + }, + }, + } + + g.bpfManagerOptions = manager.Options{ + DefaultKProbeMaxActive: 512, + VerifierOptions: ebpf.CollectionOptions{ + Programs: ebpf.ProgramOptions{ + LogSize: 2097152, + }, + }, + + RLimit: &unix.Rlimit{ + Cur: math.MaxUint64, + Max: math.MaxUint64, + }, + } + + if g.conf.EnableGlobalVar() { + // 填充 RewriteContants 对应map + g.bpfManagerOptions.ConstantEditors = g.constantEditor() + } + return nil +} + +func (m *MGnutlsProbe) initDecodeFunKeylog() error { + MasterkeyEventsMap, found, err := m.bpfManager.GetMap("mastersecret_gnutls_events") + if err != nil { + return err + } + if !found { + return errors.New("cant found map: mastersecret_gnutls_events") + } + m.eventMaps = append(m.eventMaps, MasterkeyEventsMap) + + var masterkeyEvent event.IEventStruct + + masterkeyEvent = &event.MasterSecretGnutlsEvent{} + + m.eventFuncMaps[MasterkeyEventsMap] = masterkeyEvent + return nil +} diff --git a/user/module/probe_gnutls_pcap.go b/user/module/probe_gnutls_pcap.go new file mode 100644 index 000000000..9874ba716 --- /dev/null +++ b/user/module/probe_gnutls_pcap.go @@ -0,0 +1,251 @@ +// Author: yuweizzz . +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package module + +import ( + "errors" + "fmt" + "math" + "net" + "bytes" + + "github.com/cilium/ebpf" + manager "github.com/gojue/ebpfmanager" + "github.com/gojue/ecapture/user/config" + "github.com/gojue/ecapture/user/event" + "golang.org/x/sys/unix" +) + +func (m *MGnutlsProbe) setupManagersPcap() error { + binaryPath := m.conf.(*config.GnutlsConfig).Gnutls + ifname := m.conf.(*config.GnutlsConfig).Ifname + m.ifName = ifname + interf, err := net.InterfaceByName(m.ifName) + if err != nil { + return fmt.Errorf("InterfaceByName: %s , failed: %v", m.ifName, err) + } + + // loopback devices are special, some tc probes should be skipped + isNetIfaceLo := interf.Flags&net.FlagLoopback == net.FlagLoopback + skipLoopback := true // TODO: detect loopback devices via aquasecrity/tracee/pkg/ebpf/probes/probe.go line 322 + if isNetIfaceLo && skipLoopback { + return fmt.Errorf("%s\t%s is a loopback interface, skip it", m.Name(), m.ifName) + } + m.ifIdex = interf.Index + + pcapFilter := m.conf.(*config.GnutlsConfig).PcapFilter + m.logger.Info().Str("binrayPath", binaryPath).Str("IFname", m.ifName).Int("IFindex", m.ifIdex). + Str("PcapFilter", pcapFilter).Uint8("ElfType", m.conf.(*config.GnutlsConfig).ElfType).Msg("HOOK type: Gnutls elf") + m.logger.Info().Msg("Hook masterKey function: gnutls_handshake") + + // create pcapng writer + netIfs, err := net.Interfaces() + if err != nil { + return err + } + + err = m.createPcapng(netIfs) + if err != nil { + return err + } + + // Serve pcapng writer to flush pcapng file + go func() { + m.ServePcap() + }() + + m.bpfManager = &manager.Manager{ + Probes: []*manager.Probe{ + // customize deleteed TC filter + // tc filter del dev eth0 ingress + // tc filter del dev eth0 egress + // loopback devices are special, some tc probes should be skipped + // TODO: detect loopback devices via aquasecrity/tracee/pkg/ebpf/probes/probe.go line 322 + // isNetIfaceLo := netIface.Flags&net.FlagLoopback == net.FlagLoopback + // if isNetIfaceLo && p.skipLoopback { + // return nil + // } + { + Section: "classifier/egress", + EbpfFuncName: tcFuncNameEgress, + Ifname: m.ifName, + NetworkDirection: manager.Egress, + }, + { + Section: "classifier/ingress", + EbpfFuncName: tcFuncNameIngress, + Ifname: m.ifName, + NetworkDirection: manager.Ingress, + }, + // -------------------------------------------------- + { + EbpfFuncName: "tcp_sendmsg", + Section: "kprobe/tcp_sendmsg", + AttachToFuncName: "tcp_sendmsg", + }, + { + EbpfFuncName: "udp_sendmsg", + Section: "kprobe/udp_sendmsg", + AttachToFuncName: "udp_sendmsg", + }, + // -------------------------------------------------- + { + Section: "uprobe/gnutls_handshake", + EbpfFuncName: "uprobe_gnutls_master_key", + AttachToFuncName: "gnutls_handshake", + BinaryPath: binaryPath, + UID: "uprobe_smk_gnutls_handshake", + }, + { + Section: "uretprobe/gnutls_handshake", + EbpfFuncName: "uretprobe_gnutls_master_key", + AttachToFuncName: "gnutls_handshake", + BinaryPath: binaryPath, + UID: "uretprobe_smk_gnutls_handshake", + }, + }, + + Maps: []*manager.Map{ + { + Name: "mastersecret_gnutls_events", + }, + { + Name: "skb_events", + }, + }, + } + + m.bpfManagerOptions = manager.Options{ + DefaultKProbeMaxActive: 512, + + VerifierOptions: ebpf.CollectionOptions{ + Programs: ebpf.ProgramOptions{ + LogSize: 2097152, + }, + }, + + RLimit: &unix.Rlimit{ + Cur: math.MaxUint64, + Max: math.MaxUint64, + }, + } + + if m.conf.EnableGlobalVar() { + // 填充 RewriteContants 对应map + m.bpfManagerOptions.ConstantEditors = m.constantEditor() + } + return nil +} + +func (m *MGnutlsProbe) initDecodeFunPcap() error { + // SkbEventsMap 与解码函数映射 + SkbEventsMap, found, err := m.bpfManager.GetMap("skb_events") + if err != nil { + return err + } + if !found { + return errors.New("cant found map:skb_events") + } + m.eventMaps = append(m.eventMaps, SkbEventsMap) + sslEvent := &event.TcSkbEvent{} + // sslEvent.SetModule(m) + m.eventFuncMaps[SkbEventsMap] = sslEvent + + MasterkeyEventsMap, found, err := m.bpfManager.GetMap("mastersecret_gnutls_events") + if err != nil { + return err + } + if !found { + return errors.New("cant found map: mastersecret_gnutls_events") + } + m.eventMaps = append(m.eventMaps, MasterkeyEventsMap) + + masterkeyEvent := &event.MasterSecretGnutlsEvent{} + // masterkeyEvent.SetModule(m) + m.eventFuncMaps[MasterkeyEventsMap] = masterkeyEvent + return nil +} + +func (g *MGnutlsProbe) Dispatcher(eventStruct event.IEventStruct) { + // detect eventStruct type + switch eventStruct.(type) { + case *event.MasterSecretGnutlsEvent: + g.saveMasterSecret(eventStruct.(*event.MasterSecretGnutlsEvent)) + case *event.TcSkbEvent: + err := g.dumpTcSkb(eventStruct.(*event.TcSkbEvent)) + if err != nil { + g.logger.Warn().Err(err).Msg("save packet error.") + } + } +} + +func (g *MGnutlsProbe) saveMasterSecret(secretEvent *event.MasterSecretGnutlsEvent) { + clientRandomHex := fmt.Sprintf("%02x", secretEvent.ClientRandom[0:event.GnutlsRandomSize]) + k := fmt.Sprintf("%s-%s", "CLIENT_RANDOM", clientRandomHex) + + _, f := g.masterKeys[k] + if f { + // 已存在该随机数的masterSecret,不需要重复写入 + return + } + + g.masterKeys[k] = true + buf := bytes.NewBuffer(nil) + // tls 1.2 + if secretEvent.Version == 4 { + masterSecret := secretEvent.MasterSecret[0:event.GnutlsMasterSize] + buf.WriteString(fmt.Sprintf("%s %s %02x\n", "CLIENT_RANDOM", clientRandomHex, masterSecret)) + } + // tls 1.3 + if secretEvent.Version == 5 { + // default MAC output length: 32 -- SHA256 + // secretEvent.CipherId == 6 + length := 32 + if secretEvent.CipherId == 7 { + // SHA384 + length = 48 + } + chSecret := secretEvent.ClientHandshakeSecret[0:length] + buf.WriteString(fmt.Sprintf("%s %s %02x\n", "CLIENT_HANDSHAKE_TRAFFIC_SECRET", clientRandomHex, chSecret)) + shSecret := secretEvent.ServerHandshakeSecret[0:length] + buf.WriteString(fmt.Sprintf("%s %s %02x\n", "SERVER_HANDSHAKE_TRAFFIC_SECRET", clientRandomHex, shSecret)) + emSecret := secretEvent.ExporterMasterSecret[0:length] + buf.WriteString(fmt.Sprintf("%s %s %02x\n", "EXPORTER_SECRET", clientRandomHex, emSecret)) + ctSecret := secretEvent.ClientTrafficSecret[0:length] + buf.WriteString(fmt.Sprintf("%s %s %02x\n", "CLIENT_TRAFFIC_SECRET_0", clientRandomHex, ctSecret)) + stSecret := secretEvent.ServerTrafficSecret[0:length] + buf.WriteString(fmt.Sprintf("%s %s %02x\n", "SERVER_TRAFFIC_SECRET_0", clientRandomHex, stSecret)) + } + + var e error + switch g.eBPFProgramType { + case TlsCaptureModelTypeKeylog: + _, e = g.keylogger.WriteString(buf.String()) + if e != nil { + g.logger.Warn().Err(e).Str("CLientRandom", k).Msg("save masterSecrets to keylog error") + return + } + g.logger.Info().Str("TlsVersion", string(secretEvent.Version)).Str("CLientRandom", clientRandomHex).Msg("CLIENT_RANDOM save success") + case TlsCaptureModelTypePcap: + e = g.savePcapngSslKeyLog(buf.Bytes()) + if e != nil { + g.logger.Warn().Err(e).Str("CLientRandom", k).Msg("save masterSecrets to pcapNG error") + return + } + g.logger.Info().Str("TlsVersion", string(secretEvent.Version)).Str("CLientRandom", clientRandomHex).Str("eBPFProgramType", g.eBPFProgramType.String()).Msg("CLIENT_RANDOM save success") + default: + g.logger.Warn().Uint8("eBPFProgramType", uint8(g.eBPFProgramType)).Str("CLientRandom", clientRandomHex).Msg("unhandled default case with eBPF Program type") + } +} From a9951104d555a8127531db2e6b787f1b7d7c9acd Mon Sep 17 00:00:00 2001 From: yuweizzz Date: Thu, 31 Oct 2024 17:10:25 +0800 Subject: [PATCH 04/11] consts --- kern/gnutls_masterkey.h | 60 +++++++++++++++----- user/module/probe_gnutls.go | 13 +++++ user/module/probe_gnutls_keylog.go | 91 ++++++++++++++++++++++++++++++ user/module/probe_gnutls_pcap.go | 73 ------------------------ 4 files changed, 151 insertions(+), 86 deletions(-) diff --git a/kern/gnutls_masterkey.h b/kern/gnutls_masterkey.h index b8b0e74ef..5b3d740b1 100644 --- a/kern/gnutls_masterkey.h +++ b/kern/gnutls_masterkey.h @@ -1,6 +1,16 @@ -#define GNUTLS_RANDOM_SIZE 32 -#define GNUTLS_MASTER_SIZE 48 -#define MAX_HASH_SIZE 64 +// Author: yuweizzz . +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // Ref: // https://github.com/gnutls/gnutls/blob/3.7.9/lib/gnutls_int.h @@ -71,9 +81,13 @@ // }; // +#define GNUTLS_RANDOM_SIZE 32 +#define GNUTLS_MASTER_SIZE 48 +#define MAX_HASH_SIZE 64 + struct gnutls_mastersecret_st { u32 version; - /* from ssl3.0 to tls12 */ + /* from ssl3.0 to tls1.2 */ u8 client_random[GNUTLS_RANDOM_SIZE]; u8 master_secret[GNUTLS_MASTER_SIZE]; @@ -86,6 +100,21 @@ struct gnutls_mastersecret_st { u8 exporter_master_secret[MAX_HASH_SIZE]; }; +// #define GNUTLS_MAC_SHA256 6 +// #define GNUTLS_MAC_SHA384 7 + +#define GNUTLS_SSL3 1 +#define GNUTLS_TLS1_0 2 +#define GNUTLS_TLS1 GNUTLS_TLS1_0 +#define GNUTLS_TLS1_1 3 +#define GNUTLS_TLS1_2 4 +#define GNUTLS_TLS1_3 5 +#define GNUTLS_DTLS1_0 201 +#define GNUTLS_DTLS1_2 202 + +// Supported version: gnutls 3.7.9/3.8.1/3.8.3 +// Release: Debian 12/ubuntu 23.10/ubuntu 24.04 + // gnutls_session_int->security_parameters #define GNUTLS_SESSION_INT_SECURITY_PARAMETERS 0x0 @@ -236,14 +265,14 @@ int uretprobe_gnutls_master_key(struct pt_regs *ctx) { } debug_bpf_printk("ssl_version: %d\n", ssl_version); - struct gnutls_mastersecret_st *mastersecret = make_event(); - if (!mastersecret) { - debug_bpf_printk("gnutls uretprobe/gnutls_handshake, mastersecret is null\n"); - return 0; - } - - // tls 1.2 - if (ssl_version == 4) { + /* from ssl3.0 to tls1.2 */ + if (ssl_version >= GNUTLS_SSL3 && ssl_version <= GNUTLS_TLS1_2 || + ssl_version >= GNUTLS_DTLS1_0 && ssl_version <= GNUTLS_DTLS1_2) { + struct gnutls_mastersecret_st *mastersecret = make_event(); + if (!mastersecret) { + debug_bpf_printk("gnutls uretprobe/gnutls_handshake, mastersecret is null\n"); + return 0; + } mastersecret->version = ssl_version; ret = bpf_probe_read_user(&mastersecret->client_random, sizeof(mastersecret->client_random), (void *)(gnutls_session_addr + SECURITY_PARAMETERS_ST_CLIENT_RANDOM)); @@ -261,7 +290,12 @@ int uretprobe_gnutls_master_key(struct pt_regs *ctx) { } // tls 1.3 - if (ssl_version == 5) { + if (ssl_version == GNUTLS_TLS1_3) { + struct gnutls_mastersecret_st *mastersecret = make_event(); + if (!mastersecret) { + debug_bpf_printk("gnutls uretprobe/gnutls_handshake, mastersecret is null\n"); + return 0; + } mastersecret->version = ssl_version; // mac cipher id u64 prf_addr; diff --git a/user/module/probe_gnutls.go b/user/module/probe_gnutls.go index ca8b120ca..f7b71d473 100644 --- a/user/module/probe_gnutls.go +++ b/user/module/probe_gnutls.go @@ -298,6 +298,19 @@ func (g *MGnutlsProbe) Events() []*ebpf.Map { return g.eventMaps } +func (g *MGnutlsProbe) Dispatcher(eventStruct event.IEventStruct) { + // detect eventStruct type + switch eventStruct.(type) { + case *event.MasterSecretGnutlsEvent: + g.saveMasterSecret(eventStruct.(*event.MasterSecretGnutlsEvent)) + case *event.TcSkbEvent: + err := g.dumpTcSkb(eventStruct.(*event.TcSkbEvent)) + if err != nil { + g.logger.Warn().Err(err).Msg("save packet error.") + } + } +} + func init() { RegisteFunc(NewGnutlsProbe) } diff --git a/user/module/probe_gnutls_keylog.go b/user/module/probe_gnutls_keylog.go index eb1b9855f..7be797f65 100644 --- a/user/module/probe_gnutls_keylog.go +++ b/user/module/probe_gnutls_keylog.go @@ -15,7 +15,9 @@ package module import ( + "bytes" "errors" + "fmt" "math" "os" "path" @@ -27,6 +29,30 @@ import ( "golang.org/x/sys/unix" ) +// gnutls_mac_algorithm_t: https://github.com/gnutls/gnutls/blob/master/lib/includes/gnutls/gnutls.h.in#L365 +// gnutls_protocol_t: https://github.com/gnutls/gnutls/blob/master/lib/includes/gnutls/gnutls.h.in#L822 + +const ( + _ = iota + GNUTLS_SSL3, GNUTLS_DTLS1_0 = iota, iota + 200 + GNUTLS_TLS1_0, GNUTLS_DTLS1_2 = iota, iota + 200 + GNUTLS_TLS1_1 = iota + GNUTLS_TLS1_2 + GNUTLS_TLS1_3 + GNUTLS_MAC_SHA256 + GNUTLS_MAC_SHA384 +) + +var GnutlsVersionToString = map[int32]string{ + GNUTLS_SSL3: "GNUTLS_SSL3", + GNUTLS_TLS1_0: "GNUTLS_TLS1_0", + GNUTLS_TLS1_1: "GNUTLS_TLS1_1", + GNUTLS_TLS1_2: "GNUTLS_TLS1_2", + GNUTLS_TLS1_3: "GNUTLS_TLS1_3", + GNUTLS_DTLS1_0: "GNUTLS_DTLS1_0", + GNUTLS_DTLS1_2: "GNUTLS_DTLS1_2", +} + func (g *MGnutlsProbe) setupManagersKeylog() error { var binaryPath string switch g.conf.(*config.GnutlsConfig).ElfType { @@ -103,3 +129,68 @@ func (m *MGnutlsProbe) initDecodeFunKeylog() error { m.eventFuncMaps[MasterkeyEventsMap] = masterkeyEvent return nil } + +func (g *MGnutlsProbe) saveMasterSecret(secretEvent *event.MasterSecretGnutlsEvent) { + clientRandomHex := fmt.Sprintf("%02x", secretEvent.ClientRandom[0:event.GnutlsRandomSize]) + k := fmt.Sprintf("%s-%s", "CLIENT_RANDOM", clientRandomHex) + + _, f := g.masterKeys[k] + if f { + // 已存在该随机数的masterSecret,不需要重复写入 + return + } + + g.masterKeys[k] = true + buf := bytes.NewBuffer(nil) + switch secretEvent.Version { + // tls1.3 + case GNUTLS_TLS1_3: + var length int + switch secretEvent.CipherId { + case GNUTLS_MAC_SHA384: + length = 48 + case GNUTLS_MAC_SHA256: + fallthrough + default: + // default MAC output length: 32 -- SHA256 + length = 32 + } + chSecret := secretEvent.ClientHandshakeSecret[0:length] + buf.WriteString(fmt.Sprintf("%s %s %02x\n", "CLIENT_HANDSHAKE_TRAFFIC_SECRET", clientRandomHex, chSecret)) + shSecret := secretEvent.ServerHandshakeSecret[0:length] + buf.WriteString(fmt.Sprintf("%s %s %02x\n", "SERVER_HANDSHAKE_TRAFFIC_SECRET", clientRandomHex, shSecret)) + emSecret := secretEvent.ExporterMasterSecret[0:length] + buf.WriteString(fmt.Sprintf("%s %s %02x\n", "EXPORTER_SECRET", clientRandomHex, emSecret)) + ctSecret := secretEvent.ClientTrafficSecret[0:length] + buf.WriteString(fmt.Sprintf("%s %s %02x\n", "CLIENT_TRAFFIC_SECRET_0", clientRandomHex, ctSecret)) + stSecret := secretEvent.ServerTrafficSecret[0:length] + buf.WriteString(fmt.Sprintf("%s %s %02x\n", "SERVER_TRAFFIC_SECRET_0", clientRandomHex, stSecret)) + // tls1.2 + case GNUTLS_TLS1_2: + fallthrough + // tls1.1, tls1.0, ssl3.0, dtls 1.0 and dtls 1.2 + default: + masterSecret := secretEvent.MasterSecret[0:event.GnutlsMasterSize] + buf.WriteString(fmt.Sprintf("%s %s %02x\n", "CLIENT_RANDOM", clientRandomHex, masterSecret)) + } + + var e error + switch g.eBPFProgramType { + case TlsCaptureModelTypeKeylog: + _, e = g.keylogger.WriteString(buf.String()) + if e != nil { + g.logger.Warn().Err(e).Str("ClientRandom", k).Msg("save masterSecrets to keylog error") + return + } + g.logger.Info().Str("TlsVersion", GnutlsVersionToString[secretEvent.Version]).Str("ClientRandom", clientRandomHex).Msg("CLIENT_RANDOM save success") + case TlsCaptureModelTypePcap: + e = g.savePcapngSslKeyLog(buf.Bytes()) + if e != nil { + g.logger.Warn().Err(e).Str("ClientRandom", k).Msg("save masterSecrets to pcapNG error") + return + } + g.logger.Info().Str("TlsVersion", GnutlsVersionToString[secretEvent.Version]).Str("ClientRandom", clientRandomHex).Str("eBPFProgramType", g.eBPFProgramType.String()).Msg("CLIENT_RANDOM save success") + default: + g.logger.Warn().Uint8("eBPFProgramType", uint8(g.eBPFProgramType)).Str("ClientRandom", clientRandomHex).Msg("unhandled default case with eBPF Program type") + } +} diff --git a/user/module/probe_gnutls_pcap.go b/user/module/probe_gnutls_pcap.go index 9874ba716..42eebc026 100644 --- a/user/module/probe_gnutls_pcap.go +++ b/user/module/probe_gnutls_pcap.go @@ -19,7 +19,6 @@ import ( "fmt" "math" "net" - "bytes" "github.com/cilium/ebpf" manager "github.com/gojue/ebpfmanager" @@ -177,75 +176,3 @@ func (m *MGnutlsProbe) initDecodeFunPcap() error { m.eventFuncMaps[MasterkeyEventsMap] = masterkeyEvent return nil } - -func (g *MGnutlsProbe) Dispatcher(eventStruct event.IEventStruct) { - // detect eventStruct type - switch eventStruct.(type) { - case *event.MasterSecretGnutlsEvent: - g.saveMasterSecret(eventStruct.(*event.MasterSecretGnutlsEvent)) - case *event.TcSkbEvent: - err := g.dumpTcSkb(eventStruct.(*event.TcSkbEvent)) - if err != nil { - g.logger.Warn().Err(err).Msg("save packet error.") - } - } -} - -func (g *MGnutlsProbe) saveMasterSecret(secretEvent *event.MasterSecretGnutlsEvent) { - clientRandomHex := fmt.Sprintf("%02x", secretEvent.ClientRandom[0:event.GnutlsRandomSize]) - k := fmt.Sprintf("%s-%s", "CLIENT_RANDOM", clientRandomHex) - - _, f := g.masterKeys[k] - if f { - // 已存在该随机数的masterSecret,不需要重复写入 - return - } - - g.masterKeys[k] = true - buf := bytes.NewBuffer(nil) - // tls 1.2 - if secretEvent.Version == 4 { - masterSecret := secretEvent.MasterSecret[0:event.GnutlsMasterSize] - buf.WriteString(fmt.Sprintf("%s %s %02x\n", "CLIENT_RANDOM", clientRandomHex, masterSecret)) - } - // tls 1.3 - if secretEvent.Version == 5 { - // default MAC output length: 32 -- SHA256 - // secretEvent.CipherId == 6 - length := 32 - if secretEvent.CipherId == 7 { - // SHA384 - length = 48 - } - chSecret := secretEvent.ClientHandshakeSecret[0:length] - buf.WriteString(fmt.Sprintf("%s %s %02x\n", "CLIENT_HANDSHAKE_TRAFFIC_SECRET", clientRandomHex, chSecret)) - shSecret := secretEvent.ServerHandshakeSecret[0:length] - buf.WriteString(fmt.Sprintf("%s %s %02x\n", "SERVER_HANDSHAKE_TRAFFIC_SECRET", clientRandomHex, shSecret)) - emSecret := secretEvent.ExporterMasterSecret[0:length] - buf.WriteString(fmt.Sprintf("%s %s %02x\n", "EXPORTER_SECRET", clientRandomHex, emSecret)) - ctSecret := secretEvent.ClientTrafficSecret[0:length] - buf.WriteString(fmt.Sprintf("%s %s %02x\n", "CLIENT_TRAFFIC_SECRET_0", clientRandomHex, ctSecret)) - stSecret := secretEvent.ServerTrafficSecret[0:length] - buf.WriteString(fmt.Sprintf("%s %s %02x\n", "SERVER_TRAFFIC_SECRET_0", clientRandomHex, stSecret)) - } - - var e error - switch g.eBPFProgramType { - case TlsCaptureModelTypeKeylog: - _, e = g.keylogger.WriteString(buf.String()) - if e != nil { - g.logger.Warn().Err(e).Str("CLientRandom", k).Msg("save masterSecrets to keylog error") - return - } - g.logger.Info().Str("TlsVersion", string(secretEvent.Version)).Str("CLientRandom", clientRandomHex).Msg("CLIENT_RANDOM save success") - case TlsCaptureModelTypePcap: - e = g.savePcapngSslKeyLog(buf.Bytes()) - if e != nil { - g.logger.Warn().Err(e).Str("CLientRandom", k).Msg("save masterSecrets to pcapNG error") - return - } - g.logger.Info().Str("TlsVersion", string(secretEvent.Version)).Str("CLientRandom", clientRandomHex).Str("eBPFProgramType", g.eBPFProgramType.String()).Msg("CLIENT_RANDOM save success") - default: - g.logger.Warn().Uint8("eBPFProgramType", uint8(g.eBPFProgramType)).Str("CLientRandom", clientRandomHex).Msg("unhandled default case with eBPF Program type") - } -} From c348fc0cae5767deed103c49d39d5e8f19312477 Mon Sep 17 00:00:00 2001 From: yuweizzz Date: Mon, 4 Nov 2024 16:54:09 +0800 Subject: [PATCH 05/11] update kernel --- kern/{gnutls_kern.c => gnutls.h} | 2 -- kern/gnutls_3_6_12_kern.c | 45 +++++++++++++++++++++++++++++ kern/gnutls_3_6_14_kern.c | 45 +++++++++++++++++++++++++++++ kern/gnutls_3_7_0_kern.c | 45 +++++++++++++++++++++++++++++ kern/gnutls_3_7_3_kern.c | 45 +++++++++++++++++++++++++++++ kern/gnutls_3_7_7_kern.c | 46 ++++++++++++++++++++++++++++++ kern/gnutls_3_8_4_kern.c | 45 +++++++++++++++++++++++++++++ kern/gnutls_3_8_7_kern.c | 45 +++++++++++++++++++++++++++++ kern/gnutls_masterkey.h | 49 ++++---------------------------- variables.mk | 8 +++++- 10 files changed, 328 insertions(+), 47 deletions(-) rename kern/{gnutls_kern.c => gnutls.h} (99%) create mode 100644 kern/gnutls_3_6_12_kern.c create mode 100644 kern/gnutls_3_6_14_kern.c create mode 100644 kern/gnutls_3_7_0_kern.c create mode 100644 kern/gnutls_3_7_3_kern.c create mode 100644 kern/gnutls_3_7_7_kern.c create mode 100644 kern/gnutls_3_8_4_kern.c create mode 100644 kern/gnutls_3_8_7_kern.c diff --git a/kern/gnutls_kern.c b/kern/gnutls.h similarity index 99% rename from kern/gnutls_kern.c rename to kern/gnutls.h index ac7fb0049..2b2195dbb 100644 --- a/kern/gnutls_kern.c +++ b/kern/gnutls.h @@ -218,5 +218,3 @@ int probe_ret_SSL_read(struct pt_regs* ctx) { bpf_map_delete_elem(&active_ssl_read_args_map, ¤t_pid_tgid); return 0; } - -#include "gnutls_masterkey.h" diff --git a/kern/gnutls_3_6_12_kern.c b/kern/gnutls_3_6_12_kern.c new file mode 100644 index 000000000..ab42b2d04 --- /dev/null +++ b/kern/gnutls_3_6_12_kern.c @@ -0,0 +1,45 @@ +#ifndef ECAPTURE_GNUTLS_3_6_12_KERN_H +#define ECAPTURE_GNUTLS_3_6_12_KERN_H + +// version 3.6.12, 3.6.13 + +// gnutls_session_int->security_parameters +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS 0x0 + +// gnutls_session_int->security_parameters.prf +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_PRF 0x18 + +// mac_entry_st->id +#define MAC_ENTRY_ST_ID 0x18 + +// gnutls_session_int->security_parameters.client_random +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_CLIENT_RANDOM 0x50 + +// gnutls_session_int->security_parameters.master_secret +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_MASTER_SECRET 0x20 + +// gnutls_session_int->key.proto.tls13.hs_ckey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_HS_CKEY 0x14d4 + +// gnutls_session_int->key.proto.tls13.hs_skey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_HS_SKEY 0x1514 + +// gnutls_session_int->key.proto.tls13.ap_ckey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_CKEY 0x1554 + +// gnutls_session_int->key.proto.tls13.ap_skey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_SKEY 0x1594 + +// gnutls_session_int->key.proto.tls13.ap_expkey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_EXPKEY 0x15d4 + +// security_parameters_st->pversion +#define SECURITY_PARAMETERS_ST_PVERSION 0xf8 + +// version_entry_st->id +#define VERSION_ENTRY_ST_ID 0x8 + +#include "gnutls.h" +#include "gnutls_masterkey.h" + +#endif diff --git a/kern/gnutls_3_6_14_kern.c b/kern/gnutls_3_6_14_kern.c new file mode 100644 index 000000000..09f2479f0 --- /dev/null +++ b/kern/gnutls_3_6_14_kern.c @@ -0,0 +1,45 @@ +#ifndef ECAPTURE_GNUTLS_3_6_14_KERN_H +#define ECAPTURE_GNUTLS_3_6_14_KERN_H + +// version 3.6.14, 3.6.15, 3.6.16 + +// gnutls_session_int->security_parameters +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS 0x0 + +// gnutls_session_int->security_parameters.prf +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_PRF 0x18 + +// mac_entry_st->id +#define MAC_ENTRY_ST_ID 0x18 + +// gnutls_session_int->security_parameters.client_random +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_CLIENT_RANDOM 0x50 + +// gnutls_session_int->security_parameters.master_secret +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_MASTER_SECRET 0x20 + +// gnutls_session_int->key.proto.tls13.hs_ckey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_HS_CKEY 0x17e4 + +// gnutls_session_int->key.proto.tls13.hs_skey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_HS_SKEY 0x1824 + +// gnutls_session_int->key.proto.tls13.ap_ckey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_CKEY 0x1864 + +// gnutls_session_int->key.proto.tls13.ap_skey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_SKEY 0x18a4 + +// gnutls_session_int->key.proto.tls13.ap_expkey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_EXPKEY 0x18e4 + +// security_parameters_st->pversion +#define SECURITY_PARAMETERS_ST_PVERSION 0xf8 + +// version_entry_st->id +#define VERSION_ENTRY_ST_ID 0x8 + +#include "gnutls.h" +#include "gnutls_masterkey.h" + +#endif diff --git a/kern/gnutls_3_7_0_kern.c b/kern/gnutls_3_7_0_kern.c new file mode 100644 index 000000000..fd08129ac --- /dev/null +++ b/kern/gnutls_3_7_0_kern.c @@ -0,0 +1,45 @@ +#ifndef ECAPTURE_GNUTLS_3_7_0_KERN_H +#define ECAPTURE_GNUTLS_3_7_0_KERN_H + +// version 3.7.0, 3.7.1, 3.7.2 + +// gnutls_session_int->security_parameters +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS 0x0 + +// gnutls_session_int->security_parameters.prf +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_PRF 0x18 + +// mac_entry_st->id +#define MAC_ENTRY_ST_ID 0x18 + +// gnutls_session_int->security_parameters.client_random +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_CLIENT_RANDOM 0x50 + +// gnutls_session_int->security_parameters.master_secret +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_MASTER_SECRET 0x20 + +// gnutls_session_int->key.proto.tls13.hs_ckey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_HS_CKEY 0x1804 + +// gnutls_session_int->key.proto.tls13.hs_skey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_HS_SKEY 0x1844 + +// gnutls_session_int->key.proto.tls13.ap_ckey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_CKEY 0x1884 + +// gnutls_session_int->key.proto.tls13.ap_skey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_SKEY 0x18c4 + +// gnutls_session_int->key.proto.tls13.ap_expkey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_EXPKEY 0x1904 + +// security_parameters_st->pversion +#define SECURITY_PARAMETERS_ST_PVERSION 0xf8 + +// version_entry_st->id +#define VERSION_ENTRY_ST_ID 0x8 + +#include "gnutls.h" +#include "gnutls_masterkey.h" + +#endif diff --git a/kern/gnutls_3_7_3_kern.c b/kern/gnutls_3_7_3_kern.c new file mode 100644 index 000000000..d362ac6e2 --- /dev/null +++ b/kern/gnutls_3_7_3_kern.c @@ -0,0 +1,45 @@ +#ifndef ECAPTURE_GNUTLS_3_7_3_KERN_H +#define ECAPTURE_GNUTLS_3_7_3_KERN_H + +// version 3.7.3, 3.7.4, 3.7.5, 3.7.6 + +// gnutls_session_int->security_parameters +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS 0x0 + +// gnutls_session_int->security_parameters.prf +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_PRF 0x18 + +// mac_entry_st->id +#define MAC_ENTRY_ST_ID 0x18 + +// gnutls_session_int->security_parameters.client_random +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_CLIENT_RANDOM 0x50 + +// gnutls_session_int->security_parameters.master_secret +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_MASTER_SECRET 0x20 + +// gnutls_session_int->key.proto.tls13.hs_ckey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_HS_CKEY 0x180c + +// gnutls_session_int->key.proto.tls13.hs_skey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_HS_SKEY 0x184c + +// gnutls_session_int->key.proto.tls13.ap_ckey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_CKEY 0x188c + +// gnutls_session_int->key.proto.tls13.ap_skey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_SKEY 0x18cc + +// gnutls_session_int->key.proto.tls13.ap_expkey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_EXPKEY 0x190c + +// security_parameters_st->pversion +#define SECURITY_PARAMETERS_ST_PVERSION 0xf8 + +// version_entry_st->id +#define VERSION_ENTRY_ST_ID 0x8 + +#include "gnutls.h" +#include "gnutls_masterkey.h" + +#endif diff --git a/kern/gnutls_3_7_7_kern.c b/kern/gnutls_3_7_7_kern.c new file mode 100644 index 000000000..19dc1fcfa --- /dev/null +++ b/kern/gnutls_3_7_7_kern.c @@ -0,0 +1,46 @@ +#ifndef ECAPTURE_GNUTLS_3_7_7_KERN_H +#define ECAPTURE_GNUTLS_3_7_7_KERN_H + +// version 3.7.7, 3.7.8, 3.7.9, 3.7.10, 3.7.11 +// version 3.8.0, 3.8.1, 3.8.2, 3.8.3 + +// gnutls_session_int->security_parameters +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS 0x0 + +// gnutls_session_int->security_parameters.prf +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_PRF 0x18 + +// mac_entry_st->id +#define MAC_ENTRY_ST_ID 0x18 + +// gnutls_session_int->security_parameters.client_random +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_CLIENT_RANDOM 0x50 + +// gnutls_session_int->security_parameters.master_secret +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_MASTER_SECRET 0x20 + +// gnutls_session_int->key.proto.tls13.hs_ckey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_HS_CKEY 0x1794 + +// gnutls_session_int->key.proto.tls13.hs_skey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_HS_SKEY 0x17d4 + +// gnutls_session_int->key.proto.tls13.ap_ckey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_CKEY 0x1814 + +// gnutls_session_int->key.proto.tls13.ap_skey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_SKEY 0x1854 + +// gnutls_session_int->key.proto.tls13.ap_expkey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_EXPKEY 0x1894 + +// security_parameters_st->pversion +#define SECURITY_PARAMETERS_ST_PVERSION 0xf8 + +// version_entry_st->id +#define VERSION_ENTRY_ST_ID 0x8 + +#include "gnutls.h" +#include "gnutls_masterkey.h" + +#endif diff --git a/kern/gnutls_3_8_4_kern.c b/kern/gnutls_3_8_4_kern.c new file mode 100644 index 000000000..6bebae359 --- /dev/null +++ b/kern/gnutls_3_8_4_kern.c @@ -0,0 +1,45 @@ +#ifndef ECAPTURE_GNUTLS_3_8_4_KERN_H +#define ECAPTURE_GNUTLS_3_8_4_KERN_H + +// version 3.8.4, 3.8.5, 3.8.6 + +// gnutls_session_int->security_parameters +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS 0x0 + +// gnutls_session_int->security_parameters.prf +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_PRF 0x18 + +// mac_entry_st->id +#define MAC_ENTRY_ST_ID 0x18 + +// gnutls_session_int->security_parameters.client_random +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_CLIENT_RANDOM 0x50 + +// gnutls_session_int->security_parameters.master_secret +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_MASTER_SECRET 0x20 + +// gnutls_session_int->key.proto.tls13.hs_ckey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_HS_CKEY 0x17dc + +// gnutls_session_int->key.proto.tls13.hs_skey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_HS_SKEY 0x181c + +// gnutls_session_int->key.proto.tls13.ap_ckey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_CKEY 0x185c + +// gnutls_session_int->key.proto.tls13.ap_skey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_SKEY 0x189c + +// gnutls_session_int->key.proto.tls13.ap_expkey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_EXPKEY 0x18dc + +// security_parameters_st->pversion +#define SECURITY_PARAMETERS_ST_PVERSION 0xf8 + +// version_entry_st->id +#define VERSION_ENTRY_ST_ID 0x8 + +#include "gnutls.h" +#include "gnutls_masterkey.h" + +#endif diff --git a/kern/gnutls_3_8_7_kern.c b/kern/gnutls_3_8_7_kern.c new file mode 100644 index 000000000..7ab3eca36 --- /dev/null +++ b/kern/gnutls_3_8_7_kern.c @@ -0,0 +1,45 @@ +#ifndef ECAPTURE_GNUTLS_3_8_7_KERN_H +#define ECAPTURE_GNUTLS_3_8_7_KERN_H + +// version 3.8.7 + +// gnutls_session_int->security_parameters +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS 0x0 + +// gnutls_session_int->security_parameters.prf +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_PRF 0x18 + +// mac_entry_st->id +#define MAC_ENTRY_ST_ID 0x18 + +// gnutls_session_int->security_parameters.client_random +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_CLIENT_RANDOM 0x50 + +// gnutls_session_int->security_parameters.master_secret +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_MASTER_SECRET 0x20 + +// gnutls_session_int->key.proto.tls13.hs_ckey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_HS_CKEY 0x19d4 + +// gnutls_session_int->key.proto.tls13.hs_skey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_HS_SKEY 0x1a14 + +// gnutls_session_int->key.proto.tls13.ap_ckey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_CKEY 0x1a54 + +// gnutls_session_int->key.proto.tls13.ap_skey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_SKEY 0x1a94 + +// gnutls_session_int->key.proto.tls13.ap_expkey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_EXPKEY 0x1ad4 + +// security_parameters_st->pversion +#define SECURITY_PARAMETERS_ST_PVERSION 0xf8 + +// version_entry_st->id +#define VERSION_ENTRY_ST_ID 0x8 + +#include "gnutls.h" +#include "gnutls_masterkey.h" + +#endif diff --git a/kern/gnutls_masterkey.h b/kern/gnutls_masterkey.h index 5b3d740b1..db8975b8c 100644 --- a/kern/gnutls_masterkey.h +++ b/kern/gnutls_masterkey.h @@ -112,45 +112,6 @@ struct gnutls_mastersecret_st { #define GNUTLS_DTLS1_0 201 #define GNUTLS_DTLS1_2 202 -// Supported version: gnutls 3.7.9/3.8.1/3.8.3 -// Release: Debian 12/ubuntu 23.10/ubuntu 24.04 - -// gnutls_session_int->security_parameters -#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS 0x0 - -// gnutls_session_int->security_parameters.prf -#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_PRF 0x18 - -// mac_entry_st->id -#define MAC_ENTRY_ST_ID 0x18 - -// security_parameters_st->client_random -#define SECURITY_PARAMETERS_ST_CLIENT_RANDOM 0x50 - -// security_parameters_st->master_secret -#define SECURITY_PARAMETERS_ST_MASTER_SECRET 0x20 - -// security_parameters_st->pversion -#define SECURITY_PARAMETERS_ST_PVERSION 0xf8 - -// version_entry_st->id -#define VERSION_ENTRY_ST_ID 0x8 - -// gnutls_session_int->key.proto.tls13.hs_ckey -#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_HS_CKEY 0x1794 - -// gnutls_session_int->key.proto.tls13.hs_skey -#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_HS_SKEY 0x17d4 - -// gnutls_session_int->key.proto.tls13.ap_ckey -#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_CKEY 0x1814 - -// gnutls_session_int->key.proto.tls13.ap_skey -#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_SKEY 0x1854 - -// gnutls_session_int->key.proto.tls13.ap_expkey -#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_EXPKEY 0x1894 - /////////////////////////BPF MAPS //////////////////////////////// // bpf map @@ -266,8 +227,8 @@ int uretprobe_gnutls_master_key(struct pt_regs *ctx) { debug_bpf_printk("ssl_version: %d\n", ssl_version); /* from ssl3.0 to tls1.2 */ - if (ssl_version >= GNUTLS_SSL3 && ssl_version <= GNUTLS_TLS1_2 || - ssl_version >= GNUTLS_DTLS1_0 && ssl_version <= GNUTLS_DTLS1_2) { + if ((ssl_version >= GNUTLS_SSL3 && ssl_version <= GNUTLS_TLS1_2) || + (ssl_version >= GNUTLS_DTLS1_0 && ssl_version <= GNUTLS_DTLS1_2)) { struct gnutls_mastersecret_st *mastersecret = make_event(); if (!mastersecret) { debug_bpf_printk("gnutls uretprobe/gnutls_handshake, mastersecret is null\n"); @@ -275,13 +236,13 @@ int uretprobe_gnutls_master_key(struct pt_regs *ctx) { } mastersecret->version = ssl_version; ret = bpf_probe_read_user(&mastersecret->client_random, sizeof(mastersecret->client_random), - (void *)(gnutls_session_addr + SECURITY_PARAMETERS_ST_CLIENT_RANDOM)); + (void *)(gnutls_session_addr + GNUTLS_SESSION_INT_SECURITY_PARAMETERS_CLIENT_RANDOM)); if (ret) { debug_bpf_printk("gnutls uretprobe/gnutls_handshake, get client_random failed, ret: %d\n", ret); return 0; } ret = bpf_probe_read_user(&mastersecret->master_secret, sizeof(mastersecret->master_secret), - (void *)(gnutls_session_addr + SECURITY_PARAMETERS_ST_MASTER_SECRET)); + (void *)(gnutls_session_addr + GNUTLS_SESSION_INT_SECURITY_PARAMETERS_MASTER_SECRET)); if (ret) { debug_bpf_printk("gnutls uretprobe/gnutls_handshake, get master_secret failed, ret: %d\n", ret); return 0; @@ -314,7 +275,7 @@ int uretprobe_gnutls_master_key(struct pt_regs *ctx) { debug_bpf_printk("gnutls uretprobe/gnutls_handshake, get mac_cipher_id ret: %d\n", mac_cipher_id); mastersecret->cipher_id = mac_cipher_id; ret = bpf_probe_read_user(&mastersecret->client_random, sizeof(mastersecret->client_random), - (void *)(gnutls_session_addr + SECURITY_PARAMETERS_ST_CLIENT_RANDOM)); + (void *)(gnutls_session_addr + GNUTLS_SESSION_INT_SECURITY_PARAMETERS_CLIENT_RANDOM)); if (ret) { debug_bpf_printk("gnutls uretprobe/gnutls_handshake, get client_random failed, ret: %d\n", ret); return 0; diff --git a/variables.mk b/variables.mk index 2fa62f383..5fd956a10 100644 --- a/variables.mk +++ b/variables.mk @@ -204,7 +204,13 @@ TARGETS += kern/gotls ifeq ($(ANDROID),0) TARGETS += kern/bash - TARGETS += kern/gnutls + TARGETS += kern/gnutls_3_6_12 + TARGETS += kern/gnutls_3_6_14 + TARGETS += kern/gnutls_3_7_0 + TARGETS += kern/gnutls_3_7_3 + TARGETS += kern/gnutls_3_7_7 + TARGETS += kern/gnutls_3_8_4 + TARGETS += kern/gnutls_3_8_7 TARGETS += kern/nspr TARGETS += kern/mysqld TARGETS += kern/postgres From 843c124ca134420157aded98a38f2ab5d65fd087 Mon Sep 17 00:00:00 2001 From: yuweizzz Date: Mon, 4 Nov 2024 17:04:51 +0800 Subject: [PATCH 06/11] update user --- cli/cmd/gnutls.go | 3 +- user/config/config_gnutls.go | 1 + user/module/probe_gnutls.go | 105 ++-------------- user/module/probe_gnutls_keylog.go | 17 +-- user/module/probe_gnutls_lib.go | 194 +++++++++++++++++++++++++++++ user/module/probe_gnutls_pcap.go | 2 +- user/module/probe_gnutls_text.go | 99 +++++++++++++++ 7 files changed, 308 insertions(+), 113 deletions(-) create mode 100644 user/module/probe_gnutls_lib.go create mode 100644 user/module/probe_gnutls_text.go diff --git a/cli/cmd/gnutls.go b/cli/cmd/gnutls.go index b7291dffb..89c0c70ba 100644 --- a/cli/cmd/gnutls.go +++ b/cli/cmd/gnutls.go @@ -37,7 +37,7 @@ ecapture gnutls ecapture gnutls --hex --pid=3423 ecapture gnutls -l save.log --pid=3423 ecapture gnutls --gnutls=/lib/x86_64-linux-gnu/libgnutls.so -ecapture gnutls -m keylog -k ecapture_gnutls_key.og +ecapture gnutls -m keylog -k ecapture_gnutls_key.og --ssl_version=3.7.9 ecapture gnutls -m pcap --pcapfile save.pcapng -i eth0 --gnutls=/lib/x86_64-linux-gnu/libgnutls.so tcp port 443 `, Run: gnuTlsCommandFunc, @@ -50,6 +50,7 @@ func init() { gnutlsCmd.PersistentFlags().StringVarP(&gc.KeylogFile, "keylogfile", "k", "ecapture_gnutls_key.og", "The file stores SSL/TLS keys, and eCapture captures these keys during encrypted traffic communication and saves them to the file.") gnutlsCmd.PersistentFlags().StringVarP(&gc.PcapFile, "pcapfile", "w", "save.pcapng", "write the raw packets to file as pcapng format.") gnutlsCmd.PersistentFlags().StringVarP(&gc.Ifname, "ifname", "i", "", "(TC Classifier) Interface name on which the probe will be attached.") + gnutlsCmd.PersistentFlags().StringVar(&gc.SslVersion, "ssl_version", "", "GnuTLS version, e.g: --ssl_version=\"3.7.9\"") rootCmd.AddCommand(gnutlsCmd) } diff --git a/user/config/config_gnutls.go b/user/config/config_gnutls.go index 589b9f89e..787b86b5e 100644 --- a/user/config/config_gnutls.go +++ b/user/config/config_gnutls.go @@ -26,6 +26,7 @@ type GnutlsConfig struct { KeylogFile string `json:"keylog"` Ifname string `json:"ifname"` PcapFilter string `json:"pcapfilter"` + SslVersion string `json:"sslversion"` ElfType uint8 } diff --git a/user/module/probe_gnutls.go b/user/module/probe_gnutls.go index f7b71d473..1a0f4bcb4 100644 --- a/user/module/probe_gnutls.go +++ b/user/module/probe_gnutls.go @@ -17,12 +17,9 @@ package module import ( "bytes" "context" - "errors" "fmt" "io" - "math" "os" - "path" "path/filepath" "sync" "time" @@ -50,6 +47,8 @@ type MGnutlsProbe struct { keylogger *os.File masterKeys map[string]bool eBPFProgramType TlsCaptureModelType + sslVersion string + sslBpfFile string } // 对象初始化 @@ -118,11 +117,14 @@ func (g *MGnutlsProbe) Start() error { } func (g *MGnutlsProbe) start() error { + // get gnutls sslVersion and sslBpfFile + err := g.detectGnutls() + if err != nil { + g.logger.Error().Err(err).Msg("detectGnutls failed") + return fmt.Errorf("detectGnutls failed: %v", err) + } // fetch ebpf assets - var err error - bpfFileName := g.geteBPFName("user/bytecode/gnutls_kern.o") - g.logger.Info().Str("bytecode filename", bpfFileName).Msg("BPF bytecode loaded") - byteBuf, err := assets.Asset(bpfFileName) + byteBuf, err := assets.Asset(g.sslBpfFile) if err != nil { g.logger.Error().Err(err).Strs("bytecode files", assets.AssetNames()).Msg("couldn't find bpf bytecode file") return fmt.Errorf("couldn't find asset %v", err) @@ -143,7 +145,7 @@ func (g *MGnutlsProbe) start() error { case TlsCaptureModelTypeText: fallthrough default: - err = g.setupManagers() + err = g.setupManagersText() } if err != nil { return fmt.Errorf("tls(gnutls) module couldn't find binPath %v", err) @@ -202,98 +204,11 @@ func (g *MGnutlsProbe) constantEditor() []manager.ConstantEditor { return editor } -func (g *MGnutlsProbe) setupManagers() error { - var binaryPath string - switch g.conf.(*config.GnutlsConfig).ElfType { - case config.ElfTypeSo: - binaryPath = g.conf.(*config.GnutlsConfig).Gnutls - default: - //如果没找到 "/lib/x86_64-linux-gnu/libgnutls.so.30" - binaryPath = path.Join(defaultSoPath, "libgnutls.so.30") - } - _, err := os.Stat(binaryPath) - if err != nil { - return err - } - - g.logger.Info().Str("binaryPath", binaryPath).Uint8("elfType", g.conf.(*config.GnutlsConfig).ElfType).Msg("gnutls binary path") - g.bpfManager = &manager.Manager{ - Probes: []*manager.Probe{ - { - Section: "uprobe/gnutls_record_send", - EbpfFuncName: "probe_entry_SSL_write", - AttachToFuncName: "gnutls_record_send", - BinaryPath: binaryPath, - }, - { - Section: "uretprobe/gnutls_record_send", - EbpfFuncName: "probe_ret_SSL_write", - AttachToFuncName: "gnutls_record_send", - BinaryPath: binaryPath, - }, - { - Section: "uprobe/gnutls_record_recv", - EbpfFuncName: "probe_entry_SSL_read", - AttachToFuncName: "gnutls_record_recv", - BinaryPath: binaryPath, - }, - { - Section: "uretprobe/gnutls_record_recv", - EbpfFuncName: "probe_ret_SSL_read", - AttachToFuncName: "gnutls_record_recv", - BinaryPath: binaryPath, - }, - }, - - Maps: []*manager.Map{ - { - Name: "gnutls_events", - }, - }, - } - - g.bpfManagerOptions = manager.Options{ - DefaultKProbeMaxActive: 512, - - VerifierOptions: ebpf.CollectionOptions{ - Programs: ebpf.ProgramOptions{ - LogSize: 2097152, - }, - }, - - RLimit: &unix.Rlimit{ - Cur: math.MaxUint64, - Max: math.MaxUint64, - }, - } - - if g.conf.EnableGlobalVar() { - // 填充 RewriteContants 对应map - g.bpfManagerOptions.ConstantEditors = g.constantEditor() - } - return nil -} - func (g *MGnutlsProbe) DecodeFun(em *ebpf.Map) (event.IEventStruct, bool) { fun, found := g.eventFuncMaps[em] return fun, found } -func (g *MGnutlsProbe) initDecodeFunText() error { - //GnutlsEventsMap 与解码函数映射 - GnutlsEventsMap, found, err := g.bpfManager.GetMap("gnutls_events") - if err != nil { - return err - } - if !found { - return errors.New("cant found map:gnutls_events") - } - g.eventMaps = append(g.eventMaps, GnutlsEventsMap) - g.eventFuncMaps[GnutlsEventsMap] = &event.GnutlsDataEvent{} - - return nil -} - func (g *MGnutlsProbe) Events() []*ebpf.Map { return g.eventMaps } diff --git a/user/module/probe_gnutls_keylog.go b/user/module/probe_gnutls_keylog.go index 7be797f65..064204955 100644 --- a/user/module/probe_gnutls_keylog.go +++ b/user/module/probe_gnutls_keylog.go @@ -19,8 +19,6 @@ import ( "errors" "fmt" "math" - "os" - "path" "github.com/cilium/ebpf" manager "github.com/gojue/ebpfmanager" @@ -54,20 +52,7 @@ var GnutlsVersionToString = map[int32]string{ } func (g *MGnutlsProbe) setupManagersKeylog() error { - var binaryPath string - switch g.conf.(*config.GnutlsConfig).ElfType { - case config.ElfTypeSo: - binaryPath = g.conf.(*config.GnutlsConfig).Gnutls - default: - //如果没找到 "/lib/x86_64-linux-gnu/libgnutls.so.30" - binaryPath = path.Join(defaultSoPath, "libgnutls.so.30") - } - _, err := os.Stat(binaryPath) - if err != nil { - return err - } - - g.logger.Info().Str("binaryPath", binaryPath).Uint8("elfType", g.conf.(*config.GnutlsConfig).ElfType).Msg("gnutls binary path") + binaryPath := g.conf.(*config.GnutlsConfig).Gnutls g.bpfManager = &manager.Manager{ Probes: []*manager.Probe{ { diff --git a/user/module/probe_gnutls_lib.go b/user/module/probe_gnutls_lib.go new file mode 100644 index 000000000..25afeefe5 --- /dev/null +++ b/user/module/probe_gnutls_lib.go @@ -0,0 +1,194 @@ +// Author: yuweizzz . +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package module + +import ( + "debug/elf" + "fmt" + "os" + "path" + "regexp" + + "github.com/gojue/ecapture/user/config" +) + +const GnuTLSDefaultVersion = "3.6.12" +const GnuTLSVersionLen = 32 + +func readelf(binaryPath string) (string, error) { + f, err := os.OpenFile(binaryPath, os.O_RDONLY, os.ModePerm) + if err != nil { + return "", fmt.Errorf("Can not open %s, with error: %v", binaryPath, err) + } + defer f.Close() + r, err := elf.NewFile(f) + if err != nil { + return "", fmt.Errorf("Parse the ELF file %s failed, with error: %v", binaryPath, err) + } + defer r.Close() + + switch r.FileHeader.Machine { + case elf.EM_X86_64: + case elf.EM_AARCH64: + default: + return "", fmt.Errorf( + "Unsupported arch library, ELF Header Machine is: %s, must be one of EM_X86_64 and EM_AARCH64", + r.FileHeader.Machine.String()) + } + + s := r.Section(".rodata") + if s == nil { + // .rodata not found + return "", fmt.Errorf("Detect GnuTLS version failed, cant read .rodata section from %s", binaryPath) + } + + sectionOffset := int64(s.Offset) + sectionSize := s.Size + + _, err = f.Seek(0, 0) + if err != nil { + return "", err + } + + ret, err := f.Seek(sectionOffset, 0) + if ret != sectionOffset || err != nil { + return "", err + } + + rex, err := regexp.Compile(`Enabled GnuTLS ([0-9\.]+) logging...`) + if err != nil { + return "", err + } + + buf := make([]byte, 1024*1024) // 1Mb + totalReadCount := 0 + for totalReadCount < int(sectionSize) { + readCount, err := f.Read(buf) + if err != nil { + return "", err + } + + if readCount == 0 { + break + } + + match := rex.FindSubmatch(buf) + if len(match) == 2 { + return string(match[1]), nil + } + + // just like "probe_openssl_lib.go", + // "Enabled GnuTLS 3.x.xx logging..." is 32 chars. + totalReadCount += readCount - GnuTLSVersionLen + + _, err = f.Seek(sectionOffset+int64(totalReadCount), 0) + if err != nil { + return "", err + } + clear(buf) + } + return "", fmt.Errorf("Unknown error") +} + +func (m *MGnutlsProbe) detectGnutls() error { + var binaryPath string + switch m.conf.(*config.GnutlsConfig).ElfType { + case config.ElfTypeSo: + binaryPath = m.conf.(*config.GnutlsConfig).Gnutls + default: + // Default: "/lib/x86_64-linux-gnu/libgnutls.so.30" + binaryPath = path.Join(defaultSoPath, "libgnutls.so.30") + } + _, err := os.Stat(binaryPath) + if err != nil { + return err + } + + ConfigSslVersion := m.conf.(*config.GnutlsConfig).SslVersion + if len(ConfigSslVersion) > 0 { + m.sslVersion = ConfigSslVersion + m.logger.Info().Str("GnuTLS Version", m.sslVersion).Msg("GnuTLS version configured") + } else { + sslVersion, err := readelf(binaryPath) + if err != nil { + m.logger.Error().Err(err) + } + m.sslVersion = sslVersion + if len(m.sslVersion) == 0 { + m.logger.Warn().Str("GnuTLS Version", GnuTLSDefaultVersion).Msg("GnuTLS version not found, used default version") + m.sslVersion = GnuTLSDefaultVersion + } + m.logger.Info().Str("Version", m.sslVersion).Msg("GnuTLS version found") + } + + m.logger.Info().Str("binaryPath", binaryPath).Uint8("elfType", m.conf.(*config.GnutlsConfig).ElfType).Msg("GnuTLS binary path") + switch m.sslVersion { + case "3.8.7": + m.sslBpfFile = m.geteBPFName("user/bytecode/gnutls_3_8_7_kern.o") + case "3.8.6": + fallthrough + case "3.8.5": + fallthrough + case "3.8.4": + m.sslBpfFile = m.geteBPFName("user/bytecode/gnutls_3_8_4_kern.o") + case "3.8.3": + fallthrough + case "3.8.2": + fallthrough + case "3.8.1": + fallthrough + case "3.8.0": + fallthrough + case "3.7.11": + fallthrough + case "3.7.10": + fallthrough + case "3.7.9": + fallthrough + case "3.7.8": + fallthrough + case "3.7.7": + m.sslBpfFile = m.geteBPFName("user/bytecode/gnutls_3_7_7_kern.o") + case "3.7.6": + fallthrough + case "3.7.5": + fallthrough + case "3.7.4": + fallthrough + case "3.7.3": + m.sslBpfFile = m.geteBPFName("user/bytecode/gnutls_3_7_3_kern.o") + case "3.7.2": + fallthrough + case "3.7.1": + fallthrough + case "3.7.0": + m.sslBpfFile = m.geteBPFName("user/bytecode/gnutls_3_7_0_kern.o") + case "3.6.16": + fallthrough + case "3.6.15": + fallthrough + case "3.6.14": + m.sslBpfFile = m.geteBPFName("user/bytecode/gnutls_3_6_14_kern.o") + case "3.6.13": + fallthrough + case "3.6.12": + m.sslBpfFile = m.geteBPFName("user/bytecode/gnutls_3_6_12_kern.o") + default: + m.sslBpfFile = m.geteBPFName("user/bytecode/gnutls_3_6_12_kern.o") + m.logger.Warn().Msg("GnuTLS version not supported, used default bpf bytecode file") + } + m.logger.Info().Str("bytecode filename", m.sslBpfFile).Msg("BPF bytecode loaded") + return nil +} diff --git a/user/module/probe_gnutls_pcap.go b/user/module/probe_gnutls_pcap.go index 42eebc026..861714c95 100644 --- a/user/module/probe_gnutls_pcap.go +++ b/user/module/probe_gnutls_pcap.go @@ -46,7 +46,7 @@ func (m *MGnutlsProbe) setupManagersPcap() error { pcapFilter := m.conf.(*config.GnutlsConfig).PcapFilter m.logger.Info().Str("binrayPath", binaryPath).Str("IFname", m.ifName).Int("IFindex", m.ifIdex). - Str("PcapFilter", pcapFilter).Uint8("ElfType", m.conf.(*config.GnutlsConfig).ElfType).Msg("HOOK type: Gnutls elf") + Str("PcapFilter", pcapFilter).Uint8("ElfType", m.conf.(*config.GnutlsConfig).ElfType).Msg("Hook type: Gnutls elf") m.logger.Info().Msg("Hook masterKey function: gnutls_handshake") // create pcapng writer diff --git a/user/module/probe_gnutls_text.go b/user/module/probe_gnutls_text.go new file mode 100644 index 000000000..51e5e74e9 --- /dev/null +++ b/user/module/probe_gnutls_text.go @@ -0,0 +1,99 @@ +// Author: yuweizzz . +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package module + +import ( + "errors" + "math" + + "github.com/cilium/ebpf" + manager "github.com/gojue/ebpfmanager" + "github.com/gojue/ecapture/user/config" + "github.com/gojue/ecapture/user/event" + "golang.org/x/sys/unix" +) + +func (m *MGnutlsProbe) setupManagersText() error { + binaryPath := m.conf.(*config.GnutlsConfig).Gnutls + m.bpfManager = &manager.Manager{ + Probes: []*manager.Probe{ + { + Section: "uprobe/gnutls_record_send", + EbpfFuncName: "probe_entry_SSL_write", + AttachToFuncName: "gnutls_record_send", + BinaryPath: binaryPath, + }, + { + Section: "uretprobe/gnutls_record_send", + EbpfFuncName: "probe_ret_SSL_write", + AttachToFuncName: "gnutls_record_send", + BinaryPath: binaryPath, + }, + { + Section: "uprobe/gnutls_record_recv", + EbpfFuncName: "probe_entry_SSL_read", + AttachToFuncName: "gnutls_record_recv", + BinaryPath: binaryPath, + }, + { + Section: "uretprobe/gnutls_record_recv", + EbpfFuncName: "probe_ret_SSL_read", + AttachToFuncName: "gnutls_record_recv", + BinaryPath: binaryPath, + }, + }, + + Maps: []*manager.Map{ + { + Name: "gnutls_events", + }, + }, + } + + m.bpfManagerOptions = manager.Options{ + DefaultKProbeMaxActive: 512, + + VerifierOptions: ebpf.CollectionOptions{ + Programs: ebpf.ProgramOptions{ + LogSize: 2097152, + }, + }, + + RLimit: &unix.Rlimit{ + Cur: math.MaxUint64, + Max: math.MaxUint64, + }, + } + + if m.conf.EnableGlobalVar() { + // 填充 RewriteContants 对应map + m.bpfManagerOptions.ConstantEditors = m.constantEditor() + } + return nil +} + +func (m *MGnutlsProbe) initDecodeFunText() error { + //GnutlsEventsMap 与解码函数映射 + GnutlsEventsMap, found, err := m.bpfManager.GetMap("gnutls_events") + if err != nil { + return err + } + if !found { + return errors.New("cant found map: gnutls_events") + } + m.eventMaps = append(m.eventMaps, GnutlsEventsMap) + m.eventFuncMaps[GnutlsEventsMap] = &event.GnutlsDataEvent{} + return nil +} From 244d84ce781708a41f6075409e13c0963042595f Mon Sep 17 00:00:00 2001 From: yuweizzz Date: Tue, 5 Nov 2024 11:10:44 +0800 Subject: [PATCH 07/11] [skip ci] update code style --- user/module/probe_gnutls_lib.go | 48 +++++---------------------------- 1 file changed, 6 insertions(+), 42 deletions(-) diff --git a/user/module/probe_gnutls_lib.go b/user/module/probe_gnutls_lib.go index 25afeefe5..799472cf2 100644 --- a/user/module/probe_gnutls_lib.go +++ b/user/module/probe_gnutls_lib.go @@ -137,53 +137,17 @@ func (m *MGnutlsProbe) detectGnutls() error { switch m.sslVersion { case "3.8.7": m.sslBpfFile = m.geteBPFName("user/bytecode/gnutls_3_8_7_kern.o") - case "3.8.6": - fallthrough - case "3.8.5": - fallthrough - case "3.8.4": + case "3.8.6", "3.8.5", "3.8.4": m.sslBpfFile = m.geteBPFName("user/bytecode/gnutls_3_8_4_kern.o") - case "3.8.3": - fallthrough - case "3.8.2": - fallthrough - case "3.8.1": - fallthrough - case "3.8.0": - fallthrough - case "3.7.11": - fallthrough - case "3.7.10": - fallthrough - case "3.7.9": - fallthrough - case "3.7.8": - fallthrough - case "3.7.7": + case "3.8.3", "3.8.2", "3.8.1", "3.8.0", "3.7.11", "3.7.10", "3.7.9", "3.7.8", "3.7.7": m.sslBpfFile = m.geteBPFName("user/bytecode/gnutls_3_7_7_kern.o") - case "3.7.6": - fallthrough - case "3.7.5": - fallthrough - case "3.7.4": - fallthrough - case "3.7.3": + case "3.7.6", "3.7.5", "3.7.4", "3.7.3": m.sslBpfFile = m.geteBPFName("user/bytecode/gnutls_3_7_3_kern.o") - case "3.7.2": - fallthrough - case "3.7.1": - fallthrough - case "3.7.0": + case "3.7.2", "3.7.1", "3.7.0": m.sslBpfFile = m.geteBPFName("user/bytecode/gnutls_3_7_0_kern.o") - case "3.6.16": - fallthrough - case "3.6.15": - fallthrough - case "3.6.14": + case "3.6.16", "3.6.15", "3.6.14": m.sslBpfFile = m.geteBPFName("user/bytecode/gnutls_3_6_14_kern.o") - case "3.6.13": - fallthrough - case "3.6.12": + case "3.6.13", "3.6.12": m.sslBpfFile = m.geteBPFName("user/bytecode/gnutls_3_6_12_kern.o") default: m.sslBpfFile = m.geteBPFName("user/bytecode/gnutls_3_6_12_kern.o") From 38da5a9862c040970b11d5669345b3a1c483860b Mon Sep 17 00:00:00 2001 From: yuweizzz Date: Wed, 6 Nov 2024 15:17:33 +0800 Subject: [PATCH 08/11] [skip ci] update offset.c --- utils/gnutls_offset.c | 59 +++++++++++++++++++++++++++++++++++++ utils/gnutls_offset.sh | 66 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 utils/gnutls_offset.c create mode 100755 utils/gnutls_offset.sh diff --git a/utils/gnutls_offset.c b/utils/gnutls_offset.c new file mode 100644 index 000000000..ff767ae2d --- /dev/null +++ b/utils/gnutls_offset.c @@ -0,0 +1,59 @@ +// bootstrap +// configure +// clang -I gnulib/lib/ -I lib/ -I . gnutls_offset.c -o offset + +#include +#include +#include +#include + +#define SSL_STRUCT_OFFSETS \ + X(gnutls_session_int, security_parameters) \ + X(gnutls_session_int, security_parameters.prf) \ + X(mac_entry_st, id) \ + X(gnutls_session_int, security_parameters.client_random) \ + X(gnutls_session_int, security_parameters.master_secret) \ + X(gnutls_session_int, key.proto.tls13.hs_ckey) \ + X(gnutls_session_int, key.proto.tls13.hs_skey) \ + X(gnutls_session_int, key.proto.tls13.ap_ckey) \ + X(gnutls_session_int, key.proto.tls13.ap_skey) \ + X(gnutls_session_int, key.proto.tls13.ap_expkey) + +#define SSL_ANY_STRUCT_OFFSETS \ + Y(security_parameters_st, pversion) \ + Y(version_entry_st, id) + +void toUpper(char *s) { + int i = 0; + while (s[i] != '\0') { + if (s[i] == '.') { + putchar('_'); + } else { + putchar(toupper(s[i])); + } + i++; + } +} + +void format(char *struct_name, char *field_name, size_t offset) { + printf("// %s->%s\n", struct_name, field_name); + printf("#define "); + toUpper(struct_name); + printf("_"); + toUpper(field_name); + printf(" 0x%lx\n\n", offset); +} + + +int main() { + +#define X(struct_name, field_name) format(#struct_name, #field_name, offsetof(struct struct_name, field_name)); + SSL_STRUCT_OFFSETS +#undef X + +#define Y(struct_name, field_name) format(#struct_name, #field_name, offsetof(struct_name, field_name)); + SSL_ANY_STRUCT_OFFSETS +#undef Y + + return 0; +} diff --git a/utils/gnutls_offset.sh b/utils/gnutls_offset.sh new file mode 100755 index 000000000..90d52b0f1 --- /dev/null +++ b/utils/gnutls_offset.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +set -e + +PROJECT_ROOT_DIR=$(pwd) +GNUTLS_DIR="${PROJECT_ROOT_DIR}/deps/gnutls" +OUTPUT_DIR="${PROJECT_ROOT_DIR}/kern" + +if [[ ! -f "go.mod" ]]; then + echo "Run the script from the project root directory" + exit 1 +fi + +echo "check file exists: ${GNUTLS_DIR}/.git" +# skip cloning if the header file of the max supported version is already generated +if [[ ! -f "${GNUTLS_DIR}/.git" ]]; then + echo "check directory exists: ${GNUTLS_DIR}" + # skip cloning if the gnutls directory already exists + if [[ ! -d "${GNUTLS_DIR}" ]]; then + echo "git clone gnutls to ${GNUTLS_DIR}" + git clone https://github.com/gnutls/gnutls.git ${GNUTLS_DIR} + fi +fi + + +function run() { + git fetch --tags + cp -f ${PROJECT_ROOT_DIR}/utils/gnutls_offset.c ${GNUTLS_DIR}/offset.c + main_version="3.8" + + for ver in $(seq 7 8); do + tag="${main_version}.${ver}" + underline_tag=$(echo $tag | tr "." "_") + header_file="${OUTPUT_DIR}/gnutls_${underline_tag}_kern.c" + header_define="GNUTLS_${underline_tag}_KERN_H" + + if [[ -f ${header_file} ]]; then + echo "Skip ${header_file}" + continue + fi + echo "git checkout ${tag}" + git checkout ${tag} + echo "Generating ${header_file}" + + # init + ./bootstrap + ./configure + clang -I gnulib/lib/ -I lib/ -I . offset.c -o offset + + echo -e "#ifndef ECAPTURE_${header_define}" >${header_file} + echo -e "#define ECAPTURE_${header_define}\n" >>${header_file} + ./offset >>${header_file} + echo -e "\n#include \"gnutls.h\"" >>${header_file} + echo -e "#include \"gnutls_masterkey.h\"" >>${header_file} + echo -e "\n#endif" >>${header_file} + + # clean up + make clean + done + + rm offset.c +} + +pushd ${GNUTLS_DIR} +(run) +[[ "$?" != 0 ]] && popd +popd From f514903293dfc1d1afe245a766b02bf323b8b8d5 Mon Sep 17 00:00:00 2001 From: yuweizzz Date: Fri, 8 Nov 2024 09:54:33 +0800 Subject: [PATCH 09/11] update keylogfile suffix --- cli/cmd/gnutls.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/cmd/gnutls.go b/cli/cmd/gnutls.go index 89c0c70ba..5ddc9ea51 100644 --- a/cli/cmd/gnutls.go +++ b/cli/cmd/gnutls.go @@ -47,7 +47,7 @@ func init() { //opensslCmd.PersistentFlags().StringVar(&gc.Curlpath, "wget", "", "wget file path, default: /usr/bin/wget. (Deprecated)") gnutlsCmd.PersistentFlags().StringVar(&gc.Gnutls, "gnutls", "", "libgnutls.so file path, will automatically find it from curl default.") gnutlsCmd.PersistentFlags().StringVarP(&gc.Model, "model", "m", "text", "capture model, such as : text, pcap/pcapng, key/keylog") - gnutlsCmd.PersistentFlags().StringVarP(&gc.KeylogFile, "keylogfile", "k", "ecapture_gnutls_key.og", "The file stores SSL/TLS keys, and eCapture captures these keys during encrypted traffic communication and saves them to the file.") + gnutlsCmd.PersistentFlags().StringVarP(&gc.KeylogFile, "keylogfile", "k", "ecapture_gnutls_key.log", "The file stores SSL/TLS keys, and eCapture captures these keys during encrypted traffic communication and saves them to the file.") gnutlsCmd.PersistentFlags().StringVarP(&gc.PcapFile, "pcapfile", "w", "save.pcapng", "write the raw packets to file as pcapng format.") gnutlsCmd.PersistentFlags().StringVarP(&gc.Ifname, "ifname", "i", "", "(TC Classifier) Interface name on which the probe will be attached.") gnutlsCmd.PersistentFlags().StringVar(&gc.SslVersion, "ssl_version", "", "GnuTLS version, e.g: --ssl_version=\"3.7.9\"") From 90670fd91c5b08b5bff249a151643c3c45a2420d Mon Sep 17 00:00:00 2001 From: yuweizzz Date: Sun, 10 Nov 2024 18:09:30 +0800 Subject: [PATCH 10/11] update offset.sh --- utils/gnutls_offset.sh | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/utils/gnutls_offset.sh b/utils/gnutls_offset.sh index 90d52b0f1..f596bf73a 100755 --- a/utils/gnutls_offset.sh +++ b/utils/gnutls_offset.sh @@ -42,9 +42,9 @@ function run() { echo "Generating ${header_file}" # init - ./bootstrap - ./configure - clang -I gnulib/lib/ -I lib/ -I . offset.c -o offset + ./bootstrap --skip-po --force --no-bootstrap-sync + ./configure --without-p11-kit --without-brotli --without-zstd --without-zlib --without-tpm + clang -I gnulib/lib/ -I lib/includes -I . offset.c -o offset echo -e "#ifndef ECAPTURE_${header_define}" >${header_file} echo -e "#define ECAPTURE_${header_define}\n" >>${header_file} @@ -60,6 +60,19 @@ function run() { rm offset.c } +# install deps +apt install -y \ + libtool \ + gettext \ + gperf \ + autopoint \ + gtk-doc-tools \ + nettle-dev \ + libev-dev \ + libtasn1-6-dev \ + libunistring-dev \ + libunbound-dev + pushd ${GNUTLS_DIR} (run) [[ "$?" != 0 ]] && popd From 956a95c8f0ef72731fce1d96a0f5691072cd788d Mon Sep 17 00:00:00 2001 From: yuweizzz Date: Sun, 10 Nov 2024 22:45:02 +0800 Subject: [PATCH 11/11] [skip ci] add sudo --- utils/gnutls_offset.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/gnutls_offset.sh b/utils/gnutls_offset.sh index f596bf73a..f4966054e 100755 --- a/utils/gnutls_offset.sh +++ b/utils/gnutls_offset.sh @@ -61,7 +61,7 @@ function run() { } # install deps -apt install -y \ +sudo apt install -y \ libtool \ gettext \ gperf \