分析某款go扫描器之五

2024-05-11 02:20

本文主要是介绍分析某款go扫描器之五,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、概述

前面几篇文章已经把实现的功能都说完了,这篇主要分析下启动函数,并且说明脚本的参数项作用。

项目来源:https://github.com/XinRoom/go-portScan/blob/main/util/file.go

二、cmd/go-portScan.go

1、设置全局变量

这些变量被定义为在整个程序中使用的全局变量,用于存储命令行参数的值


var (ipStr       stringportStr     stringpn          boolpt          boolsT          boolrate        intsV          booltimeout     intrateP       inthostGroup   intiL          stringdevices     boolnexthop     stringhttpx       boolnetLive     boolmaxOpenPort intoCsv        stringoFile       string
)

2、命令行参数的解析

  • func parseFlag(c *cli.Context)

这个函数的作用是解析命令行参数,并将解析后的值存储到全局变量中。它利用了 cli.Context 对象来获取命令行中各个选项的值,并将这些值存储到程序中事先定义好的全局变量中。

// 从命令行参数获取各个选项的值,并存储到对应的全局变量中
func parseFlag(c *cli.Context) {ipStr = c.String("ip")iL = c.String("iL")portStr = c.String("port")nexthop = c.String("nexthop")devices = c.Bool("devices")pn = c.Bool("Pn")rateP = c.Int("rateP")hostGroup = c.Int("hostGroup")pt = c.Bool("PT")rate = c.Int("rate")sT = c.Bool("sT")sV = c.Bool("sV")timeout = c.Int("timeout")httpx = c.Bool("httpx")netLive = c.Bool("netLive")maxOpenPort = c.Int("maxOpenPort")oCsv = c.String("oCsv")oFile = c.String("oFile")
}
  • func run(c *cli.Context) error

这个函数是应用程序的主要执行逻辑,主要负责根据命令行参数执行相应的操作。它首先根据不同的命令行参数进行一些设置,比如忽略特定信号、初始化日志记录器,并根据特定的选项执行相应的操作。例如:

  • 如果设置了 -nohup 选项,则忽略 SIGHUP 和 SIGTERM 信号。
  • 如果设置了 -devices 选项,则调用 syn.GetAllDevs() 函数来列出设备信息。
  • 如果未提供 IP 或端口参数,则显示应用程序的帮助信息并退出。
  • 如果端口参数是 -,则将其替换为默认的端口范围。

接下来的部分代码,根据不同的参数设置,进行了一系列的初始化和配置操作,包括解析 IP 和端口、初始化并发池、进行扫描任务、收集扫描结果等。

总体而言,run 函数是这个程序的核心逻辑部分,负责根据命令行参数配置和执行相应的扫描任务,以及对结果进行处理和输出。

func run(c *cli.Context) error {if c.NumFlags() == 0 {cli.ShowAppHelpAndExit(c, 0)}parseFlag(c)// 执行具体的扫描任务if c.Bool("nohup") {// 忽略SIGHUP和SIGTERM信号signal.Ignore(syscall.SIGHUP)signal.Ignore(syscall.SIGTERM)}myLog := util.NewLogger(oFile, true)// 检查是否需要列出设备信息if devices {if r, err := syn.GetAllDevs(); err != nil {myLog.Fatal(err.Error())} else {myLog.Print(r)}os.Exit(0)}// 检查IP和端口参数,如果参数为空,则显示应用程序帮助信息并退出if ipStr == "" && iL == "" {cli.ShowAppHelpAndExit(c, 0)}if portStr == "-" {portStr = "1-65535"}ipRangeGroup := make([]*iprange.Iter, 0)// ip parsevar firstIp net.IPvar ips []stringif ipStr != "" {ips = strings.Split(ipStr, ",")}if iL != "" {var err errorips, err = util.GetLines(iL)if err != nil {myLog.Fatalf("open file failed: %s", err.Error())}}for _, _ip := range ips {it, startIp, err := iprange.NewIter(_ip)if err != nil {var iprecords []net.IPiprecords, _ = net.LookupIP(_ip)if len(iprecords) > 0 {_ip = iprecords[0].String()} else {myLog.Fatalf("[error] %s is not ip/hostname!\n", _ip)}it, startIp, err = iprange.NewIter(_ip)if err != nil {myLog.Fatalf("[error] %s is not ip!\n", _ip)}}if firstIp == nil {firstIp = startIp}ipRangeGroup = append(ipRangeGroup, it)}// netLivevar wgIpsLive sync.WaitGroup// Pool - ipsLivepoolIpsLive, _ := ants.NewPoolWithFunc(rateP, func(ip interface{}) {_ip := ip.([]net.IP)for _, ip2 := range _ip {if host.IsLive(ip2.String(), pt, time.Duration(tcp.DefaultTcpOption.Timeout)*time.Millisecond) {myLog.Printf("[+] %s is live\n", ip2.String())break}}wgIpsLive.Done()})defer poolIpsLive.Release()if netLive {// 按c段探测for _, ir := range ipRangeGroup { // ip groupfor i := uint64(0); i < ir.TotalNum(); i = i + 256 { // ip indexip := make(net.IP, len(ir.GetIpByIndex(0)))copy(ip, ir.GetIpByIndex(i)) // Note: dup copy []byte when concurrent (GetIpByIndex not to do dup copy)ipLastByte := []byte{1, 2, 254, 253, byte(100 + rand.Intn(20)), byte(200 + rand.Intn(20))}ips2 := make([]net.IP, 6)for j := 0; j < 6; j++ {ips2[j] = make(net.IP, len(ip))ip[3] = ipLastByte[j]copy(ips2[j], ip)}wgIpsLive.Add(1)poolIpsLive.Invoke(ips2)}}wgIpsLive.Wait()return nil}// port parseports, err := port.ShuffleParseAndMergeTopPorts(portStr)if err != nil {myLog.Fatalf("[error] %s is not port!\n", err)}// recvsingle := make(chan struct{})retChan := make(chan port.OpenIpPort, 5000)// ip port num statusipPortNumMap := make(map[string]int) // 记录该IP端口开放数量var ipPortNumRW sync.RWMutex// csv outputvar csvFile *os.Filevar csvWrite *csv.Writerif oCsv != "" {csvFile, err = os.Create(oCsv)if err != nil {myLog.Fatalln("[-]", err)}defer csvFile.Close()csvWrite = csv.NewWriter(csvFile)csvWrite.Write([]string{"IP", "PORT", "SERVICE", "BANNER", "HTTP_TITLE", "HTTP_STATUS", "HTTP_SERVER", "HTTP_TLS", "HTTP_FINGERS"})}go func() {for ret := range retChan {if maxOpenPort > 0 {ipPortNumRW.Lock()if _, ok := ipPortNumMap[ret.Ip.String()]; ok {ipPortNumMap[ret.Ip.String()] += 1}ipPortNumRW.Unlock()}myLog.Println(ret.String())if csvWrite != nil {line := []string{ret.Ip.String(), strconv.Itoa(int(ret.Port)), ret.Service, "", "", "", "", "", ""}line[3] = strings.NewReplacer("\\r", "\r", "\\n", "\n").Replace(strings.Trim(strconv.Quote(string(ret.Banner)), "\""))if ret.HttpInfo != nil {line[4] = ret.HttpInfo.Titleline[5] = strconv.Itoa(ret.HttpInfo.StatusCode)line[6] = ret.HttpInfo.Serverline[7] = ret.HttpInfo.TlsCNline[8] = strings.Join(ret.HttpInfo.Fingers, ",")}csvWrite.Write(line)csvWrite.Flush()csvFile.Sync()}}single <- struct{}{}}()// Initialize the Scannervar s port.Scanneroption := port.Option{Rate:        rate,Timeout:     timeout,NextHop:     nexthop,FingerPrint: sV,Httpx:       httpx,}if sT {// tcpif option.Rate == -1 {option.Rate = tcp.DefaultTcpOption.Rate}if option.Timeout == -1 {option.Timeout = tcp.DefaultTcpOption.Timeout}s, err = tcp.NewTcpScanner(retChan, option)} else {// synif option.Rate == -1 {option.Rate = syn.DefaultSynOption.Rate}if option.Timeout == -1 {option.Timeout = syn.DefaultSynOption.Timeout}s, err = syn.NewSynScanner(firstIp, retChan, option)}if err != nil {fmt.Fprintf(os.Stderr, "[error] Initialize Scanner: %s\n", err)os.Exit(-1)}start := time.Now()var wgPing sync.WaitGroup// port scan funcportScan := func(ip net.IP) {var ipPortNum intvar ipPortNumOk boolif maxOpenPort > 0 {ipPortNumRW.Lock()ipPortNumMap[ip.String()] = 0ipPortNumRW.Unlock()}for _, _port := range ports { // ports.WaitLimiter() // limit rateif maxOpenPort > 0 {ipPortNumRW.RLock()ipPortNum, ipPortNumOk = ipPortNumMap[ip.String()]ipPortNumRW.RUnlock()if ipPortNumOk && ipPortNum >= maxOpenPort {break}}s.Scan(ip, _port)}if maxOpenPort > 0 {ipPortNumRW.Lock()delete(ipPortNumMap, ip.String())ipPortNumRW.Unlock()}}// host group scan funcvar wgHostScan sync.WaitGrouphostScan, _ := ants.NewPoolWithFunc(hostGroup, func(ip interface{}) {_ip := ip.(net.IP)portScan(_ip)wgHostScan.Done()})defer hostScan.Release()// Pool - ping and port scanpoolPing, _ := ants.NewPoolWithFunc(rateP, func(ip interface{}) {_ip := ip.(net.IP)if host.IsLive(_ip.String(), pt, time.Duration(option.Timeout)*time.Millisecond) {wgHostScan.Add(1)hostScan.Invoke(_ip)}wgPing.Done()})defer poolPing.Release()// start scanfor _, ir := range ipRangeGroup { // ip groupshuffle := util.NewShuffle(ir.TotalNum())    // shufflefor i := uint64(0); i < ir.TotalNum(); i++ { // ip indexip := make(net.IP, len(ir.GetIpByIndex(0)))copy(ip, ir.GetIpByIndex(shuffle.Get(i))) // Note: dup copy []byte when concurrent (GetIpByIndex not to do dup copy)if !pn {                                  // pingwgPing.Add(1)_ = poolPing.Invoke(ip)} else {wgHostScan.Add(1)hostScan.Invoke(ip)}}}wgPing.Wait()     // PING组wgHostScan.Wait() // HostGroupSs.Wait()          // 扫描器-等s.Close()         // 扫描器-收<-single          // 接收器-收myLog.Printf("[*] elapsed time: %s\n", time.Since(start))return nil
}
}

3、主函数

  • 1
  • main 函数是整个程序的入口点。
  • cli.App 结构中,通过设置 NameDescription 来定义应用程序的名称和描述。
  • Action: run 指定当执行命令时调用的函数为 run 函数。
  • Flags 列表包含了各种命令行选项,比如 -ip-port 等。每个选项使用 cli.Flag 定义了选项的名称、用法说明、是否必需以及默认值。
  • 这段代码的作用是初始化一个命令行应用程序,并定义了一系列命令行选项。当程序运行时,它会解析命令行参数,并根据这些参数执行相应的操作,其中 err := app.Run(os.Args) 表示运行应用程序,并使用 os.Args 中的参数作为输入参数。如果运行过程中出现错误,则会将错误信息打印到控制台。

func main() {app := &cli.App{Name:        "PortScan",Description: "High-performance port scanner",Action:      run,Flags: []cli.Flag{&cli.StringFlag{Name:     "ip",Usage:    "target ip, eg: \"1.1.1.1/30,1.1.1.1-1.1.1.2,1.1.1.1-2\"",Required: false,Value:    "",},&cli.StringFlag{Name:     "iL",Usage:    "target ip file, eg: \"ips.txt\"",Required: false,Value:    "",},&cli.StringFlag{Name:    "port",Aliases: []string{"p"},Usage:   "eg: \"top1000,5612,65120,-\"",Value:   "top1000",},&cli.BoolFlag{Name:  "Pn",Usage: "no ping probe",Value: false,},&cli.IntFlag{Name:    "rateP",Aliases: []string{"rp"},Usage:   "concurrent num when ping probe each ip",Value:   300,},&cli.IntFlag{Name:    "hostGroup",Aliases: []string{"hp"},Usage:   "host concurrent num",Value:   200,},&cli.BoolFlag{Name:  "PT",Usage: "use TCP-PING mode",Value: false,},&cli.BoolFlag{Name:  "sT",Usage: "TCP-mode(support IPv4 and IPv6)",Value: false,},&cli.IntFlag{Name:    "timeout",Aliases: []string{"to"},Usage:   "TCP-mode SYN-mode timeout. unit is ms.",Value:   800,},&cli.BoolFlag{Name:  "sS",Usage: "Use SYN-mode(Only IPv4)",Value: true,},&cli.StringFlag{Name:    "nexthop",Aliases: []string{"nh"},Usage:   "specified nexthop gw add to pcap dev",Value:   "",},&cli.IntFlag{Name:    "rate",Aliases: []string{"r"},Usage:   fmt.Sprintf("number of packets sent per second. If set -1, TCP-mode is %d, SYN-mode is %d(SYN-mode is restricted by the network adapter, 2000=1M)", tcp.DefaultTcpOption.Rate, syn.DefaultSynOption.Rate),Value:   -1,},&cli.BoolFlag{Name:    "devices",Aliases: []string{"ld"},Usage:   "list devices name",Value:   false,},&cli.BoolFlag{Name:  "sV",Usage: "port service identify",Value: false,},&cli.BoolFlag{Name:  "httpx",Usage: "http server identify",Value: false,},&cli.BoolFlag{Name:  "netLive",Usage: "Detect live C-class networks, eg: -ip 192.168.0.0/16,172.16.0.0/12,10.0.0.0/8",Value: false,},&cli.IntFlag{Name:    "maxOpenPort",Aliases: []string{"mop"},Usage:   "Stop the ip scan, when the number of open-port is maxOpenPort",Value:   0,},&cli.StringFlag{Name:    "oCsv",Aliases: []string{"oC"},Usage:   "output csv file",Value:   "",},&cli.StringFlag{Name:    "oFile",Aliases: []string{"o"},Usage:   "output to file",Value:   "",},&cli.BoolFlag{Name:  "nohup",Usage: "nohup",Value: false,},},}err := app.Run(os.Args)if err != nil {fmt.Println("err:", err)}
}

这篇关于分析某款go扫描器之五的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/978236

相关文章

使用Go语言开发一个命令行文件管理工具

《使用Go语言开发一个命令行文件管理工具》这篇文章主要为大家详细介绍了如何使用Go语言开发一款命令行文件管理工具,支持批量重命名,删除,创建,移动文件,需要的小伙伴可以了解下... 目录一、工具功能一览二、核心代码解析1. 主程序结构2. 批量重命名3. 批量删除4. 创建文件/目录5. 批量移动三、如何安

Springboot中分析SQL性能的两种方式详解

《Springboot中分析SQL性能的两种方式详解》文章介绍了SQL性能分析的两种方式:MyBatis-Plus性能分析插件和p6spy框架,MyBatis-Plus插件配置简单,适用于开发和测试环... 目录SQL性能分析的两种方式:功能介绍实现方式:实现步骤:SQL性能分析的两种方式:功能介绍记录

Go路由注册方法详解

《Go路由注册方法详解》Go语言中,http.NewServeMux()和http.HandleFunc()是两种不同的路由注册方式,前者创建独立的ServeMux实例,适合模块化和分层路由,灵活性高... 目录Go路由注册方法1. 路由注册的方式2. 路由器的独立性3. 灵活性4. 启动服务器的方式5.

最长公共子序列问题的深度分析与Java实现方式

《最长公共子序列问题的深度分析与Java实现方式》本文详细介绍了最长公共子序列(LCS)问题,包括其概念、暴力解法、动态规划解法,并提供了Java代码实现,暴力解法虽然简单,但在大数据处理中效率较低,... 目录最长公共子序列问题概述问题理解与示例分析暴力解法思路与示例代码动态规划解法DP 表的构建与意义动

Go语言中三种容器类型的数据结构详解

《Go语言中三种容器类型的数据结构详解》在Go语言中,有三种主要的容器类型用于存储和操作集合数据:本文主要介绍三者的使用与区别,感兴趣的小伙伴可以跟随小编一起学习一下... 目录基本概念1. 数组(Array)2. 切片(Slice)3. 映射(Map)对比总结注意事项基本概念在 Go 语言中,有三种主要

Go Mongox轻松实现MongoDB的时间字段自动填充

《GoMongox轻松实现MongoDB的时间字段自动填充》这篇文章主要为大家详细介绍了Go语言如何使用mongox库,在插入和更新数据时自动填充时间字段,从而提升开发效率并减少重复代码,需要的可以... 目录前言时间字段填充规则Mongox 的安装使用 Mongox 进行插入操作使用 Mongox 进行更

C#使用DeepSeek API实现自然语言处理,文本分类和情感分析

《C#使用DeepSeekAPI实现自然语言处理,文本分类和情感分析》在C#中使用DeepSeekAPI可以实现多种功能,例如自然语言处理、文本分类、情感分析等,本文主要为大家介绍了具体实现步骤,... 目录准备工作文本生成文本分类问答系统代码生成翻译功能文本摘要文本校对图像描述生成总结在C#中使用Deep

Go语言利用泛型封装常见的Map操作

《Go语言利用泛型封装常见的Map操作》Go语言在1.18版本中引入了泛型,这是Go语言发展的一个重要里程碑,它极大地增强了语言的表达能力和灵活性,本文将通过泛型实现封装常见的Map操作,感... 目录什么是泛型泛型解决了什么问题Go泛型基于泛型的常见Map操作代码合集总结什么是泛型泛型是一种编程范式,允

基于Go语言实现一个压测工具

《基于Go语言实现一个压测工具》这篇文章主要为大家详细介绍了基于Go语言实现一个简单的压测工具,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录整体架构通用数据处理模块Http请求响应数据处理Curl参数解析处理客户端模块Http客户端处理Grpc客户端处理Websocket客户端

Go中sync.Once源码的深度讲解

《Go中sync.Once源码的深度讲解》sync.Once是Go语言标准库中的一个同步原语,用于确保某个操作只执行一次,本文将从源码出发为大家详细介绍一下sync.Once的具体使用,x希望对大家有... 目录概念简单示例源码解读总结概念sync.Once是Go语言标准库中的一个同步原语,用于确保某个操