文登seo排名,珠海百度seo代理,国家企业信息查询平台官网,绍兴建站模板厂家一、概述
上文提到实现IP的探测存活以及tcp扫描的实现#xff0c;这部分来分析实现本机网卡信息获取#xff0c;以及维护一张mac地址表以及ip扫描端口状态表#xff0c;同时实现syn扫描功能。
项目来源#xff1a;https://github.com/XinRoom/go-portScan/blob/main/util…一、概述
上文提到实现IP的探测存活以及tcp扫描的实现这部分来分析实现本机网卡信息获取以及维护一张mac地址表以及ip扫描端口状态表同时实现syn扫描功能。
项目来源https://github.com/XinRoom/go-portScan/blob/main/util/file.go
二、识别本机网卡信息获取网卡IP、mac、网关地址等信息
1、/core/syn/device.go
文件代码主要是用于获取网络接口信息包括查找设备、获取设备信息、获取设备的 MAC 地址以及确定路由器等。
func GetAllDevs() (string, error) 获取所有的网络设备列表并返回其名称、描述和地址 func GetAllDevs() (string, error) {pcapDevices, err : pcap.FindAllDevs()//获取所有网络设备if err ! nil {return , errors.New(fmt.Sprintf(list pcapDevices failed: %s, err.Error()))}var buf strings.Builderfor _, dev : range pcapDevices {buf.WriteString(fmt.Sprint(Dev:, dev.Name, \tDes:, dev.Description))//获取网卡名称if len(dev.Addresses) 0 {buf.WriteString(fmt.Sprint(\tAddr:, dev.Addresses[0].IP.String()))//获取第一个IP}buf.WriteString(\n)}return buf.String(), nil
} func GetDevByIp(ip net.IP) (devName string, err error) 根据给定的 IP 地址获取对应的网络设备名称。 // GetDevByIp get dev name by dev ip (use pcap)
func GetDevByIp(ip net.IP) (devName string, err error) {devices, err : pcap.FindAllDevs()if err ! nil {return}for _, d : range devices {//遍历匹配每个网络设备的IPfor _, address : range d.Addresses {_ip : address.IP.To4()if _ip ! nil _ip.IsGlobalUnicast() _ip.Equal(ip) {return d.Name, nil}}}return , errors.New(can not find dev)
} func GetIfaceMac(ifaceAddr net.IP) (src net.IP, mac net.HardwareAddr) 这个函数首先获取系统上所有的网络接口信息。然后它遍历每个网络接口检查每个接口的地址列表以确定是否包含了给定的接口 IP 地址。一旦找到包含指定 IP 的接口它就会返回该 IP 地址以及对应网络接口的 MAC 地址。如果未找到对应的接口 IP 地址函数将返回两个 nil 值。 // GetIfaceMac get interface mac addr by interface ip (use golang net)
func GetIfaceMac(ifaceAddr net.IP) (src net.IP, mac net.HardwareAddr) {// 获取系统上所有的网络接口信息interfaces, _ : net.Interfaces()for _, iface : range interfaces {// 获取当前网络接口的地址列表if addrs, err : iface.Addrs(); err nil {for _, addr : range addrs {// 检查当前地址是否包含给定的接口 IP 地址if addr.(*net.IPNet).Contains(ifaceAddr) {// 如果包含返回对应的 IP 地址和网络接口的 MAC 地址return addr.(*net.IPNet).IP, iface.HardwareAddr}}}}// 如果未找到对应的接口 IP 地址则返回 nilreturn nil, nil
}func GetMacByGw(gw net.IP) (srcIp net.IP, srcMac net.HardwareAddr, devname string, err error) 这个函数首先使用 GetIfaceMac 函数获取网关对应的源 IP 地址和源 MAC 地址。如果未找到与网关关联的源 IP 地址函数会返回一个错误信息。接着它将获取的源 IP 地址转换为 IPv4 格式并获取系统中的所有网络设备信息。随后函数遍历设备列表检查每个设备是否存在地址信息并且与源 IP 地址匹配。如果找到匹配的设备则将该设备的名称赋值给 devname并返回。如果未找到匹配的设备函数会返回另一个错误信息。 func GetMacByGw(gw net.IP) (srcIp net.IP, srcMac net.HardwareAddr, devname string, err error) {// 使用 GetIfaceMac 函数获取网关对应的源 IP 地址和源 MAC 地址srcIp, srcMac GetIfaceMac(gw)// 如果获取到的源 IP 地址为空说明无法找到与该网关关联的设备if srcIp nil {err errors.New(can not find this dev by gw)return}// 将源 IP 地址转换为 IPv4 格式srcIp srcIp.To4()// 获取系统中的所有网络设备信息devices, err : pcap.FindAllDevs()if err ! nil {return}// 遍历设备列表for _, d : range devices {// 检查当前设备是否存在地址信息并且与源 IP 地址匹配if len(d.Addresses) 0 d.Addresses[0].IP.String() srcIp.String() {// 如果匹配成功将设备名称赋值为当前设备的名称devname d.Namereturn}}// 如果未找到与源 IP 地址匹配的设备则返回相应的错误信息err errors.New(can not find this dev)return
}func GetRouterV4(dst net.IP) (srcIp net.IP, srcMac net.HardwareAddr, gw net.IP, devName string, err error) 这个函数的作用是根据给定的目标 IP 地址获取路由相关的信息包括源 IP 地址、源 MAC 地址、网关 IP 地址、设备名称和错误信息。以下是逐行解释 func GetRouterV4(dst net.IP) (srcIp net.IP, srcMac net.HardwareAddr, gw net.IP, devName string, err error) {// 获取目标 IP 对应的源 IP 和源 MAC 地址srcIp, srcMac GetIfaceMac(dst)// 如果源 IP 为空说明无法找到与目标 IP 相关的设备if srcIp nil {// 创建一个路由对象并获取路由表信息var r routing.Routerr, err netroute.New()if err nil {// 获取与目标 IP 相关的路由信息var iface *net.Interfaceiface, gw, srcIp, err r.Route(dst)if err nil {// 如果找到路由检查对应接口是否存在如果存在则获取其 MAC 地址否则再次使用目标 IP 获取源 MAC 地址if iface ! nil {srcMac iface.HardwareAddr} else {_, srcMac GetIfaceMac(srcIp)}}}// 如果发生错误或者源 MAC 地址为空尝试获取默认网关if err ! nil || srcMac nil {gw, err gateway.DiscoverGateway()if err nil {srcIp, srcMac GetIfaceMac(gw)}}}// 转换网关和源 IP 地址为 IPv4 格式gw gw.To4()srcIp srcIp.To4()// 获取与源 IP 地址相关的设备名称devName, err GetDevByIp(srcIp)// 如果源 IP 为空或者发生错误或者源 MAC 地址为空则返回相应的错误信息if srcIp nil || err ! nil || srcMac nil {if err nil {err fmt.Errorf(err)}return nil, nil, nil, , fmt.Errorf(no router, %s, err)}// 返回结果return
}三、维护IP状态表和mac地址表
1、/core/port/syn/watchIpStatus.go
这个代码文件里的结构和方法集合提供了一种有效管理 IP 状态更新的方式包括记录最后更新时间、检查端口记录以及清理超时数据等功能。 watchIpStatus结构体 ReceivedPort 是一个 map[uint16]struct{}用于记录接收到的端口号。LastTime 是一个 time.Time 类型的字段用于记录最后一次更新的时间戳。 type watchIpStatus struct {ReceivedPort map[uint16]struct{}LastTime time.Time
} watchIpStatusTable 结构体 watchIpS: 一个映射表将 IP 地址与其状态关联起来。lock: 用于对共享数据进行读写锁操作的 sync.RWMutex 实例。isDone: 标记是否完成清理过期数据的操作。 // IP状态更新表
type watchIpStatusTable struct {watchIpS map[string]*watchIpStatuslock sync.RWMutexisDone bool
} func newWatchIpStatusTable(timeout time.Duration) (w *watchIpStatusTable) 创建并返回一个新的 watchIpStatusTable 实例。启动一个单独的 goroutine (go w.cleanTimeout(timeout)) 来清理过期数据 func newWatchIpStatusTable(timeout time.Duration) (w *watchIpStatusTable) {w watchIpStatusTable{watchIpS: make(map[string]*watchIpStatus),}go w.cleanTimeout(timeout) //清理过期数据return
} func (w *watchIpStatusTable) UpdateLastTime(ip string) 用于更新指定 IP 的最后更新时间。如果 IP 不存在则新建一个 watchIpStatus 对象并将其添加到 watchIpS 映射表中。 // UpdateLastTime 新建或者更新LastTime
func (w *watchIpStatusTable) UpdateLastTime(ip string) {lastTime : time.Now()w.lock.Lock()wi, ok : w.watchIpS[ip]if ok {wi.LastTime lastTime} else {//IP不存在则新建一个映射表w.watchIpS[ip] watchIpStatus{LastTime: lastTime, ReceivedPort: make(map[uint16]struct{})}}w.lock.Unlock()
} func (w *watchIpStatusTable) RecordPort(ip string, port uint16) 记录给定 IP 收到的指定端口信息。如果 IP 存在于映射表中则将该端口添加到对应 IP 的 ReceivedPort 映射中。 // RecordPort 记录收到的端口
func (w *watchIpStatusTable) RecordPort(ip string, port uint16) {w.lock.Lock()wi, ok : w.watchIpS[ip]if ok {wi.ReceivedPort[port] struct{}{}}w.lock.Unlock()
} func (w *watchIpStatusTable) HasPort(ip string, port uint16) (has bool) 检查给定 IP 是否曾经检测过特定端口。如果 IP 存在于映射表中并且端口也被记录则返回 true否则返回 false。 // HasPort 判断是否检测过对应端口
func (w *watchIpStatusTable) HasPort(ip string, port uint16) (has bool) {w.lock.RLock()wi, ok : w.watchIpS[ip]if ok {_, has wi.ReceivedPort[port]}w.lock.RUnlock()return
} func (w *watchIpStatusTable) HasIp(ip string) (has bool) 检查是否正在监视给定的 IP。如果 IP 存在于映射表中则返回 true否则返回 false。 // HasIp 判断是否在监视对应IP
func (w *watchIpStatusTable) HasIp(ip string) (has bool) {w.lock.RLock()_, has w.watchIpS[ip]w.lock.RUnlock()return
} func (w *watchIpStatusTable) IsEmpty() (empty bool) 检查表是否为空。如果映射表中没有任何 IP 记录则返回 true否则返回 false。 // IsEmpty 判断目前表是否为空
func (w *watchIpStatusTable) IsEmpty() (empty bool) {w.lock.RLock()empty len(w.watchIpS) 0w.lock.RUnlock()return
} func (w *watchIpStatusTable) Close() 设置 isDone 为 true表示关闭监视表。 func (w *watchIpStatusTable) Close() {w.isDone true
} func (w *watchIpStatusTable) cleanTimeout(timeout time.Duration) 这个方法 cleanTimeout 是一个后台清理过期数据的功能。它使用一个无限循环来持续检查数据中记录的 IP 地址的最后更新时间如果超过了设定的超时时间就会将其删除。这里使用了一个无限循环 for {} 来持续检查。 // 清理过期数据
func (w *watchIpStatusTable) cleanTimeout(timeout time.Duration) {var needDel map[string]struct{}for {needDel make(map[string]struct{})if w.isDone {break}time.Sleep(time.Second)w.lock.RLock()for k, v : range w.watchIpS {if time.Since(v.LastTime) timeout*time.Millisecond {needDel[k] struct{}{}}}w.lock.RUnlock()if len(needDel) 0 {for k : range needDel {w.lock.Lock()delete(w.watchIpS, k)w.lock.Unlock()}}}
} 2、/core/port/syn/watchMacCache.go
这个代码文件定义了一个 watchMacCacheTable 结构体它管理着缓存的 MAC 地址和监听表。其中的方法包括添加、获取和判断是否需要监听某个 IP 地址的 MAC 地址
watchMacCache结构体 type watchMacCache struct {LastTime time.TimeMac net.HardwareAddr
} watchMacCacheTable结构体 // Mac缓存和监听表
type watchMacCacheTable struct {watchMacC map[string]*watchMacCachelock sync.RWMutexisDone bool
} func newWatchMacCacheTable() (w *watchMacCacheTable) newWatchMacCacheTable() 是用于创建新的 watchMacCacheTable 实例的函数。它初始化了一个空的 watchMacC map这个map用来存储IP地址和对应的缓存信息。同时这个函数也启动了一个后台任务定期清理过期的缓存数据 func newWatchMacCacheTable() (w *watchMacCacheTable) {w watchMacCacheTable{watchMacC: make(map[string]*watchMacCache),}go w.cleanTimeout()return
} func (w *watchMacCacheTable) UpdateLastTime(ip string) UpdateLastTime(ip string) 方法用于更新或添加某个IP地址的最后更新时间。如果这个IP地址已经存在于缓存中它会更新其最后更新时间否则它会创建一个新的缓存项并设置最后更新时间。 // UpdateLastTime 新建或者更新LastTime
func (w *watchMacCacheTable) UpdateLastTime(ip string) {lastTime : time.Now()w.lock.Lock()wi, ok : w.watchMacC[ip] //如果IP存在则只更新时间if ok {wi.LastTime lastTime} else {w.watchMacC[ip] watchMacCache{LastTime: lastTime}}w.lock.Unlock()
} func (w *watchMacCacheTable) SetMac(ip string, mac net.HardwareAddr) SetMac(ip string, mac net.HardwareAddr) 方法用于设置某个IP地址对应的MAC地址。它会检查缓存中是否已存在这个IP地址如果存在则更新其最后更新时间和MAC地址如果不存在则创建新的缓存项并设置MAC地址。 // SetMac 设置Mac地址
func (w *watchMacCacheTable) SetMac(ip string, mac net.HardwareAddr) {lastTime : time.Now()w.lock.Lock()wi, ok : w.watchMacC[ip]//IP存在则更新IP和时间if ok {wi.LastTime lastTimewi.Mac mac} else {//不存在则重新建一个mac映射表w.watchMacC[ip] watchMacCache{LastTime: lastTime, Mac: mac}wi.Mac mac}w.lock.Unlock()
} func (w *watchMacCacheTable) GetMac(ip string) (mac net.HardwareAddr) GetMac(ip string) (mac net.HardwareAddr) 方法用于获取某个IP地址对应的MAC地址。它会根据传入的IP地址在缓存中查找并返回相应的MAC地址。 // GetMac 获取Mac地址缓存
func (w *watchMacCacheTable) GetMac(ip string) (mac net.HardwareAddr) {w.lock.RLock()wi, ok : w.watchMacC[ip]if ok {mac wi.Mac}w.lock.RUnlock()return
} func (w *watchMacCacheTable) IsNeedWatch(ip string) (has bool) IsNeedWatch(ip string) (has bool) 方法用于判断是否需要监听某个IP地址的MAC地址。它会检查对应IP地址的MAC地址是否为 nil如果是 nil则表示需要监听。 // IsNeedWatch 判断是否需要监视
func (w *watchMacCacheTable) IsNeedWatch(ip string) (has bool) {w.lock.RLock()wm, ok : w.watchMacC[ip]has ok wm.Mac nilw.lock.RUnlock()return
} func (w *watchMacCacheTable) IsEmpty() (empty bool) IsEmpty() (empty bool) 方法用于判断当前缓存表是否为空 // IsEmpty 判断目前表是否为空
func (w *watchMacCacheTable) IsEmpty() (empty bool) {w.lock.RLock()empty len(w.watchMacC) 0w.lock.RUnlock()return
} func (w *watchMacCacheTable) Close() Close() 方法用于关闭清理过期数据的后台任务。 func (w *watchMacCacheTable) Close() {w.isDone true
} func (w *watchMacCacheTable) cleanTimeout() 这个方法是一个后台循环任务它会定期检查缓存中所有IP地址的最后更新时间。如果某个IP地址的最后更新时间超过了设定的超时时间这里是10秒则会将这些过期的IP地址从缓存中删除。这个过程会一直持续直到 isDone 被设置为 true。 // 清理过期数据
func (w *watchMacCacheTable) cleanTimeout() {var needDel map[string]struct{}for {needDel make(map[string]struct{})if w.isDone {break}time.Sleep(2 * time.Second)w.lock.RLock()for k, v : range w.watchMacC {if time.Since(v.LastTime) 10*time.Second {needDel[k] struct{}{}}}w.lock.RUnlock()if len(needDel) 0 {for k : range needDel {w.lock.Lock()delete(w.watchMacC, k)w.lock.Unlock()}}}
} 四、tcp SYN端口扫描的实现
1、/core/port/syn/syn.go
这段代码是一个TCP SYN扫描器的实现用于检测主机上开放的TCP端口
SynScanner 结构体 包含了需要的网络信息和扫描器的配置选项同时还有一些用于发送和接收数据包的方法。 type SynScanner struct {srcMac, gwMac net.HardwareAddr // macAddrdevName string // eth dev(pcap)// gateway (if applicable), and source IP addresses to use.gw, srcIp net.IP// pcaphandle *pcap.Handle// opts and buf allow us to easily serialize packets in the send() method.opts gopacket.SerializeOptions// Buffer复用bufPool *sync.Pool//option port.OptionopenPortChan chan port.OpenIpPort // inside chanportProbeWg sync.WaitGroupretChan chan port.OpenIpPort // results chanlimiter *limiter.Limiterctx context.ContextwatchIpStatusT *watchIpStatusTable // IpStatusCacheTablewatchMacCacheT *watchMacCacheTable // MacCachesisDone bool
} func NewSynScanner(firstIp net.IP, retChan chan port.OpenIpPort, option port.Option) (ss *SynScanner, err error) 扫描器的构造函数用于初始化扫描器并返回一个 SynScanner 实例。它初始化了网络接口、获取了网关信息、创建了 pcap 实例用于抓取和发送数据包设置了相关的过滤器同时启动了接收数据包的协程。此外它还创建了用于监视IP状态和MAC缓存的数据结构。 // NewSynScanner 用于创建 SynScanner 对象的函数firstIp: 用于选择路由; openPortChan: 结果返回通道
func NewSynScanner(firstIp net.IP, retChan chan port.OpenIpPort, option port.Option) (ss *SynScanner, err error) {// 选项验证if option.Rate 10 {err errors.New(速率不能小于 10)return}// 设备和网络信息的变量var devName stringvar srcIp net.IPvar srcMac net.HardwareAddrvar gw net.IP// 根据 NextHop 选项指定设备或获取路由器信息if option.NextHop ! {// 如果指定了 NextHop基于该网关 IP 获取信息gw net.ParseIP(option.NextHop).To4()srcIp, srcMac, devName, err GetMacByGw(gw)} else {// 使用提供的第一个 IP 获取路由器信息srcIp, srcMac, gw, devName, err GetRouterV4(firstIp)}if err ! nil {return}if devName {err errors.New(获取路由器信息失败没有设备名称)return}rand.Seed(time.Now().Unix())// 使用必要的详细信息初始化 SynScanner 对象ss SynScanner{opts: gopacket.SerializeOptions{FixLengths: true,ComputeChecksums: true,},srcIp: srcIp,srcMac: srcMac,devName: devName,bufPool: sync.Pool{New: func() interface{} {return gopacket.NewSerializeBuffer()},},option: option,openPortChan: make(chan port.OpenIpPort, cap(retChan)),retChan: retChan,limiter: limiter.NewLimiter(limiter.Every(time.Second/time.Duration(option.Rate)), option.Rate/10),ctx: context.Background(),watchIpStatusT: newWatchIpStatusTable(time.Duration(option.Timeout)),watchMacCacheT: newWatchMacCacheTable(),}// 处理不同的扫描模式if ss.option.FingerPrint || ss.option.Httpx {// 如果启用了指纹识别或 HTTP 探测启动端口探测处理程序go ss.portProbeHandle()} else {// 否则在单独的 goroutine 中处理开放端口通道go func() {for t : range ss.openPortChan {ss.portProbeWg.Add(1)ss.retChan - tss.portProbeWg.Done()}}()}// 为网络包捕获打开 pcap 句柄handle, err : pcap.OpenLive(devName, 1024, false, pcap.BlockForever)if err ! nil {return}// 设置包过滤器以减少监控包的数量handle.SetBPFFilter(fmt.Sprintf(ether dst %s (arp || tcp[tcpflags] tcp-syn|tcp-ack), srcMac.String()))ss.handle handle// 开始监听接收到的包go ss.recv()if gw ! nil {// 如果存在网关则检索其 MAC 地址var dstMac net.HardwareAddrdstMac, err ss.getHwAddrV4(gw)if err ! nil {return}ss.gwMac dstMac}return
}func (ss *SynScanner) Scan(dstIp net.IP, dst uint16) (err error) Scan 方法用于扫描指定IP和端口。它首先检查IP是否为IPv4地址然后更新了要监视的IP状态。接着构建了需要发送的TCP SYN数据包发送给目标主机。在发送数据包的过程中会动态调整发送频率根据待发送队列的占用情况来控制发送速率。 // Scan 用于扫描此扫描器的目标 IP 地址和端口。
func (ss *SynScanner) Scan(dstIp net.IP, dst uint16) (err error) {if ss.isDone {return io.EOF // 如果扫描已完成则返回 EOF 错误}// 当队列缓冲区达到一定比例时调整发送速率if len(ss.openPortChan)*10 cap(ss.openPortChan)*8 {if ss.option.Rate/2 ! 0 {ss.limiter.SetLimit(limiter.Every(time.Second / time.Duration(ss.option.Rate/2)))}} else if len(ss.openPortChan)*10 cap(ss.openPortChan)*9 {ss.limiter.SetLimit(1)} else {ss.limiter.SetLimit(limiter.Every(time.Second / time.Duration(ss.option.Rate)))}dstIp dstIp.To4()if dstIp nil {return errors.New(不是 IPv4 地址) // 如果不是 IPv4 地址则返回错误}// 观察 IP首先更新其时间ipStr : dstIp.String()ss.watchIpStatusT.UpdateLastTime(ipStr)// 首先获取应该发送数据包的 MAC 地址。var dstMac net.HardwareAddrif ss.gwMac ! nil {dstMac ss.gwMac} else {// 如果是内网 IP则从缓存中获取 MAC 地址否则获取该 IP 的 MAC 地址mac : ss.watchMacCacheT.GetMac(ipStr)if mac ! nil {dstMac mac} else {dstMac, err ss.getHwAddrV4(dstIp)if err ! nil {return}}}// 构建所需的所有网络层eth : layers.Ethernet{SrcMAC: ss.srcMac,DstMAC: dstMac,EthernetType: layers.EthernetTypeIPv4,}ip4 : layers.IPv4{SrcIP: ss.srcIp,DstIP: dstIp,Version: 4,TTL: 128,Id: uint16(40000 rand.Intn(10000)),Flags: layers.IPv4DontFragment,Protocol: layers.IPProtocolTCP,}tcp : layers.TCP{SrcPort: layers.TCPPort(49000 rand.Intn(10000)), // 随机源端口用于确定接收目标端口范围DstPort: layers.TCPPort(dst),SYN: true,Window: 65280,Seq: uint32(500000 rand.Intn(10000)),Options: []layers.TCPOption{{OptionType: layers.TCPOptionKindMSS,OptionLength: 4,OptionData: []byte{0x05, 0x50}, // 1360},{OptionType: layers.TCPOptionKindNop,},{OptionType: layers.TCPOptionKindWindowScale,OptionLength: 3,OptionData: []byte{0x08},},{OptionType: layers.TCPOptionKindNop,},{OptionType: layers.TCPOptionKindNop,},{OptionType: layers.TCPOptionKindSACKPermitted,OptionLength: 2,},},}tcp.SetNetworkLayerForChecksum(ip4)// 每次循环迭代发送一个数据包直到发送完毕ss.send(eth, ip4, tcp)return
}func (ss *SynScanner) Wait() Wait 方法用于等待扫描器完成所有扫描任务。它首先等待IP状态监视表为空然后等待扫描结果通道中的数据发送完毕。 func (ss *SynScanner) Wait() {// Delay 2s for a reply from the last packetfor i : 0; i 20; i {if ss.watchIpStatusT.IsEmpty() {break}time.Sleep(time.Millisecond * 100)}// wait inside chan is emptyfor len(ss.openPortChan) ! 0 {time.Sleep(time.Millisecond * 20)}// wait portProbe taskss.portProbeWg.Wait()
} func (ss *SynScanner) Close() Close 方法用于关闭扫描器清理相关资源包括关闭 pcap 实例、关闭通道并将 SynScanner 结构体标记为已完成状态。 // Close 清理处理程序和通道。
func (ss *SynScanner) Close() {ss.isDone true // 标记扫描完成if ss.handle ! nil {// 在 Linux 下如果没有数据包要嗅探pcap 不能使用 BlockForever 停止// 参考https://github.com/google/gopacket/issues/890// 参考https://github.com/google/gopacket/issues/1089if runtime.GOOS linux {// 创建一个 ARP 数据包发送以关闭 pcap使用自身 MAC 地址和 IP 地址eth : layers.Ethernet{SrcMAC: ss.srcMac,DstMAC: ss.srcMac,EthernetType: layers.EthernetTypeARP,}arp : layers.ARP{AddrType: layers.LinkTypeEthernet,Protocol: layers.EthernetTypeIPv4,HwAddressSize: 6,ProtAddressSize: 4,Operation: layers.ARPReply,SourceHwAddress: []byte(ss.srcMac),SourceProtAddress: []byte(ss.srcIp),DstHwAddress: []byte(ss.srcMac),DstProtAddress: []byte(ss.srcIp),}// 打开一个新的 pcap 句柄发送 ARP 数据包并关闭句柄handle, _ : pcap.OpenLive(ss.devName, 1024, false, time.Second)buf : ss.bufPool.Get().(gopacket.SerializeBuffer)gopacket.SerializeLayers(buf, ss.opts, eth, arp)handle.WritePacketData(buf.Bytes())handle.Close()buf.Clear()ss.bufPool.Put(buf)}ss.handle.Close() // 关闭 pcap 句柄}// 关闭观察 MAC 地址表和 IP 状态表if ss.watchMacCacheT ! nil {ss.watchMacCacheT.Close()}if ss.watchIpStatusT ! nil {ss.watchIpStatusT.Close()}// 清理变量并关闭通道ss.watchMacCacheT nilss.watchIpStatusT nilclose(ss.openPortChan)close(ss.retChan)
}func (ss *SynScanner) Wait() WaitLimiter 方法用于等待速率限制它调用了 limiter.Wait() 方法确保发送速率不超过预设的限制。 func (ss *SynScanner) WaitLimiter() error {return ss.limiter.Wait(ss.ctx)
}func (ss *SynScanner) GetDevName() string GetDevName 方法用于返回扫描器所选设备的名称 // GetDevName Get the device name after the route selection
func (ss *SynScanner) GetDevName() string {return ss.devName
} func (ss *SynScanner) portProbeHandle() portProbeHandle 方法是一个协程用于处理端口扫描结果。它不断从 openPortChan 通道中获取扫描结果并针对每个端口进行服务识别和探测。如果启用了服务识别和 HTTP 探测会调用 fingerprint.PortIdentify 和 fingerprint.ProbeHttpInfo 方法识别端口所提供的服务类型和 HTTP 信息并根据需要更新 _openIpPort 的相关信息。最后将结果发送到 retChan 通道中。 func (ss *SynScanner) portProbeHandle() {for openIpPort : range ss.openPortChan {ss.portProbeWg.Add(1)go func(_openIpPort port.OpenIpPort) {if _openIpPort.Port ! 0 {if ss.option.FingerPrint {ss.WaitLimiter()_openIpPort.Service, _openIpPort.Banner, _ fingerprint.PortIdentify(tcp, _openIpPort.Ip, _openIpPort.Port, 2*time.Second)}if ss.option.Httpx (_openIpPort.Service || _openIpPort.Service http || _openIpPort.Service https) {ss.WaitLimiter()_openIpPort.HttpInfo, _ fingerprint.ProbeHttpInfo(_openIpPort.Ip, _openIpPort.Port, 2*time.Second)if _openIpPort.HttpInfo ! nil {if strings.HasPrefix(_openIpPort.HttpInfo.Url, https) {_openIpPort.Service https} else {_openIpPort.Service http}}}}ss.retChan - _openIpPortss.portProbeWg.Done()}(openIpPort)}
}func (ss *SynScanner) getHwAddrV4(arpDst net.IP) (mac net.HardwareAddr, err error) getHwAddrV4 方法用于获取数据包的目标硬件地址它发送 ARP 请求并等待 ARP 回复以获取目标 IP 的 MAC 地址。在循环中它会不断发送 ARP 请求并尝试从缓存中获取 MAC 地址直到获取到回复或超时为止 // getHwAddrV4 获取我们数据包的目标硬件地址。
func (ss *SynScanner) getHwAddrV4(arpDst net.IP) (mac net.HardwareAddr, err error) {ipStr : arpDst.String()// 检查是否需要监视此 IP 的 ARPif ss.watchMacCacheT.IsNeedWatch(ipStr) {return nil, errors.New(此 IP 的 ARP 已在监视中)}ss.watchMacCacheT.UpdateLastTime(ipStr) // 更新 IP 监视时间// 准备发送 ARP 请求的网络层。eth : layers.Ethernet{SrcMAC: ss.srcMac,DstMAC: net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, // 目标 MAC 地址为广播地址EthernetType: layers.EthernetTypeARP,}arp : layers.ARP{AddrType: layers.LinkTypeEthernet,Protocol: layers.EthernetTypeIPv4,HwAddressSize: 6,ProtAddressSize: 4,Operation: layers.ARPRequest, // ARP 请求SourceHwAddress: []byte(ss.srcMac),SourceProtAddress: []byte(ss.srcIp),DstHwAddress: []byte{0, 0, 0, 0, 0, 0}, // 目标硬件地址为空DstProtAddress: []byte(arpDst), // 目标 IP 地址}// 发送 ARP 请求if err ss.sendArp(eth, arp); err ! nil {return nil, err}start : time.Now() // 记录开始时间var retry int// 循环等待获取 ARP 回复for {mac ss.watchMacCacheT.GetMac(ipStr) // 获取缓存的 MAC 地址if mac ! nil {return mac, nil // 如果找到了 MAC 地址返回}// 等待 600 毫秒获取 ARP 回复if time.Since(start) time.Millisecond*600 {return nil, errors.New(获取 ARP 回复超时)}retry 1// 每 25 次尝试重新发送 ARP 请求if retry%25 0 {if err ss.send(eth, arp); err ! nil {return nil, err}}time.Sleep(time.Millisecond * 10) // 休眠 10 毫秒}
}func (ss *SynScanner) send(l ...gopacket.SerializableLayer) errorfunc (ss *SynScanner) sendArp(l ...gopacket.SerializableLayer) error send 和 sendArp 方法用于发送数据包到网络。send 方法用于发送TCP数据包而 sendArp 方法用于发送ARP请求。注意到在 sendArp 方法中发送的ARP包长度被硬编码为42字节可能存在需要校正的情况。 // send sends the given layers as a single packet on the network.
func (ss *SynScanner) send(l ...gopacket.SerializableLayer) error {buf : ss.bufPool.Get().(gopacket.SerializeBuffer)defer func() {buf.Clear()ss.bufPool.Put(buf)}()if err : gopacket.SerializeLayers(buf, ss.opts, l...); err ! nil {return err}return ss.handle.WritePacketData(buf.Bytes())
}// send sends the given layers as a single packet on the network., need fix padding
func (ss *SynScanner) sendArp(l ...gopacket.SerializableLayer) error {buf : ss.bufPool.Get().(gopacket.SerializeBuffer)defer func() {buf.Clear()ss.bufPool.Put(buf)}()if err : gopacket.SerializeLayers(buf, ss.opts, l...); err ! nil {return err}return ss.handle.WritePacketData(buf.Bytes()[:42]) // need fix padding
} func (ss *SynScanner) recv() recv 方法负责接收网络上的数据包。它首先解析数据包的各个层级Ethernet、IPv4、TCP、ARP并根据协议类型进行处理。对于ARP包它会解析获取到的IP地址和MAC地址并更新缓存。对于TCP包它会匹配源IP和端口并根据特定条件发送TCP响应包并将开放端口的信息发送到 openPortChan 通道中 func (ss *SynScanner) recv() {// 初始化网络层数据eth : layers.Ethernet{SrcMAC: ss.srcMac,DstMAC: nil,EthernetType: layers.EthernetTypeIPv4,}ip4 : layers.IPv4{SrcIP: ss.srcIp,DstIP: []byte{}, // 空的目标 IP 地址Version: 4,TTL: 64,Protocol: layers.IPProtocolTCP,}tcp : layers.TCP{SrcPort: 0,DstPort: 0,RST: true,ACK: true,Seq: 1,}// 解码相关的网络层var ipLayer layers.IPv4var tcpLayer layers.TCPvar arpLayer layers.ARPvar ethLayer layers.Ethernetvar foundLayerTypes []gopacket.LayerType// 创建一个包解析器用于解析数据包中的各层信息parser : gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet,ethLayer,ipLayer,tcpLayer,arpLayer,)// 全局变量var err errorvar data []bytevar ipStr stringvar _port uint16for {// 读取下一个数据包data, _, err ss.handle.ReadPacketData()if err ! nil {if err io.EOF {return // 如果出现 EOF则退出循环}continue // 继续读取下一个数据包}// 如果操作已完成则退出循环if ss.isDone {return}// 解码 TCP 或 ARP 数据包err parser.DecodeLayers(data, foundLayerTypes)if len(foundLayerTypes) 0 {continue // 如果未找到任何层信息继续下一个数据包的解析}// 解析 ARP 数据包if arpLayer.SourceProtAddress ! nil {ipStr net.IP(arpLayer.SourceProtAddress).String()if ss.watchMacCacheT.IsNeedWatch(ipStr) {ss.watchMacCacheT.SetMac(ipStr, arpLayer.SourceHwAddress)}arpLayer.SourceProtAddress nil // 清除 ARP 解析状态continue}// 匹配 IP 和端口的 TCP 数据包if tcpLayer.DstPort ! 0 tcpLayer.DstPort 49000 tcpLayer.DstPort 59000 {ipStr ipLayer.SrcIP.String()_port uint16(tcpLayer.SrcPort)if !ss.watchIpStatusT.HasIp(ipStr) { // 检查 IPcontinue} else {if ss.watchIpStatusT.HasPort(ipStr, _port) { // 检查端口continue} else {ss.watchIpStatusT.RecordPort(ipStr, _port) // 记录端口}}// 如果收到 SYN 和 ACK则将结果发送到 openPortChanif tcpLayer.SYN tcpLayer.ACK {ss.openPortChan - port.OpenIpPort{Ip: ipLayer.SrcIP,Port: _port,}// 回复目标eth.DstMAC ethLayer.SrcMACip4.DstIP ipLayer.SrcIPtcp.DstPort tcpLayer.SrcPorttcp.SrcPort tcpLayer.DstPort// RST ACKtcp.Ack tcpLayer.Seq 1tcp.Seq tcpLayer.Acktcp.SetNetworkLayerForChecksum(ip4)ss.send(eth, ip4, tcp) // 发送响应}tcpLayer.DstPort 0 // 清除 TCP 解析状态}}
}2、/core/port/syn/syn_test.go
这份代码中的结构体和方法都是占位符它们并没有真正的实现功能而是简单地返回错误或空值。可能是为了暴露接口并允许使用者在不同情况下自定义更多功能的实现。 type synScanner struct { } 定义了一个 synScanner 结构体但它并未实现 port.SynScanner 接口。 NewSynScanner 函数 这是一个函数签名该函数预期创建一个 synScanner 类型的结构体。它接受三个参数firstIp 用于选择路由retChan 是结果返回通道option 是端口扫描的选项配置。然而在这个实现中它只是返回一个 nil 结构体和一个 ErrorNoSyn 错误意味着没有实际功能被实现。 Scan、WaitLimiter、Wait 和 Close 方法 这些方法是 synScanner 结构体的方法但它们只是返回了 nil 或空的操作。它们用于实现 port.SynScanner 接口但在这个实现中并未真正执行任何操作。 GetAllDevs 函数 这个函数用于获取所有设备信息但类似其他方法它只是返回一个空字符串和 ErrorNoSyn 错误。 //go:build nosynpackage synimport (github.com/XinRoom/go-portScan/core/portnet
)type synScanner struct {
}// NewSynScanner firstIp: Used to select routes; openPortChan: Result return channel
func NewSynScanner(firstIp net.IP, retChan chan port.OpenIpPort, option port.Option) (ss *synScanner, err error) {return nil, ErrorNoSyn
}func (ss *synScanner) Scan(dstIp net.IP, dst uint16) error {return nil
}
func (ss *synScanner) WaitLimiter() error {return nil
}
func (ss *synScanner) Wait() {}
func (ss *synScanner) Close() {}func GetAllDevs() (string, error) {return , ErrorNoSyn
} 3、/core/port/syn/comm.go 这个主要设置syn扫描选项的一些初始值 package synimport (errorsgithub.com/XinRoom/go-portScan/core/port
)var ErrorNoSyn errors.New(no syn support)var DefaultSynOption port.Option{Rate: 1500,Timeout: 800,
}