gotoscan:CMS指纹识别工具

gotoscan

前言

项目地址

https://github.com/ErSuan/gotoscan

结合自己学习到的Go相关知识,通过实现这个简易的CMS指纹识别工具来锻炼一下自己写代码的能力。

常见的指纹识别的方式:

  1. 特定文件的MD5值:一些CMS的特定的静态资源:图片、js文件、css文件一般是不会修改的。所以可以根据这些文件的md5值来进行匹配。
  2. 正常页面或错误页面中包含的关键字:比如说在robots.txt等,可能包含了标识CMS的关键字。或者是一些报错页面的报错信息也包含了CMS关键字,比如thinkphp3的错误页面。
  3. 请求头关键字信息的匹配
  4. 部分URL中包含的关键字

整体介绍

在这块,我们解析的特征文件也只包含了上述常见方式的前两种,即对比文件md5值和页面中的关键字。

代码整体逻辑还是比较好理解的,解析包含CMS特征的json文件,并根据解析的内容发起请求,并验证其是否包含了CMS的特征。

所以代码也主要由三个块内容:负责解析json文件的parsecms.go、负责发起请求的request.go、和最后实现测试的scancms.go

parsecms.go

首先先看一下json文件中的数据:

"gowinsoft_jw": [{
		"path": "/web/web/web/images/4bt1.jpg",
		"option": "md5",
		"content": "ef1ee9c8708cde1bd25a90054de85690"
	}, {
	...
	}],
	"maticsoftsns": [{
		"path": "/msgbox/images/gb_tip_layer.png",
		"option": "md5",
		"content": "c8cb16e8b61bc549ebd339858e66fa5c"
	}, {
	...
	}],
	.....

json文件主要还是由每一个小的json数据块组成,是CMS名字和对应的特征。首先需要定义一个将json数据解析为Go结构体的类型。通常我们将json数据解析成Go中的map[][],那么我们对上面的json就可以由如下Go结构体来对应。

//对应CMS特征的内容
type CmsFeature struct {
	Path    string `json:"path"`
	Option  string `json:"option"`
	Content string `json:"content"`
}
//通过map来对应json形式的数据,k就是cms名,v就是对应的特征切片。
map[string][]CmsFeature

通过借助"encoding/json"包的方法来实现对json文件的解析。将解析后的内容存放在map[string][]CmsFeature中返回。具体有关"encoding/json"包内的其他方法可以去看看api文档

json.Unmarshal(data, &cmslist);

request.go

发起请求这一块主要是先对服务器发起head请求,head请求状态码为200再发起get请求。先发起head请求主要是由于其响应头和get是完全一样的,但是服务器不会返回请求的实体数据,避免了传输请求、响应体的数据浪费。head请求时非常快的。

scancms.go

扫描这一部分最重要的部分就是并发。一方面是同时扫描多台主机,另一方面时同时扫描多个CMS。

Go语言的并发是易得的,只需要再你想要的并发的函数前面加上关键字go就实现了简单的并发操作。

控制同时扫描多台主机

func HostWorker(hosts []string, cmslist map[string][]CmsFeature, sortList CmsSortList) []string {
	hostsChan := make(chan string)
	resultChan := make(chan string)
	var resultList []string
    //根据我们获取到的目标数量来开启并发。
	for i := 0; i < len(hosts); i++ {
		go cmsWorker(hostsChan, cmslist, sortList, resultChan)
	}
	for _, host := range hosts {
		hostsChan <- host
	}
    //使用通道来控制线程,因为发送的工作单元数量和接收到结果的数量是相同的,所以程序直到何时关闭通道并随后关闭cmsWorker线程。
	for i := 0; i < len(hosts); i++ {
        //程序会在这里阻塞直到有数据传入通道。
		result := <-resultChan
		resultList = append(resultList, result)
	}

	close(hostsChan)
	close(resultChan)
	return resultList
}

控制同时扫描多个CMS特征。与上述不同这里我们需要用到sync.WaitGroup来控制线程。因为这里发送和接收是不同的。

//对多个cms的并发操作
func cmsWorker(hosts chan string, cmslist map[string][]CmsFeature, sortList CmsSortList, resultChan chan string) {
	for host := range hosts {
		var scanStatus bool = false
		cmsListChan := make(chan map[string][]CmsFeature, 10)

		var wg sync.WaitGroup
		for i := 0; i < cap(cmsListChan); i++ {
			go featureWorker(host, cmsListChan, &wg, &scanStatus, resultChan)
		}
        
        //这里需要先判断是否已经匹配到了,没匹配到计数器才能+1。匹配到了就直接return
		for _, data := range sortList {
			if !scanStatus {
				wg.Add(1)
				cmsListChan <- map[string][]CmsFeature{data.Name: cmslist[data.Name]}
			} else {
				//wg1.Done()
				return
			}

		}
		
		wg.Wait()
        //扫描完所有特征都没匹配到,向结果通道添加一条数据,也就是所谓发起和接收要一样。每个host都应该有一个扫描结果
		resultChan <- fmt.Sprintf("The host: %s has no matching results", host)
		close(cmsListChan)

	}

}

总结

至此一个简单的CMS工具就实现了,其他部分的代码可以直接点击左下角去我的GitHub上看源码就好了。当然啦CMS识别不仅仅是需要你多种可以匹配的方式,最重要的还是需要足够完备的特征库。

这次通过这个工具,还是学到了很多Go一些包的用法以及一些接口。代码还是还是要多敲。

Todo

  • 请求头关键字信息的匹配
  • 同时扫描多个特征
  • 根据cmd参数来控制线程
上一篇:Android Camera 原理之拍照流程zsl优化方案


下一篇:如何将word图片粘贴到动易CMS里面