分析某款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语言采用了一种独特而明确的错误处理哲学,与其他主流编程语言形成鲜明对比,本文将为大家详细介绍Go语言中错误处理详细方法,希望对大家有所帮助... 目录1 Go 错误处理哲学与核心机制1.1 错误接口设计1.2 错误与异常的区别2 错误创建与检查2.1 基础

Nginx分布式部署流程分析

《Nginx分布式部署流程分析》文章介绍Nginx在分布式部署中的反向代理和负载均衡作用,用于分发请求、减轻服务器压力及解决session共享问题,涵盖配置方法、策略及Java项目应用,并提及分布式事... 目录分布式部署NginxJava中的代理代理分为正向代理和反向代理正向代理反向代理Nginx应用场景

Redis中的有序集合zset从使用到原理分析

《Redis中的有序集合zset从使用到原理分析》Redis有序集合(zset)是字符串与分值的有序映射,通过跳跃表和哈希表结合实现高效有序性管理,适用于排行榜、延迟队列等场景,其时间复杂度低,内存占... 目录开篇:排行榜背后的秘密一、zset的基本使用1.1 常用命令1.2 Java客户端示例二、zse

Redis中的AOF原理及分析

《Redis中的AOF原理及分析》Redis的AOF通过记录所有写操作命令实现持久化,支持always/everysec/no三种同步策略,重写机制优化文件体积,与RDB结合可平衡数据安全与恢复效率... 目录开篇:从日记本到AOF一、AOF的基本执行流程1. 命令执行与记录2. AOF重写机制二、AOF的

Go语言中json操作的实现

《Go语言中json操作的实现》本文主要介绍了Go语言中的json操作的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录 一、jsOChina编程N 与 Go 类型对应关系️ 二、基本操作:编码与解码 三、结构体标签(Struc

MyBatis Plus大数据量查询慢原因分析及解决

《MyBatisPlus大数据量查询慢原因分析及解决》大数据量查询慢常因全表扫描、分页不当、索引缺失、内存占用高及ORM开销,优化措施包括分页查询、流式读取、SQL优化、批处理、多数据源、结果集二次... 目录大数据量查询慢的常见原因优化方案高级方案配置调优监控与诊断总结大数据量查询慢的常见原因MyBAT

分析 Java Stream 的 peek使用实践与副作用处理方案

《分析JavaStream的peek使用实践与副作用处理方案》StreamAPI的peek操作是中间操作,用于观察元素但不终止流,其副作用风险包括线程安全、顺序混乱及性能问题,合理使用场景有限... 目录一、peek 操作的本质:有状态的中间操作二、副作用的定义与风险场景1. 并行流下的线程安全问题2. 顺

MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决

《MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决》MyBatis默认开启一级缓存,同一事务中循环调用查询方法时会重复使用缓存数据,导致获取的序列主键值均为1,... 目录问题原因解决办法如果是存储过程总结问题myBATis有如下代码获取序列作为主键IdMappe

使用Go调用第三方API的方法详解

《使用Go调用第三方API的方法详解》在现代应用开发中,调用第三方API是非常常见的场景,比如获取天气预报、翻译文本、发送短信等,Go作为一门高效并发的编程语言,拥有强大的标准库和丰富的第三方库,可以... 目录引言一、准备工作二、案例1:调用天气查询 API1. 注册并获取 API Key2. 代码实现3

基于Go语言开发一个 IP 归属地查询接口工具

《基于Go语言开发一个IP归属地查询接口工具》在日常开发中,IP地址归属地查询是一个常见需求,本文将带大家使用Go语言快速开发一个IP归属地查询接口服务,有需要的小伙伴可以了解下... 目录功能目标技术栈项目结构核心代码(main.go)使用方法扩展功能总结在日常开发中,IP 地址归属地查询是一个常见需求: