package Plugins import ( "bytes" "encoding/hex" "fmt" "net" "strings" "time" "github.com/shadow1ng/fscan/common" "github.com/shadow1ng/fscan/common/output" ) // SMBInfo 主函数 - 基于最原始参考代码实现 func SMBInfo(info *common.HostInfo) error { if info.Ports != "445" && info.Ports != "139" { return fmt.Errorf("SMBInfo插件仅支持139和445端口") } realhost := fmt.Sprintf("%s:%s", info.Host, info.Ports) conn, err := net.DialTimeout("tcp", realhost, time.Duration(common.Timeout)*time.Second) if err != nil { return fmt.Errorf("连接失败: %v", err) } defer conn.Close() // 发送SMBv1第一个协商包 _, err = conn.Write(SMBInfoNegotiateSMBv1Data1) if err != nil { return fmt.Errorf("发送SMBv1协商包失败: %v", err) } r1, err := ReadBytes(conn) if err != nil { common.LogDebug(fmt.Sprintf("读取SMBv1协商响应失败: %v", err)) } var result string // ff534d42 SMBv1的标示 // fe534d42 SMBv2的标示 // 先发送探测SMBv1的payload,不支持的SMBv1的时候返回为空,然后尝试发送SMBv2的探测数据包 if len(r1) > 0 { // SMBv1 路径 result = handleSMBv1(conn, info) } else { // SMBv2 路径 result = handleSMBv2(realhost, info) } // 显示和保存结果 if result != "" { displaySMBInfo(info, result, len(r1) > 0) saveSMBInfo(info, result) } else { // 即使没有详细信息也显示基本连接信息 displayBasicSMBInfo(info) } return nil } // handleSMBv1 处理SMBv1协议 func handleSMBv1(conn net.Conn, info *common.HostInfo) string { // 发送第二个SMBv1包 _, err := conn.Write(SMBInfoNegotiateSMBv1Data2) if err != nil { common.LogDebug(fmt.Sprintf("发送SMBv1 Session Setup失败: %v", err)) return "" } ret, err := ReadBytes(conn) if err != nil || len(ret) < 45 { common.LogDebug(fmt.Sprintf("读取SMBv1 Session Setup响应失败: %v", err)) return "" } // 解析blob信息 blob_length := uint16(bytesToUint16(ret[43:45])) blob_count := uint16(bytesToUint16(ret[45:47])) if int(blob_count) > len(ret) { common.LogDebug("blob_count超出数据范围") return "" } gss_native := ret[47:] off_ntlm := bytes.Index(gss_native, []byte("NTLMSSP")) if off_ntlm == -1 { common.LogDebug("未找到NTLMSSP数据") return "" } // 提取native OS和LM信息 native := gss_native[int(blob_length):blob_count] ss := strings.Split(string(native), "\x00\x00") var nativeOS, nativeLM string if len(ss) > 0 { nativeOS = trimName(ss[0]) } if len(ss) > 1 { nativeLM = trimName(ss[1]) } // 解析NTLM信息 bs := gss_native[off_ntlm:blob_length] ntlmInfo := parseNTLMChallenge(bs) // 组合结果 result := ntlmInfo if nativeOS != "" { result += fmt.Sprintf("NativeOS: %s\n", nativeOS) } if nativeLM != "" { result += fmt.Sprintf("NativeLM: %s\n", nativeLM) } return result } // handleSMBv2 处理SMBv2协议 func handleSMBv2(realhost string, info *common.HostInfo) string { conn2, err := net.DialTimeout("tcp", realhost, time.Duration(common.Timeout)*time.Second) if err != nil { common.LogDebug(fmt.Sprintf("SMBv2连接失败: %v", err)) return "" } defer conn2.Close() // 发送SMBv2第一个协商包 _, err = conn2.Write(SMBInfoNegotiateSMBv2Data1) if err != nil { common.LogDebug(fmt.Sprintf("发送SMBv2协商包失败: %v", err)) return "" } r2, err := ReadBytes(conn2) if err != nil { common.LogDebug(fmt.Sprintf("读取SMBv2协商响应失败: %v", err)) return "" } // 根据响应构建NTLM数据包 var ntlmSSPNegotiatev2Data []byte if len(r2) > 70 && hex.EncodeToString(r2[70:71]) == "03" { flags := []byte{0x15, 0x82, 0x08, 0xa0} ntlmSSPNegotiatev2Data = getNTLMSSPNegotiateData(flags) } else { flags := []byte{0x05, 0x80, 0x08, 0xa0} ntlmSSPNegotiatev2Data = getNTLMSSPNegotiateData(flags) } // 发送第二个SMBv2包 _, err = conn2.Write(SMBInfoNegotiateSMBv2Data2) if err != nil { common.LogDebug(fmt.Sprintf("发送SMBv2第二包失败: %v", err)) return "" } _, err = ReadBytes(conn2) if err != nil { common.LogDebug(fmt.Sprintf("读取SMBv2第二包响应失败: %v", err)) return "" } // 发送NTLM协商包 _, err = conn2.Write(ntlmSSPNegotiatev2Data) if err != nil { common.LogDebug(fmt.Sprintf("发送SMBv2 NTLM包失败: %v", err)) return "" } ret, err := ReadBytes(conn2) if err != nil { common.LogDebug(fmt.Sprintf("读取SMBv2 NTLM响应失败: %v", err)) return "" } ntlmOff := bytes.Index(ret, []byte("NTLMSSP")) if ntlmOff == -1 { common.LogDebug("SMBv2响应中未找到NTLMSSP数据") return "" } return parseNTLMChallenge(ret[ntlmOff:]) } // 原始参考代码中的数据包定义 var SMBInfoNegotiateSMBv1Data1 = []byte{ 0x00, 0x00, 0x00, 0x85, 0xFF, 0x53, 0x4D, 0x42, 0x72, 0x00, 0x00, 0x00, 0x00, 0x18, 0x53, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0x02, 0x50, 0x43, 0x20, 0x4E, 0x45, 0x54, 0x57, 0x4F, 0x52, 0x4B, 0x20, 0x50, 0x52, 0x4F, 0x47, 0x52, 0x41, 0x4D, 0x20, 0x31, 0x2E, 0x30, 0x00, 0x02, 0x4C, 0x41, 0x4E, 0x4D, 0x41, 0x4E, 0x31, 0x2E, 0x30, 0x00, 0x02, 0x57, 0x69, 0x6E, 0x64, 0x6F, 0x77, 0x73, 0x20, 0x66, 0x6F, 0x72, 0x20, 0x57, 0x6F, 0x72, 0x6B, 0x67, 0x72, 0x6F, 0x75, 0x70, 0x73, 0x20, 0x33, 0x2E, 0x31, 0x61, 0x00, 0x02, 0x4C, 0x4D, 0x31, 0x2E, 0x32, 0x58, 0x30, 0x30, 0x32, 0x00, 0x02, 0x4C, 0x41, 0x4E, 0x4D, 0x41, 0x4E, 0x32, 0x2E, 0x31, 0x00, 0x02, 0x4E, 0x54, 0x20, 0x4C, 0x4D, 0x20, 0x30, 0x2E, 0x31, 0x32, 0x00, } var SMBInfoNegotiateSMBv1Data2 = []byte{ 0x00, 0x00, 0x01, 0x0A, 0xFF, 0x53, 0x4D, 0x42, 0x73, 0x00, 0x00, 0x00, 0x00, 0x18, 0x07, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFE, 0x00, 0x00, 0x40, 0x00, 0x0C, 0xFF, 0x00, 0x0A, 0x01, 0x04, 0x41, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD4, 0x00, 0x00, 0xA0, 0xCF, 0x00, 0x60, 0x48, 0x06, 0x06, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x02, 0xA0, 0x3E, 0x30, 0x3C, 0xA0, 0x0E, 0x30, 0x0C, 0x06, 0x0A, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x02, 0x0A, 0xA2, 0x2A, 0x04, 0x28, 0x4E, 0x54, 0x4C, 0x4D, 0x53, 0x53, 0x50, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x82, 0x08, 0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x02, 0xCE, 0x0E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x57, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x64, 0x00, 0x6F, 0x00, 0x77, 0x00, 0x73, 0x00, 0x20, 0x00, 0x53, 0x00, 0x65, 0x00, 0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x20, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x33, 0x00, 0x20, 0x00, 0x33, 0x00, 0x37, 0x00, 0x39, 0x00, 0x30, 0x00, 0x20, 0x00, 0x53, 0x00, 0x65, 0x00, 0x72, 0x00, 0x76, 0x00, 0x69, 0x00, 0x63, 0x00, 0x65, 0x00, 0x20, 0x00, 0x50, 0x00, 0x61, 0x00, 0x63, 0x00, 0x6B, 0x00, 0x20, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x64, 0x00, 0x6F, 0x00, 0x77, 0x00, 0x73, 0x00, 0x20, 0x00, 0x53, 0x00, 0x65, 0x00, 0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x20, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x33, 0x00, 0x20, 0x00, 0x35, 0x00, 0x2E, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, } var SMBInfoNegotiateSMBv2Data1 = []byte{ 0x00, 0x00, 0x00, 0x45, 0xFF, 0x53, 0x4D, 0x42, 0x72, 0x00, 0x00, 0x00, 0x00, 0x18, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xAC, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x02, 0x4E, 0x54, 0x20, 0x4C, 0x4D, 0x20, 0x30, 0x2E, 0x31, 0x32, 0x00, 0x02, 0x53, 0x4D, 0x42, 0x20, 0x32, 0x2E, 0x30, 0x30, 0x32, 0x00, 0x02, 0x53, 0x4D, 0x42, 0x20, 0x32, 0x2E, 0x3F, 0x3F, 0x3F, 0x00, } var SMBInfoNegotiateSMBv2Data2 = []byte{ 0x00, 0x00, 0x00, 0x68, 0xFE, 0x53, 0x4D, 0x42, 0x40, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x02, } func getNTLMSSPNegotiateData(flags []byte) []byte { return []byte{ 0x00, 0x00, 0x00, 0x9A, 0xFE, 0x53, 0x4D, 0x42, 0x40, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x40, 0x06, 0x06, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x02, 0xA0, 0x36, 0x30, 0x34, 0xA0, 0x0E, 0x30, 0x0C, 0x06, 0x0A, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x02, 0x0A, 0xA2, 0x22, 0x04, 0x20, 0x4E, 0x54, 0x4C, 0x4D, 0x53, 0x53, 0x50, 0x00, 0x01, 0x00, 0x00, 0x00, flags[0], flags[1], flags[2], flags[3], 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, } } // 辅助函数 func bytesToUint16(b []byte) uint16 { return uint16(b[0]) | uint16(b[1])<<8 } func trimName(s string) string { return strings.Trim(strings.TrimSpace(s), "\x00") } // NTLM AV_PAIR类型常量 const ( MsvAvEOL = 0x0000 // End of list MsvAvNbComputerName = 0x0001 // NetBIOS computer name MsvAvNbDomainName = 0x0002 // NetBIOS domain name MsvAvDnsComputerName = 0x0003 // DNS computer name MsvAvDnsDomainName = 0x0004 // DNS domain name MsvAvDnsTreeName = 0x0005 // DNS forest name MsvAvFlags = 0x0006 // Server flags MsvAvTimestamp = 0x0007 // Server timestamp MsvAvSingleHost = 0x0008 // Single host data MsvAvTargetName = 0x0009 // Target name MsvAvChannelBindings = 0x000A // Channel bindings ) // parseNTLMChallenge 解析NTLM Type 2 (Challenge) 消息 func parseNTLMChallenge(data []byte) string { if len(data) < 32 { return "" } var result strings.Builder // 检查NTLM签名 "NTLMSSP\x00" if !bytes.Equal(data[0:8], []byte("NTLMSSP\x00")) { return "" } // 检查消息类型 (应该是 Type 2 = 0x00000002) if len(data) < 12 { return "" } messageType := bytesToUint32(data[8:12]) if messageType != 2 { common.LogDebug(fmt.Sprintf("非Type 2 NTLM消息, 类型: %d", messageType)) return "" } result.WriteString("Protocol: NTLM Type 2 (Challenge)\n") // 解析Target Name (偏移12-20, 8字节的Security Buffer) if len(data) >= 20 { targetLength := bytesToUint16(data[12:14]) targetOffset := bytesToUint32(data[16:20]) if targetLength > 0 && int(targetOffset) < len(data) && int(targetOffset+uint32(targetLength)) <= len(data) { targetName := parseUnicodeString(data[targetOffset:targetOffset+uint32(targetLength)]) if targetName != "" { result.WriteString(fmt.Sprintf("Target Name: %s\n", targetName)) } } } // 解析Flags (偏移20-24) if len(data) >= 24 { flags := bytesToUint32(data[20:24]) parseNTLMFlags(flags, &result) } // 解析Challenge (偏移24-32, 8字节) if len(data) >= 32 { challenge := data[24:32] result.WriteString(fmt.Sprintf("Server Challenge: %s\n", hex.EncodeToString(challenge))) } // 解析Target Info (AV_PAIR结构) - 偏移44开始的Security Buffer if len(data) >= 52 { targetInfoLength := bytesToUint16(data[40:42]) targetInfoOffset := bytesToUint32(data[44:48]) if targetInfoLength > 0 && int(targetInfoOffset) < len(data) && int(targetInfoOffset+uint32(targetInfoLength)) <= len(data) { targetInfoData := data[targetInfoOffset:targetInfoOffset+uint32(targetInfoLength)] parseTargetInfo(targetInfoData, &result) } } // 解析OS版本信息 (如果存在, 偏移48-56) if len(data) >= 56 { // 检查是否包含版本信息 (通过flags判断) if len(data) >= 24 { flags := bytesToUint32(data[20:24]) // NTLMSSP_NEGOTIATE_VERSION = 0x02000000 if flags&0x02000000 != 0 && len(data) >= 56 { parseOSVersion(data[48:56], &result) } } } return result.String() } // parseUnicodeString 解析UTF-16LE编码的字符串 func parseUnicodeString(data []byte) string { if len(data)%2 != 0 { return "" } var runes []rune for i := 0; i < len(data); i += 2 { if i+1 >= len(data) { break } // UTF-16LE: 低字节在前 r := uint16(data[i]) | uint16(data[i+1])<<8 if r == 0 { break } runes = append(runes, rune(r)) } return string(runes) } // parseNTLMFlags 解析NTLM标志位 func parseNTLMFlags(flags uint32, result *strings.Builder) { flagNames := map[uint32]string{ 0x00000001: "NEGOTIATE_UNICODE", 0x00000002: "NEGOTIATE_OEM", 0x00000004: "REQUEST_TARGET", 0x00000010: "NEGOTIATE_SIGN", 0x00000020: "NEGOTIATE_SEAL", 0x00000040: "NEGOTIATE_DATAGRAM", 0x00000080: "NEGOTIATE_LM_KEY", 0x00000200: "NEGOTIATE_NTLM", 0x00001000: "NEGOTIATE_DOMAIN_SUPPLIED", 0x00002000: "NEGOTIATE_WORKSTATION_SUPPLIED", 0x00004000: "NEGOTIATE_LOCAL_CALL", 0x00008000: "NEGOTIATE_ALWAYS_SIGN", 0x00010000: "TARGET_TYPE_DOMAIN", 0x00020000: "TARGET_TYPE_SERVER", 0x00040000: "TARGET_TYPE_SHARE", 0x00080000: "NEGOTIATE_EXTENDED_SESSIONSECURITY", 0x00100000: "NEGOTIATE_IDENTIFY", 0x02000000: "NEGOTIATE_VERSION", 0x20000000: "NEGOTIATE_128", 0x40000000: "NEGOTIATE_KEY_EXCH", 0x80000000: "NEGOTIATE_56", } var activeFlags []string for flag, name := range flagNames { if flags&flag != 0 { activeFlags = append(activeFlags, name) } } if len(activeFlags) > 0 { result.WriteString(fmt.Sprintf("NTLM Flags: %s\n", strings.Join(activeFlags, ", "))) } } // parseTargetInfo 解析NTLM Target Information (AV_PAIR结构) func parseTargetInfo(data []byte, result *strings.Builder) { offset := 0 for offset+4 <= len(data) { // 读取AV_PAIR结构: AvId (2字节) + AvLen (2字节) + Value (AvLen字节) avId := bytesToUint16(data[offset:offset+2]) avLen := bytesToUint16(data[offset+2:offset+4]) if avId == MsvAvEOL { break // 列表结束 } if offset+4+int(avLen) > len(data) { break // 数据不足 } value := data[offset+4:offset+4+int(avLen)] switch avId { case MsvAvNbComputerName: computerName := parseUnicodeString(value) if computerName != "" { result.WriteString(fmt.Sprintf("NetBIOS Computer Name: %s\n", computerName)) } case MsvAvNbDomainName: domainName := parseUnicodeString(value) if domainName != "" { result.WriteString(fmt.Sprintf("NetBIOS Domain Name: %s\n", domainName)) } case MsvAvDnsComputerName: dnsComputerName := parseUnicodeString(value) if dnsComputerName != "" { result.WriteString(fmt.Sprintf("DNS Computer Name: %s\n", dnsComputerName)) } case MsvAvDnsDomainName: dnsDomainName := parseUnicodeString(value) if dnsDomainName != "" { result.WriteString(fmt.Sprintf("DNS Domain Name: %s\n", dnsDomainName)) } case MsvAvDnsTreeName: forestName := parseUnicodeString(value) if forestName != "" { result.WriteString(fmt.Sprintf("DNS Forest Name: %s\n", forestName)) } case MsvAvFlags: if len(value) >= 4 { serverFlags := bytesToUint32(value[0:4]) parseServerFlags(serverFlags, result) } case MsvAvTimestamp: if len(value) >= 8 { timestamp := bytesToUint64(value[0:8]) // Windows FILETIME: 100纳秒间隔自1601年1月1日 if timestamp > 0 { // 转换为Unix时间戳 (简化版本) unixTime := int64((timestamp - 116444736000000000) / 10000000) if unixTime > 0 { t := time.Unix(unixTime, 0) result.WriteString(fmt.Sprintf("Server Timestamp: %s\n", t.Format(time.RFC3339))) } } } case MsvAvTargetName: targetName := parseUnicodeString(value) if targetName != "" { result.WriteString(fmt.Sprintf("Target SPN: %s\n", targetName)) } } offset += 4 + int(avLen) } } // parseServerFlags 解析服务器标志 func parseServerFlags(flags uint32, result *strings.Builder) { var serverFlags []string if flags&0x00000001 != 0 { serverFlags = append(serverFlags, "CONSTRAINED_AUTHENTICATION") } if flags&0x00000002 != 0 { serverFlags = append(serverFlags, "MIC_PROVIDED") } if flags&0x00000004 != 0 { serverFlags = append(serverFlags, "UNTRUSTED_SPN_SOURCE") } if len(serverFlags) > 0 { result.WriteString(fmt.Sprintf("Server Flags: %s\n", strings.Join(serverFlags, ", "))) } } // parseOSVersion 解析操作系统版本信息 func parseOSVersion(data []byte, result *strings.Builder) { if len(data) < 8 { return } majorVersion := data[0] minorVersion := data[1] buildNumber := bytesToUint16(data[2:4]) reserved := bytesToUint32(data[4:8]) // Windows版本映射 var osName string switch { case majorVersion == 10 && minorVersion == 0: if buildNumber >= 22000 { osName = "Windows 11" } else { osName = "Windows 10" } case majorVersion == 6 && minorVersion == 3: osName = "Windows 8.1 / Server 2012 R2" case majorVersion == 6 && minorVersion == 2: osName = "Windows 8 / Server 2012" case majorVersion == 6 && minorVersion == 1: osName = "Windows 7 / Server 2008 R2" case majorVersion == 6 && minorVersion == 0: osName = "Windows Vista / Server 2008" case majorVersion == 5 && minorVersion == 2: osName = "Windows XP x64 / Server 2003" case majorVersion == 5 && minorVersion == 1: osName = "Windows XP" case majorVersion == 5 && minorVersion == 0: osName = "Windows 2000" default: osName = fmt.Sprintf("Windows %d.%d", majorVersion, minorVersion) } result.WriteString(fmt.Sprintf("OS Version: %s (Build %d)\n", osName, buildNumber)) if reserved != 0 { result.WriteString(fmt.Sprintf("OS Reserved: 0x%08X\n", reserved)) } } // bytesToUint32 将字节数组转换为32位无符号整数 (小端序) func bytesToUint32(b []byte) uint32 { if len(b) < 4 { return 0 } return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 } // bytesToUint64 将字节数组转换为64位无符号整数 (小端序) func bytesToUint64(b []byte) uint64 { if len(b) < 8 { return 0 } return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 } // displaySMBInfo 显示SMB信息 func displaySMBInfo(hostInfo *common.HostInfo, info string, isSMBv1 bool) { target := fmt.Sprintf("%s:%s", hostInfo.Host, hostInfo.Ports) smbVersion := "SMB2" if isSMBv1 { smbVersion = "SMB1" } // 提取操作系统信息用于主显示行 osInfo := extractOSInfo(info) computerName := extractComputerName(info) var successMsg string if osInfo != "" && computerName != "" { successMsg = fmt.Sprintf("SMBInfo %s [%s] %s %s", target, osInfo, computerName, smbVersion) } else if osInfo != "" { successMsg = fmt.Sprintf("SMBInfo %s [%s] %s", target, osInfo, smbVersion) } else if computerName != "" { successMsg = fmt.Sprintf("SMBInfo %s %s %s", target, computerName, smbVersion) } else { successMsg = fmt.Sprintf("SMBInfo %s %s", target, smbVersion) } common.LogSuccess(successMsg) } // extractOSInfo 从信息中提取操作系统信息 func extractOSInfo(info string) string { lines := strings.Split(info, "\n") for _, line := range lines { if strings.Contains(line, "OS Version:") { parts := strings.Split(line, ":") if len(parts) > 1 { osVersion := strings.TrimSpace(parts[1]) // 简化OS版本显示,提取主要版本号 if strings.Contains(osVersion, "Windows") { if strings.Contains(osVersion, "Build") { parts := strings.Split(osVersion, " (Build") if len(parts) > 0 { return strings.TrimSpace(parts[0]) } } return osVersion } return osVersion } } if strings.Contains(line, "Windows版本:") { parts := strings.Split(line, ":") if len(parts) > 1 { return strings.TrimSpace(parts[1]) } } if strings.Contains(line, "NativeOS:") { parts := strings.Split(line, ":") if len(parts) > 1 { return strings.TrimSpace(parts[1]) } } } return "" } // extractComputerName 从信息中提取计算机名 func extractComputerName(info string) string { lines := strings.Split(info, "\n") for _, line := range lines { if strings.Contains(line, "NetBIOS Computer Name:") { parts := strings.Split(line, ":") if len(parts) > 1 { return strings.TrimSpace(parts[1]) } } if strings.Contains(line, "NetBIOS计算机名:") { parts := strings.Split(line, ":") if len(parts) > 1 { return strings.TrimSpace(parts[1]) } } if strings.Contains(line, "DNS Computer Name:") { parts := strings.Split(line, ":") if len(parts) > 1 { return strings.TrimSpace(parts[1]) } } if strings.Contains(line, "DNS计算机名:") { parts := strings.Split(line, ":") if len(parts) > 1 { return strings.TrimSpace(parts[1]) } } } return "" } // displayBasicSMBInfo 显示基本SMB信息 func displayBasicSMBInfo(hostInfo *common.HostInfo) { target := fmt.Sprintf("%s:%s", hostInfo.Host, hostInfo.Ports) msg := fmt.Sprintf("SMBInfo %s SMB service detected", target) common.LogSuccess(msg) } // saveSMBInfo 保存SMB信息 func saveSMBInfo(hostInfo *common.HostInfo, info string) { result := &output.ScanResult{ Time: time.Now(), Type: output.TypeService, Target: hostInfo.Host, Status: "open", Details: map[string]interface{}{ "port": hostInfo.Ports, "protocol": "smb", "info": info, }, } common.SaveResult(result) }