获取内网活跃IP

获取内网活跃IP #

https://rogerzhu.gitbooks.io/-tcp-udp-ip/content/chapter1/arp-lian-jie-mac-he-ip.html

内网广播ARP Request #

ARP(Address Resolution Protocol),地址解析协议,是根据IP地址获取物理地址的一个TCP/IP协议。主机发送信息时将包含目标IP地址的ARP请求广播到网络上的所有主机,并接收返回信息,以此确认目标的物理地址。

当我们要向以太网中另一台主机发送IP数据时,我们本地会根据目的主机的IP地址在ARP高速缓存中查询相应的以太网地址,ARP高速缓存是主机维护的一个IP地址到相应以太网地址的映射表。如果查询失败,ARP会广播一个询问(op字段为1)目的主机硬件地址的报文,等待目标主机的响应。 因为ARP高速缓存有时效性,读取到目标主机的硬件地址后,最好发送一个ICMP包验证目标是否在线。当然也可以选择不从高速缓存里读取数据,而是直接并发发送arp包,等待在线主机回应ARP报文。

通过内网IP和子网掩码计算内网IP范围 #

// 获取所有网卡
func Test_Net(t *testing.T) {
	// 获取所有网卡接口
	interfaces, err := net.Interfaces()
	if err != nil {
		fmt.Println("Error:", err)
		return
	}

	for _, iface := range interfaces {
		fmt.Printf("Name: %s\n", iface.Name)
		fmt.Printf("MTU: %d\n", iface.MTU)
		fmt.Printf("HardwareAddr: %s\n", iface.HardwareAddr)
		fmt.Printf("Flags: %s\n", iface.Flags)

		// 获取每个接口的地址信息
		addrs, err := iface.Addrs()
		if err != nil {
			fmt.Println("Error:", err)
			continue
		}

		for i, addr := range addrs {
			fmt.Printf("  Addr: %s\n", addr.String())
			ip, ipNet, err := net.ParseCIDR(addr.String())
			if err != nil {
				fmt.Println("Error:", err.Error())
			} else {
				if ip.To4() != nil && isPrivateIp(ip) {
					fmt.Printf("IP: %s\n", ip.String())
					fmt.Println("子网掩码", string(ip.Mask(ip.DefaultMask())))
					it, _ := net.InterfaceByIndex(i)
					fmt.Println("Mac地址:", it.HardwareAddr)
					fmt.Println("IP范围:", Table(ipNet))

				}
			}
		}
	}
}
type IP uint32

// 根据IP和mask换算内网IP范围
func Table(ipNet *net.IPNet) []IP {
	ip := ipNet.IP.To4()
	fmt.Println("本机ip:", ip)
	var min, max IP
	var data []IP
	for i := 0; i < 4; i++ {
		b := IP(ip[i] & ipNet.Mask[i])
		min += b << ((3 - uint(i)) * 8)
	}
	one, _ := ipNet.Mask.Size()
	max = min | IP(math.Pow(2, float64(32-one))-1)
	fmt.Println("内网IP范围:", min, " --- ", max)
	// max 是广播地址,忽略
	// i & 0x000000ff  == 0 是尾段为0的IP,根据RFC的规定,忽略
	for i := min; i < max; i++ {
		if i&0x000000ff == 0 {
			continue
		}
		data = append(data, i)
	}
	return data
}
//判断是否是内网IP
func isPrivateIp(ip net.IP) bool {
	pricateIPBlocks := []*net.IPNet{
		{IP: net.ParseIP("10.0.0.0"), Mask: net.CIDRMask(8, 32)},
		{IP: net.ParseIP("172.16.0.0"), Mask: net.CIDRMask(12, 32)},
		{IP: net.ParseIP("192.168.0.0"), Mask: net.CIDRMask(16, 32)},
	}
	for _, block := range pricateIPBlocks {
		if block.Contains(ip) {
			return true
		}
	}
	return false
}

gopacket有封装好的ARP报文:

type ARP struct {
	BaseLayer
	AddrType          LinkType     // 硬件类型
	Protocol          EthernetType // 协议类型
	HwAddressSize     uint8        // 硬件地址长度
	ProtAddressSize   uint8        // 协议地址长度
	Operation         uint16       // 操作符(1代表request 2代表reply)
	SourceHwAddress   []byte       // 发送者硬件地址
	SourceProtAddress []byte       // 发送者IP地址
	DstHwAddress      []byte       // 目标硬件地址(可以填写00:00:00:00:00:00)
	DstProtAddress    []byte       // 目标IP地址
}

给出项目中具体的代码:

// 发送arp包
// ip 目标IP地址
func sendArpPackage(ip IP) {
    srcIp := net.ParseIP(ipNet.IP.String()).To4()
    dstIp := net.ParseIP(ip.String()).To4()
    if srcIp == nil || dstIp == nil {
        log.Fatal("ip 解析出问题")
    }
    // 以太网首部
    // EthernetType 0x0806  ARP
    ether := &layers.Ethernet{
        SrcMAC: localHaddr,
        DstMAC: net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
        EthernetType: layers.EthernetTypeARP,
    }
    
    a := &layers.ARP{
        AddrType: layers.LinkTypeEthernet,
        Protocol: layers.EthernetTypeIPv4,
        HwAddressSize: uint8(6),
        ProtAddressSize: uint8(4),
        Operation: uint16(1), // 0x0001 arp request 0x0002 arp response
        SourceHwAddress: localHaddr,
        SourceProtAddress: srcIp,
        DstHwAddress: net.HardwareAddr{0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
        DstProtAddress: dstIp,
    }
    
    buffer := gopacket.NewSerializeBuffer()
    var opt gopacket.SerializeOptions
    gopacket.SerializeLayers(buffer, opt, ether, a)
    outgoingPacket := buffer.Bytes()
    
    handle, err := pcap.OpenLive(iface, 2048, false, 30 * time.Second)
    if err != nil {
        log.Fatal("pcap打开失败:", err)
    }
    defer handle.Close()
    
    err = handle.WritePacketData(outgoingPacket)
    if err != nil {
        log.Fatal("发送arp数据包失败..")
    }
}

我们只需要将第一步得到的内网IP表,开启一个goruntime遍历发送arp报文就可以。

监听并抓取ARP Response包,记录IP和Mac地址 #

在上一步已经发送了arp请求,只需要开启一个arp的监听goruntime,所有有返回arp response包的,就是内网在线的host。

func listenARP(ctx context.Context) {
    handle, err := pcap.OpenLive(iface, 1024, false, 10 * time.Second)
    if err != nil {
        log.Fatal("pcap打开失败:", err)
    }
    defer handle.Close()
    handle.SetBPFFilter("arp")
    ps := gopacket.NewPacketSource(handle, handle.LinkType())
    for {
        select {
        case <-ctx.Done():
            return
        case p := <-ps.Packets():
            arp := p.Layer(layers.LayerTypeARP).(*layers.ARP)
            if arp.Operation == 2 {
                mac := net.HardwareAddr(arp.SourceHwAddress)
                pushData(ParseIP(arp.SourceProtAddress).String(), mac, "", manuf.Search(mac.String()))
                go sendMdns(ParseIP(arp.SourceProtAddress), mac)
                go sendNbns(ParseIP(arp.SourceProtAddress), mac)
            }
        }
    }
}

发活跃IP发送MDNS和NBNS包,并监听和解析hostname #

在上一步的过程中,我们在接受到一个arp的response后,就可以发起mdns和nbns包等待hostname的返回。

go sendMdns(ParseIP(arp.SourceProtAddress), mac)
go sendNbns(ParseIP(arp.SourceProtAddress), mac)

mDNS:往对方的5353端口和01:00:5E:00:00:FB的mac地址发送UDP的mdns(Multicast DNS)包,如果目标系统支持,回返回host name。详细协议介绍和报文格式可以查看维基百科的介绍。 NBNS:也是一个种常见的查看目标机器hostname的一种协议,和mDNS一样,传输层也是UDP,端口是在137。 篇幅太长了,具体的代码请看github上的nbns.go 和 mdns.go。

根据Mac地址计算出厂家信息 #

我们可以通过目标主机的硬件地址,获取到设备的生产厂家信息。这样的话,即使遇到防御比较好的系统,我们无法获取到hostname,也能从厂家信息里获取一定的信息量,比如厂家信息是oneplus或则Smartisan,就可以判断是手机了 文件片段:

00:03:8F	Weinsche	Weinschel Corporation
00:03:90	DigitalV	Digital Video Communications, Inc.
00:03:91	Advanced	Advanced Digital Broadcast, Ltd.
00:03:92	HyundaiT	Hyundai Teletek Co., Ltd.
00:03:93	Apple	Apple, Inc.
00:03:94	ConnectO	Connect One
00:03:95	Californ	California Amplifier
00:03:96	EzCast	EZ Cast Co., Ltd.
00:03:97	Watchfro	Watchfront Limited
package manuf

import (
    "os"
    "bufio"
    "strings"
    "io"
    "strconv"
    "runtime"
    "path"
)

const hexDigit = "0123456789ABCDEF"

var d map[int]interface{}

func init() {
    d = make(map[int]interface{})
    _, file, _, _ := runtime.Caller(0)
    f := path.Join(path.Dir(file), "manuf")
    err := readLine(f, func(s string) {
        l := strings.Split(s, "\t")
        if len(l) > 2 {
            parse(l[0], l[2])
        }
    })
    if err != nil {
        panic(err)
    }
}

func parse(mac, comment string) {
    g := strings.Split(mac, "/")
    m := strings.Split(g[0], ":")
    var b int
    if len(g) != 2 {
        b = 48 - len(m) * 8
    } else {
        b, _ = strconv.Atoi(g[1])
    }
    if _, ok := d[b]; !ok {
        d[b] = make(map[uint64]string)
        
    }
    d[b].(map[uint64]string)[b2uint64(m)] = comment
}
func b2uint64(sList []string) uint64 {
    var t uint64
    for i, b := range sList {
        l := strings.Index(hexDigit, string(b[0]))
        r := strings.Index(hexDigit, string(b[1]))
        t += uint64((l << 4) + r) << uint8((6 - i - 1) * 8)
    }
    
    return t
}

func Search(mac string) string {
    s := strings.Split(strings.ToUpper(mac) , ":")
    bint := b2uint64(s)
    for b := range d {
        k := 48 - b
        bint = (bint >> uint8(k)) << uint8(k)
        if _, ok := d[b].(map[uint64]string)[bint]; ok {
            return d[b].(map[uint64]string)[bint]
        }
    }
    return ""
}

func readLine(fileName string, handler func(string)) error {
    f, err := os.Open(fileName)
    defer f.Close()
    if err != nil {
        return err
    }
    buf := bufio.NewReader(f)
    for {
        line, err := buf.ReadString('\n')
        line = strings.TrimSpace(line)
        handler(line)
        if err != nil {
            if err == io.EOF {
                return nil
            }
            return err
        }
    }
    return nil
}

通过pro-bing包获取内网活跃IP #

func Test_GenerateIPs(t *testing.T) {
	ips := generateIPs("192.168.1.", 1, 254)
	var wg sync.WaitGroup

	for _, ip := range ips {
		wg.Add(1)
		go func(ip string) {
			defer wg.Done()

			pinger, err := probing.NewPinger(ip)
			if err != nil {
				fmt.Println("Error creating pinger:", err)
				return
			}
			pinger.SetPrivileged(true) //windows要加这一句
			pinger.Count = 1
			pinger.Timeout = 100 * time.Millisecond
			err = pinger.Run()
			if err != nil {
				fmt.Println(err.Error(), "   ", ip)
			} else {
				if pinger.Statistics().PacketsRecv > 0 {
					fmt.Println("Active IP:", ip, "    ", pinger.Statistics().PacketsRecv)
				}
			}
		}(ip)
	}

	wg.Wait()
}
func generateIPs(base string, start, end int) []string {
	var ips []string
	for i := start; i <= end; i++ {
		ips = append(ips, fmt.Sprintf("%s%d", base, i))
	}
	return ips
}

设置网关IP地址、子网掩码等信息 #

// 设置本地ip地址、子网掩码、默认网关、DNS、备用DNS,是否自动获取ip地址
func Test_SetWork(t *testing.T) {
	// 修改网卡配置,假设网卡名为"Ethernet"
	if err := setWindowsNetworkConfig("以太网 3", "192.168.1.10", "255.255.0.0", "192.168.1.10", "8.9.9.9", "8.8.9.4", true); err != nil {
		fmt.Println("Error:", err)
	} else {
		fmt.Println("Network configuration updated successfully")
	}
}

func setWindowsNetworkConfig(interfaceName, ip, subnet, gateway, dns1, dns2 string, autoSetIp bool) error {
	if autoSetIp {
		cmd := exec.Command("netsh", "interface", "ip", "set", "address", fmt.Sprintf("name=%s", interfaceName), "source=dhcp")
		if err := cmd.Run(); err != nil {
			return fmt.Errorf("failed to set IP address and subnet mask: %w", err)
		}
		return nil
	}
	// 设置IP地址和子网掩码
	cmd := exec.Command("netsh", "interface", "ip", "set", "address", fmt.Sprintf("name=%s", interfaceName), "source=static", fmt.Sprintf("addr=%s", ip), fmt.Sprintf("mask=%s", subnet), fmt.Sprintf("gateway=%s", gateway))
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("failed to set IP address and subnet mask: %w", err)
	}
	if dns1 != "" {
		// 设置DNS服务器
		cmd = exec.Command("netsh", "interface", "ip", "set", "dns", interfaceName, "static", dns1, "register=primary")
		if err := cmd.Run(); err != nil {
			return fmt.Errorf("failed to set primary DNS server: %w", err)
		}
	}

	if dns2 != "" {
		// 设置备用DNS服务器
		cmd = exec.Command("netsh", "interface", "ip", "add", "dns", fmt.Sprintf("name=%s", interfaceName), fmt.Sprintf("addr=%s", dns2), "index=2")
		if err := cmd.Run(); err != nil {
			return fmt.Errorf("failed to set secondary DNS server: %w", err)
		}
	}

	return nil
}

获取所有网卡信息 #

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"regexp"
	"strings"

	"vmodel/network"
) 
type NICInfo struct {
	Name           string   `name:"网络接口名称"`
	Ip             string   `name:"IP地址"`
	SubnetMask     string   `name:"子网掩码"`
	DefaultGateway string   `name:"默认网关"`
	DNSServers     []string `name:"DNS服务器地址"`
}

func (n *NetworkService) GetNICList() (NICList []network.NICInfo, err error) {
	nicInfos := getNICInfosByCmd()
	// 获取所有网卡接口
	interfaces, err := net.Interfaces()
	if err != nil {
		fmt.Println("Error:", err)
		return
	}

	for _, iface := range interfaces {
		var nicInfo network.NICInfo
		nicInfo.Name = iface.Name

		// 获取每个接口的地址信息
		addrs, err := iface.Addrs()
		if err != nil {
			fmt.Println("Error:", err)
			continue
		}
		for _, addr := range addrs {
			ip, _, err := net.ParseCIDR(addr.String())
			if err != nil {
				fmt.Println("Error:", err.Error())
			}
			if ip.To4() != nil && isPrivateIp(ip) {
				ipNet, ok := addr.(*net.IPNet)
				if !ok {
					continue
				}
				nicInfo.Ip = ipNet.IP.String()
				mask := ipNet.Mask
				nicInfo.SubnetMask = fmt.Sprintf(" %d.%d.%d.%d", mask[0], mask[1], mask[2], mask[3])
			}
		}
		if info, ok := nicInfos[nicInfo.Name]; ok {
			if nicInfo.Ip == "" {
				nicInfo.Ip = info.Ip
			}
			if nicInfo.SubnetMask == "" {
				nicInfo.SubnetMask = info.SubnetMask
			}
			nicInfo.DefaultGateway = info.DefaultGateway
			nicInfo.DNSServers = info.DNSServers
		}
		NICList = append(NICList, nicInfo)
	}
	return

}

func isPrivateIp(ip net.IP) bool {
	pricateIPBlocks := []*net.IPNet{
		{IP: net.ParseIP("10.0.0.0"), Mask: net.CIDRMask(8, 32)},
		{IP: net.ParseIP("172.16.0.0"), Mask: net.CIDRMask(12, 32)},
		{IP: net.ParseIP("192.168.0.0"), Mask: net.CIDRMask(16, 32)},
	}
	for _, block := range pricateIPBlocks {
		if block.Contains(ip) {
			return true
		}
	}
	return false
}
func getNICInfosByCmd() (nicList map[string]network.NICInfo) {
	out, err := runCmd("netsh", "interface", "ipv4", "show", "config")
	if err != nil {
		return
	}
	var NICList [][]string
	var NICInfo []string
	lines := bytes.Split(out, []byte{'\r', '\n'})
	for _, line := range lines {
		if bytes.HasPrefix(line, []byte("接口 ")) && bytes.HasSuffix(line, []byte(" 的配置")) {
			if len(NICInfo) != 0 {
				var info = NICInfo
				NICList = append(NICList, info)
				NICInfo = []string{}
			}
		}
		if len(line) != 0 {
			NICInfo = append(NICInfo, string(line))
		}

	}
	if len(NICInfo) != 0 {
		NICList = append(NICList, NICInfo)
	}
	return stingsDispose(NICList)
}
func runCmd(args ...string) ([]byte, error) {
	removeUTF8BOM := func(b []byte) []byte {
		if len(b) >= 3 && b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF {
			return b[3:]
		}
		return b
	}
	f, err := os.CreateTemp("", "netcmd")
	if err != nil {
		return nil, err
	}
	f.Close()
	defer os.Remove(f.Name())
	cmd := fmt.Sprintf(`%s | Out-File "%s" -encoding UTF8`, strings.Join(args, " "), f.Name())
	out, err := exec.Command("powershell", "-Command", cmd).CombinedOutput()
	if err != nil {
		if len(out) != 0 {
			return nil, fmt.Errorf("%s failed: %v: %q", args[0], err, string(removeUTF8BOM(out)))
		}
		var err2 error
		out, err2 = os.ReadFile(f.Name())
		if err2 != nil {
			return nil, err2
		}
		if len(out) != 0 {
			return nil, fmt.Errorf("%s failed: %v: %q", args[0], err, string(removeUTF8BOM(out)))
		}
		return nil, fmt.Errorf("%s failed: %v", args[0], err)
	}
	out, err = os.ReadFile(f.Name())
	if err != nil {
		return nil, err
	}
	return removeUTF8BOM(out), nil
}

func stingsDispose(NICList [][]string) (nicList map[string]network.NICInfo) {
	nicList = make(map[string]network.NICInfo, 0)
	for _, nicInfo := range NICList {
		var nicinfo network.NICInfo
		nicinfo.DNSServers = []string{}
		for i, line := range nicInfo {
			if strings.HasPrefix(line, "接口 \"") && strings.HasSuffix(line, "\" 的配置") {
				f := line[len("接口 \""):]
				f = f[:len(f)-len("\" 的配置")]
				nicinfo.Name = f
				continue
			}
			if strings.Contains(line, "IP 地址:") {
				nicinfo.Ip = regexp.MustCompile(`(\d{1,3}\.){3}\d{1,3}`).FindString(line)
				continue
			}
			if strings.Contains(line, "子网前缀:") {
				match := regexp.MustCompile(`掩码\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})`).FindStringSubmatch(line)
				if len(match) > 0 {
					nicinfo.SubnetMask = match[1]
				}
				continue
			}
			if strings.Contains(line, "默认网关:") {
				nicinfo.DefaultGateway = regexp.MustCompile(`(\d{1,3}\.){3}\d{1,3}`).FindString(line)
				continue
			}
			if strings.Contains(line, "静态配置的 DNS 服务器:") {
				dns := regexp.MustCompile(`(\d{1,3}\.){3}\d{1,3}`).FindString(line)
				if len(dns) == 0 {
					continue
				}
				nicinfo.DNSServers = append(nicinfo.DNSServers, dns)
				if len(nicInfo) > i {
					nextInfo := nicInfo[i+1]
					if strings.HasPrefix(nextInfo, "               ") {
						nicinfo.DNSServers = append(nicinfo.DNSServers, regexp.MustCompile(`(\d{1,3}\.){3}\d{1,3}`).FindString(nextInfo))
					}
				}
				continue
			}
		}
		nicList[nicinfo.Name] = nicinfo
	}
	return
}