Skip to content

Commit

Permalink
Hierarchical dissection (#10)
Browse files Browse the repository at this point in the history
* Subfields : setting up everything on the Go plugin side tpackage + example)

* Wirego package cleanup

* SPlit wirego_plugin code to multiple source files

* Cleanup

* Cleanup

* Almost in place, but strange regression

* Subtrees working, needsq more tests

* Updated documentation for sub-fields

---------

Co-authored-by: Benoit <benoit@MacBook-Pro-de-Benoit.local>
  • Loading branch information
NothNoth and Benoit committed Apr 1, 2024
1 parent 8ff932c commit 7eb51b6
Show file tree
Hide file tree
Showing 20 changed files with 745 additions and 546 deletions.
5 changes: 5 additions & 0 deletions DEVGUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@ func (WiregoExample) DissectPacket(packetNumber int, src string, dst string, lay
//Add a few fields and refer to them using our own "internalId"
res.Fields = append(res.Fields, wirego.DissectField{WiregoFieldId: FieldIdCustom1, Offset: 0, Length: 2})
res.Fields = append(res.Fields, wirego.DissectField{WiregoFieldId: FieldIdCustom2, Offset: 2, Length: 4})

//And add a field "Custom 1" with a sub-field "Custom 2"
subField := wirego.DissectField{WiregoFieldId: FieldIdCustom2, Offset: 8, Length: 2}
res.Fields = append(res.Fields, wirego.DissectField{WiregoFieldId: FieldIdCustom1, Offset: 0, Length: 2, SubFields: []wirego.DissectField{subField})

return &res
}
```
Expand Down
31 changes: 20 additions & 11 deletions wirego/example/wirego_example.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
package main

import (
"encoding/hex"
"fmt"

"github.com/quarkslab/wirego/wirego/wirego"
)

var fields []wirego.WiresharkField

// Define here enum identifiers, used to refer to a specific field
const (
FieldIdCustom1 wirego.FieldId = 1
FieldIdCustom2 wirego.FieldId = 2
FieldIdCustom1 wirego.FieldId = 1
FieldIdCustom2 wirego.FieldId = 2
FieldIdCustomWithSubFields wirego.FieldId = 3
)

// Since we implement the wirego.WiregoInterface we need some structure to hold it.
Expand All @@ -34,10 +32,6 @@ func init() {
// This function is called when the plugin is loaded.
func (WiregoExample) Setup() error {

//Setup our wireshark custom fields
fields = append(fields, wirego.WiresharkField{WiregoFieldId: FieldIdCustom1, Name: "Custom1", Filter: "wirego.custom01", ValueType: wirego.ValueTypeUInt8, DisplayMode: wirego.DisplayModeHexadecimal})
fields = append(fields, wirego.WiresharkField{WiregoFieldId: FieldIdCustom2, Name: "Custom2", Filter: "wirego.custom02", ValueType: wirego.ValueTypeUInt16, DisplayMode: wirego.DisplayModeDecimal})

return nil
}

Expand All @@ -54,6 +48,13 @@ func (WiregoExample) GetFilter() string {
// GetFields returns the list of fields descriptor that we may eventually return
// when dissecting a packet payload
func (WiregoExample) GetFields() []wirego.WiresharkField {
var fields []wirego.WiresharkField

//Setup our wireshark custom fields
fields = append(fields, wirego.WiresharkField{WiregoFieldId: FieldIdCustom1, Name: "Custom1", Filter: "wirego.custom01", ValueType: wirego.ValueTypeUInt8, DisplayMode: wirego.DisplayModeHexadecimal})
fields = append(fields, wirego.WiresharkField{WiregoFieldId: FieldIdCustom2, Name: "Custom2", Filter: "wirego.custom02", ValueType: wirego.ValueTypeUInt16, DisplayMode: wirego.DisplayModeDecimal})
fields = append(fields, wirego.WiresharkField{WiregoFieldId: FieldIdCustomWithSubFields, Name: "CustomWith Subs", Filter: "wirego.custom_subs", ValueType: wirego.ValueTypeUInt32, DisplayMode: wirego.DisplayModeHexadecimal})

return fields
}

Expand Down Expand Up @@ -100,7 +101,15 @@ func (WiregoExample) DissectPacket(packetNumber int, src string, dst string, lay
//Add a few fields and refer to them using our own "internalId"
res.Fields = append(res.Fields, wirego.DissectField{WiregoFieldId: FieldIdCustom1, Offset: 0, Length: 2})
res.Fields = append(res.Fields, wirego.DissectField{WiregoFieldId: FieldIdCustom2, Offset: 2, Length: 4})
fmt.Println(layer, " ", src, " to ", dst)
fmt.Println(hex.Dump(packet))

//Add a field with two sub field
subField1 := wirego.DissectField{WiregoFieldId: FieldIdCustom1, Offset: 6, Length: 2}
subField2 := wirego.DissectField{WiregoFieldId: FieldIdCustom1, Offset: 8, Length: 2}
field := wirego.DissectField{WiregoFieldId: FieldIdCustomWithSubFields, Offset: 6, Length: 4, SubFields: []wirego.DissectField{subField1, subField2}}
res.Fields = append(res.Fields, field)

//Dump packet contents
//fmt.Println(layer, " ", src, " to ", dst)
//fmt.Println(hex.Dump(packet))
return &res
}
61 changes: 61 additions & 0 deletions wirego/wirego/detection.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package wirego

import "C"

//export wirego_detect_int
func wirego_detect_int(matchValue *C.int, idx C.int) *C.char {
if wg.listener == nil {
return nil
}

cnt := 0
for _, f := range wg.pluginDetectionFilters {
if f.FilterType == DetectionFilterTypeInt {
if cnt == int(idx) {
*matchValue = C.int(f.ValueInt)
name := C.CString(f.Name)
return name
}
cnt++
}
}

*matchValue = 0
return nil
}

//export wirego_detect_string
func wirego_detect_string(matchValue **C.char, idx C.int) *C.char {
if wg.listener == nil {
return nil
}

cnt := 0
for _, f := range wg.pluginDetectionFilters {
if f.FilterType == DetectionFilterTypeString {
if cnt == int(idx) {

*matchValue = C.CString(f.ValueString)
name := C.CString(f.Name)
return name
}
cnt++
}
}

*matchValue = nil
return nil
}

//export wirego_detect_heuristic
func wirego_detect_heuristic(idx C.int) *C.char {
if wg.listener == nil {
return nil
}

if idx >= C.int(len(wg.pluginDetectionHeuristicsParents)) {
return nil
}

return C.CString(wg.pluginDetectionHeuristicsParents[idx])
}
90 changes: 90 additions & 0 deletions wirego/wirego/dissect.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package wirego

import "C"
import (
"fmt"
"unsafe"
)

type DissectResultFlattenEntry struct {
Protocol string
Info string
fields []DissectResultFlatten
}
type DissectResultFlatten struct {
parentIdx int
wiregoFieldId FieldId
offset int
length int
}

/*
Note: there's probably a way to return the complete DissectResult structure
to the C environment. At the end of the day, this would be super opaque so for now
let's use some dummy accessors and a result cache.
*/
//export wirego_dissect_packet
func wirego_dissect_packet(packetNumber C.int, src *C.char, dst *C.char, layer *C.char, packet *C.char, packetSize C.int) C.int {
if wg.listener == nil || wg.resultsCache == nil {
return C.int(-1)
}

if (src == nil) || (dst == nil) || (layer == nil) || (packet == nil) || packetSize == 0 {
return C.int(-1)
}

wg.lock.Lock()
_, found := wg.resultsCache[packetNumber]
wg.lock.Unlock()

if found {
return packetNumber
}

result := wg.listener.DissectPacket(int(packetNumber), C.GoString(src), C.GoString(dst), C.GoString(layer), C.GoBytes(unsafe.Pointer(packet), packetSize))

if result == nil {
return C.int(-1)
}

//Check results
for _, r := range result.Fields {
if C.int(r.Offset) >= packetSize {
fmt.Printf("Wirego plugin did return an invalid Offset : %d (packet size is %d bytes)\n", r.Offset, packetSize)
return C.int(-1)
}
if C.int(r.Offset+r.Length) > packetSize {
fmt.Printf("Wirego plugin did return an invalid Length : %d (offset is %d and packet size is %d bytes)\n", r.Length, r.Offset, packetSize)
return C.int(-1)
}
_, found := wg.wiregoFieldIds[int(r.WiregoFieldId)]
if !found {
fmt.Printf("Wirego plugin did return an invalid WiregoFieldId : %d\n", r.WiregoFieldId)
return C.int(-1)
}
}

//Flatten results to a simple list with parenIdx pointing to parent's entry
var flatten DissectResultFlattenEntry
flatten.Info = result.Info
flatten.Protocol = result.Protocol
for _, r := range result.Fields {
addFieldsRec(&flatten, -1, &r)
}

//Add to cache
pinner.Pin(&flatten)
wg.lock.Lock()
defer wg.lock.Unlock()
wg.resultsCache[packetNumber] = &flatten
return packetNumber
}

func addFieldsRec(flatten *DissectResultFlattenEntry, parentIdx int, field *DissectField) {
flatten.fields = append(flatten.fields, DissectResultFlatten{parentIdx: parentIdx, wiregoFieldId: field.WiregoFieldId, offset: field.Offset, length: field.Length})
newParentIdx := len(flatten.fields) - 1

for _, sub := range field.SubFields {
addFieldsRec(flatten, newParentIdx, &sub)
}
}
82 changes: 82 additions & 0 deletions wirego/wirego/plugininterface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package wirego

// WiregoInterface is implemented by the actual wirego plugin
type WiregoInterface interface {
GetName() string
GetFilter() string
Setup() error
GetFields() []WiresharkField
GetDetectionFilters() []DetectionFilter
GetDetectionHeuristicsParents() []string
DetectionHeuristic(packetNumber int, src string, dst string, stack string, packet []byte) bool
DissectPacket(packetNumber int, src string, dst string, stack string, packet []byte) *DissectResult
}

// Display modes
type DisplayMode int

const (
DisplayModeNone DisplayMode = 0x01
DisplayModeDecimal DisplayMode = 0x02
DisplayModeHexadecimal DisplayMode = 0x03
)

// Fields types
type ValueType int

const (
ValueTypeNone ValueType = 0x01
ValueTypeBool ValueType = 0x02

ValueTypeUInt8 ValueType = 0x03
ValueTypeInt8 ValueType = 0x04

ValueTypeUInt16 ValueType = 0x05
ValueTypeInt16 ValueType = 0x06

ValueTypeUInt32 ValueType = 0x07
ValueTypeInt32 ValueType = 0x08

ValueTypeCString ValueType = 0x09
ValueTypeString ValueType = 0x10
)

// A field descriptor, to be provided by the actual plugin
type FieldId int
type WiresharkField struct {
WiregoFieldId FieldId
Name string
Filter string
ValueType ValueType
DisplayMode DisplayMode
}

type DetectionFilterType int

const (
DetectionFilterTypeInt DetectionFilterType = iota
DetectionFilterTypeString DetectionFilterType = iota
)

// A detection filter provides a wireshark filter use to match traffic to be sent to the dissector
type DetectionFilter struct {
FilterType DetectionFilterType
Name string
ValueInt int
ValueString string
}

// A field, as returned by the dissector
type DissectField struct {
WiregoFieldId FieldId
Offset int
Length int
SubFields []DissectField
}

// A dissector result is a protocol name, an info string and a list of extracted fields
type DissectResult struct {
Protocol string
Info string
Fields []DissectField
}
2 changes: 1 addition & 1 deletion wirego/wirego/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ package wirego
// Wirego version, for API compability issues management
// Note: must match the ones defined in ../wirego_plugin/version.h
const WIREGO_VERSION_MAJOR = 1
const WIREGO_VERSION_MINOR = 1
const WIREGO_VERSION_MINOR = 2
Loading

0 comments on commit 7eb51b6

Please sign in to comment.