Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[system-probe] Add static binary inspection to create lookup tables for crypto/tls.(*Conn).{Read,Write,Close} - copy #12897

Merged
merged 9 commits into from
Aug 1, 2022
Merged
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: 3 additions & 0 deletions LICENSE-3rdparty.csv
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ core,github.com/freddierice/go-losetup,MIT,Copyright (c) 2017 Freddie Rice
core,github.com/fsnotify/fsnotify,BSD-3-Clause,Copyright (c) 2012 The Go Authors. All rights reserved. | Copyright (c) 2012-2019 fsnotify Authors. All rights reserved.
core,github.com/ghodss/yaml,MIT,Copyright (c) 2012 The Go Authors. All rights reserved | Copyright (c) 2014 Sam Ghods
core,github.com/go-delve/delve/pkg/dwarf/godwarf,MIT,Copyright (c) 2014 Derek Parker
core,github.com/go-delve/delve/pkg/dwarf/loclist,MIT,Copyright (c) 2014 Derek Parker
core,github.com/go-delve/delve/pkg/dwarf/op,MIT,Copyright (c) 2014 Derek Parker
core,github.com/go-delve/delve/pkg/dwarf/util,MIT,Copyright (c) 2014 Derek Parker
core,github.com/go-delve/delve/pkg/goversion,MIT,Copyright (c) 2014 Derek Parker
Expand Down Expand Up @@ -929,6 +930,8 @@ core,go.uber.org/zap/zapcore,MIT,"Copyright (c) 2016-2017 Uber Technologies, Inc
core,go.uber.org/zap/zapgrpc,MIT,"Copyright (c) 2016-2017 Uber Technologies, Inc"
core,go4.org/intern,BSD-3-Clause,"Copyright (c) 2020, Brad Fitzpatrick"
core,go4.org/unsafe/assume-no-moving-gc,BSD-3-Clause,"Copyright (c) 2020, Brad Fitzpatrick"
core,golang.org/x/arch/arm64/arm64asm,BSD-3-Clause,Copyright (c) 2015 The Go Authors. All rights reserved
core,golang.org/x/arch/x86/x86asm,BSD-3-Clause,Copyright (c) 2015 The Go Authors. All rights reserved
core,golang.org/x/crypto/cast5,BSD-3-Clause,Copyright (c) 2009 The Go Authors. All rights reserved
core,golang.org/x/crypto/cryptobyte,BSD-3-Clause,Copyright (c) 2009 The Go Authors. All rights reserved
core,golang.org/x/crypto/cryptobyte/asn1,BSD-3-Clause,Copyright (c) 2009 The Go Authors. All rights reserved
Expand Down
5 changes: 5 additions & 0 deletions generate_tools.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ package tools
// This is the currently recommended approach: https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module

import (
_ "github.com/go-delve/delve/pkg/goversion"
_ "github.com/mailru/easyjson/easyjson"
_ "github.com/tinylib/msgp"
_ "golang.org/x/tools/cmd/stringer"

_ "github.com/DataDog/datadog-agent/pkg/network/go/bininspect"
_ "github.com/DataDog/datadog-agent/pkg/network/go/dwarfutils"
_ "github.com/DataDog/datadog-agent/pkg/network/go/lutgen"
)
5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,10 @@ require (
sigs.k8s.io/yaml v1.3.0 // indirect
)

require github.com/go-delve/delve v1.9.0
require (
github.com/go-delve/delve v1.9.0
golang.org/x/arch v0.0.0-20190927153633-4e8777c89be4
)

// Fixing a CVE on a transitive dep of k8s/etcd, should be cleaned-up once k8s.io/apiserver dep is removed (but double-check with `go mod why` that no other dep pulls it)
replace github.com/dgrijalva/jwt-go => github.com/golang-jwt/jwt v3.2.1+incompatible
Expand Down
1 change: 1 addition & 0 deletions go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

132 changes: 132 additions & 0 deletions pkg/network/go/asmscan/scan.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2022-present Datadog, Inc.

//go:build linux_bpf
// +build linux_bpf

package asmscan

import (
"debug/elf"
"fmt"

"golang.org/x/arch/arm64/arm64asm"
"golang.org/x/arch/x86/x86asm"
)

// ScanFunction finds the program counter (PC) positions of machine code instructions
// within a specific range of the text section of a binary,
// using the provided callback to disassemble and scan the buffer of machine code.
// The callback should return a slice of indices into the buffer
// that point to positions within the larger binary.
// These positions will then be adjusted based on the offset of the text section
// to provide the same positiions as PC positions,
// which will be returned from the outer function.
//
// lowPC, highPC forms an interval that contains all machine code bytes
// up to but not including high PC.
// In practice, this works well for scanning the instructions of functions,
// since highPC (in both DWARF parlance and in the Go symbol table),
// seems to refer to the address of the first location *past* the last instruction of the function.
//
// This function was intended to be used to find return instructions for functions in Go binaries,
// for the purpose of then attaching eBPF uprobes to these locations.
// This is needed because uretprobes don't work well with Go.
// See the following links for more info:
// - https://github.com/iovisor/bcc/issues/1320
// - https://github.com/iovisor/bcc/issues/1320#issuecomment-407927542
// (which describes how this approach works as a workaround)
// - https://github.com/golang/go/issues/22008
func ScanFunction(textSection *elf.Section, lowPC, highPC uint64, scanInstructions func(data []byte) ([]uint64, error)) ([]uint64, error) {
// Determine the offset in the section that the function starts at
offset := int64(lowPC - textSection.Addr)
buf := make([]byte, int(highPC-lowPC))

readBytes, err := textSection.ReadAt(buf, offset)
if err != nil {
return nil, fmt.Errorf("could not read text section: %w", err)
}
data := buf[:readBytes]

// instructionIndices contains indices into `buf`.
instructionIndices, err := scanInstructions(data)
if err != nil {
return nil, fmt.Errorf("error while scanning instructions in text section: %w", err)
}

// Add the function lowPC to each index to obtain the actual locations
adjustedLocations := make([]uint64, len(instructionIndices))
for i, instructionIndex := range instructionIndices {
adjustedLocations[i] = instructionIndex + lowPC
}

return adjustedLocations, nil
}

// FindX86_64ReturnInstructions is a callback for ScanFunction
// that scans for all x86_64 return instructions (RET)
// contained in the given buffer of machine code.
// On success, this function returns the index into the buffer
// of the start of each return instruction.
//
// Note that this may not behave well with panics or defer statements.
// See the following links for more context:
// - https://github.com/go-delve/delve/pull/2704/files#diff-fb7b7a020e32bf8bf477c052ac2d2857e7e587478be6039aebc7135c658417b2R769
// - https://github.com/go-delve/delve/blob/75bbbbb60cecda0d65c63de7ae8cb8b8412d6fc3/pkg/proc/breakpoints.go#L86-L95
// - https://github.com/go-delve/delve/blob/75bbbbb60cecda0d65c63de7ae8cb8b8412d6fc3/pkg/proc/breakpoints.go#L374
func FindX86_64ReturnInstructions(data []byte) ([]uint64, error) {
// x86_64 => mode is 64
mode := 64
returnOffsets := []uint64{}
cursor := 0
for cursor < len(data) {
instruction, err := x86asm.Decode(data[cursor:], mode)
if err != nil {
return nil, fmt.Errorf("failed to decode x86-64 instruction at offset %d within function machine code: %w", cursor, err)
}

if instruction.Op == x86asm.RET {
returnOffsets = append(returnOffsets, uint64(cursor))
}

cursor += instruction.Len
}

return returnOffsets, nil
}

// FindARM64ReturnInstructions is a callback for ScanFunction
// that scans for all ARM 64-bit return instructions (RET, not RETAA/RETAB)
// contained in the given buffer of machine code.
// On success, this function returns the index into the buffer
// of the start of each return instruction.
//
// Note that this may not behave well with panics or defer statements.
// See the following links for more context:
// - https://github.com/go-delve/delve/pull/2704/files#diff-fb7b7a020e32bf8bf477c052ac2d2857e7e587478be6039aebc7135c658417b2R769
// - https://github.com/go-delve/delve/blob/75bbbbb60cecda0d65c63de7ae8cb8b8412d6fc3/pkg/proc/breakpoints.go#L86-L95
// - https://github.com/go-delve/delve/blob/75bbbbb60cecda0d65c63de7ae8cb8b8412d6fc3/pkg/proc/breakpoints.go#L374
func FindARM64ReturnInstructions(data []byte) ([]uint64, error) {
// It seems like we only need to look for RET instructions
// (and not RETAA/RETAB, since gc doesn't support pointer authentication:
// https://github.com/golang/go/issues/39208)
returnOffsets := []uint64{}
cursor := 0
for cursor < len(data) {
instruction, err := arm64asm.Decode(data[cursor:])
if err != nil {
return nil, fmt.Errorf("failed to decode ARM 64 instruction at offset %d within function machine code: %w", cursor, err)
}

if instruction.Op == arm64asm.RET {
returnOffsets = append(returnOffsets, uint64(cursor))
}

// Each instruction is 4 bytes long
cursor += 4
}

return returnOffsets, nil
}
Loading