This is a creation in Article, where the information may have evolved or changed.
Article keywords
- Go/golang
- Gopacket
- Grab Bag
- Pcap/libpcap
- Arp
- Nbns
- MDNs
- Manuf
Program
Description
This article for the go language itself is not too much to explain, want to spend more time on a few network protocol explanation, I hope this article on the plan or are using go for TCP/IP programming and grasping the package of friends to bring help.
GitHub Address: Https://github.com/timest/goscan
Program Ideas
- Intranet IP range is calculated by intranet IP and subnet mask
- Broadcast ARP Request to intranet
- Listen and crawl ARP response packet, record IP and MAC address
- Sends active IP to send MDNs and NBNS packets, and listens and parses hostname
- Calculate the factory information according to the MAC address
Intranet IP range is calculated by intranet IP and subnet mask
If just know an IP address, is not aware of the network IP segment, can not simply change the last byte of the native IP to 1-255. Need to use subnet mask to calculate the network segment of the intranet, this piece is relatively simple, here do not repeat, there is doubt on-line search subnet mask to obtain more information. It is worth mentioning that the last field of the IP address is not 0 and 255, the former is RFC, the latter is generally the broadcast address.
// 单网卡模式addrs, err := net.InterfaceAddrs()if err != nil { log.Fatal("无法获取本地网络信息:", err)}for i, a := range addrs { if ip, ok := a.(*net.IPNet); ok && !ip.IP.IsLoopback() { if ip.IP.To4() != nil { fmt.Println("IP:", ip.IP) fmt.Println("子网掩码:", ip.Mask) it, _ := net.InterfaceByIndex(i) fmt.Println("Mac地址:", it.HardwareAddr) break } }}
According to the Ipnet obtained above, the intranet IP range can be calculated:
type IP uint32// 根据IP和mask换算内网IP范围func Table(ipNet *net.IPNet) []IP { ip := ipNet.IP.To4() log.Info("本机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) log.Infof("内网IP范围:%s --- %s", 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}
Broadcast ARP Request to intranet
ARP (Address Resolution Protocol), which is a TCP/IP protocol that obtains a physical address based on an IP address, is a protocol for addressing resolution. When the host sends the message, the ARP request containing the destination IP address is broadcast to all hosts on the network, and the return message is received to determine the destination's physical address------Baidu Encyclopedia
When we want to send the IP data to another host in the Ethernet, we will query the corresponding Ethernet address in the ARP cache based on the IP address of the destination host, and the ARP cache is a mapping table for the host to maintain an IP address to the corresponding Ethernet address. If the query fails, ARP broadcasts a message asking for the destination host hardware address (OP field 1), waiting for the target host to respond.
Because the ARP cache is sensitive, it is best to send an ICMP packet to verify that the target is online after it has been read to the destination host's hardware address. Of course, you can choose not to read the data from the cache, but directly send ARP packets, waiting for the online host to respond to the ARP message.
Arp
The Gopacket has encapsulated ARP messages:
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地址}
Give the specific code in the project:
Send ARP packet//IP Destination IP address 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 parsing Problem")}//Ethernet header//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, time. Second) If err! = Nil {log. Fatal ("Pcap Open failed:", err)} defer handle. Close () Err = handle. Writepacketdata (outgoingpacket) if err! = Nil {log. Fatal ("Sending ARP packet failed:") }}
We only need to take the first step to get the Intranet IP table, open a goruntime traversal send ARP message can be.
Listen and crawl ARP response packet, record IP and MAC address
In the previous step has been sent ARP request, only need to open an ARP listening goruntime, all have to return ARP response packet, is the intranet online 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) } } }}
Sends active IP to send MDNs and NBNS packets, and listens and parses hostname
In the previous step, after we received an ARP response, we were able to initiate MDNs and NBNS packets waiting for the return of the hostname.
go sendMdns(ParseIP(arp.SourceProtAddress), mac)go sendNbns(ParseIP(arp.SourceProtAddress), mac)
MDNS: Send UDP MDNS (multicast DNS) packets to each other's 5353 port and 01:00:5E:00:00:FB MAC address, if the target system supports, return to host Name Detailed protocol introduction and message format can be seen in Wikipedia's introduction.
NBNS: A common kind of protocol to view the target machine hostname, like mdns, the transport layer is also UDP, the port is at 137.
The length is too long, please see NBNS.GO and mdns.go on GitHub for a specific code.
Calculate the factory information according to the MAC address
We can obtain the manufacturer information of the device through the hardware address of the target host. In this case, even if you encounter a better defense system, we can not get to the hostname, but also from the factory information to obtain a certain amount, such as factory information is OnePlus or Smartisan, you can judge is a mobile phone
Manuf file, file fragment:
00:03:8F Weinsche Weinschel Corporation00: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 One00:03:95 Californ California Amplifier00:03:96 EzCast EZ Cast Co., Ltd.00:03:97 Watchfro Watchfront Limited
Code is not affixed, directly see the code, 100 lines of code, or is quite simple: manuf.go. The MAC address of test result 99% can be mapped to the corresponding vendor information.