15
15
package config
16
16
17
17
import (
18
+ "bytes"
19
+ "debug/buildinfo"
18
20
"debug/elf"
21
+ "debug/gosym"
22
+ "encoding/binary"
19
23
"errors"
20
24
"fmt"
21
25
"os"
22
26
"runtime"
23
-
24
- "golang.org/x/arch/arm64/arm64asm"
25
- "golang.org/x/arch/x86/x86asm"
27
+ "strings"
26
28
)
27
29
28
30
const (
29
- // Arm64armInstSize via : arm64/arm64asm/decode.go:Decode() size = 4
30
- Arm64armInstSize = 4
31
-
31
+ // 46EE50 - 46EDB0
32
+ //
32
33
GoTlsReadFunc = "crypto/tls.(*Conn).Read"
34
+
35
+ GoTlsWriteFunc = "crypto/tls.(*Conn).writeRecordLocked"
36
+ GoTlsMasterSecretFunc = "crypto/tls.(*Config).writeKeyLog"
37
+ // crypto_tls._ptr_Conn.Read
38
+ GoTlsReadFuncArm64 = "crypto_tls._ptr_Conn.Read"
33
39
)
34
40
35
41
var (
36
- ErrorGoBINNotFound = errors .New ("The executable program (compiled by Golang) was not found" )
37
- ErrorSymbolNotFound = errors .New ("symbol not found" )
38
- ErrorNoRetFound = errors .New ("no RET instructions found" )
42
+ ErrorGoBINNotFound = errors .New ("The executable program (compiled by Golang) was not found" )
43
+ ErrorSymbolEmpty = errors .New ("symbol is empty" )
44
+ ErrorSymbolNotFound = errors .New ("symbol not found" )
45
+ ErrorSymbolNotFoundFromTable = errors .New ("symbol not found from table" )
46
+ ErrorNoRetFound = errors .New ("no RET instructions found" )
47
+ ErrorNoRetFoundFromSymTabFun = errors .New ("no RET instructions found from golang symbol table with Fun" )
39
48
)
40
49
50
+ // From go/src/debug/gosym/pclntab.go
51
+ const (
52
+ go12magic = 0xfffffffb
53
+ go116magic = 0xfffffffa
54
+ go118magic = 0xfffffff0
55
+ go120magic = 0xfffffff1
56
+ )
57
+
58
+ // Select the magic number based on the Go version
59
+ func magicNumber (goVersion string ) []byte {
60
+ bs := make ([]byte , 4 )
61
+ var magic uint32
62
+ if strings .Compare (goVersion , "go1.20" ) >= 0 {
63
+ magic = go120magic
64
+ } else if strings .Compare (goVersion , "go1.18" ) >= 0 {
65
+ magic = go118magic
66
+ } else if strings .Compare (goVersion , "go1.16" ) >= 0 {
67
+ magic = go116magic
68
+ } else {
69
+ magic = go12magic
70
+ }
71
+ binary .LittleEndian .PutUint32 (bs , magic )
72
+ return bs
73
+ }
74
+
75
+ type FuncOffsets struct {
76
+ Start uint64
77
+ Returns []uint64
78
+ }
79
+
41
80
// GoTLSConfig represents configuration for Go SSL probe
42
81
type GoTLSConfig struct {
43
82
eConfig
44
- Path string `json:"path"` // golang application path to binary built with Go toolchain.
45
- PcapFile string `json:"pcapFile"` // pcapFile the raw packets to file rather than parsing and printing them out.
46
- KeylogFile string `json:"keylogFile"` // keylogFile The file stores SSL/TLS keys, and eCapture captures these keys during encrypted traffic communication and saves them to the file.
47
- Model string `json:"model"` // model such as : text, pcapng/pcap, key/keylog.
48
- Ifname string `json:"ifName"` // (TC Classifier) Interface name on which the probe will be attached.
49
- PcapFilter string `json:"pcapFilter"` // pcap filter
50
- goElfArch string //
51
- goElf * elf.File //
52
- ReadTlsAddrs []int
83
+ Path string `json:"path"` // golang application path to binary built with Go toolchain.
84
+ PcapFile string `json:"pcapFile"` // pcapFile the raw packets to file rather than parsing and printing them out.
85
+ KeylogFile string `json:"keylogFile"` // keylogFile The file stores SSL/TLS keys, and eCapture captures these keys during encrypted traffic communication and saves them to the file.
86
+ Model string `json:"model"` // model such as : text, pcapng/pcap, key/keylog.
87
+ Ifname string `json:"ifName"` // (TC Classifier) Interface name on which the probe will be attached.
88
+ PcapFilter string `json:"pcapFilter"` // pcap filter
89
+ goElfArch string //
90
+ goElf * elf.File //
91
+ buildinfo * buildinfo.BuildInfo
92
+ ReadTlsAddrs []int
93
+ GoTlsWriteAddr uint64
94
+ GoTlsMasterSecretAddr uint64
95
+ IsPieBuildMode bool
96
+ goSymTab * gosym.Table
53
97
}
54
98
55
99
// NewGoTLSConfig creates a new config for Go SSL
@@ -74,6 +118,12 @@ func (gc *GoTLSConfig) Check() error {
74
118
return err
75
119
}
76
120
121
+ // Read the build information of the Go application
122
+ gc .buildinfo , err = buildinfo .ReadFile (gc .Path )
123
+ if err != nil {
124
+ return err
125
+ }
126
+
77
127
var goElf * elf.File
78
128
goElf , err = elf .Open (gc .Path )
79
129
if err != nil {
@@ -102,10 +152,56 @@ func (gc *GoTLSConfig) Check() error {
102
152
}
103
153
gc .goElfArch = goElfArch
104
154
gc .goElf = goElf
105
- gc .ReadTlsAddrs , err = gc .findRetOffsets (GoTlsReadFunc )
155
+ // If built with PIE and stripped, gopclntab is
156
+ // unlabeled and nested under .data.rel.ro.
157
+ for _ , bs := range gc .buildinfo .Settings {
158
+ if bs .Key == "-buildmode" {
159
+ if bs .Value == "pie" {
160
+ gc .IsPieBuildMode = true
161
+ }
162
+ break
163
+ }
164
+ }
165
+ if gc .IsPieBuildMode {
166
+ gc .goSymTab , err = gc .ReadTable ()
167
+ if err != nil {
168
+ return err
169
+ }
170
+ fun := gc .goSymTab .LookupFunc (GoTlsWriteFunc )
171
+ if fun != nil {
172
+ gc .GoTlsWriteAddr = fun .Entry
173
+ }
174
+ fun = gc .goSymTab .LookupFunc (GoTlsMasterSecretFunc )
175
+ if fun != nil {
176
+ gc .GoTlsMasterSecretAddr = fun .Entry
177
+ }
178
+ gc .ReadTlsAddrs , err = gc .findRetOffsetsPie (GoTlsReadFunc )
179
+ if err != nil {
180
+ return err
181
+ }
182
+ } else {
183
+ gc .ReadTlsAddrs , err = gc .findRetOffsets (GoTlsReadFunc )
184
+ if err != nil {
185
+ return err
186
+ }
187
+ }
106
188
return err
107
189
}
108
190
191
+ func (gc * GoTLSConfig ) findRetOffsetsPie (symbolName string ) ([]int , error ) {
192
+ var offsets []int
193
+ var err error
194
+
195
+ fun := gc .goSymTab .LookupFunc (symbolName )
196
+ if fun == nil {
197
+ return nil , ErrorSymbolNotFoundFromTable
198
+ }
199
+
200
+ //fmt.Printf("found in %s, entry:%x, end:%x , end-entry:%x\n", fun.Name, fun.Entry, fun.End, fun.End-fun.Entry)
201
+ offsets , err = gc .findFuncOffsetBySymfun (fun , gc .goElf )
202
+ return offsets , err
203
+ }
204
+
109
205
// FindRetOffsets searches for the addresses of all RET instructions within
110
206
// the instruction set associated with the specified symbol in an ELF program.
111
207
// It is used for mounting uretprobe programs for Golang programs,
@@ -125,7 +221,7 @@ func (gc *GoTLSConfig) findRetOffsets(symbolName string) ([]int, error) {
125
221
}
126
222
127
223
if len (allSymbs ) == 0 {
128
- return nil , fmt . Errorf ( "no symbols found" )
224
+ return nil , ErrorSymbolEmpty
129
225
}
130
226
131
227
var found bool
@@ -163,30 +259,6 @@ func (gc *GoTLSConfig) findRetOffsets(symbolName string) ([]int, error) {
163
259
return offsets , nil
164
260
}
165
261
166
- // decodeInstruction Decode into assembly instructions and identify the RET instruction to return the offset.
167
- func (gc * GoTLSConfig ) decodeInstruction (instHex []byte ) ([]int , error ) {
168
- var offsets []int
169
- for i := 0 ; i < len (instHex ); {
170
- if gc .goElfArch == "amd64" {
171
- inst , err := x86asm .Decode (instHex [i :], 64 )
172
- if err != nil {
173
- return nil , err
174
- }
175
- if inst .Op == x86asm .RET {
176
- offsets = append (offsets , i )
177
- }
178
- i += inst .Len
179
- } else {
180
- inst , _ := arm64asm .Decode (instHex [i :]) // Why ignore error: https://github.com/gojue/ecapture/pull/506
181
- if inst .Op == arm64asm .RET {
182
- offsets = append (offsets , i )
183
- }
184
- i += Arm64armInstSize
185
- }
186
- }
187
- return offsets , nil
188
- }
189
-
190
262
func (gc * GoTLSConfig ) checkModel () (string , error ) {
191
263
var m string
192
264
var e error
@@ -204,3 +276,72 @@ func (gc *GoTLSConfig) checkModel() (string, error) {
204
276
}
205
277
return m , e
206
278
}
279
+
280
+ func (gc * GoTLSConfig ) ReadTable () (* gosym.Table , error ) {
281
+ sectionLabel := ".data.rel.ro"
282
+ section := gc .goElf .Section (sectionLabel )
283
+ if section == nil {
284
+ // binary may be built with -pie
285
+ sectionLabel = ".gopclntab"
286
+ section = gc .goElf .Section (sectionLabel )
287
+ if section == nil {
288
+ return nil , fmt .Errorf ("could not read section %s from %s " , sectionLabel , gc .Path )
289
+ }
290
+ }
291
+ tableData , err := section .Data ()
292
+ if err != nil {
293
+ return nil , fmt .Errorf ("found section but could not read %s from %s " , sectionLabel , gc .Path )
294
+ }
295
+
296
+ // Find .gopclntab by magic number even if there is no section label
297
+ magic := magicNumber (gc .buildinfo .GoVersion )
298
+ pclntabIndex := bytes .Index (tableData , magic )
299
+ //fmt.Printf("buildinfo :%v, magic:%x, pclntabIndex:%d offset:%x , section:%v \n", gc.buildinfo, magic, pclntabIndex, section.Offset, section)
300
+ if pclntabIndex < 0 {
301
+ return nil , fmt .Errorf ("could not find magic number in %s " , gc .Path )
302
+ }
303
+ tableData = tableData [pclntabIndex :]
304
+ addr := gc .goElf .Section (".text" ).Addr
305
+ lineTable := gosym .NewLineTable (tableData , addr )
306
+ symTable , err := gosym .NewTable ([]byte {}, lineTable )
307
+ if err != nil {
308
+ return nil , ErrorSymbolNotFoundFromTable
309
+ }
310
+ return symTable , nil
311
+ }
312
+
313
+ func (gc * GoTLSConfig ) findFuncOffsetBySymfun (f * gosym.Func , elfF * elf.File ) ([]int , error ) {
314
+ var offsets []int
315
+ var err error
316
+
317
+ for _ , prog := range elfF .Progs {
318
+ if prog .Type != elf .PT_LOAD || (prog .Flags & elf .PF_X ) == 0 {
319
+ continue
320
+ }
321
+ // For more info on this calculation: stackoverflow.com/a/40249502
322
+ /*
323
+ ida : start : 46EE50, end : 46F258 , len :0x408
324
+ elf info: start : 46ed30, end : , len: 0x3B0, 0x58
325
+
326
+ */
327
+ if prog .Vaddr <= f .Value && f .Value < (prog .Vaddr + prog .Memsz ) {
328
+ funcLen := f .End - f .Entry
329
+ fmt .Printf ("name :%s, f.Value:%x, f.Entry:%x, f.End:%x, funcLen:%X \n " , f .Name , f .Value , f .Entry , f .End , funcLen )
330
+ data := make ([]byte , funcLen )
331
+ _ , err = prog .ReadAt (data , int64 (f .Value - prog .Vaddr ))
332
+ if err != nil {
333
+ return offsets , fmt .Errorf ("finding function return: %w" , err )
334
+ }
335
+
336
+ offsets , err = gc .decodeInstruction (data )
337
+ if err != nil {
338
+ return offsets , fmt .Errorf ("finding function return: %w" , err )
339
+ }
340
+ for i , offset := range offsets {
341
+ offsets [i ] = int (f .Entry ) + offset
342
+ }
343
+ return offsets , nil
344
+ }
345
+ }
346
+ return offsets , ErrorNoRetFoundFromSymTabFun
347
+ }
0 commit comments