Go编写工具教程第二课 高并发主机发现

2021-02-04 18:05:52 浏览数 (1)

声明

本文作者:TheNorth

本文字数:2000

阅读时长:20分钟

附件/链接:点击查看原文下载

声明:请勿用作违法用途,否则后果自负

本文属于WgpSec原创奖励计划,未经许可禁止转载

前言

    这是Go编写工具教程第二课:高并发主机发现,本文持续连载在【狼组安全平台】plat.wgpsec.org

一、

什么是主机发现?

以我的理解,就是我们主动与目标主机建立通信,如果目标主机给了我们反馈,我们就认为该主机是存活的,否则就是死亡的。

二、

主机发现方式

基于ARP协议主机发现

ARP协议是一种实现将IP地址转换成主机的MAC地址的一种网络协议。

优点:扫描速度快、可靠

缺点:不可路由

基于ICMP协议的主机发现

优点:可路由

缺点:扫描速度慢,会被防火墙拦截

基于TCP、UDP协议的主机发现

优点:可路由,扫描主机的时候可以获取端口是否开放,很少被防火墙过滤

缺点:扫描速度慢

三、

实现一个高并发的主机发现工具

基于上面的方式,由于我们主要想既可以判断局域网的主机是否存活,又可以判断互联网上的主机是否存活,因此,我们决定采用ICMP协议来实现主机发现,由于ping是基于ICMP协议实现的,而且无论是windows还是Linux系统中,都自带ping程序,所以我们实现一个利用ping程序实现的一个高并发主机发现工具。

关于Go的一些基础知识,师傅们可以参考我的上篇文章

GO编写一个高并发端口扫描工具

实现一个非并发的主机发现工具

hostscan.go

代码语言:javascript复制
package IcmpScan

import (
	"io/ioutil"
	"log"
	"os/exec"
	"runtime"
	"strconv"
	"strings"
	"sync"
)

func ScanHost(ip string, count int) bool {
	var cmd = &exec.Cmd{}
    //判断当前系统是什么系统,不同的系统ping的使用稍有不同
	switch runtime.GOOS {
	case "windows":
        //go中使用exec.Command执行系统命令,每一个参数都各占一个位置
		cmd = exec.Command("ping", "-n", strconv.Itoa(count), ip)
	default:
		cmd = exec.Command("ping", "-c", strconv.Itoa(count), ip)
	}
	output, err := cmd.StdoutPipe()
	defer output.Close()
	cmd.Start()
	if err != nil {
		log.Fatal(err)
	} else {
		result, err := ioutil.ReadAll(output)
		if err != nil {
			log.Fatal(err)
		}
        //通过返回的内容中是否存在TTL字段来判断主机是否在线
		if strings.Contains(string(result), "TTL") || strings.Contains(string(result), "ttl") {
			return true
		} else {
			return false
		}
	}
	return false
}

main.go

代码语言:javascript复制
package main

import (
	"AstaGo/Tools/IcmpScan"
	"fmt"
	"time"
)

func main() {
	hosts := [...]string{
		"127.0.0.1", "110.242.68.4", "192.168.10.3"}
	start := time.Now()
	for _, host := range hosts {
		res := IcmpScan.ScanHost(host, 4)
		if res == true {
			fmt.Println("开放的主机", host)
		}
	}
	end := time.Since(start)
	fmt.Println("花费的时间", end)
}

高并发的主机发现工具

hostscan.go

代码语言:javascript复制
package IcmpScan

import (
	"io/ioutil"
	"log"
	"os/exec"
	"runtime"
	"strconv"
	"strings"
	"sync"
)

func ScanHost(ip string, count int) bool {
	var cmd = &exec.Cmd{}
	switch runtime.GOOS {
	case "windows":
		cmd = exec.Command("ping", "-n", strconv.Itoa(count), ip)
	default:
		cmd = exec.Command("ping", "-c", strconv.Itoa(count), ip)
	}
	output, err := cmd.StdoutPipe()
	defer output.Close()
	cmd.Start()
	if err != nil {
		log.Fatal(err)
	} else {
		result, err := ioutil.ReadAll(output)
		if err != nil {
			log.Fatal(err)
		}
		if strings.Contains(string(result), "TTL") || strings.Contains(string(result), "ttl") {
			return true
		} else {
			return false
		}
	}
	return false
}
//hostsChan存放要扫描的主机,resChan存放扫描的结果,exitChan存储每一个goroutine完成的状态,wg用来同步各个goroutiine
func ScanHostTasks(hostsChan chan string, resChan chan string, exitChan chan bool, wg *sync.WaitGroup) {
	defer wg.Done()
	for {
		host, ok := <-hostsChan
		if !ok {
			break
		} else {
			res := ScanHost(host, 4)
			if res {
				resChan <- host
			}
		}
	}
	exitChan <- true
}

main.go

代码语言:javascript复制
func main() {
	hosts := [...]string{
		"127.0.0.1", "110.242.68.4", "192.168.10.3"}

	tasksChan := make(chan string, len(hosts))
	resChan := make(chan string, len(hosts))
	exitChan := make(chan bool, 4)
	var wg sync.WaitGroup
	for _, host := range hosts {
		tasksChan <- host
	}
	close(tasksChan)
	start := time.Now()
	for i := 0; i < 4; i   {
		wg.Add(1)
		go IcmpScan.ScanHostTasks(tasksChan, resChan, exitChan, &wg)
	}
	wg.Wait()
	for i := 0; i < 4; i   {
		<-exitChan
	}
	close(resChan)
	end := time.Since(start)
	for {
		openhost, ok := <-resChan
		if !ok {
			break
		}
		fmt.Println("开放的主机", openhost)
	}
	fmt.Println("花费的时间", end)
}

四、

优化主机发现工具

使用flag包

main.go

代码语言:javascript复制
package main

import (
	"AstaGo/Tools/IcmpScan"
	"flag"
	"fmt"
	"strings"
	"sync"
	"time"
)

func main() {
	//hosts := [...]string{
	//	"127.0.0.1", "110.242.68.4", "192.168.10.3"}

	var hostslist string
	var gonum int
	flag.StringVar(&hostslist, "i", "", "输入要扫描的地址,默认为空")
	flag.IntVar(&gonum, "g", 1, "需要开启的goroutine的数量")
	flag.Parse()
	if len(hostslist) == 0 {
		fmt.Println("请输入要扫描的主机")
	} else {
		hosts := strings.Split(hostslist, ",")
		tasksChan := make(chan string, len(hosts))
		resChan := make(chan string, len(hosts))
		exitChan := make(chan bool, 4)
		var wg sync.WaitGroup
		for _, host := range hosts {
			tasksChan <- host
		}
		close(tasksChan)
		start := time.Now()
		for i := 0; i < gonum; i   {
			wg.Add(1)
			go IcmpScan.ScanHostTasks(tasksChan, resChan, exitChan, &wg)
		}
		wg.Wait()
		for i := 0; i < gonum; i   {
			<-exitChan
		}
		close(resChan)
		end := time.Since(start)
		for {
			openhost, ok := <-resChan
			if !ok {
				break
			}
			fmt.Println("开放的主机", openhost)
		}
		fmt.Println("花费的时间", end)
	}
}

后记

        使用系统自带的ping工具实现主机发现,但是扫描花费的时间还是比较长,但是有两个地方可以修改,一个是选择发包的的数量,目前设置的是4,如果只发一个包会提高效率,但是准确率可能会下降,虽然ping是基于ICMP协议实现的,下一步打算直接使用ICMP协议发现主机,看看效率能不能提高。

师傅们存在的问题或者建议可以留言,到 https://github.com/wgpsec/AstaGo提issue 。

0 人点赞