github.com/chainreactors/fingers@v1.2.1/nmap/gonmap_test.go (about) 1 package gonmap 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "os" 7 "strings" 8 "testing" 9 "time" 10 11 "github.com/chainreactors/fingers/common" 12 "github.com/chainreactors/fingers/resources" 13 ) 14 15 // TestParseLogic 测试解析逻辑 16 func TestParseLogic(t *testing.T) { 17 content, err := os.ReadFile("../resources/nmap-service-probes.txt") 18 if err != nil { 19 t.Skip("nmap-service-probes.txt not available, skipping parse logic test") 20 } 21 22 fmt.Printf("\n=== 解析逻辑测试 ===\n") 23 24 // 统计原始文件中的Probe行数 25 lines := strings.Split(string(content), "\n") 26 probeLineCount := 0 27 tcpProbeCount := 0 28 udpProbeCount := 0 29 30 for _, line := range lines { 31 if strings.HasPrefix(line, "Probe TCP") { 32 tcpProbeCount++ 33 probeLineCount++ 34 } else if strings.HasPrefix(line, "Probe UDP") { 35 udpProbeCount++ 36 probeLineCount++ 37 } 38 } 39 40 fmt.Printf("原始文件统计:\n") 41 fmt.Printf(" - Probe行总数: %d\n", probeLineCount) 42 fmt.Printf(" - TCP Probe: %d\n", tcpProbeCount) 43 fmt.Printf(" - UDP Probe: %d\n", udpProbeCount) 44 45 // 使用TempParser解析 46 parser := NewTempParser(string(content)) 47 probes := parser.GetProbes() 48 49 fmt.Printf("\n解析结果:\n") 50 fmt.Printf(" - 解析出的Probe数量: %d\n", len(probes)) 51 52 // 统计解析出的TCP和UDP probe 53 parsedTCP := 0 54 parsedUDP := 0 55 for _, probe := range probes { 56 if strings.HasPrefix(probe.Name, "TCP_") { 57 parsedTCP++ 58 } else if strings.HasPrefix(probe.Name, "UDP_") { 59 parsedUDP++ 60 } 61 } 62 63 fmt.Printf(" - TCP Probe: %d\n", parsedTCP) 64 fmt.Printf(" - UDP Probe: %d\n", parsedUDP) 65 66 // 检查是否有差异 67 if probeLineCount != len(probes) { 68 fmt.Printf("\n⚠️ 警告: 原始文件有%d个Probe行,但只解析出%d个Probe\n", probeLineCount, len(probes)) 69 fmt.Printf("差异: %d个Probe未被解析\n", probeLineCount-len(probes)) 70 } else { 71 fmt.Printf("\n✓ 解析完整,所有Probe都被正确解析\n") 72 } 73 } 74 func TestJSONDataStructure(t *testing.T) { 75 // 解压缩数据 76 decompressedProbes, err := resources.DecompressGzip(resources.NmapServiceProbesData) 77 if err != nil { 78 t.Fatalf("Failed to decompress probes data: %v", err) 79 } 80 81 decompressedServices, err := resources.DecompressGzip(resources.NmapServicesData) 82 if err != nil { 83 t.Fatalf("Failed to decompress services data: %v", err) 84 } 85 86 // 直接解析JSON查看原始数据 87 var data NmapProbesData 88 if err := json.Unmarshal(decompressedProbes, &data); err != nil { 89 t.Fatalf("Failed to unmarshal JSON: %v", err) 90 } 91 92 // 解析services数据 93 var servicesData ServicesData 94 if err := json.Unmarshal(decompressedServices, &servicesData); err != nil { 95 t.Fatalf("Failed to unmarshal services JSON: %v", err) 96 } 97 98 fmt.Printf("\n=== JSON Data Structure ===\n") 99 fmt.Printf("Probes in JSON: %d\n", len(data.Probes)) 100 fmt.Printf("Services in JSON: %d\n", len(servicesData.Services)) 101 fmt.Printf("Decompressed probes size: %d bytes\n", len(decompressedProbes)) 102 fmt.Printf("Decompressed services size: %d bytes\n", len(decompressedServices)) 103 104 // 统计match总数 105 totalMatches := 0 106 totalSoftMatches := 0 107 for _, probe := range data.Probes { 108 for _, match := range probe.MatchGroup { 109 if match.Soft { 110 totalSoftMatches++ 111 } 112 } 113 totalMatches += len(probe.MatchGroup) 114 } 115 fmt.Printf("\nTotal match rules: %d\n", totalMatches) 116 fmt.Printf(" - Hard matches: %d\n", totalMatches-totalSoftMatches) 117 fmt.Printf(" - Soft matches: %d\n", totalSoftMatches) 118 } 119 120 func TestScan(t *testing.T) { 121 if testing.Short() { 122 t.Skip("skipping live network test in short mode") 123 } 124 engine, err := NewNmapEngine(resources.NmapServiceProbesData, resources.NmapServicesData) 125 if err != nil { 126 t.Fatalf("Failed to create nmap engine: %v", err) 127 } 128 129 // Test port 135 MSRPC detection 130 fmt.Println("=== Testing Port 135 MSRPC Detection ===") 131 res := engine.ServiceMatch("127.0.0.1", "135", 2, common.NewServiceSender(time.Second*5), nil) 132 133 if res != nil && res.Framework != nil { 134 fmt.Printf("✓ Successfully detected service on port 135\n") 135 fmt.Printf(" Service: %s, Product: %s\n", res.Framework.Name, res.Framework.Product) 136 } else { 137 fmt.Println("⚠ No service detected on port 135 (may not be running)") 138 } 139 } 140 141 // TestNoGuessFlag verifies that the NoGuess flag is respected 142 func TestNoGuessFlag(t *testing.T) { 143 engine, err := NewNmapEngine(resources.NmapServiceProbesData, resources.NmapServicesData) 144 if err != nil { 145 t.Fatalf("Failed to create nmap engine: %v", err) 146 } 147 148 fmt.Println("\n=== Testing NoGuess Flag Behavior ===") 149 150 // Save original NoGuess value and restore after test 151 originalNoGuess := common.NoGuess 152 defer func() { 153 common.NoGuess = originalNoGuess 154 }() 155 156 // Test 1: NoGuess = false (default) - guess should work 157 fmt.Println("\n1. Testing with NoGuess = false (guess enabled)") 158 common.NoGuess = false 159 160 // Test against a high port that's unlikely to be running but might be guessable 161 // Using port 9999 which is unlikely to have a service but has a guess entry 162 res1 := engine.ServiceMatch("127.0.0.1", "9999", 1, common.NewServiceSender(time.Second*2), nil) 163 if res1 != nil && res1.Framework != nil && res1.Framework.IsGuess() { 164 fmt.Printf(" ✓ Guess result returned: %s (from: %s)\n", res1.Framework.Name, res1.Framework.From.String()) 165 } else { 166 fmt.Println(" ⚠ No guess result (port may be closed or matched exactly)") 167 } 168 169 // Test 2: NoGuess = true - guess should be skipped 170 fmt.Println("\n2. Testing with NoGuess = true (guess disabled)") 171 common.NoGuess = true 172 173 res2 := engine.ServiceMatch("127.0.0.1", "9999", 1, common.NewServiceSender(time.Second*2), nil) 174 if res2 == nil { 175 fmt.Println(" ✓ No result returned when NoGuess is true (correct behavior)") 176 } else if res2.Framework != nil && !res2.Framework.IsGuess() { 177 fmt.Printf(" ✓ Non-guess result returned: %s (from: %s)\n", res2.Framework.Name, res2.Framework.From.String()) 178 } else if res2.Framework != nil && res2.Framework.IsGuess() { 179 t.Errorf(" ✗ Guess result returned when NoGuess is true (incorrect behavior)") 180 } 181 182 fmt.Println("\n✓ NoGuess flag test completed") 183 } 184 185 // TestProbeDataIntegrity verifies that binary probe data is correctly preserved 186 func TestProbeDataIntegrity(t *testing.T) { 187 n := &Nmap{ 188 probeNameMap: make(map[string]*Probe), 189 rarityProbeMap: make(map[int][]*Probe), 190 portProbeMap: make(map[int]ProbeList), 191 sslSecondProbeMap: make(ProbeList, 0), 192 sslProbeMap: make(ProbeList, 0), 193 } 194 195 // 解压缩数据 196 decompressedProbes, err := resources.DecompressGzip(resources.NmapServiceProbesData) 197 if err != nil { 198 t.Fatalf("Failed to decompress probes data: %v", err) 199 } 200 201 decompressedServices, err := resources.DecompressGzip(resources.NmapServicesData) 202 if err != nil { 203 t.Fatalf("Failed to decompress services data: %v", err) 204 } 205 206 n.loadProbesFromBytes(decompressedProbes) 207 n.loadServicesFromBytes(decompressedServices) 208 209 // 输出探针统计信息 210 fmt.Printf("\n=== Probe Statistics ===\n") 211 fmt.Printf("Total probes loaded: %d\n", len(n.probeNameMap)) 212 fmt.Printf("Decompressed probes data size: %d bytes\n", len(decompressedProbes)) 213 fmt.Printf("Decompressed services data size: %d bytes\n", len(decompressedServices)) 214 215 // 统计match总数 216 totalMatches := 0 217 for _, probe := range n.probeNameMap { 218 totalMatches += len(probe.MatchGroup) 219 } 220 fmt.Printf("Total match rules: %d\n", totalMatches) 221 222 // 列出前10个探针名称及其match数量 223 fmt.Printf("\nFirst 10 probes with match counts:\n") 224 count := 0 225 for name, probe := range n.probeNameMap { 226 if count >= 10 { 227 break 228 } 229 fmt.Printf(" - %s: %d matches\n", name, len(probe.MatchGroup)) 230 count++ 231 } 232 233 // Verify SMBProgNeg probe data integrity 234 probe, exists := n.probeNameMap["TCP_SMBProgNeg"] 235 if !exists { 236 t.Fatal("TCP_SMBProgNeg probe not found") 237 } 238 239 // Check data length (should be 168 bytes for SMBProgNeg) 240 fmt.Printf("\nSMBProgNeg probe SendRaw length: %d bytes\n", len(probe.SendRaw)) 241 if len(probe.SendRaw) != 168 { 242 t.Errorf("Expected probe data length 168, got %d", len(probe.SendRaw)) 243 } 244 245 // Check for UTF-8 replacement characters (0xef 0xbf 0xbd) 246 // These indicate corrupted binary data 247 for i := 0; i < len(probe.SendRaw)-2; i++ { 248 if probe.SendRaw[i] == 0xef && probe.SendRaw[i+1] == 0xbf && probe.SendRaw[i+2] == 0xbd { 249 t.Errorf("Found UTF-8 replacement character at position %d - binary data is corrupted", i) 250 } 251 } 252 253 // Verify specific bytes that should be present (0xa4 and 0xff from SMB header) 254 if probe.SendRaw[3] != 0xa4 { 255 t.Errorf("Expected byte 0xa4 at position 3, got 0x%02x", probe.SendRaw[3]) 256 } 257 if probe.SendRaw[4] != 0xff { 258 t.Errorf("Expected byte 0xff at position 4, got 0x%02x", probe.SendRaw[4]) 259 } 260 }