fscan/Core/ICMP.go

411 lines
9.0 KiB
Go
Raw Normal View History

2024-12-19 15:24:10 +08:00
package Core
2020-12-29 17:17:10 +08:00
import (
"bytes"
"fmt"
2024-12-18 22:00:18 +08:00
"github.com/shadow1ng/fscan/Common"
2023-11-13 10:45:48 +08:00
"golang.org/x/net/icmp"
2020-12-29 17:17:10 +08:00
"net"
"os/exec"
"runtime"
"strings"
"sync"
"time"
)
2021-05-29 20:16:01 +08:00
var (
AliveHosts []string // 存活主机列表
ExistHosts = make(map[string]struct{}) // 已发现主机记录
livewg sync.WaitGroup // 存活检测等待组
2021-05-29 20:16:01 +08:00
)
2020-12-29 17:17:10 +08:00
// CheckLive 检测主机存活状态
2023-11-13 10:45:48 +08:00
func CheckLive(hostslist []string, Ping bool) []string {
// 创建主机通道
2021-05-29 20:16:01 +08:00
chanHosts := make(chan string, len(hostslist))
2020-12-29 17:17:10 +08:00
// 处理存活主机
go handleAliveHosts(chanHosts, hostslist, Ping)
// 根据Ping参数选择检测方式
if Ping {
// 使用ping方式探测
2021-05-29 20:16:01 +08:00
RunPing(hostslist, chanHosts)
2020-12-29 17:17:10 +08:00
} else {
probeWithICMP(hostslist, chanHosts)
}
// 等待所有检测完成
livewg.Wait()
close(chanHosts)
// 输出存活统计信息
printAliveStats(hostslist)
return AliveHosts
}
// handleAliveHosts 处理存活主机信息
func handleAliveHosts(chanHosts chan string, hostslist []string, isPing bool) {
for ip := range chanHosts {
if _, ok := ExistHosts[ip]; !ok && IsContain(hostslist, ip) {
ExistHosts[ip] = struct{}{}
// 输出存活信息
if !Common.Silent {
protocol := "ICMP"
if isPing {
protocol = "PING"
2021-09-10 20:32:51 +08:00
}
fmt.Printf("目标 %-15s 存活 (%s)\n", ip, protocol)
2021-06-18 11:45:47 +08:00
}
AliveHosts = append(AliveHosts, ip)
2021-06-18 11:45:47 +08:00
}
livewg.Done()
}
}
// probeWithICMP 使用ICMP方式探测
func probeWithICMP(hostslist []string, chanHosts chan string) {
// 尝试监听本地ICMP
conn, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0")
if err == nil {
RunIcmp1(hostslist, conn, chanHosts)
return
2020-12-29 17:17:10 +08:00
}
Common.LogError(fmt.Sprintf("ICMP监听失败: %v", err))
fmt.Println("正在尝试无监听ICMP探测...")
// 尝试无监听ICMP探测
conn2, err := net.DialTimeout("ip4:icmp", "127.0.0.1", 3*time.Second)
if err == nil {
defer conn2.Close()
RunIcmp2(hostslist, chanHosts)
return
}
Common.LogError(fmt.Sprintf("ICMP连接失败: %v", err))
fmt.Println("当前用户权限不足,无法发送ICMP包")
fmt.Println("切换为PING方式探测...")
// 降级使用ping探测
RunPing(hostslist, chanHosts)
}
// printAliveStats 打印存活统计信息
func printAliveStats(hostslist []string) {
// 大规模扫描时输出 /16 网段统计
if len(hostslist) > 1000 {
2024-12-18 22:00:18 +08:00
arrTop, arrLen := ArrayCountValueTop(AliveHosts, Common.LiveTop, true)
for i := 0; i < len(arrTop); i++ {
output := fmt.Sprintf("B段 %-16s 存活主机数: %d", arrTop[i]+".0.0/16", arrLen[i])
2024-12-18 22:00:18 +08:00
Common.LogSuccess(output)
}
}
// 输出 /24 网段统计
2022-01-08 14:46:26 +08:00
if len(hostslist) > 256 {
2024-12-18 22:00:18 +08:00
arrTop, arrLen := ArrayCountValueTop(AliveHosts, Common.LiveTop, false)
for i := 0; i < len(arrTop); i++ {
output := fmt.Sprintf("C段 %-16s 存活主机数: %d", arrTop[i]+".0/24", arrLen[i])
2024-12-18 22:00:18 +08:00
Common.LogSuccess(output)
}
}
2020-12-29 17:17:10 +08:00
}
// RunIcmp1 使用ICMP批量探测主机存活(监听模式)
2021-05-29 20:16:01 +08:00
func RunIcmp1(hostslist []string, conn *icmp.PacketConn, chanHosts chan string) {
endflag := false
// 启动监听协程
go func() {
for {
if endflag {
return
}
// 接收ICMP响应
msg := make([]byte, 100)
_, sourceIP, _ := conn.ReadFrom(msg)
if sourceIP != nil {
2021-05-29 20:16:01 +08:00
livewg.Add(1)
chanHosts <- sourceIP.String()
}
}
}()
2020-12-29 17:17:10 +08:00
// 发送ICMP请求
for _, host := range hostslist {
2021-05-29 20:16:01 +08:00
dst, _ := net.ResolveIPAddr("ip", host)
IcmpByte := makemsg(host)
conn.WriteTo(IcmpByte, dst)
2020-12-29 17:17:10 +08:00
}
// 等待响应
2021-05-29 15:55:05 +08:00
start := time.Now()
for {
// 所有主机都已响应则退出
2021-05-29 15:55:05 +08:00
if len(AliveHosts) == len(hostslist) {
break
}
// 根据主机数量设置超时时间
2023-06-05 19:02:55 +03:00
since := time.Since(start)
wait := time.Second * 6
if len(hostslist) <= 256 {
2021-05-29 15:55:05 +08:00
wait = time.Second * 3
}
2021-05-29 15:55:05 +08:00
if since > wait {
break
}
2020-12-29 17:17:10 +08:00
}
endflag = true
conn.Close()
2020-12-29 17:17:10 +08:00
}
// RunIcmp2 使用ICMP并发探测主机存活(无监听模式)
2021-05-29 20:16:01 +08:00
func RunIcmp2(hostslist []string, chanHosts chan string) {
// 控制并发数
2021-05-29 20:16:01 +08:00
num := 1000
if len(hostslist) < num {
num = len(hostslist)
}
2021-05-29 20:16:01 +08:00
var wg sync.WaitGroup
limiter := make(chan struct{}, num)
// 并发探测
2021-05-29 20:16:01 +08:00
for _, host := range hostslist {
wg.Add(1)
limiter <- struct{}{}
2021-05-29 20:16:01 +08:00
go func(host string) {
defer func() {
<-limiter
wg.Done()
}()
2021-05-29 20:16:01 +08:00
if icmpalive(host) {
livewg.Add(1)
chanHosts <- host
}
}(host)
}
2021-05-29 20:16:01 +08:00
wg.Wait()
close(limiter)
}
// icmpalive 检测主机ICMP是否存活
2021-05-29 20:16:01 +08:00
func icmpalive(host string) bool {
startTime := time.Now()
// 建立ICMP连接
2021-05-29 20:16:01 +08:00
conn, err := net.DialTimeout("ip4:icmp", host, 6*time.Second)
if err != nil {
return false
}
2023-11-13 17:41:54 +08:00
defer conn.Close()
// 设置超时时间
2021-05-29 20:16:01 +08:00
if err := conn.SetDeadline(startTime.Add(6 * time.Second)); err != nil {
return false
}
// 构造并发送ICMP请求
2021-05-29 20:16:01 +08:00
msg := makemsg(host)
if _, err := conn.Write(msg); err != nil {
return false
}
// 接收ICMP响应
2021-05-29 20:16:01 +08:00
receive := make([]byte, 60)
if _, err := conn.Read(receive); err != nil {
return false
}
return true
}
// RunPing 使用系统Ping命令并发探测主机存活
2021-05-29 20:16:01 +08:00
func RunPing(hostslist []string, chanHosts chan string) {
var wg sync.WaitGroup
// 限制并发数为50
2021-05-29 20:16:01 +08:00
limiter := make(chan struct{}, 50)
// 并发探测
2021-05-29 20:16:01 +08:00
for _, host := range hostslist {
wg.Add(1)
limiter <- struct{}{}
2021-05-29 20:16:01 +08:00
go func(host string) {
defer func() {
<-limiter
wg.Done()
}()
2023-11-13 17:41:54 +08:00
if ExecCommandPing(host) {
2021-05-29 20:16:01 +08:00
livewg.Add(1)
chanHosts <- host
}
}(host)
}
2021-05-29 20:16:01 +08:00
wg.Wait()
2020-12-29 17:17:10 +08:00
}
// ExecCommandPing 执行系统Ping命令检测主机存活
2023-11-13 17:41:54 +08:00
func ExecCommandPing(ip string) bool {
2024-12-19 22:17:09 +08:00
// 过滤黑名单字符
forbiddenChars := []string{";", "&", "|", "`", "$", "\\", "'", "%", "\"", "\n"}
for _, char := range forbiddenChars {
if strings.Contains(ip, char) {
return false
}
}
2020-12-29 17:17:10 +08:00
var command *exec.Cmd
// 根据操作系统选择不同的ping命令
2023-11-13 17:41:54 +08:00
switch runtime.GOOS {
case "windows":
command = exec.Command("cmd", "/c", "ping -n 1 -w 1 "+ip+" && echo true || echo false")
2023-11-13 17:41:54 +08:00
case "darwin":
command = exec.Command("/bin/bash", "-c", "ping -c 1 -W 1 "+ip+" && echo true || echo false")
default: // linux
command = exec.Command("/bin/bash", "-c", "ping -c 1 -w 1 "+ip+" && echo true || echo false")
2020-12-29 17:17:10 +08:00
}
// 捕获命令输出
var outinfo bytes.Buffer
2020-12-29 17:17:10 +08:00
command.Stdout = &outinfo
// 执行命令
if err := command.Start(); err != nil {
2020-12-29 17:17:10 +08:00
return false
}
if err := command.Wait(); err != nil {
2020-12-29 17:17:10 +08:00
return false
}
// 分析输出结果
output := outinfo.String()
return strings.Contains(output, "true") && strings.Count(output, ip) > 2
2020-12-29 17:17:10 +08:00
}
// makemsg 构造ICMP echo请求消息
2021-05-29 20:16:01 +08:00
func makemsg(host string) []byte {
msg := make([]byte, 40)
// 获取标识符
2021-05-29 20:16:01 +08:00
id0, id1 := genIdentifier(host)
// 设置ICMP头部
msg[0] = 8 // Type: Echo Request
msg[1] = 0 // Code: 0
msg[2] = 0 // Checksum高位(待计算)
msg[3] = 0 // Checksum低位(待计算)
msg[4], msg[5] = id0, id1 // Identifier
msg[6], msg[7] = genSequence(1) // Sequence Number
// 计算校验和
2021-05-29 20:16:01 +08:00
check := checkSum(msg[0:40])
msg[2] = byte(check >> 8) // 设置校验和高位
msg[3] = byte(check & 255) // 设置校验和低位
2021-05-29 20:16:01 +08:00
return msg
2020-12-29 17:17:10 +08:00
}
2021-05-29 20:16:01 +08:00
// checkSum 计算ICMP校验和
2021-05-29 20:16:01 +08:00
func checkSum(msg []byte) uint16 {
sum := 0
length := len(msg)
// 按16位累加
2021-05-29 20:16:01 +08:00
for i := 0; i < length-1; i += 2 {
sum += int(msg[i])*256 + int(msg[i+1])
2020-12-29 17:17:10 +08:00
}
// 处理奇数长度情况
2021-05-29 20:16:01 +08:00
if length%2 == 1 {
sum += int(msg[length-1]) * 256
}
// 将高16位加到低16位
2021-05-29 20:16:01 +08:00
sum = (sum >> 16) + (sum & 0xffff)
sum = sum + (sum >> 16)
// 取反得到校验和
return uint16(^sum)
2021-05-29 20:16:01 +08:00
}
// genSequence 生成ICMP序列号
2021-05-29 20:16:01 +08:00
func genSequence(v int16) (byte, byte) {
ret1 := byte(v >> 8) // 高8位
ret2 := byte(v & 255) // 低8位
2021-05-29 20:16:01 +08:00
return ret1, ret2
}
// genIdentifier 根据主机地址生成标识符
2021-05-29 20:16:01 +08:00
func genIdentifier(host string) (byte, byte) {
return host[0], host[1] // 使用主机地址前两个字节
2020-12-29 17:17:10 +08:00
}
// ArrayCountValueTop 统计IP地址段存活数量并返回TOP N结果
func ArrayCountValueTop(arrInit []string, length int, flag bool) (arrTop []string, arrLen []int) {
if len(arrInit) == 0 {
return
}
// 统计各网段出现次数
segmentCounts := make(map[string]int)
for _, ip := range arrInit {
segments := strings.Split(ip, ".")
if len(segments) != 4 {
continue
}
// 根据flag确定统计B段还是C段
var segment string
if flag {
segment = fmt.Sprintf("%s.%s", segments[0], segments[1]) // B段
} else {
segment = fmt.Sprintf("%s.%s.%s", segments[0], segments[1], segments[2]) // C段
}
segmentCounts[segment]++
}
// 创建副本用于排序
sortMap := make(map[string]int)
for k, v := range segmentCounts {
sortMap[k] = v
}
// 获取TOP N结果
for i := 0; i < length && len(sortMap) > 0; i++ {
maxSegment := ""
maxCount := 0
// 查找当前最大值
for segment, count := range sortMap {
if count > maxCount {
maxCount = count
maxSegment = segment
}
}
// 添加到结果集
arrTop = append(arrTop, maxSegment)
arrLen = append(arrLen, maxCount)
// 从待处理map中删除已处理项
delete(sortMap, maxSegment)
}
return
}