Skip to content

Commit

Permalink
Optimized the fingerprint packet sending logic
Browse files Browse the repository at this point in the history
  • Loading branch information
XinRoom committed Apr 26, 2022
1 parent 230ebc8 commit 652e293
Show file tree
Hide file tree
Showing 2 changed files with 189 additions and 84 deletions.
262 changes: 179 additions & 83 deletions core/port/fingerprint/fingerprint.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ package fingerprint
import (
"bytes"
"crypto/tls"
"errors"
"fmt"
"net"
"regexp"
"strconv"
"strings"
"sync"
"time"
)
Expand Down Expand Up @@ -40,104 +42,66 @@ var readBufPool = &sync.Pool{
// PortIdentify 端口识别
func PortIdentify(network string, ip net.IP, _port uint16) string {

// 指纹匹配函数
var err error
var isTls bool
var conn net.Conn
var connTls *tls.Conn
matchRule := func(network string, ip net.IP, _port uint16, serviceRule serviceRule) bool {
matchedRule := make(map[string]struct{})

address := fmt.Sprintf("%s:%d", ip, _port)
unknown := "unknown"
var matchStatus int

// 建立连接
if serviceRule.Tls {
// tls
connTls, err = tls.DialWithDialer(&net.Dialer{Timeout: 2 * time.Second}, network, address, &tls.Config{
InsecureSkipVerify: true,
})
if err != nil || connTls == nil {
return false
}
defer connTls.Close()
isTls = true
} else {
conn, err = net.DialTimeout(network, address, time.Second*2)
if err != nil || conn == nil {
return false
// 优先判断port可能的服务
if serviceNames, ok := portServiceOrder[_port]; ok {
for _, service := range serviceNames {
matchedRule[service] = struct{}{}
matchStatus = matchRule(network, ip, _port, serviceRules[service])
if matchStatus == 1 {
return service
} else if matchStatus == -1 {
return unknown
}
defer conn.Close()
}
}

// onlyRecv
{
var conn net.Conn
var err error
var n int
buf := readBufPool.Get().([]byte)
defer func() {
readBufPool.Put(buf)
}()

// 读函数
read := func() (int, error) {
if isTls {
connTls.SetReadDeadline(time.Now().Add(time.Second))
return connTls.Read(buf[:])
} else {
conn.SetReadDeadline(time.Now().Add(time.Second))
return conn.Read(buf[:])
}
}

data := []byte("")
// 逐个判断
for _, rule := range serviceRule.DataGroup {
if rule.Data != nil {
data = rule.Data
address := fmt.Sprintf("%s:%d", ip, _port)
for _, service := range onlyRecv {
_, ok := matchedRule[service]
if ok {
continue
}
data = bytes.Replace(rule.Data, []byte("{IP}"), []byte(ip.String()), -1)
data = bytes.Replace(data, []byte("{PORT}"), []byte(strconv.Itoa(int(_port))), -1)
if rule.Action == ActionSend {
if isTls {
connTls.SetWriteDeadline(time.Now().Add(time.Second))
_, err = connTls.Write(data)
} else {
conn.SetWriteDeadline(time.Now().Add(time.Second))
_, err = conn.Write(data)
}
if conn == nil {
conn, err = net.DialTimeout(network, address, time.Second*2)
if err != nil {
// 出错就退出
return false
}
} else {
var n int
n, err = read()
// 出错就退出
if err != nil || n == 0 {
return false
}
// 包含数据就正确
if rule.Regexps != nil {
for _, _regex := range rule.Regexps {
if _regex.Match(buf[:n]) {
return true
}
if strings.HasSuffix(err.Error(), "i/o timeout") {
return unknown
}
continue
}
if bytes.Compare(data, []byte("")) != 0 && bytes.Contains(buf[:n], data) {
return true
n, err = read(conn, buf)
conn.Close()
// read出错就退出
if err != nil {
if strings.HasSuffix(err.Error(), "i/o timeout") {
break
}
continue
}
}
}

return false
}

matchedRule := make(map[string]struct{})

// 优先判断port可能的服务
if serviceNames, ok := portServiceOrder[_port]; ok {
for _, serviceName := range serviceNames {
matchedRule[serviceName] = struct{}{}
if matchRule(network, ip, _port, serviceRules[serviceName]) {
return serviceName
matchStatus = matchRuleWhithBuf(buf[:n], ip, _port, serviceRules[service])
if matchStatus == 1 {
return service
}
}
for _, service := range onlyRecv {
matchedRule[service] = struct{}{}
}
}

// 优先判断Top服务
Expand All @@ -147,8 +111,11 @@ func PortIdentify(network string, ip net.IP, _port uint16) string {
continue
}
matchedRule[service] = struct{}{}
if matchRule(network, ip, _port, serviceRules[service]) {
matchStatus = matchRule(network, ip, _port, serviceRules[service])
if matchStatus == 1 {
return service
} else if matchStatus == -1 {
return unknown
}
}

Expand All @@ -158,10 +125,139 @@ func PortIdentify(network string, ip net.IP, _port uint16) string {
if ok {
continue
}
if matchRule(network, ip, _port, rule) {
matchStatus = matchRule(network, ip, _port, rule)
if matchStatus == 1 {
return service
} else if matchStatus == -1 {
return unknown
}
}

return "unknown"
return unknown
}

// 指纹匹配函数
func matchRuleWhithBuf(buf, ip net.IP, _port uint16, serviceRule serviceRule) int {
data := []byte("")
// 逐个判断
for _, rule := range serviceRule.DataGroup {
if rule.Data != nil {
data = bytes.Replace(rule.Data, []byte("{IP}"), []byte(ip.String()), -1)
data = bytes.Replace(data, []byte("{PORT}"), []byte(strconv.Itoa(int(_port))), -1)
}
// 包含数据就正确
if rule.Regexps != nil {
for _, _regex := range rule.Regexps {
if _regex.Match(buf) {
return 1
}
}
}
if bytes.Compare(data, []byte("")) != 0 && bytes.Contains(buf, data) {
return 1
}
}
return 0
}

// 指纹匹配函数
func matchRule(network string, ip net.IP, _port uint16, serviceRule serviceRule) int {
var err error
var isTls bool
var conn net.Conn
var connTls *tls.Conn

address := fmt.Sprintf("%s:%d", ip, _port)

// 建立连接
if serviceRule.Tls {
// tls
connTls, err = tls.DialWithDialer(&net.Dialer{Timeout: 2 * time.Second}, network, address, &tls.Config{
InsecureSkipVerify: true,
})
if err != nil {
if strings.HasSuffix(err.Error(), "i/o timeout") {
return -1
}
return 0
}
defer connTls.Close()
isTls = true
} else {
conn, err = net.DialTimeout(network, address, time.Second*2)
if err != nil {
if strings.HasSuffix(err.Error(), "i/o timeout") {
return -1
}
return 0
}
defer conn.Close()
}

buf := readBufPool.Get().([]byte)
defer func() {
readBufPool.Put(buf)
}()

data := []byte("")
// 逐个判断
for _, rule := range serviceRule.DataGroup {
if rule.Data != nil {
data = bytes.Replace(rule.Data, []byte("{IP}"), []byte(ip.String()), -1)
data = bytes.Replace(data, []byte("{PORT}"), []byte(strconv.Itoa(int(_port))), -1)
}

if rule.Action == ActionSend {
if isTls {
connTls.SetWriteDeadline(time.Now().Add(time.Second))
_, err = connTls.Write(data)
} else {
conn.SetWriteDeadline(time.Now().Add(time.Second))
_, err = conn.Write(data)
}
if err != nil {
// 出错就退出
if strings.HasSuffix(err.Error(), "i/o timeout") {
return -1
}
return 0
}
} else {
var n int
if isTls {
n, err = read(connTls, buf)
} else {
n, err = read(conn, buf)
}
// 出错就退出
if err != nil || n == 0 {
return 0
}
// 包含数据就正确
if rule.Regexps != nil {
for _, _regex := range rule.Regexps {
if _regex.Match(buf[:n]) {
return 1
}
}
}
if bytes.Compare(data, []byte("")) != 0 && bytes.Contains(buf[:n], data) {
return 1
}
}
}

return 0
}

func read(conn interface{}, buf []byte) (int, error) {
switch conn.(type) {
case net.Conn:
conn.(net.Conn).SetReadDeadline(time.Now().Add(time.Second))
return conn.(net.Conn).Read(buf[:])
case *tls.Conn:
conn.(*tls.Conn).SetReadDeadline(time.Now().Add(time.Second))
return conn.(*tls.Conn).Read(buf[:])
}
return 0, errors.New("unknown type")
}
11 changes: 10 additions & 1 deletion core/port/fingerprint/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import "regexp"

// Available variables: {PORT},{IP}

var serviceOrder = []string{"http", "https"}
var serviceOrder = []string{"http", "https", "ssh", "redis", "mysql"}

var onlyRecv []string

var portServiceOrder = map[uint16][]string{
21: {"ftp"},
Expand Down Expand Up @@ -261,4 +263,11 @@ func init() {
},
},
}

// onlyRecv
for k, m := range serviceRules {
if len(m.DataGroup) == 1 {
onlyRecv = append(onlyRecv, k)
}
}
}

0 comments on commit 652e293

Please sign in to comment.