Skip to content

Commit

Permalink
user: imporve #463, impact on the performance of the tested program
Browse files Browse the repository at this point in the history
* When calling `SSL_connect` in the OpenSSL library in a client role or `SSL_accept` in a server role, the execution flow ultimately enters the `state_machine` function in `ssl/statem/statem.c` for TLS handshake.
* Therefore, the optional scope is functions within this `state_machine` function that start with an uppercase `SSL`.
* When using OpenSSL synchronously, a successful TLS handshake returns 1, i.e., `ret = 1`. Thus, after this variable is assigned, the called functions can obtain the desired memory data.
* Under this premise, the only function within the `state_machine` function that meets the requirements is `SSL_get_wbio`.
* Adding an alternate HOOK function, `SSL_in_before`, to the scope.

Signed-off-by: CFC4N <cfc4n.cs@gmail.com>
  • Loading branch information
cfc4n committed Jan 28, 2024
1 parent 1362476 commit ed1383a
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 42 deletions.
26 changes: 25 additions & 1 deletion user/module/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ const (
)

const (
MasterKeyHookFuncOpenSSL = "SSL_write"
// 备选 HOOK的函数 SSL_is_init_finished \ SSL_get_wbio \ SSL_write
MasterKeyHookFuncOpenSSL = "SSL_is_init_finished"

/*
在boringSSL类库里,SSL_write函数调用了 SSL_do_handshake ,
Expand All @@ -49,6 +50,29 @@ const (
MasterKeyHookFuncBoringSSL = "SSL_in_init"
)

var (
/*
* 为了读取到TLS握手完成后的client_random等密钥,必需要选择一个合适的HOOK函数。
* SSL_write\SSL_read时,TLS握手是建立完成的,但调用过于频繁,会带来性能问题,参见https://github.com/gojue/ecapture/issues/463
* 综合来看,合适的HOOK函数需要满足以下几个条件:
* 1. 函数是在TLS握手完成后调用
* 2. 函数名在动态链接库的符号表中是导出状态
* 3. 函数是低频调用
*
* 在 openssl 类库中,以客户端角色调用 `SSL_connect` 或者以服务端角色 `SSL_accept` ,最终都会进入 `ssl/statem/statem.c` 的 `state_machine` 函数进行TLS握手。
* 所以,可选范围是在这个函数内以大写`SSL`开头的函数。
* 当使用openssl的方式为同步调用时,TLS握手成功会返回1,也就是`ret = 1`,即需要在这个变量赋值后,被调用的函数,才能拿到符合要求的内存数据。
* 在这个前提下,`的state_machine`函数内符合要求的就只有`SSL_get_wbio`了。
*/
MasterKeyHookFuncs = []string{
"SSL_get_wbio", // openssl
//"SSL_is_init", // boringssl
// 备用HOOK 函数
//"SSL_is_init_finished",
"SSL_in_before",
}
)

const (
MasterSecretKeyLogName = "ecapture_masterkey.log"
)
Expand Down
84 changes: 68 additions & 16 deletions user/module/probe_openssl.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ type MOpenSSLProbe struct {
sslVersionBpfMap map[string]string // bpf map key: ssl version, value: bpf map key
sslBpfFile string // ssl bpf file
isBoringSSL bool //
masterHookFunc string // SSL_in_init on boringSSL, SSL_write on openssl
masterHookFuncs []string // SSL_in_init on boringSSL, SSL_write on openssl
cgroupPath string
}

Expand All @@ -89,6 +89,7 @@ func (m *MOpenSSLProbe) Init(ctx context.Context, logger *log.Logger, conf confi
m.pidLocker = new(sync.Mutex)
m.masterKeys = make(map[string]bool)
m.sslVersionBpfMap = make(map[string]string)
m.masterHookFuncs = MasterKeyHookFuncs

//fd := os.Getpid()
var err error
Expand Down Expand Up @@ -146,9 +147,9 @@ func (m *MOpenSSLProbe) getSslBpfFile(soPath, sslVersion string) error {
defer func() {
if strings.Contains(m.sslBpfFile, "boringssl") {
m.isBoringSSL = true
m.masterHookFunc = MasterKeyHookFuncBoringSSL
//m.masterHookFuncs = MasterKeyHookFuncBoringSSL
} else {
m.masterHookFunc = MasterKeyHookFuncOpenSSL
//m.masterHookFuncs = MasterKeyHookFuncOpenSSL
}
}()

Expand Down Expand Up @@ -356,13 +357,17 @@ func (m *MOpenSSLProbe) saveMasterSecret(secretEvent *event.MasterSecretEvent) {
// 已存在该随机数的masterSecret,不需要重复写入
return
}
m.masterKeys[k] = true

// save to file
var b *bytes.Buffer
switch secretEvent.Version {
case event.Tls12Version:
var length = event.MasterSecretMaxLen
if m.oSSLEvent12NullSecrets(length, secretEvent) {
return
}
b = bytes.NewBufferString(fmt.Sprintf("%s %02x %02x\n", hkdf.KeyLogLabelTLS12, secretEvent.ClientRandom, secretEvent.MasterKey))
m.masterKeys[k] = true
case event.Tls13Version:
var length int
var transcript crypto.Hash
Expand All @@ -374,7 +379,9 @@ func (m *MOpenSSLProbe) saveMasterSecret(secretEvent *event.MasterSecretEvent) {
length = 48
transcript = crypto.SHA384
default:
m.logger.Printf("non-TLSv1.3 cipher suite found, CipherId: %d", secretEvent.CipherId)
length = 32
transcript = crypto.SHA256
m.logger.Printf("non-TLSv1.3 cipher suite found, CipherId: %d, clientrandom:%02x", secretEvent.CipherId, secretEvent.ClientRandom)
return
}

Expand All @@ -385,14 +392,24 @@ func (m *MOpenSSLProbe) saveMasterSecret(secretEvent *event.MasterSecretEvent) {

serverHandshakeSecret := hkdf.ExpandLabel(secretEvent.HandshakeSecret[:length],
hkdf.ServerHandshakeTrafficLabel, secretEvent.HandshakeTrafficHash[:length], length, transcript)

var clientHandshakeSecret1, serverHandshakeSecret1 [64]byte
copy(clientHandshakeSecret1[:length], clientHandshakeSecret)
copy(serverHandshakeSecret1[:length], serverHandshakeSecret)
// 判断 密钥是否为空
if m.oSSLEvent13NullSecrets(length, secretEvent, clientHandshakeSecret1, serverHandshakeSecret1) {
return
}
m.masterKeys[k] = true

b.WriteString(fmt.Sprintf("%s %02x %02x\n",
hkdf.KeyLogLabelServerHandshake, secretEvent.ClientRandom, serverHandshakeSecret))

b.WriteString(fmt.Sprintf("%s %02x %02x\n",
hkdf.KeyLogLabelClientTraffic, secretEvent.ClientRandom, secretEvent.ClientAppTrafficSecret))
hkdf.KeyLogLabelClientTraffic, secretEvent.ClientRandom, secretEvent.ClientAppTrafficSecret[:length]))

b.WriteString(fmt.Sprintf("%s %02x %02x\n",
hkdf.KeyLogLabelServerTraffic, secretEvent.ClientRandom, secretEvent.ServerAppTrafficSecret))
hkdf.KeyLogLabelServerTraffic, secretEvent.ClientRandom, secretEvent.ServerAppTrafficSecret[:length]))

b.WriteString(fmt.Sprintf("%s %02x %02x\n",
hkdf.KeyLogLabelExporterSecret, secretEvent.ClientRandom, secretEvent.ExporterMasterSecret[:length]))
Expand Down Expand Up @@ -490,47 +507,82 @@ func (m *MOpenSSLProbe) saveMasterSecretBSSL(secretEvent *event.MasterSecretBSSL
}

func (m *MOpenSSLProbe) bSSLEvent12NullSecrets(e *event.MasterSecretBSSLEvent) bool {
return m.mk12NullSecrets(int(e.HashLen), e.Secret)
}

func (m *MOpenSSLProbe) oSSLEvent12NullSecrets(hashLen int, e *event.MasterSecretEvent) bool {
return m.mk12NullSecrets(hashLen, e.MasterKey)
}

func (m *MOpenSSLProbe) mk12NullSecrets(hashLen int, secret [48]byte) bool {
var isNull = true
var hashLen = int(e.HashLen)
for i := 0; i < hashLen; i++ {
if hashLen >= len(e.Secret) {
if hashLen > len(secret) {
break
}
if e.Secret[i] != 0 {
if secret[i] != 0 {
isNull = false
break
}
}
return isNull
}

// bSSLEvent13NullSecrets 检测boringssl Secret Event 是不是空密钥
func (m *MOpenSSLProbe) bSSLEvent13NullSecrets(e *event.MasterSecretBSSLEvent) bool {
var hashLen = int(e.HashLen)
return m.mk13NullSecrets(hashLen,
e.ClientHandshakeSecret,
e.ClientTrafficSecret0,
e.ServerHandshakeSecret,
e.ServerTrafficSecret0,
e.ExporterSecret,
)
}

// oSSLEvent13NullSecrets 检测openssl Secret Event 是不是空密钥
func (m *MOpenSSLProbe) oSSLEvent13NullSecrets(hashLen int, e *event.MasterSecretEvent, ClientHandshakeSecret, ServerHandshakeSecret [64]byte) bool {
return m.mk13NullSecrets(hashLen,
ClientHandshakeSecret,
e.ClientAppTrafficSecret,
ServerHandshakeSecret,
e.ServerAppTrafficSecret,
e.ExporterMasterSecret,
)
}

func (m *MOpenSSLProbe) mk13NullSecrets(hashLen int,
ClientHandshakeSecret [64]byte,
ClientTrafficSecret0 [64]byte,
ServerHandshakeSecret [64]byte,
ServerTrafficSecret0 [64]byte,
ExporterSecret [64]byte,
) bool {
var isNUllCount = 5

var hashLen = int(e.HashLen)
var chsChecked, ctsChecked, shsChecked, stsChecked, esChecked bool
for i := 0; i < hashLen; i++ {
if !chsChecked && e.ClientHandshakeSecret[i] != 0 {
if !chsChecked && ClientHandshakeSecret[i] != 0 {
isNUllCount -= 1
chsChecked = true
}

if !ctsChecked && e.ClientTrafficSecret0[i] != 0 {
if !ctsChecked && ClientTrafficSecret0[i] != 0 {
isNUllCount -= 1
ctsChecked = true
}

if !shsChecked && e.ServerHandshakeSecret[i] != 0 {
if !shsChecked && ServerHandshakeSecret[i] != 0 {
isNUllCount -= 1
shsChecked = true
}

if !stsChecked && e.ServerTrafficSecret0[i] != 0 {
if !stsChecked && ServerTrafficSecret0[i] != 0 {
isNUllCount -= 1
stsChecked = true
}

if !esChecked && e.ExporterSecret[i] != 0 {
if !esChecked && ExporterSecret[i] != 0 {
isNUllCount -= 1
esChecked = true
}
Expand Down
24 changes: 12 additions & 12 deletions user/module/probe_openssl_keylog.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"ecapture/user/config"
"ecapture/user/event"
"errors"
"fmt"
"github.com/cilium/ebpf"
manager "github.com/gojue/ebpfmanager"
"golang.org/x/sys/unix"
Expand Down Expand Up @@ -50,26 +51,25 @@ func (m *MOpenSSLProbe) setupManagersKeylog() error {
}

m.logger.Printf("%s\tHOOK type:%d, binrayPath:%s\n", m.Name(), m.conf.(*config.OpensslConfig).ElfType, binaryPath)
m.logger.Printf("%s\tHook masterKey function:%s\n", m.Name(), m.masterHookFunc)
m.logger.Printf("%s\tHook masterKey function:%s\n", m.Name(), m.masterHookFuncs)

m.bpfManager = &manager.Manager{
Probes: []*manager.Probe{
// openssl masterkey
{
Section: "uprobe/SSL_write_key",
EbpfFuncName: "probe_ssl_master_key",
AttachToFuncName: m.masterHookFunc, // SSL_do_handshake or SSL_write
BinaryPath: binaryPath,
UID: "uprobe_ssl_master_key",
},
},

Maps: []*manager.Map{
{
Name: "mastersecret_events",
},
},
}
m.bpfManager.Probes = make([]*manager.Probe, 0)
for _, masterFunc := range m.masterHookFuncs {
m.bpfManager.Probes = append(m.bpfManager.Probes, &manager.Probe{
Section: "uprobe/SSL_write_key",
EbpfFuncName: "probe_ssl_master_key",
AttachToFuncName: masterFunc,
BinaryPath: binaryPath,
UID: fmt.Sprintf("uprobe_smk_%s", masterFunc),
})
}

m.bpfManagerOptions = manager.Options{
DefaultKProbeMaxActive: 512,
Expand Down
21 changes: 10 additions & 11 deletions user/module/probe_openssl_pcap.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func (m *MOpenSSLProbe) setupManagersPcap() error {

m.logger.Printf("%s\tHOOK type:%d, binrayPath:%s\n", m.Name(), m.conf.(*config.OpensslConfig).ElfType, binaryPath)
m.logger.Printf("%s\tIfname:%s, Ifindex:%d, Port:%d, Pcapng filepath:%s\n", m.Name(), m.ifName, m.ifIdex, m.conf.(*config.OpensslConfig).Port, m.pcapngFilename)
m.logger.Printf("%s\tHook masterKey function:%s\n", m.Name(), m.masterHookFunc)
m.logger.Printf("%s\tHook masterKey function:%s\n", m.Name(), m.masterHookFuncs)

// create pcapng writer
netIfs, err := net.Interfaces()
Expand Down Expand Up @@ -116,16 +116,6 @@ func (m *MOpenSSLProbe) setupManagersPcap() error {
NetworkDirection: manager.Ingress,
},
// --------------------------------------------------

// openssl masterkey
{
Section: "uprobe/SSL_write_key",
EbpfFuncName: "probe_ssl_master_key",
AttachToFuncName: m.masterHookFunc, // SSL_do_handshake or SSL_write
BinaryPath: binaryPath,
UID: "uprobe_ssl_master_key",
},
//
{
EbpfFuncName: "tcp_sendmsg",
Section: "kprobe/tcp_sendmsg",
Expand All @@ -143,6 +133,15 @@ func (m *MOpenSSLProbe) setupManagersPcap() error {
},
}

for _, masterFunc := range m.masterHookFuncs {
m.bpfManager.Probes = append(m.bpfManager.Probes, &manager.Probe{
Section: "uprobe/SSL_write_key",
EbpfFuncName: "probe_ssl_master_key",
AttachToFuncName: masterFunc,
BinaryPath: binaryPath,
UID: fmt.Sprintf("uprobe_smk_%s", masterFunc),
})
}
m.bpfManagerOptions = manager.Options{
DefaultKProbeMaxActive: 512,

Expand Down
4 changes: 2 additions & 2 deletions user/module/probe_openssl_text.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func (m *MOpenSSLProbe) setupManagersText() error {
}

m.logger.Printf("%s\tHOOK type:%d, binrayPath:%s\n", m.Name(), m.conf.(*config.OpensslConfig).ElfType, binaryPath)
m.logger.Printf("%s\tHook masterKey function:%s\n", m.Name(), m.masterHookFunc)
m.logger.Printf("%s\tHook masterKey function:%s\n", m.Name(), m.masterHookFuncs)

m.bpfManager = &manager.Manager{
Probes: []*manager.Probe{
Expand Down Expand Up @@ -91,7 +91,7 @@ func (m *MOpenSSLProbe) setupManagersText() error {
/*{
Section: "uprobe/SSL_write_key",
EbpfFuncName: "probe_ssl_master_key",
AttachToFuncName: m.masterHookFunc,
AttachToFuncName: m.masterHookFuncs,
BinaryPath: binaryPath,
UID: "uprobe_ssl_master_key",
},*/
Expand Down

0 comments on commit ed1383a

Please sign in to comment.