声明
本文作者: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 。