fscan/Core/PortInfo.go
2025-02-07 12:08:14 +08:00

477 lines
12 KiB
Go

package Core
import (
"fmt"
"github.com/shadow1ng/fscan/Common"
"io"
"net"
"strings"
"time"
)
// ServiceInfo 定义服务识别的结果信息
type ServiceInfo struct {
Name string // 服务名称,如 http、ssh 等
Banner string // 服务返回的横幅信息
Version string // 服务版本号
Extras map[string]string // 其他额外信息,如操作系统、产品名等
}
// Result 定义单次探测的结果
type Result struct {
Service Service // 识别出的服务信息
Banner string // 服务横幅
Extras map[string]string // 额外信息
Send []byte // 发送的探测数据
Recv []byte // 接收到的响应数据
}
// Service 定义服务的基本信息
type Service struct {
Name string // 服务名称
Extras map[string]string // 服务的额外属性
}
// Info 定义单个端口探测的上下文信息
type Info struct {
Address string // 目标IP地址
Port int // 目标端口
Conn net.Conn // 网络连接
Result Result // 探测结果
Found bool // 是否成功识别服务
}
// PortInfoScanner 定义端口服务识别器
type PortInfoScanner struct {
Address string // 目标IP地址
Port int // 目标端口
Conn net.Conn // 网络连接
Timeout time.Duration // 超时时间
info *Info // 探测上下文
}
// 预定义的基础探测器
var (
null = new(Probe) // 空探测器,用于基本协议识别
common = new(Probe) // 通用探测器,用于常见服务识别
)
// NewPortInfoScanner 创建新的端口服务识别器实例
func NewPortInfoScanner(addr string, port int, conn net.Conn, timeout time.Duration) *PortInfoScanner {
return &PortInfoScanner{
Address: addr,
Port: port,
Conn: conn,
Timeout: timeout,
info: &Info{
Address: addr,
Port: port,
Conn: conn,
Result: Result{
Service: Service{},
},
},
}
}
// Identify 执行服务识别,返回识别结果
func (s *PortInfoScanner) Identify() (*ServiceInfo, error) {
Common.LogDebug(fmt.Sprintf("开始识别服务 %s:%d", s.Address, s.Port))
s.info.PortInfo()
// 构造返回结果
serviceInfo := &ServiceInfo{
Name: s.info.Result.Service.Name,
Banner: s.info.Result.Banner,
Version: s.info.Result.Service.Extras["version"],
Extras: make(map[string]string),
}
// 复制额外信息
for k, v := range s.info.Result.Service.Extras {
serviceInfo.Extras[k] = v
}
Common.LogDebug(fmt.Sprintf("服务识别完成 %s:%d => %s", s.Address, s.Port, serviceInfo.Name))
return serviceInfo, nil
}
// PortInfo 执行端口服务识别的主要逻辑
func (i *Info) PortInfo() {
// 1. 首先尝试读取服务的初始响应
if response, err := i.Read(); err == nil && len(response) > 0 {
Common.LogDebug(fmt.Sprintf("收到初始响应: %d 字节", len(response)))
// 使用基础探测器检查响应
Common.LogDebug("尝试使用基础探测器(null/common)检查响应")
if i.tryProbes(response, []*Probe{null, common}) {
Common.LogDebug("基础探测器匹配成功")
return
}
Common.LogDebug("基础探测器未匹配")
} else if err != nil {
Common.LogDebug(fmt.Sprintf("读取初始响应失败: %v", err))
}
// 记录已使用的探测器,避免重复使用
usedProbes := make(map[string]struct{})
// 2. 尝试使用端口专用探测器
Common.LogDebug(fmt.Sprintf("尝试使用端口 %d 的专用探测器", i.Port))
if i.processPortMapProbes(usedProbes) {
Common.LogDebug("端口专用探测器匹配成功")
return
}
Common.LogDebug("端口专用探测器未匹配")
// 3. 使用默认探测器列表
Common.LogDebug("尝试使用默认探测器列表")
if i.processDefaultProbes(usedProbes) {
Common.LogDebug("默认探测器匹配成功")
return
}
Common.LogDebug("默认探测器未匹配")
// 4. 如果所有探测都失败,标记为未知服务
if strings.TrimSpace(i.Result.Service.Name) == "" {
Common.LogDebug("未识别出服务,标记为 unknown")
i.Result.Service.Name = "unknown"
}
}
// tryProbes 尝试使用指定的探测器列表检查响应
func (i *Info) tryProbes(response []byte, probes []*Probe) bool {
for _, probe := range probes {
Common.LogDebug(fmt.Sprintf("尝试探测器: %s", probe.Name))
i.GetInfo(response, probe)
if i.Found {
Common.LogDebug(fmt.Sprintf("探测器 %s 匹配成功", probe.Name))
return true
}
}
return false
}
// processPortMapProbes 处理端口映射中的专用探测器
func (i *Info) processPortMapProbes(usedProbes map[string]struct{}) bool {
// 检查是否存在端口专用探测器
if len(Common.PortMap[i.Port]) == 0 {
Common.LogDebug(fmt.Sprintf("端口 %d 没有专用探测器", i.Port))
return false
}
// 遍历端口专用探测器
for _, name := range Common.PortMap[i.Port] {
Common.LogDebug(fmt.Sprintf("尝试端口专用探测器: %s", name))
usedProbes[name] = struct{}{}
probe := v.ProbesMapKName[name]
// 解码探测数据
probeData, err := DecodeData(probe.Data)
if err != nil || len(probeData) == 0 {
Common.LogDebug(fmt.Sprintf("探测器 %s 数据解码失败", name))
continue
}
// 发送探测数据并获取响应
Common.LogDebug(fmt.Sprintf("发送探测数据: %d 字节", len(probeData)))
if response := i.Connect(probeData); len(response) > 0 {
Common.LogDebug(fmt.Sprintf("收到响应: %d 字节", len(response)))
// 使用当前探测器检查响应
i.GetInfo(response, &probe)
if i.Found {
return true
}
// 根据探测器类型进行额外检查
switch name {
case "GenericLines":
if i.tryProbes(response, []*Probe{null}) {
return true
}
case "NULL":
continue
default:
if i.tryProbes(response, []*Probe{common}) {
return true
}
}
}
}
return false
}
// processDefaultProbes 处理默认探测器列表
func (i *Info) processDefaultProbes(usedProbes map[string]struct{}) bool {
failCount := 0
const maxFailures = 10 // 最大失败次数
// 遍历默认探测器列表
for _, name := range Common.DefaultMap {
// 跳过已使用的探测器
if _, used := usedProbes[name]; used {
continue
}
probe := v.ProbesMapKName[name]
probeData, err := DecodeData(probe.Data)
if err != nil || len(probeData) == 0 {
continue
}
// 发送探测数据并获取响应
response := i.Connect(probeData)
if len(response) == 0 {
failCount++
if failCount > maxFailures {
return false
}
continue
}
// 使用当前探测器检查响应
i.GetInfo(response, &probe)
if i.Found {
return true
}
// 根据探测器类型进行额外检查
switch name {
case "GenericLines":
if i.tryProbes(response, []*Probe{null}) {
return true
}
case "NULL":
continue
default:
if i.tryProbes(response, []*Probe{common}) {
return true
}
}
// 尝试使用端口映射中的其他探测器
if len(Common.PortMap[i.Port]) > 0 {
for _, mappedName := range Common.PortMap[i.Port] {
usedProbes[mappedName] = struct{}{}
mappedProbe := v.ProbesMapKName[mappedName]
i.GetInfo(response, &mappedProbe)
if i.Found {
return true
}
}
}
}
return false
}
// GetInfo 分析响应数据并提取服务信息
func (i *Info) GetInfo(response []byte, probe *Probe) {
Common.LogDebug(fmt.Sprintf("开始分析响应数据,长度: %d", len(response)))
// 响应数据有效性检查
if len(response) <= 0 {
Common.LogDebug("响应数据为空")
return
}
result := &i.Result
var (
softMatch Match
softFound bool
)
// 处理主要匹配规则
Common.LogDebug(fmt.Sprintf("处理探测器 %s 的主要匹配规则", probe.Name))
if matched, match := i.processMatches(response, probe.Matchs); matched {
Common.LogDebug("找到硬匹配")
return
} else if match != nil {
Common.LogDebug("找到软匹配")
softFound = true
softMatch = *match
}
// 处理回退匹配规则
if probe.Fallback != "" {
Common.LogDebug(fmt.Sprintf("尝试回退匹配: %s", probe.Fallback))
if fbProbe, ok := v.ProbesMapKName[probe.Fallback]; ok {
if matched, match := i.processMatches(response, fbProbe.Matchs); matched {
Common.LogDebug("回退匹配成功")
return
} else if match != nil {
Common.LogDebug("找到回退软匹配")
softFound = true
softMatch = *match
}
}
}
// 处理未找到匹配的情况
if !i.Found {
Common.LogDebug("未找到硬匹配,处理未匹配情况")
i.handleNoMatch(response, result, softFound, softMatch)
}
}
// processMatches 处理匹配规则集
func (i *Info) processMatches(response []byte, matches *[]Match) (bool, *Match) {
Common.LogDebug(fmt.Sprintf("开始处理匹配规则,共 %d 条", len(*matches)))
var softMatch *Match
for _, match := range *matches {
if !match.MatchPattern(response) {
continue
}
if !match.IsSoft {
Common.LogDebug(fmt.Sprintf("找到硬匹配: %s", match.Service))
i.handleHardMatch(response, &match)
return true, nil
} else if softMatch == nil {
Common.LogDebug(fmt.Sprintf("找到软匹配: %s", match.Service))
tmpMatch := match
softMatch = &tmpMatch
}
}
return false, softMatch
}
// handleHardMatch 处理硬匹配结果
func (i *Info) handleHardMatch(response []byte, match *Match) {
Common.LogDebug(fmt.Sprintf("处理硬匹配结果: %s", match.Service))
result := &i.Result
extras := match.ParseVersionInfo(response)
extrasMap := extras.ToMap()
result.Service.Name = match.Service
result.Extras = extrasMap
result.Banner = trimBanner(response)
result.Service.Extras = extrasMap
// 特殊处理 microsoft-ds 服务
if result.Service.Name == "microsoft-ds" {
Common.LogDebug("特殊处理 microsoft-ds 服务")
result.Service.Extras["hostname"] = result.Banner
}
i.Found = true
Common.LogDebug(fmt.Sprintf("服务识别结果: %s, Banner: %s", result.Service.Name, result.Banner))
}
// handleNoMatch 处理未找到匹配的情况
func (i *Info) handleNoMatch(response []byte, result *Result, softFound bool, softMatch Match) {
Common.LogDebug("处理未匹配情况")
result.Banner = trimBanner(response)
if !softFound {
// 尝试识别 HTTP 服务
if strings.Contains(result.Banner, "HTTP/") ||
strings.Contains(result.Banner, "html") {
Common.LogDebug("识别为HTTP服务")
result.Service.Name = "http"
} else {
Common.LogDebug("未知服务")
result.Service.Name = "unknown"
}
} else {
Common.LogDebug("使用软匹配结果")
extras := softMatch.ParseVersionInfo(response)
result.Service.Extras = extras.ToMap()
result.Service.Name = softMatch.Service
i.Found = true
Common.LogDebug(fmt.Sprintf("软匹配服务: %s", result.Service.Name))
}
}
// Connect 发送数据并获取响应
func (i *Info) Connect(msg []byte) []byte {
i.Write(msg)
reply, _ := i.Read()
return reply
}
const WrTimeout = 5 // 默认读写超时时间(秒)
// Write 写入数据到连接
func (i *Info) Write(msg []byte) error {
if i.Conn == nil {
return nil
}
// 设置写入超时
i.Conn.SetWriteDeadline(time.Now().Add(time.Second * time.Duration(WrTimeout)))
// 写入数据
_, err := i.Conn.Write(msg)
if err != nil && strings.Contains(err.Error(), "close") {
i.Conn.Close()
// 连接关闭时重试
i.Conn, err = net.DialTimeout("tcp4", fmt.Sprintf("%s:%d", i.Address, i.Port), time.Duration(6)*time.Second)
if err == nil {
i.Conn.SetWriteDeadline(time.Now().Add(time.Second * time.Duration(WrTimeout)))
_, err = i.Conn.Write(msg)
}
}
// 记录发送的数据
if err == nil {
i.Result.Send = msg
}
return err
}
// Read 从连接读取响应
func (i *Info) Read() ([]byte, error) {
if i.Conn == nil {
return nil, nil
}
// 设置读取超时
i.Conn.SetReadDeadline(time.Now().Add(time.Second * time.Duration(WrTimeout)))
// 读取数据
result, err := readFromConn(i.Conn)
if err != nil && strings.Contains(err.Error(), "close") {
return result, err
}
// 记录接收到的数据
if len(result) > 0 {
i.Result.Recv = result
}
return result, err
}
// readFromConn 从连接读取数据的辅助函数
func readFromConn(conn net.Conn) ([]byte, error) {
size := 2 * 1024 // 读取缓冲区大小
var result []byte
for {
buf := make([]byte, size)
count, err := conn.Read(buf)
if count > 0 {
result = append(result, buf[:count]...)
}
if err != nil {
if len(result) > 0 {
return result, nil
}
if err == io.EOF {
return result, nil
}
return result, err
}
if count < size {
return result, nil
}
}
}