分析某款go端口扫描器之一

一、概述

        进来在学go的端口检测部分,但是自己写遇到很多问题,又不知道从何入手,故找来网上佬们写的现成工具,学习一波怎么实现的。分析过程杂乱,没啥思路,勿喷。

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

二、目录结构分析

总体来说,这个工具主要三部分,cmd(主程序)、core(核心框架部分)、util(工具部分),后续的分析也从这三个部分开始讲解

三、util目录

此目录下主要有三个文件,分别为file.go、log.go、shuffle.go,以下逐一分析

1、file.go(逐行读取文件内容)

  • func GetLines(filename string) (out []string, err error)

主要内容为一个GetLines方法,其接收一个文件名作为参数,并返回文件中非空行的内容组成字符串切片和可能的错误,主要作用是逐行读取文件,并将非空行的内容添加到“out"切片中。

func GetLines(filename string) (out []string, err error) {
	if filename == "" {//先判断文件名是否为空,为空则提示错误
		return out, errors.New("no filename")
	}
	file, err := os.Open(filename)//打开文件
	if err != nil {
		return out, err
	}

	defer file.Close()//读取完记得关闭
	scanner := bufio.NewScanner(file)//读取文件的内容
	scanner.Split(bufio.ScanLines)//一行一行读取分隔

	for scanner.Scan() {//逐行读取并将文本内容追加到out切片中
		line := strings.TrimSpace(scanner.Text())
		if line != "" {
			out = append(out, line)
		}
	}
	return
}

2、log.go(日志记录)

  • func NewLogger(filename string, std bool) *log.Logger

主要内容为一个 NewLogger方法,它根据提供的参数创建一个新的日志记录器对象。该函数接受一个文件名和一个布尔值参数。

  • filename 参数用于指定日志输出的文件名,如果为空字符串则表示日志将输出到标准输出(stdout)。
  • std 参数是一个布尔值,如果设置为 true,则日志会同时输出到文件和标准输出;如果设置为 false,则只输出到文件。

这个函数的目的是根据参数创建一个日志记录器,可以指定输出到文件还是标准输出,并可以选择是否同时输出到文件和标准输出。

func NewLogger(filename string, std bool) *log.Logger {
	var out io.Writer
	if filename == "" {
		out = os.Stdout //如果传入的filename为空,将out设置为标准输出
	} else {//如果不为空,则打开这个文件,
		outFile, _ := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
		if std {//如果 std为true,则将输出同时定向到标准输出和文件中,通过 io.MultiWriter 将os.Stdout 和打开的文件合并成一个多写入器
			out = io.MultiWriter(os.Stdout, outFile)
		} else {//如果 std 参数为 false,则直接将输出定向到打开的文件中
			out = outFile
		}
	}
	logger := log.New(out, "", 0)//使用 log.New 方法创建一个新的日志记录器对象 logger,将输出对象 out 作为日志记录器的输出,设置空的前缀,并且不添加任何额外的选项(flag)
	return logger
}

3、shuffle.go

主要包含3个方法:NewShuffle、Get、IsUint16InList,以及一个结构体Shuffle

  • type Shuffle struct

type Shuffle struct {
	rl   []uint16 // 乱序序列,存储的是一般轮次的乱序序列
	rl2  []uint16 // 最后一轮乱序序列(无法整除时使用)
	n    uint16   // 乱序精度,用来限制乱序序列的长度
	size uint64    //乱序序列的大小
}
  • func NewShuffle(size uint64) *Shuffle

函数接收一个 size 参数作为生成乱序序列的大小。

// NewShuffle 局部乱序
func NewShuffle(size uint64) *Shuffle {
	if size == 0 { //如果size为0, 返回nil
		return nil
	}
	sf := &Shuffle{size: size}// 创建一个新的 Shuffle 结构体,设置其 size 字段为传入的值
	if size > 100 {
		sf.n = 100 //如果size>100,设置乱序精度为100
	} else {
		sf.n = uint16(size)//否则设置乱序精度为size 的 uint16 类型。
	}

    //通过循环填充 rl 切片,创建一般轮次的乱序序列。
    //使用 rand 包生成随机数种子,对 rl 进行乱序化操作。
    //如果 size 无法整除 n,则设置 rl2 切片,并生成最后一轮乱序序列
	// 通用轮次
	sf.rl = make([]uint16, sf.n)
	for i := uint16(0); i < sf.n; i++ {
		sf.rl[i] = i
	}
	// 洗牌方法
	r := rand.New(rand.NewSource(int64(size)))
	r.Shuffle(int(sf.n), func(i, j int) {
		sf.rl[i], sf.rl[j] = sf.rl[j], sf.rl[i]
	})
	// 最后一轮无法整除时新建对应长度的rl2
	t := uint16(size % uint64(sf.n))
	if t != 0 {
		sf.rl2 = make([]uint16, t)
		for i := uint16(0); i < t; i++ {
			sf.rl2[i] = i
		}
		r.Shuffle(int(t), func(i, j int) {
			sf.rl2[i], sf.rl2[j] = sf.rl2[j], sf.rl2[i]
		})
	}
	return sf
}
  • func (sf *Shuffle) Get(index uint64) uint6

Get 方法接收一个索引 index,用于获取转换后的索引值。首先计算 t 为 index 对 sf.n 取模得到的结果。然后根据索引 index 与 n 的关系,决定使用哪个乱序序列。如果无法整除,则使用 rl2,否则使用 rl

// Get 根据索引获取转换后的索引值
func (sf *Shuffle) Get(index uint64) uint64 {
	t := index % uint64(sf.n)
	// 最后一轮无法整除时用rl2
	if index-t+uint64(sf.n) > sf.size {
		return index - t + uint64(sf.rl2[uint16(t)])
	}
	return index - t + uint64(sf.rl[uint16(t)])
}
  • func IsUint16InList(code uint16, list []uint16) bool

IsUint16InList 函数接收一个 code 和一个 list,用于判断 list 中是否存在 code。

它遍历 list 切片,如果发现存在与 code 相等的元素,则返回 true;如果遍历完 list 后都没有找到,则返回 false。

func IsUint16InList(code uint16, list []uint16) bool {
	for _, e := range list {
		if e == code {
			return true
		}
	}
	return false
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
设计一个简单的 SSRF(Server Side Request Forgery)端口扫描是一个复杂的任务,因为它涉及到网络编程、漏洞利用和安全边界的知识。SSRF是指服务被恶意请求所利用,发起对其他服务的请求,这通常发生在应用没有正确限制访问范围的时候。以下是一些基本步骤来创建一个基础的扫描: 1. **目标选择**:明确你要扫描的目标URL范围或模式,例如特定域名下的所有子域。 2. **协议识别**:确定要扫描的协议,如HTTP、HTTPS、FTP等,因为不同协议的默认端口不同。 3. **请求构建**:使用HTTP客户端库(如Python的requests、Go的http.Client)创建请求,可以包含自定义的User-Agent、Cookie等信息。 4. **端口遍历**:对于每个目标URL,尝试不同的端口号,从常见的服务端口(如80, 443, 21)到不那么常见的端口。 5. **响应处理**:对每个响应进行解析,检查HTTP状态码,如果遇到404或403错误,可能是开放的端口。 6. **异常处理**:捕获可能的网络错误或服务拒绝连接,避免程序中断。 7. **结果记录**:将扫描结果保存到文件或数据库中,方便后续分析。 8. **安全性考虑**:确保扫描过程尽可能地模拟正常用户行为,避免触发防火墙或安全策略。 9. **法律遵从**:在进行此类操作前,请确保你的行为符合当地的法律法规,尊重目标服务的所有权。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值