分析某款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

相关文章

C++ Sort函数使用场景分析

《C++Sort函数使用场景分析》sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变,如果某些场景需要保持相同元素间的相对顺序,可使... 目录C++ Sort函数详解一、sort函数调用的两种方式二、sort函数使用场景三、sort函数排序

kotlin中const 和val的区别及使用场景分析

《kotlin中const和val的区别及使用场景分析》在Kotlin中,const和val都是用来声明常量的,但它们的使用场景和功能有所不同,下面给大家介绍kotlin中const和val的区别,... 目录kotlin中const 和val的区别1. val:2. const:二 代码示例1 Java

Go标准库常见错误分析和解决办法

《Go标准库常见错误分析和解决办法》Go语言的标准库为开发者提供了丰富且高效的工具,涵盖了从网络编程到文件操作等各个方面,然而,标准库虽好,使用不当却可能适得其反,正所谓工欲善其事,必先利其器,本文将... 目录1. 使用了错误的time.Duration2. time.After导致的内存泄漏3. jsO

go中空接口的具体使用

《go中空接口的具体使用》空接口是一种特殊的接口类型,它不包含任何方法,本文主要介绍了go中空接口的具体使用,具有一定的参考价值,感兴趣的可以了解一下... 目录接口-空接口1. 什么是空接口?2. 如何使用空接口?第一,第二,第三,3. 空接口几个要注意的坑坑1:坑2:坑3:接口-空接口1. 什么是空接

Spring事务中@Transactional注解不生效的原因分析与解决

《Spring事务中@Transactional注解不生效的原因分析与解决》在Spring框架中,@Transactional注解是管理数据库事务的核心方式,本文将深入分析事务自调用的底层原理,解释为... 目录1. 引言2. 事务自调用问题重现2.1 示例代码2.2 问题现象3. 为什么事务自调用会失效3

找不到Anaconda prompt终端的原因分析及解决方案

《找不到Anacondaprompt终端的原因分析及解决方案》因为anaconda还没有初始化,在安装anaconda的过程中,有一行是否要添加anaconda到菜单目录中,由于没有勾选,导致没有菜... 目录问题原因问http://www.chinasem.cn题解决安装了 Anaconda 却找不到 An

Spring定时任务只执行一次的原因分析与解决方案

《Spring定时任务只执行一次的原因分析与解决方案》在使用Spring的@Scheduled定时任务时,你是否遇到过任务只执行一次,后续不再触发的情况?这种情况可能由多种原因导致,如未启用调度、线程... 目录1. 问题背景2. Spring定时任务的基本用法3. 为什么定时任务只执行一次?3.1 未启用

利用Go语言开发文件操作工具轻松处理所有文件

《利用Go语言开发文件操作工具轻松处理所有文件》在后端开发中,文件操作是一个非常常见但又容易出错的场景,本文小编要向大家介绍一个强大的Go语言文件操作工具库,它能帮你轻松处理各种文件操作场景... 目录为什么需要这个工具?核心功能详解1. 文件/目录存javascript在性检查2. 批量创建目录3. 文件

C++ 各种map特点对比分析

《C++各种map特点对比分析》文章比较了C++中不同类型的map(如std::map,std::unordered_map,std::multimap,std::unordered_multima... 目录特点比较C++ 示例代码 ​​​​​​代码解释特点比较1. std::map底层实现:基于红黑

Spring、Spring Boot、Spring Cloud 的区别与联系分析

《Spring、SpringBoot、SpringCloud的区别与联系分析》Spring、SpringBoot和SpringCloud是Java开发中常用的框架,分别针对企业级应用开发、快速开... 目录1. Spring 框架2. Spring Boot3. Spring Cloud总结1. Sprin