分析某款go扫描器之五

一、概述

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

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

二、cmd/go-portScan.go

1、设置全局变量

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


var (
	ipStr       string
	portStr     string
	pn          bool
	pt          bool
	sT          bool
	rate        int
	sV          bool
	timeout     int
	rateP       int
	hostGroup   int
	iL          string
	devices     bool
	nexthop     string
	httpx       bool
	netLive     bool
	maxOpenPort int
	oCsv        string
	oFile       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 parse
	var firstIp net.IP
	var ips []string
	if ipStr != "" {
		ips = strings.Split(ipStr, ",")
	}
	if iL != "" {
		var err error
		ips, 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.IP
			iprecords, _ = 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)
	}

	// netLive
	var wgIpsLive sync.WaitGroup
	// Pool - ipsLive
	poolIpsLive, _ := 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 group
			for i := uint64(0); i < ir.TotalNum(); i = i + 256 { // ip index
				ip := 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 parse
	ports, err := port.ShuffleParseAndMergeTopPorts(portStr)
	if err != nil {
		myLog.Fatalf("[error] %s is not port!\n", err)
	}

	// recv
	single := make(chan struct{})
	retChan := make(chan port.OpenIpPort, 5000)

	// ip port num status
	ipPortNumMap := make(map[string]int) // 记录该IP端口开放数量
	var ipPortNumRW sync.RWMutex

	// csv output
	var csvFile *os.File
	var csvWrite *csv.Writer
	if 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.Title
					line[5] = strconv.Itoa(ret.HttpInfo.StatusCode)
					line[6] = ret.HttpInfo.Server
					line[7] = ret.HttpInfo.TlsCN
					line[8] = strings.Join(ret.HttpInfo.Fingers, ",")
				}
				csvWrite.Write(line)
				csvWrite.Flush()
				csvFile.Sync()
			}
		}
		single <- struct{}{}
	}()

	// Initialize the Scanner
	var s port.Scanner
	option := port.Option{
		Rate:        rate,
		Timeout:     timeout,
		NextHop:     nexthop,
		FingerPrint: sV,
		Httpx:       httpx,
	}
	if sT {
		// tcp
		if option.Rate == -1 {
			option.Rate = tcp.DefaultTcpOption.Rate
		}
		if option.Timeout == -1 {
			option.Timeout = tcp.DefaultTcpOption.Timeout
		}
		s, err = tcp.NewTcpScanner(retChan, option)
	} else {
		// syn
		if 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 func
	portScan := func(ip net.IP) {
		var ipPortNum int
		var ipPortNumOk bool
		if maxOpenPort > 0 {
			ipPortNumRW.Lock()
			ipPortNumMap[ip.String()] = 0
			ipPortNumRW.Unlock()
		}
		for _, _port := range ports { // port
			s.WaitLimiter() // limit rate

			if 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 func
	var wgHostScan sync.WaitGroup
	hostScan, _ := ants.NewPoolWithFunc(hostGroup, func(ip interface{}) {
		_ip := ip.(net.IP)
		portScan(_ip)
		wgHostScan.Done()
	})
	defer hostScan.Release()

	// Pool - ping and port scan
	poolPing, _ := 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 scan
	for _, ir := range ipRangeGroup { // ip group
		shuffle := util.NewShuffle(ir.TotalNum())    // shuffle
		for i := uint64(0); i < ir.TotalNum(); i++ { // ip index
			ip := 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 {                                  // ping
				wgPing.Add(1)
				_ = poolPing.Invoke(ip)
			} else {
				wgHostScan.Add(1)
				hostScan.Invoke(ip)
			}
		}
	}
	wgPing.Wait()     // PING组
	wgHostScan.Wait() // HostGroupS
	s.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)
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/611543.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

运维开发工程师教程之MongoDB单机版设置

MongoDB单机版设置 一、创建虚拟机 在VMware Workstation软件中新建一个虚拟机&#xff0c;具体操作步骤如下&#xff1a; ①运行VMware Workstation软件&#xff0c;进入到主界面&#xff0c;单击“创建新的虚拟机”来创建新的虚拟机&#xff0c;如图3-1所示。 图3-1 VMware…

算法设计与分析 动态规划/回溯

1.最大子段和 int a[N]; int maxn(int n) {int tempa[0];int ans0;ansmax(temp,ans);for(int i1;i<n;i){if(temp>0){tempa[i];}else tempa[i];ansmax(temp,ans);}return ans; } int main() {int n,ans0;cin>>n;for(int i0;i<n;i) cin>>a[i];ansmaxn(n);co…

智慧之巅:大数据与算力中心的融合演进

智慧之巅&#xff1a;大数据与算力中心的融合演进 1 引言 在这个数据驱动的时代&#xff0c;我们站在了一个前所未有的历史节点上。大数据和算力中心&#xff0c;这两个曾经各自为政的领域&#xff0c;如今正以一种前所未有的方式交织在一起&#xff0c;共同推动着数字经济的蓬…

PDF高效编辑:一键批量,PDF转图片的快速解决方案

在数字化时代&#xff0c;PDF文件已成为工作和学习中不可或缺的一部分。然而&#xff0c;有时我们可能需要将PDF转换为图片&#xff0c;以便更轻松地编辑、共享或处理。为了满足这一需求&#xff0c;许多高效的PDF编辑工具应运而生&#xff0c;其中“办公提效工具”一键批量PDF…

C++对象的拷贝构造函数

如果一个构造函数的第一个参数是类本身的引用,且没有其它参数(或者其它的参数都有默认值),则该构造函数为拷贝构造函数。 拷贝(复制)构造函数:利用同类对象构造一个新的对象 ●1.函数名和类同名 (构造函数) ●2.没有返回值 (构造函数) ●3.第一个参数必…

【甲辰雜俎】世界上最不可靠的就是人

"世界上最不可靠的就是人" 人是一個多元的複變函數, 今天經受住考驗, 明天你就有可能叛變。 過去是戰場上的仇敵, 明天就有可能成為政治上的盟友。 —— 擷取自電視劇《黑冰》 人的不可預測性, 的確是一個普遍的現象。 每個人都是一個獨特的個體, 受到不同的…

基于WPF的DynamicDataDisplay曲线显示

一、DynamicDataDisplay下载和引用 1.新建项目,下载DynamicDataDisplay引用: 如下图: 二、前端开发: <Border Grid.Row="0" Grid.Column="2" BorderBrush="Purple" BorderThickness="1" Margin="2"><Grid>…

5.11学习记录

20长安杯部分 检材 1 的操作系统版本 CentOS Linux 7.6.1810 (Core) 检材 1 中&#xff0c;操作系统的内核版本是 3.10.0-957.el7.x86_64 检材 1 中磁盘包含一个 LVM 逻辑卷&#xff0c;该 LVM 开始的逻辑区块地址&#xff08;LBA&#xff09;是 2099200 物理卷&#xff…

Web服务器--虚拟主机配置

实验1&#xff1a;建立两个基于ip地址访问的网站&#xff0c;要求如下 该网站ip地址的主机位为100&#xff0c;设置DocumentRoot为/www/ip/100&#xff0c;网页内容为&#xff1a;this is 100。 该网站ip地址主机位为200&#xff0c;设置DocumentRoot为/www/ip/200&#xff0c…

EmploLeaks:一款针对企业安全的组织员工信息收集OSINT工具

关于EmploLeaks EmploLeaks是一款针对企业安全的组织员工信息收集OSINT工具&#xff0c;在该工具的帮助下&#xff0c;企业内部的安全人员和管理员可以有效地收集组织内员工的各种信息&#xff0c;并以此来判断组织内部的网络安全态势。 工作机制 首先&#xff0c;该工具会在…

数据驱动实战二

目标 掌握数据驱动的开发流程掌握如何读取JSON数据文件巩固PO模式 1. 案例 对TPshop网站的登录模块进行单元测试 1.1 实现步骤 编写测试用例采用PO模式的分层思想对页面进行封装编写测试脚本定义数据文件&#xff0c;实现参数化 1.2 用例设计 1.3 数据文件 {"login…

STM32CubeMX学习笔记30---FreeRTOS内存管理

一、简介 1 基本概念 FreeRTOS 操作系统将内核与内存管理分开实现&#xff0c;操作系统内核仅规定了必要的内存管理函数原型&#xff0c;而不关心这些内存管理函数是如何实现的&#xff0c;所以在 FreeRTOS 中提供了多种内存分配算法&#xff08;分配策略&#xff09;&#xf…

【Web】2023浙江大学生省赛初赛 secObj 题解

目录 step 0 step 1 step 2 step 3 题目本身是不难&#xff0c;简单复健一下 step 0 pom依赖就是spring 反序列化入口在./admin/user/readObj 输入流做了黑名单的过滤&#xff0c;TemplatesImpl不能直接打 可以jackson打SignedObject二次反序列化绕过 具体原理看下面这…

基于ESP32和ESP8266的物联网开发过程(二)

在做这个项目前&#xff0c;也做了一些调研。项目的初衷是想要用于智能家居。我比较了小米IoT、阿里云、ESPHOME、巴沙云、点灯科技和ONENET等几个平台。最终选择了Onenet&#xff0c;部分原因是之前用过它的多协议版本&#xff0c;但现在这个版本已经下线了。 小米IoT的公测名…

基于JAVAEE的停车场管理系统(论文 + 源码)

【免费】基于JAVAEE的停车场管理系统.zip资源-CSDN文库https://download.csdn.net/download/JW_559/89292324 基于JAVAEE的停车场管理系统 摘 要 如今&#xff0c;我国现代化发展迅速&#xff0c;人口比例急剧上升&#xff0c;在一些大型的商场&#xff0c;显得就格外拥挤&…

深入浅出JavaScript继承机制:解密原型、原型链与面向对象实战攻略

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 &#x1f525; 引言&#x1f9f1; 原型基础⛓️ 原型链的形成&#x1f504; 修改原型的影响&#x1f3c1; 原型链的尽头为什么null标志着结束&#xff1f;实际意义 &#x1f310; &#x1f504; 继承的实现方式1. 原型链继承…

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-11.1,11.2-BSP文件目录组织

前言&#xff1a; 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM&#xff08;MX6U&#xff09;裸机篇”视频的学习笔记&#xff0c;在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…

基于SpringBoot的全国风景区WebGIS按省展示实践

目录 前言 一、全国风景区信息介绍 1、全国范围内数据分布 2、全国风景区分布 3、PostGIS空间关联查询 二、后台查询的设计与实现 1、Model和Mapper层 2、业务层和控制层设计 三、WebGIS可视化 1、省份范围可视化 2、省级风景区可视化展示 3、成果展示 总结 前…

【Vulhub靶场】Nginx 中间件漏洞复现

【Vulhub靶场】Nginx 中间件漏洞复现 一、Nginx 文件名逻辑漏洞&#xff08;CVE-2013-4547&#xff09;1. 影响版本2. 漏洞原理3. 漏洞复现 二、Nginx越界读取缓存漏洞&#xff08;CVE-2017-7529&#xff09;1. 漏洞详情2. 影响版本3. 漏洞复现 三、Nginx 配置错误导致漏洞&…

建发弘爱 X 袋鼠云:加速提升精细化、数字化医疗健康服务能力

厦门建发弘爱医疗集团有限公司&#xff08;简称“建发弘爱”&#xff09;创立于2022年&#xff0c;是厦门建发医疗健康投资有限公司的全资子公司&#xff0c;专业从事医疗健康领域的医疗服务。 建发弘爱通过医疗、健康及产业服务三大板块&#xff0c;为百姓提供医疗和健康全生…
最新文章