Skip to content

Commit

Permalink
Support Go binaries introspection
Browse files Browse the repository at this point in the history
Introduce minimal support for Golang HTTPS request introspection.

Signed-off-by: Hengqi Chen <chenhengqi@outlook.com>
  • Loading branch information
chenhengqi committed Jun 21, 2022
1 parent 010baaa commit 07180ab
Show file tree
Hide file tree
Showing 7 changed files with 307 additions and 2 deletions.
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ TARGETS += kern/gnutls
TARGETS += kern/nspr
TARGETS += kern/mysqld
TARGETS += kern/postgres
TARGETS += kern/gossl

# Generate file name-scheme based on TARGETS
KERN_SOURCES = ${TARGETS:=_kern.c}
Expand Down Expand Up @@ -351,4 +352,4 @@ format:
@clang-format -i -style=$(STYLE) kern/common.h

autogen: .checkver_$(CMD_BPFTOOL)
$(AUTOGENCMD)
$(AUTOGENCMD)
6 changes: 5 additions & 1 deletion cli/cmd/tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
var oc = user.NewOpensslConfig()
var gc = user.NewGnutlsConfig()
var nc = user.NewNsprConfig()
var goc = user.NewGoSSLConfig()

// opensslCmd represents the openssl command
var opensslCmd = &cobra.Command{
Expand All @@ -38,6 +39,7 @@ func init() {
opensslCmd.PersistentFlags().StringVar(&nc.Firefoxpath, "firefox", "", "firefox file path, default: /usr/lib/firefox/firefox.")
opensslCmd.PersistentFlags().StringVar(&nc.Nsprpath, "nspr", "", "libnspr44.so file path, will automatically find it from curl default.")
opensslCmd.PersistentFlags().StringVar(&oc.Pthread, "pthread", "", "libpthread.so file path, use to hook connect to capture socket FD.will automatically find it from curl.")
opensslCmd.PersistentFlags().StringVar(&goc.Path, "gobin", "", "path to binary built with Go toolchain.")

rootCmd.AddCommand(opensslCmd)
}
Expand All @@ -57,7 +59,7 @@ func openSSLCommandFunc(command *cobra.Command, args []string) {
}
log.Printf("pid info :%d", os.Getpid())

modNames := []string{user.MODULE_NAME_OPENSSL, user.MODULE_NAME_GNUTLS, user.MODULE_NAME_NSPR}
modNames := []string{user.MODULE_NAME_OPENSSL, user.MODULE_NAME_GNUTLS, user.MODULE_NAME_NSPR, user.MODULE_NAME_GOSSL}

var runMods uint8
for _, modName := range modNames {
Expand All @@ -76,6 +78,8 @@ func openSSLCommandFunc(command *cobra.Command, args []string) {
conf = gc
case user.MODULE_NAME_NSPR:
conf = nc
case user.MODULE_NAME_GOSSL:
conf = goc
default:
}

Expand Down
104 changes: 104 additions & 0 deletions kern/gossl_kern.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/* Copyright © 2022 Hengqi Chen */
#include "ecapture.h"

struct go_ssl_event {
__u64 ts_ns;
__u32 pid;
__u32 tid;
int data_len;
char comm[TASK_COMM_LEN];
char data[MAX_DATA_SIZE_OPENSSL];
};

struct {
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
} events SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__type(key, __u32);
__type(value, struct go_ssl_event);
__uint(max_entries, 1);
} heap SEC(".maps");

#if defined(__TARGET_ARCH_x86)
#define GO_REG1(x) ((x)->ax)
#define GO_REG2(x) ((x)->bx)
#define GO_REG3(x) ((x)->cx)
#define GO_REG4(x) ((x)->di)
#define GO_SP(x) ((x)->sp)
#elif defined(__TARGET_ARCH_arm64)
#define GO_REG1(x) PT_REGS_PARM1(x)
#define GO_REG2(x) PT_REGS_PARM2(x)
#define GO_REG3(x) PT_REGS_PARM3(x)
#define GO_REG4(x) PT_REGS_PARM4(x)
#define GO_SP(x) PT_REGS_SP(x)
#endif

static struct go_ssl_event *get_event()
{
static const int zero = 0;
struct go_ssl_event *event;
__u64 id;

event = bpf_map_lookup_elem(&heap, &zero);
if (!event)
return NULL;

id = bpf_get_current_pid_tgid();
event->ts_ns = bpf_ktime_get_ns();
event->pid = id >> 32;
event->tid = (__u32)id;
bpf_get_current_comm(event->comm, sizeof(event->comm));
return event;
}

SEC("uprobe/abi_stack")
int BPF_KPROBE(probe_stack)
{
struct go_ssl_event *event;
__u64 *sp = (void *)GO_SP(ctx), addr;
int len, record_type;
const char *str;

bpf_probe_read_user(&record_type, sizeof(record_type), sp + 2);
if (record_type != 23)
return 0;

bpf_probe_read_user(&addr, sizeof(addr), sp + 3);
bpf_probe_read_user(&len, sizeof(len), sp + 4);

event = get_event();
if (!event)
return 0;

str = (void *)addr;
bpf_probe_read_user_str(event->data, sizeof(event->data), str);
event->data_len = len;
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, event, sizeof(*event));
return 0;
}

SEC("uprobe/abi_register")
int BPF_KPROBE(probe_register)
{
struct go_ssl_event *event;
int len, record_type;
const char *str;

record_type = GO_REG2(ctx);
str = (void *)GO_REG3(ctx);
len = GO_REG4(ctx);

if (record_type != 23)
return 0;

event = get_event();
if (!event)
return 0;

bpf_probe_read_user_str(event->data, sizeof(event->data), str);
event->data_len = len;
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, event, sizeof(*event));
return 0;
}
26 changes: 26 additions & 0 deletions user/config_gossl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright © 2022 Hengqi Chen
package user

import (
"errors"
"os"
)

// GoSSLConfig represents configuration for Go SSL probe
type GoSSLConfig struct {
eConfig
Path string
}

// NewGoSSLConfig creates a new config for Go SSL
func NewGoSSLConfig() *GoSSLConfig {
return &GoSSLConfig{}
}

func (c *GoSSLConfig) Check() error {
if c.Path == "" {
return errors.New("go binary not found")
}
_, err := os.Stat(c.Path)
return err
}
1 change: 1 addition & 0 deletions user/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const (
MODULE_NAME_OPENSSL = "EBPFProbeOPENSSL"
MODULE_NAME_GNUTLS = "EBPFProbeGNUTLS"
MODULE_NAME_NSPR = "EBPFProbeNSPR"
MODULE_NAME_GOSSL = "EBPFProbeGoSSL"
)

const (
Expand Down
52 changes: 52 additions & 0 deletions user/event_gossl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright © 2022 Hengqi Chen
package user

import (
"bytes"
"encoding/binary"
"fmt"
)

type inner struct {
TimestampNS uint64
Pid uint32
Tid uint32
Len int32
Comm [16]byte
Data [4096]byte
}

type goSSLEvent struct {
m IModule
inner
}

func (e *goSSLEvent) Decode(payload []byte) error {
r := bytes.NewBuffer(payload)
return binary.Read(r, binary.LittleEndian, &e.inner)
}

func (e *goSSLEvent) String() string {
s := fmt.Sprintf("PID: %d, Comm: %s, TID: %d, Payload: %s\n", e.Pid, string(e.Comm[:]), e.Tid, string(e.Data[:e.Len]))
return s
}

func (e *goSSLEvent) StringHex() string {
return e.String()
}

func (e *goSSLEvent) Clone() IEventStruct {
return &goSSLEvent{}
}

func (e *goSSLEvent) Module() IModule {
return e.m
}

func (e *goSSLEvent) SetModule(m IModule) {
e.m = m
}

func (e *goSSLEvent) EventType() EVENT_TYPE {
return EVENT_TYPE_OUTPUT
}
117 changes: 117 additions & 0 deletions user/probe_gossl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// Copyright © 2022 Hengqi Chen
package user

import (
"bytes"
"context"
"ecapture/assets"
"ecapture/pkg/proc"
"log"
"math"

"github.com/cilium/ebpf"
manager "github.com/ehids/ebpfmanager"
"golang.org/x/sys/unix"
)

func init() {
mod := &GoSSLProbe{}
Register(mod)
}

// GoSSLProbe represents a probe for Go SSL
type GoSSLProbe struct {
Module

mngr *manager.Manager
path string
isRegisterABI bool
}

func (p *GoSSLProbe) Init(ctx context.Context, l *log.Logger, cfg IConfig) error {
p.Module.Init(ctx, l)
p.Module.SetChild(p)

p.path = cfg.(*GoSSLConfig).Path
ver, err := proc.ExtraceGoVersion(p.path)
if err != nil {
return err
}

if ver.After(1, 15) {
p.isRegisterABI = true
}
return nil
}

func (p *GoSSLProbe) Name() string {
return MODULE_NAME_GOSSL
}

func (p *GoSSLProbe) Start() error {
var (
sec string
fn string
)

if p.isRegisterABI {
sec = "uprobe/abi_register"
fn = "probe_register"
} else {
sec = "uprobe/abi_stack"
fn = "probe_stack"
}

p.mngr = &manager.Manager{
Probes: []*manager.Probe{
{
Section: sec,
EbpfFuncName: fn,
AttachToFuncName: "crypto/tls.(*Conn).writeRecordLocked",
BinaryPath: p.path,
},
},
Maps: []*manager.Map{
{
Name: "events",
},
},
}

data, err := assets.Asset("user/bytecode/gossl_kern.o")
if err != nil {
return err
}

opts := manager.Options{
RLimit: &unix.Rlimit{
Cur: math.MaxUint64,
Max: math.MaxUint64,
},
}
if err := p.mngr.InitWithOptions(bytes.NewReader(data), opts); err != nil {
return err
}

return p.mngr.Start()
}

func (p *GoSSLProbe) Events() []*ebpf.Map {
var maps []*ebpf.Map

m, ok, err := p.mngr.GetMap("events")
if err != nil || !ok {
return maps
}

maps = append(maps, m)
return maps
}

func (p *GoSSLProbe) DecodeFun(m *ebpf.Map) (IEventStruct, bool) {
return &goSSLEvent{}, true
}

func (p *GoSSLProbe) Close() error {
return nil
}

0 comments on commit 07180ab

Please sign in to comment.