HFish源码浅析

在这里插入图片描述

本文是对蜜罐项目HFish最初的源码的浅析,该版本是v0.2,代码里面写的。
在这里插入图片描述

熟悉HFIsh的朋友可能了解,目前最新版是闭源的,但是在gitee上还是有前期开源的项目分享了出来,本文是基于该版本对HFish进行分析。

调试环境准备

使用 goland2022.1
在这里插入图片描述
源码下载地址,HFish源码

HFish的入口main.go文件

main.go文件的代码只有二十几行,下面是它的源码:

func main() {
	args := os.Args
	if args == nil || len(args) < 2 {
		setting.Help()
	} else {
		if args[1] == "help" || args[1] == "--help" {
			setting.Help()
		} else if args[1] == "init" || args[1] == "--init" {
			setting.Init()
		} else if args[1] == "version" || args[1] == "--version" {
			fmt.Println("v0.2")
		} else if args[1] == "run" || args[1] == "--run" {
			setting.Run()
		} else {
			setting.Help()
		}
	}
}

HFish的官网有一张图
在这里插入图片描述
其中就介绍了关键的三个参数:
1.run ---- 运行
2. version ---- 版本
3. help ---- 帮助

// 省略代码...
		} else if args[1] == "init" || args[1] == "--init" {
			setting.Init()
//省略代码...

控制台打印出来的三个参数如上,在源码里面还有第四个参数init,下面我们进入到项目里面去了解一下。

进入HFish/utils/setting文件夹

在这里插入图片描述
总共六个函数,通过函数名简单推断,

  1. RunWeb ---- 运行Web蜜罐
  2. RunDeep ---- 运行 暗网蜜罐
  3. RunAdmin ---- 运行admin管理后台
  4. Run ---- 对应main函数,也就是主程序的run参数
  5. Init ---- 上一步的init参数
  6. Help ---- 主程序的help参数

我是想了解一下这个init到底是干啥的,结果…

func Init() {
	fmt.Println("test")
}

好叭~~~先放过它!

咱们从这个结构看特别好理解,HFish最关键的函数还是Run函数,所以接下来还是把聚光灯放在它这儿先吧。

Run函数

通过查看run函数,可以看到它是从配置文件中读取信息,然后判断用户是否开启某个功能的蜜罐,再对该功能进行判断,结构特别清新,下面是部分截取代码

	// 启动 FTP 蜜罐
	ftpStatus := conf.Get("ftp", "status")
	//省略代码...

	// 启动 Telnet 蜜罐
	telnetStatus := conf.Get("telnet", "status")
	//省略代码...

	// 启动 Mysql 蜜罐
	mysqlStatus := conf.Get("mysql", "status")
	//省略代码...

	// 启动 Redis 蜜罐
	redisStatus := conf.Get("redis", "status")
	//省略代码...

	// 启动 SSH 蜜罐
	sshStatus := conf.Get("ssh", "status")
	//省略代码...

	// 启动 Web 蜜罐
	webStatus := conf.Get("web", "status")
	//省略代码...

	// 启动 暗网 蜜罐
	deepStatus := conf.Get("deep", "status")
	//省略代码...

	// 启动 RPC
	rpcStatus := conf.Get("rpc", "status")
	//省略代码...

	// 启动 admin 管理后台
	adminAddr := conf.Get("admin", "addr")

	serverAdmin := &http.Server{
		Addr:         adminAddr,
		Handler:      RunAdmin(),
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 10 * time.Second,
	}

	serverAdmin.ListenAndServe()

看到这里就不得不去看一下这个配置文件长啥样了。

项目的配置文件config.ini

通过Run函数里面conf对象的Get方法来到HFish/utils/conf路径下,可以看到下面源码:

func Get(node string, key string) string {
	myConfig := new(Config)
	myConfig.InitConfig("./config.ini")
	r := myConfig.read(node, key)
	return r
}

虽然上面的代码只有几行,但是可以阅读到不少项目的关键信息。
Get函数的返回值是读取一个字符串,结合前面的Run函数来看就是获取某个蜜罐的状态是否开启,通过一个字段来判断,01
里面创建了一个Config结构体,深入可得知它的结构是这样的

type Config struct {
	Mymap  map[string]string
	MyNode map[string]string
	strcet string
}

接下来是对myConfig 对象进行初始化,InitConfig方法这里头主要是对配置文件内容的解析。
初始化函数的声明如下:

func (c *Config) InitConfig(path string)

Get函数已经间接的介绍了初始化函数的使用方法,直接放入一个路径名。

下面是初始化函数的源码,我在下面加了一些注释,方便理解这个函数的逻辑

func (c *Config) InitConfig(path string) {
	c.Mymap = make(map[string]string)
	c.MyNode = make(map[string]string)

	//使用 os.Open 函数打开指定路径的配置文件,并检查是否出现错误
	f, err := os.Open(path)
	if err != nil {
		panic(err)
	}
	defer f.Close()

	//使用 bufio.NewReader 函数创建一个读取器对象,用于逐行读取配置文件的内容
	r := bufio.NewReader(f)
	for {
		//使用 ReadLine 方法逐行读取配置文件的内容
		b, _, err := r.ReadLine()
		if err != nil {
			if err == io.EOF {
				break
			}
			panic(err)
		}

		//过滤掉注释行#和空行
		s := strings.TrimSpace(string(b))
		if strings.Index(s, "#") == 0 {
			continue
		}

		//解析节点(以 [...] 包裹的行),并设置当前节点(c.strcet 字段)
		n1 := strings.Index(s, "[")
		n2 := strings.LastIndex(s, "]")
		//这里的判断比较巧妙,目的是取出配置文件的节点字段
		if n1 > -1 && n2 > -1 && n2 > n1+1 {
			c.strcet = strings.TrimSpace(s[n1+1 : n2])
			continue
		}

		if len(c.strcet) == 0 {
			continue
		}

		//解析键值对
		index := strings.Index(s, "=")
		if index < 0 {
			continue
		}

		frist := strings.TrimSpace(s[:index])
		if len(frist) == 0 {
			continue
		}
		second := strings.TrimSpace(s[index+1:])

		pos := strings.Index(second, "\t#")
		if pos > -1 {
			second = second[0:pos]
		}

		pos = strings.Index(second, " #")
		if pos > -1 {
			second = second[0:pos]
		}

		pos = strings.Index(second, "\t//")
		if pos > -1 {
			second = second[0:pos]
		}

		pos = strings.Index(second, " //")
		if pos > -1 {
			second = second[0:pos]
		}

		if len(second) == 0 {
			continue
		}

		//将解析得到的键值对存储到 Config 结构体的 Mymap 字段中
		key := c.strcet + middle + frist
		c.Mymap[key] = strings.TrimSpace(second)

		key = c.strcet + middle + "introduce"
		introduce, found := c.Mymap[key]
		if !found {
		}

		key = c.strcet + middle + "mode"
		mode, found := c.Mymap[key]
		if !found {
		}

		c.MyNode[c.strcet] = strings.TrimSpace(mode) + "&&" + strings.TrimSpace(introduce)
	}
}

通过调试代码可以看到它是这么解析键值对的,结合下面的配置文件更容易理解
在这里插入图片描述

通过上面的代码可以知道,配置文件是位于项目根目录下的config.ini文件
在这里插入图片描述

下面是该项目的配置文件内容

[rpc]
status = 0                                   # 模式 0关闭 1服务端 2客户端
addr = 127.0.0.1:7879                        # RPC 服务端地址 or 客户端地址
name = Server                                # 状态1 服务端 名称 状态2 客户端 名称

[admin]                                      # RPC 状态为2 集群客户端的时候 admin 可以删掉
addr = 127.0.0.1:9001                        # 管理后台启动地址
account = admin                              # 登录账号
password = admin                             # 登录密码

[api]
status = 1                                   # 是否启动 API 1 启动 0 关闭
web_url = /api/v1/post/report                # 管理后台启动地址
deep_url = /api/v1/post/deep_report          # 管理后台启动地址
sec_key = 9cbf8a4dcb8e30682b927f352d6559a0   # API 认证秘钥

[web]
status = 0                                   # 是否启动 WEB 1 启动 0 关闭, 启动 API 后 WEB 方可上报结果
addr = 0.0.0.0:9000                          # WEB 启动地址,0.0.0.0 对外开放,127.0.0.1 对内开放 可走 Nginx 反向代理
template = wordPress/html                    # WEB 模板路径
index = index.html                           # WEB 首页文件
static = wordPress/static                    # WEB 静态文件路径  注意:必须存在两个目录,html 文件 和静态文件 不能平级
url = /                                      # WEB 访问目录,默认 / 可更改成 index.html index.asp index.php

[deep]
status = 0                                   # 是否启动 暗网 1 启动 0 关闭, 启动 API 后 方可上报结果
addr = 0.0.0.0:9002                          # 暗网 WEB 启动地址
template = deep/html                         # 暗网 WEB 模板路径
index = index.html                           # 暗网 WEB 首页文件
static = deep/static                         # 暗网 WEB 静态文件路径  注意:必须存在两个目录,html 文件 和静态文件 不能平级
url = /                                      # 暗网 WEB 访问目录,默认 / 可更改成 index.html index.asp index.php

[ssh]
status = 0                                   # 是否启动 SSH 1 启动 0 关闭
addr = 0.0.0.0:22                            # SSH 服务端地址 注意端口冲突,请先关闭服务器 openssh 服务 或 修改端口

[redis]
status = 0                                   # 是否启动 Redis 1 启动 0 关闭
addr = 0.0.0.0:6379                          # Redis 服务端地址 注意端口冲突

[mysql]
status = 0                                   # 是否启动 Mysql 1 启动 0 关闭
addr = 0.0.0.0:3306                          # Mysql 服务端地址 注意端口冲突
files = /etc/passwd,/etc/group               # Mysql 服务端读取客户端任意文件; 多写逗号分隔,会随机取

[telnet]
status = 0                                   # 是否启动 Telnet 1 启动 0 关闭
addr = 0.0.0.0:23                            # Telnet 服务端地址 注意端口冲突

[ftp]
status = 0                                   # 是否启动 Ftp 1 启动 0 关闭
addr = 0.0.0.0:21                            # Ftp 服务端地址 注意端口冲突

关于Run函数的一点想法

阅读Run函数能看到调用了很多次conf.Get,这个Get函数里面每次都调用了一次初始化函数,为了避免不必要的性能开销,还能对代码逻辑进行精简,在主程序初始化的时候将配置文件存放到全局变量中,之后的操作只需要读取这个全局变量即可。获取在HFish之后的版本已经有所优化了叭。

未完待续…

本文对很多知识点的描述并不严谨,我只是对HFish这个项目很感兴趣,并且刚好找到了它的早期源码,在这里做一下记录和分享,希望同样有兴趣的朋友可以在这篇文章里面获取到你们想知道的信息。

  • 17
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值