Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/kubeshark/tracer

go 1.22.2
go 1.22.4

toolchain go1.22.7

Expand All @@ -21,6 +21,7 @@ require (
github.com/knightsc/gapstone v0.0.0-20191231144527-6fa5afaf11a9
github.com/kubeshark/api v1.1.32
github.com/kubeshark/gopacket v1.1.30
github.com/kubeshark/offsetdb v0.0.0-20250529155907-b5b962037b6b
github.com/kubeshark/procfs v0.0.0-20250312150455-4b9efb18c324
github.com/kubeshark/tracerproto v1.0.3-0.20240730073449-de3a99a3719c
github.com/kubeshark/utils v0.0.0-20250210221556-322c90ef9b16
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ github.com/kubeshark/api v1.1.32 h1:qZ4so2FBJOgw7Eqmlvr1Kz29hALM4hsFgHzC/cULoMM=
github.com/kubeshark/api v1.1.32/go.mod h1:+Ua35OiwreWiUYfqJz0Aswn3UmsLctLUQh3tvxagQz4=
github.com/kubeshark/gopacket v1.1.30 h1:Dz6eo7b6+NdVCrgiyKxlGEVTm0L6PwgbVvSomsuwIyU=
github.com/kubeshark/gopacket v1.1.30/go.mod h1:Qo8/i/tdT74CCT7/pjO0L55Pktv5dQfj7M/Arv8MKm8=
github.com/kubeshark/offsetdb v0.0.0-20250529155907-b5b962037b6b h1:U0/ZtIE0O7T//FERnXxGDdLnEpDd2Q4E8Rr7UpPC9Vs=
github.com/kubeshark/offsetdb v0.0.0-20250529155907-b5b962037b6b/go.mod h1:qynxtfFHWWfNBVNSYHdW4x5+uiBFvV6eD7oEFiAhpak=
github.com/kubeshark/procfs v0.0.0-20250312150455-4b9efb18c324 h1:bkkaNmy70yebjPIPeZv5bDamFl0fmAKQn038NWf/GBU=
github.com/kubeshark/procfs v0.0.0-20250312150455-4b9efb18c324/go.mod h1:eOQ5k+THNnxNiyNdy1cWkGWTecBMrbxT1IHjdt6kLsY=
github.com/kubeshark/tracerproto v1.0.0/go.mod h1:+efDYkwXxwakmHRpxHVEekyXNtg/aFx0uSo/I0lGV9k=
Expand Down
2 changes: 0 additions & 2 deletions pkg/discoverer/pids.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"regexp"
"strconv"
"strings"

"unsafe"

"github.com/cilium/ebpf/link"
Expand Down Expand Up @@ -138,7 +137,6 @@ func (p *pids) untargetCgroup(cgroupId uint64) {
func (p *pids) handleFoundNewPIDs() {
for {
record, err := p.readerFoundPid.Read()

if err != nil {
if errors.Is(err, perf.ErrClosed) {
log.Info().Msg("found pid handler is closed")
Expand Down
156 changes: 154 additions & 2 deletions pkg/hooks/ssl/ssllib_hooks.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,52 @@
package ssl

import (
"debug/elf"
"fmt"
"path/filepath"
"strconv"
"sync"

"github.com/cilium/ebpf/link"
"github.com/go-errors/errors"
lru "github.com/hashicorp/golang-lru/v2"
"github.com/kubeshark/offsetdb/hasher"
"github.com/kubeshark/offsetdb/models"
"github.com/kubeshark/offsetdb/store"
"github.com/kubeshark/tracer/pkg/bpf"
"github.com/kubeshark/tracer/pkg/utils"
"github.com/rs/zerolog/log"
)

const (
offsetdb = "/app/offsets.json"
)

var offStore = store.NewOffsetStore()

type SslHooks struct {
links []link.Link
}

// TODO: incapsulate, add devuce id to the key, delete on file is deleted
var hookInodes, _ = lru.New[uint64, uint32](16384)

func init() {
if err := offStore.LoadOffsets(offsetdb); err != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This must be duplication of onceFunc part - to be removed

}
}

func (s *SslHooks) InstallUprobes(bpfObjects *bpf.BpfObjects, sslLibraryPath string) error {
var once sync.Once
onceFunc := func() {
err := offStore.LoadOffsets(offsetdb)
if err != nil {
log.Warn().Msgf("failed to load offset db: %v", err)
offStore = nil
}
}
once.Do(onceFunc)

var isEnvoy bool
if filepath.Base(sslLibraryPath) == "envoy" {
isEnvoy = true
Expand All @@ -32,13 +61,30 @@ func (s *SslHooks) InstallUprobes(bpfObjects *bpf.BpfObjects, sslLibraryPath str
}

sslLibrary, err := link.OpenExecutable(sslLibraryPath)

if err != nil {
return errors.Wrap(err, 0)
}

if isEnvoy {
return s.installEnvoySslHooks(bpfObjects, sslLibrary)
// Compute the has of the binary
hash, err := hasher.ComputeFileSHA256(sslLibraryPath)
if err != nil {
return fmt.Errorf("fallback: sha256 failed: %w", err)
}

// Check if the hash is in the offset store
var info *models.OffsetInfo
var found bool
if offStore != nil {
info, found = offStore.GetOffsets(hash)
}
// Check if the hash is in the offset store
if !found {
// Try to install the hooks by symbols
return s.installEnvoySslHooks(bpfObjects, sslLibrary)
}

return s.installEnvoySslHooksWithOffset(bpfObjects, sslLibrary, sslLibraryPath, info)
}

return s.installSslHooks(bpfObjects, sslLibrary)
Expand Down Expand Up @@ -145,3 +191,109 @@ func (s *SslHooks) Close() []error {

return returnValue
}

func (s *SslHooks) installEnvoySslHooksWithOffset(
bpfObjects *bpf.BpfObjects,
sslLibrary *link.Executable,
sslLibraryPath string,
info *models.OffsetInfo,
) error {
var err error
var relativeOffset, baseOffset, absoluteOffset uint64

baseOffset, err = findStrippedExecutableSegmentOffset(sslLibraryPath)
if err != nil {
return fmt.Errorf("failed to find base offset in SSL library '%s': %w", sslLibraryPath, err)
}

// --- SSL_write ---
if relativeOffset, err = parseOffset(info.SSLWriteOffset); err != nil {
return fmt.Errorf("parsing SSLWriteOffset: %w", err)
}
absoluteOffset = baseOffset + relativeOffset

// ENTRY SSL_write
upWrite, err := sslLibrary.Uprobe(
"",
bpfObjects.BpfObjs.SslWrite,
&link.UprobeOptions{Address: absoluteOffset},
)
if err != nil {
return fmt.Errorf("attaching SSL_write uprobe at offset 0x%x : %w", absoluteOffset, err)
}
s.links = append(s.links, upWrite)

// EXIT SSL_write (uses the same address as the entry)
urWrite, err := sslLibrary.Uretprobe(
"",
bpfObjects.BpfObjs.SslRetWrite,
&link.UprobeOptions{Address: absoluteOffset},
)
if err != nil {
return fmt.Errorf("attaching SSL_write uretprobe at offset 0x%x : %w", absoluteOffset, err)
}
s.links = append(s.links, urWrite)

// --- SSL_read ---
if relativeOffset, err = parseOffset(info.SSLReadOffset); err != nil {
return fmt.Errorf("parsing SSLReadOffset: %w", err)
}
absoluteOffset = baseOffset + relativeOffset

// ENTRY SSL_read
upRead, err := sslLibrary.Uprobe(
"",
bpfObjects.BpfObjs.SslRead,
&link.UprobeOptions{Address: absoluteOffset},
)
if err != nil {
return fmt.Errorf("attaching SSL_read uprobe at offset 0x%x : %w", absoluteOffset, err)
}
s.links = append(s.links, upRead)

// EXIT SSL_read (uses the same address as the entry)
urRead, err := sslLibrary.Uretprobe(
"",
bpfObjects.BpfObjs.SslRetRead,
&link.UprobeOptions{Address: absoluteOffset},
)
if err != nil {
return fmt.Errorf("attaching SSL_read uretprobe at offset 0x%x : %w", absoluteOffset, err)
}
s.links = append(s.links, urRead)

return nil
}

// parseOffset turns a hex- or dec-formatted string into a uint64
func parseOffset(s string) (uint64, error) {
// Let strconv auto-detect the base from “0x…” prefix or plain digits
val, err := strconv.ParseUint(s, 0, 64)
if err != nil {
return 0, fmt.Errorf("invalid offset %q: %w", s, err)
}
return val, nil
}

// findStrippedExecutableSegmentOffset finds the file offset of the first executable segment
// or the .text section in an ELF file.
func findStrippedExecutableSegmentOffset(path string) (uint64, error) {
f, err := elf.Open(path)
if err != nil {
return 0, fmt.Errorf("elf.Open %s: %w", path, err)
}
defer f.Close()

// Prefer .text section offset when available
if sec := f.Section(".text"); sec != nil && sec.Offset != 0 {
return sec.Offset, nil
}

// Otherwise, pick the first executable PT_LOAD
for _, prog := range f.Progs {
if prog.Type == elf.PT_LOAD && (prog.Flags&elf.PF_X) != 0 {
return prog.Off, nil
}
}
return 0, errors.New("no executable segment or .text section found")
}
Loading