-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce minimal support for Golang HTTPS request introspection. Signed-off-by: Hengqi Chen <chenhengqi@outlook.com>
- Loading branch information
1 parent
010baaa
commit 07180ab
Showing
7 changed files
with
307 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |