bettercap/network/hid_device.go
2019-06-30 22:22:36 +02:00

220 lines
4.4 KiB
Go

package network
import (
"encoding/hex"
"encoding/json"
"fmt"
"sort"
"strings"
"sync"
"time"
"github.com/evilsocket/islazy/str"
)
type HIDType int
const (
HIDTypeUnknown HIDType = 0
HIDTypeLogitech HIDType = 1
HIDTypeAmazon HIDType = 2
HIDTypeMicrosoft HIDType = 3
HIDTypeDell HIDType = 4
)
func (t HIDType) String() string {
switch t {
case HIDTypeLogitech:
return "Logitech"
case HIDTypeAmazon:
return "Amazon"
case HIDTypeMicrosoft:
return "Microsoft"
case HIDTypeDell:
return "Dell"
}
return ""
}
type HIDPayload []byte
type HIDDevice struct {
sync.Mutex
LastSeen time.Time
Type HIDType
Alias string
Address string
RawAddress []byte
channels map[int]bool
payloads []HIDPayload
payloadsSz uint64
}
type hidDeviceJSON struct {
LastSeen time.Time `json:"last_seen"`
Type string `json:"type"`
Address string `json:"address"`
Alias string `json:"alias"`
Channels []string `json:"channels"`
Payloads []string `json:"payloads"`
PayloadsSize uint64 `json:"payloads_size"`
}
func NormalizeHIDAddress(address string) string {
parts := strings.Split(address, ":")
for i, p := range parts {
if len(p) < 2 {
parts[i] = "0" + p
}
}
return strings.ToLower(strings.Join(parts, ":"))
}
func HIDAddress(address []byte) string {
octects := []string{}
for _, b := range address {
octects = append(octects, fmt.Sprintf("%02x", b))
}
return strings.ToLower(strings.Join(octects, ":"))
}
func NewHIDDevice(address []byte, channel int, payload []byte) *HIDDevice {
dev := &HIDDevice{
LastSeen: time.Now(),
Type: HIDTypeUnknown,
RawAddress: address,
Address: HIDAddress(address),
channels: make(map[int]bool),
payloads: make([]HIDPayload, 0),
payloadsSz: 0,
}
dev.AddChannel(channel)
dev.AddPayload(payload)
return dev
}
func (dev *HIDDevice) MarshalJSON() ([]byte, error) {
dev.Lock()
defer dev.Unlock()
doc := hidDeviceJSON{
LastSeen: dev.LastSeen,
Type: dev.Type.String(),
Address: dev.Address,
Alias: dev.Alias,
Channels: dev.channelsListUnlocked(),
Payloads: make([]string, 0),
PayloadsSize: dev.payloadsSz,
}
// get the latest 50 payloads
for i := len(dev.payloads) - 1; i >= 0; i-- {
data := str.Trim(hex.Dump(dev.payloads[i]))
doc.Payloads = append([]string{data}, doc.Payloads...)
if len(doc.Payloads) == 50 {
break
}
}
return json.Marshal(doc)
}
func (dev *HIDDevice) AddChannel(ch int) {
dev.Lock()
defer dev.Unlock()
dev.channels[ch] = true
}
func (dev *HIDDevice) channelsListUnlocked() []string {
chans := []string{}
for ch := range dev.channels {
chans = append(chans, fmt.Sprintf("%d", ch))
}
sort.Strings(chans)
return chans
}
func (dev *HIDDevice) ChannelsList() []string {
dev.Lock()
defer dev.Unlock()
return dev.channelsListUnlocked()
}
func (dev *HIDDevice) Channels() string {
return strings.Join(dev.ChannelsList(), ",")
}
// credits to https://github.com/insecurityofthings/jackit/tree/master/jackit/plugins
func (dev *HIDDevice) onEventFrame(p []byte, sz int) {
// return if type has been already determined
if dev.Type != HIDTypeUnknown {
return
}
if sz == 6 {
dev.Type = HIDTypeAmazon
return
}
if sz == 10 && p[0] == 0x00 && p[1] == 0xc2 {
dev.Type = HIDTypeLogitech // mouse movement
return
} else if sz == 22 && p[0] == 0x00 && p[1] == 0xd3 {
dev.Type = HIDTypeLogitech // keystroke
return
} else if sz == 5 && p[0] == 0x00 && p[1] == 0x40 {
dev.Type = HIDTypeLogitech // keepalive
return
// TODO: review this condition
} else if sz == 10 && p[0] == 0x00 { //&& p[0] == 0x4f {
dev.Type = HIDTypeLogitech // sleep timer
return
}
if sz == 19 && (p[0] == 0x08 || p[0] == 0x0c) && p[6] == 0x40 {
dev.Type = HIDTypeMicrosoft
return
}
// TODO: Dell
}
func (dev *HIDDevice) AddPayload(payload []byte) {
dev.Lock()
defer dev.Unlock()
sz := len(payload)
if payload != nil && sz > 0 {
dev.payloads = append(dev.payloads, payload)
dev.payloadsSz += uint64(sz)
dev.onEventFrame(payload, sz)
}
}
func (dev *HIDDevice) EachPayload(cb func([]byte) bool) {
dev.Lock()
defer dev.Unlock()
for _, payload := range dev.payloads {
if done := cb(payload); done {
break
}
}
}
func (dev *HIDDevice) NumPayloads() int {
dev.Lock()
defer dev.Unlock()
return len(dev.payloads)
}
func (dev *HIDDevice) PayloadsSize() uint64 {
dev.Lock()
defer dev.Unlock()
return dev.payloadsSz
}