Skip to content

Commit

Permalink
fix(user/module): read version from libcrypto.so (#661)
Browse files Browse the repository at this point in the history
Since OpenSSL 3.0, in the built dynamic link library product, the symbols in libssl.so no longer contain the `OPENSSL_VERSION_TEXT` string, causing eCapture to be unable to accurately identify the version of OpenSSL, causing it to fail to work properly.
For more information please refer to: #675

fix: #609
Co-authored-by: CFC4N <cfc4n.cs@gmail.com>

Signed-off-by: xxxxxliil <xliilQwQ@outlook.com>
  • Loading branch information
xxxxxliil authored Nov 28, 2024
1 parent fcddaeb commit fe474e6
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 47 deletions.
67 changes: 65 additions & 2 deletions user/module/probe_openssl.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,8 +198,71 @@ func (m *MOpenSSLProbe) getSslBpfFile(soPath, sslVersion string) error {
}
}

// 未找到对应的bpf文件,尝试从so文件中获取
err := m.detectOpenssl(soPath)
err, verString := m.detectOpenssl(soPath)
if errors.Is(err, ErrProbeOpensslVerNotFound) {
// 未找到版本号, try libcrypto.so.x
if strings.Contains(soPath, "libssl.so.3") {
m.logger.Warn().Err(err).Str("soPath", soPath).Msg("OpenSSL/BoringSSL version not found.")
m.logger.Warn().Msg("Try to detect libcrypto.so.3. If you have doubts")
m.logger.Warn().Msg("See https://github.com/gojue/ecapture/discussions/675 for more information.")

// 从 libssl.so.3 中获取 libcrypto.so.3 的路径
var libcryptoName = "libcrypto.so.3"
var imd []string
imd, err = getImpNeeded(soPath)
if err == nil {
for _, im := range imd {
// 匹配 包含 libcrypto.so 字符的动态链接库库名
if strings.Contains(im, "libcrypto.so") {
libcryptoName = im
break
}
}
}
soPath = strings.Replace(soPath, "libssl.so.3", libcryptoName, 1)
m.logger.Info().Str("soPath", soPath).Str("imported", libcryptoName).Msg("Try to detect imported libcrypto.so ")
err, verString = m.detectOpenssl(soPath)
if err != nil {
if !errors.Is(err, ErrProbeOpensslVerNotFound) {
return err
} else {
m.logger.Warn().Err(err).Str("soPath", soPath).Msg("OpenSSL(libcrypto.so.3) version not found.")
}
} else {
m.logger.Info().Str("soPath", soPath).Str("imported", libcryptoName).Str("version", verString).Msg("OpenSSL/BoringSSL version found from imported libcrypto.so")
}
}
}

if err != nil {
m.logger.Error().Str("soPath", soPath).Err(err).Msg("OpenSSL/BoringSSL version check failed")
return err
}

m.conf.(*config.OpensslConfig).SslVersion = verString
m.logger.Info().Str("origin versionKey", verString).Str("versionKeyLower", verString).Send()
// find the sslVersion bpfFile from sslVersionBpfMap

isAndroid := m.conf.(*config.OpensslConfig).IsAndroid
androidVer := m.conf.(*config.OpensslConfig).AndroidVer
bpfFileKey := verString
if isAndroid {
// sometimes,boringssl version always was "boringssl 1.1.1" on android. but offsets are different.
// see kern/boringssl_a_13_kern.c and kern/boringssl_a_14_kern.c
// Perhaps we can utilize the Android Version to choose a specific version of boringssl.
// use the corresponding bpfFile
bpfFileKey = fmt.Sprintf("boringssl_a_%s", androidVer)
}
bpfFile, found := m.sslVersionBpfMap[bpfFileKey]
if found {
m.sslBpfFile = bpfFile
m.logger.Info().Bool("Android", isAndroid).Str("library version", bpfFileKey).Msg("OpenSSL/BoringSSL version found")
return nil
}

bpfFile = m.getSoDefaultBytecode(soPath, isAndroid)
m.sslBpfFile = bpfFile
m.logger.Error().Str("sslVersion", sslVersion).Str("bpfFile", bpfFile).Msg("OpenSSL/BoringSSL version not found, used default version, if you want to use the specific version, please set the sslVersion parameter with `--ssl_version=\"openssl x.x.x\"`")
return err
}

Expand Down
104 changes: 59 additions & 45 deletions user/module/probe_openssl_lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ package module

import (
"debug/elf"
"errors"
"fmt"
"github.com/gojue/ecapture/user/config"
"os"
"regexp"
"strings"
Expand Down Expand Up @@ -50,6 +50,11 @@ const (
SupportedOpenSSL34Version0 = 0 // openssl 3.4.0
)

var (
ErrProbeOpensslVerNotFound = errors.New("OpenSSL/BoringSSL version not found")
ErrProbeOpensslVerBytecodeNotFound = errors.New("OpenSSL/BoringSSL version bytecode not found")
)

// initOpensslOffset initial BpfMap
func (m *MOpenSSLProbe) initOpensslOffset() {
m.sslVersionBpfMap = map[string]string{
Expand Down Expand Up @@ -147,27 +152,27 @@ func (m *MOpenSSLProbe) initOpensslOffset() {

}

func (m *MOpenSSLProbe) detectOpenssl(soPath string) error {
func (m *MOpenSSLProbe) detectOpenssl(soPath string) (error, string) {
f, err := os.OpenFile(soPath, os.O_RDONLY, os.ModePerm)
if err != nil {
return fmt.Errorf("can not open %s, with error:%v", soPath, err)
return fmt.Errorf("can not open %s, with error:%v", soPath, err), ""
}
r, e := elf.NewFile(f)
if e != nil {
return fmt.Errorf("parse the ELF file %s failed, with error:%v", soPath, err)
return fmt.Errorf("parse the ELF file %s failed, with error:%v", soPath, err), ""
}

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())
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 {
// not found
return fmt.Errorf("detect openssl version failed, cant read .rodata section from %s", soPath)
return fmt.Errorf("detect openssl version failed, cant read .rodata section from %s", soPath), ""
}

sectionOffset := int64(s.Offset)
Expand All @@ -177,12 +182,12 @@ func (m *MOpenSSLProbe) detectOpenssl(soPath string) error {

_, err = f.Seek(0, 0)
if err != nil {
return err
return err, ""
}

ret, err := f.Seek(sectionOffset, 0)
if ret != sectionOffset || err != nil {
return err
return err, ""
}

versionKey := ""
Expand All @@ -191,7 +196,7 @@ func (m *MOpenSSLProbe) detectOpenssl(soPath string) error {
// OpenSSL 3.2.0 23 Nov 2023
rex, err := regexp.Compile(`(OpenSSL\s\d\.\d\.[0-9a-z]+)`)
if err != nil {
return nil
return err, ""
}

buf := make([]byte, 1024*1024) // 1Mb
Expand Down Expand Up @@ -233,46 +238,55 @@ func (m *MOpenSSLProbe) detectOpenssl(soPath string) error {
_ = f.Close()
//buf = buf[:0]

var bpfFile string
var found bool
if versionKey != "" {
versionKeyLower := strings.ToLower(versionKey)
m.conf.(*config.OpensslConfig).SslVersion = versionKeyLower
m.logger.Info().Str("origin versionKey", versionKey).Str("versionKeyLower", versionKeyLower).Msg("OpenSSL/BoringSSL version found")
// find the sslVersion bpfFile from sslVersionBpfMap
bpfFile, found = m.sslVersionBpfMap[versionKeyLower]
if found {
m.sslBpfFile = bpfFile
return nil
}
if versionKey == "" {
return ErrProbeOpensslVerNotFound, ""
}

isAndroid := m.conf.(*config.OpensslConfig).IsAndroid
androidVer := m.conf.(*config.OpensslConfig).AndroidVer
versionKeyLower := strings.ToLower(versionKey)

return nil, versionKeyLower
}

func (m *MOpenSSLProbe) getSoDefaultBytecode(soPath string, isAndroid bool) string {
var bpfFile string

// if not found, use default
if isAndroid {
// sometimes,boringssl version always was "boringssl 1.1.1" on android. but offsets are different.
// see kern/boringssl_a_13_kern.c and kern/boringssl_a_14_kern.c
// Perhaps we can utilize the Android Version to choose a specific version of boringssl.
// use the corresponding bpfFile
bpfFildAndroid := fmt.Sprintf("boringssl_a_%s", androidVer)
bpfFile, found = m.sslVersionBpfMap[bpfFildAndroid]
if found {
m.sslBpfFile = bpfFile
m.logger.Info().Str("BoringSSL Version", androidVer).Msg("OpenSSL/BoringSSL version found")
} else {
bpfFile, _ = m.sslVersionBpfMap[AndroidDefauleFilename]
m.logger.Warn().Str("BoringSSL Version", AndroidDefauleFilename).Msg("OpenSSL/BoringSSL version not found, used default version")
}
bpfFile, _ = m.sslVersionBpfMap[AndroidDefauleFilename]
m.logger.Warn().Str("BoringSSL Version", AndroidDefauleFilename).Msg("OpenSSL/BoringSSL version not found, used default version")
return bpfFile
}

if strings.Contains(soPath, "libssl.so.3") {
bpfFile, _ = m.sslVersionBpfMap[Linuxdefaulefilename30]
m.logger.Warn().Str("OpenSSL Version", Linuxdefaulefilename30).Msg("OpenSSL/BoringSSL version not found from shared library file, used default version")
} else {
if strings.Contains(soPath, "libssl.so.3") {
bpfFile, _ = m.sslVersionBpfMap[Linuxdefaulefilename30]
m.logger.Warn().Str("OpenSSL Version", Linuxdefaulefilename30).Msg("OpenSSL/BoringSSL version not found from shared library file, used default version")
} else {
bpfFile, _ = m.sslVersionBpfMap[Linuxdefaulefilename111]
m.logger.Warn().Str("OpenSSL Version", Linuxdefaulefilename111).Msg("OpenSSL/BoringSSL version not found from shared library file, used default version")
}
bpfFile, _ = m.sslVersionBpfMap[Linuxdefaulefilename111]
m.logger.Warn().Str("OpenSSL Version", Linuxdefaulefilename111).Msg("OpenSSL/BoringSSL version not found from shared library file, used default version")
}
return bpfFile
}

func getImpNeeded(soPath string) ([]string, error) {
var importedNeeded []string
f, err := os.OpenFile(soPath, os.O_RDONLY, os.ModePerm)
if err != nil {
return importedNeeded, fmt.Errorf("can not open %s, with error:%v", soPath, err)
}

elfFile, err := elf.NewFile(f)
if err != nil {
return importedNeeded, fmt.Errorf("parse the ELF file %s failed, with error:%v", soPath, err)
}

// 打印外部依赖的动态链接库
is, err := elfFile.DynString(elf.DT_NEEDED)
//is, err := elfFile.ImportedSymbols()
if err != nil {
return importedNeeded, err
}
for _, s := range is {
importedNeeded = append(importedNeeded, s)
}
m.sslBpfFile = bpfFile
return nil
return importedNeeded, nil
}

0 comments on commit fe474e6

Please sign in to comment.