【毕设扫描器】【参数Fuzz】第二篇:动态爬虫的创建、启动和协程池

前言

在 《第一篇:数据的定义、读取和装配(爬虫数据和Payload数据)》文章中,我们完成了 URL 数据的读取、以及 Payload 的加载部分。

接下来要做的是发送请求对象,根据响应对象判断是否存在漏洞。

本篇文章要探究的,就是 CrawlerGo 动态爬虫发送请求和处理的过程,以求为参数 Fuzz 发送请求和处理响应提供参考和借鉴。

Crawlergo发送请求的代码节点

CrawlerGo 为动态爬虫装配数据的代码,前面我们基本阅读完毕。接下来看创建爬虫任务、以及启动爬虫部分的代码,查看具体的实现细节。

// crawlergo_cmd.go 代码 311 行

// 开始爬虫任务
	task, err := pkg.NewCrawlerTask(targets, taskConfig)
	if err != nil {
    
    
		logger.Logger.Error("create crawler task failed.")
		os.Exit(-1)
	}
	……
	go handleExit(task)
	logger.Logger.Info("Start crawling.")
	task.Run()
	result := task.Result

// 追踪 task.Run()
// task_main.go 代码 173行
/*
开始当前任务
*/
func (t *CrawlerTask) Run() {
    
    
	defer t.Pool.Release()  // 释放协程池
	defer t.Browser.Close() // 关闭浏览器

	// 为 Targets 设置 reqsFromRobots 变量
	if t.Config.PathFromRobots {
    
    
		reqsFromRobots := GetPathsFromRobots(*t.Targets[0])
		logger.Logger.Info("get paths from robots.txt: ", len(reqsFromRobots))
		t.Targets = append(t.Targets, reqsFromRobots...)
	}

	// 为 Targets 设置 reqsByFuzz 变量
	if t.Config.FuzzDictPath != "" {
    
    
		if t.Config.PathByFuzz {
    
    
			logger.Logger.Warn("`--fuzz-path` is ignored, using `--fuzz-path-dict` instead")
		}
		reqsByFuzz := GetPathsByFuzzDict(*t.Targets[0], t.Config.FuzzDictPath)
		t.Targets = append(t.Targets, reqsByFuzz...)
	} else if t.Config.PathByFuzz {
    
    
		reqsByFuzz := GetPathsByFuzz(*t.Targets[0])
		logger.Logger.Info("get paths by fuzzing: ", len(reqsByFuzz))
		t.Targets = append(t.Targets, reqsByFuzz...)
	}


	t.Result.AllReqList = t.Targets[:]

	// 创建 Request 对象用于 task 的初始化
	var initTasks []*model.Request

	// 遍历 Targets 数组
	for _, req := range t.Targets {
    
    
		if t.smartFilter.DoFilter(req) {
    
    
			logger.Logger.Debugf("filter req: " + req.URL.RequestURI())
			continue
		}
		initTasks = append(initTasks, req)
		t.Result.ReqList = append(t.Result.ReqList, req)
	}
	logger.Logger.Info("filter repeat, target count: ", len(initTasks))

	for _, req := range initTasks {
    
    
		if !engine2.IsIgnoredByKeywordMatch(*req, t.Config.IgnoreKeywords) {
    
    
			t.addTask2Pool(req)
		}
	}

	t.taskWG.Wait()

	// 对全部请求进行唯一去重
	todoFilterAll := make([]*model.Request, len(t.Result.AllReqList))
	for index := range t.Result.AllReqList {
    
    
		todoFilterAll[index] = t.Result.AllReqList[index]
	}

	t.Result.AllReqList = []*model.Request{
    
    }
	var simpleFilter filter2.SimpleFilter
	for _, req := range todoFilterAll {
    
    
		if !simpleFilter.UniqueFilter(req) {
    
    
			t.Result.AllReqList = append(t.Result.AllReqList, req)
		}
	}

	// 全部域名
	t.Result.AllDomainList = AllDomainCollect(t.Result.AllReqList)
	// 子域名
	t.Result.SubDomainList = SubDomainCollect(t.Result.AllReqList, t.RootDomain)
}

设置代理调试寻找(未抓到包)

(1)设置代理参数,开启 Burpsuite,没抓到 HTTP 数据包。

--request-proxy http://127.0.0.1:8080 --output-json debug.json http://127.0.0.1/sqli-labs-master/Less-1/?id=1

(2)尝试内置 request-proxy 参数,添加 Value 字段设置参数默认值,从控制台截图可以看到设置成功了,但 Burp 仍然没有收到 HTTP 数据包。

&cli.StringFlag{
    
    
				Name:        "request-proxy",
				Usage:       "all requests connect through defined proxy server.",
				Value:       "http://127.0.0.1:8080",
				Destination: &taskConfig.Proxy,
			},

在这里插入图片描述

查看 task_main.go 文件,搜索 Proxy,可以看到是在初始化浏览器时加载了代理。

	crawlerTask.Browser = engine2.InitBrowser(taskConf.ChromiumPath, taskConf.IncognitoContext, taskConf.ExtraHeaders, taskConf.Proxy, taskConf.NoHeadless)
	crawlerTask.RootDomain = targets[0].URL.RootDomain()

WireShark本地抓包(找到代码节点)

参考文章:wireShark抓取本地http包,分析状态

前往 Npcap官网 下载安装包,安装时记得勾选 Adapter for lookback traffic capture 选项。打开 WireShark 选择该过滤器监听。

调试 CrawlerGo,观察执行哪一部分代码后 HTTP 请求中出现了目标网站的访问记录。

在这里插入图片描述

经过简单对比,发现执行 t.taskWG.Wait() 代码后发送HTTP请求。

// 追踪 task.Run()
// task_main.go 代码 173行
/*
开始当前任务
*/
func (t *CrawlerTask) Run() {
    
    
	defer t.Pool.Release()  // 释放协程池
	defer t.Browser.Close() // 关闭浏览器

	// 为 Targets 设置 reqsFromRobots 变量

	// 为 Targets 设置 reqsByFuzz 变量

	t.Result.AllReqList = t.Targets[:]

	// 创建 Request 对象用于 task 的初始化

	// 遍历 Targets 数组
	
	// 遍历初始化任务
	for _, req := range initTasks {
    
    
		if !engine2.IsIgnoredByKeywordMatch(*req, t.Config.IgnoreKeywords) {
    
    
			t.addTask2Pool(req)
		}
	}

	t.taskWG.Wait()

	// 对全部请求进行唯一去重
	todoFilterAll := make([]*model.Request, len(t.Result.AllReqList))
	for index := range t.Result.AllReqList {
    
    
		todoFilterAll[index] = t.Result.AllReqList[index]
	}

CrawlerGo设置多线程爬虫

爬虫调用代码

// crawlergo_cmd.go 代码 312 行
// 开始爬虫任务
	task, err := pkg.NewCrawlerTask(targets, taskConfig)
	if err != nil {
    
    
		logger.Logger.Error("create crawler task failed.")
		os.Exit(-1)
	}
	……

	go handleExit(task)
	logger.Logger.Info("Start crawling.")
	task.Run()
	result := task.Result

创建爬虫任务

分析代码,该部分主要操作了如下三部分内容:

  • 给爬虫任务装配数据
  • 以无头模式启动浏览器
  • 创建协程池并设置最大协程数量
	// 开始爬虫任务
	task, err := pkg.NewCrawlerTask(targets, taskConfig)

// task_main.go 代码 70 行
/**新建爬虫任务
	接收配置好的 Request 、TaskConfig 对象
*/
func NewCrawlerTask(targets []*model.Request, taskConf TaskConfig) (*CrawlerTask, error) {
    
    
	// 定义爬虫任务的数据类型:字典
	crawlerTask := CrawlerTask{
    
    
		Result: &Result{
    
    },
		Config: &taskConf,
		smartFilter: filter2.SmartFilter{
    
    
			SimpleFilter: filter2.SimpleFilter{
    
    
				HostLimit: targets[0].URL.Host,
			},
		},
	}

	// 单个目标,测试也使用单目标站点
	// 定义 newReq 接收 targets[0],确定通信协议
	if len(targets) == 1 {
    
    
		_newReq := *targets[0]
		newReq := &_newReq
		_newURL := *_newReq.URL
		newReq.URL = &_newURL
		// 首先确定站点使用的通信协议
		if targets[0].URL.Scheme == "http" {
    
    
			newReq.URL.Scheme = "https"
		} else {
    
    
			newReq.URL.Scheme = "http"
		}
		// 把配置好的 newReq 再添加到 targets,此时 Targets 有两个目标信息的元素
		targets = append(targets, newReq)
	}
	// 把 Targets 添加到 crawlerTask/爬虫任务的字典
	crawlerTask.Targets = targets[:]

	// 设置请求对象的 source 为常量 "Target"
	for _, req := range targets {
    
    
		req.Source = config.FromTarget
	}

	// 装配形式参数变量 Taskconfig
	// 设置 TaskConfig 即 taskConf 的变量,如果未设置则使用默认值
	设置 taskConf 的 变量
		.TabRunTimeout
		.MaxTabsCount
		.FilterMode
		.MaxCrawlCount
		.DomContentLoadedTimeout
		.EventTriggerInterval
		.BeforeExitDelay
		.EventTriggerMode
		.IgnoreKeywords

	// 设置请求头变量
	if taskConf.ExtraHeadersString != "" {
    
    
		err := json.Unmarshal([]byte(taskConf.ExtraHeadersString), &taskConf.ExtraHeaders)
		if err != nil {
    
    
			logger.Logger.Error("custom headers can't be Unmarshal.")
			return nil, err
		}
	}

	// 以无头模式开启 chrome.exe
	crawlerTask.Browser = engine2.InitBrowser(taskConf.ChromiumPath, taskConf.IncognitoContext, taskConf.ExtraHeaders, taskConf.Proxy, taskConf.NoHeadless)
	// 设置爬虫任务的根域名
	crawlerTask.RootDomain = targets[0].URL.RootDomain()

	// 初始化智能过滤器
	crawlerTask.smartFilter.Init()

	// 创建协程池
	// 创建协程池,并且把协程池保存到爬虫任务的 Pool 键
	p, _ := ants.NewPool(taskConf.MaxTabsCount)
	crawlerTask.Pool = p

	// 爬虫任务完成数据装配、无头模式启动浏览器、创建协程池
	return &crawlerTask, nil
}

启动爬虫任务

代码把请求对象添加到线程池,然后等待异步线程执行结束。

task_main.go 代码 210 行的函数 t.addTask2Pool(req),继续追溯。

	// crawlergo_cmd.go
	task.Run()

/*
开始当前任务
*/
func (t *CrawlerTask) Run() {
    
    
	defer t.Pool.Release()  // 释放协程池
	defer t.Browser.Close() // 关闭浏览器

	是否从 robots.txt 获取信息,如果是则把信息添加到 t.Targets 对象

	是否 Fuzz 目录,如果是则读取字典文件并添加到 t.Targets 对象

	// 创建数组对象 AllReqList
	t.Result.AllReqList = t.Targets[:]

	// 创建 Request 类型的初始化任务变量
	var initTasks []*model.Request
	for _, req := range t.Targets {
    
    
		if t.smartFilter.DoFilter(req) {
    
    
			logger.Logger.Debugf("filter req: " + req.URL.RequestURI())
			continue
		}
		// 从 t.Targets 对象中获取请求对象
		initTasks = append(initTasks, req)
		t.Result.ReqList = append(t.Result.ReqList, req)
	}
	logger.Logger.Info("filter repeat, target count: ", len(initTasks))

	// 遍历初始化任务数组的请求对象,逐个添加到协程池
	for _, req := range initTasks {
    
    
		if !engine2.IsIgnoredByKeywordMatch(*req, t.Config.IgnoreKeywords) {
    
    
			t.addTask2Pool(req)
		}
	}
	// 运行则发送请求
	// 等待异步线程执行完成
	t.taskWG.Wait()

	// 对全部请求进行唯一去重
	todoFilterAll := make([]*model.Request, len(t.Result.AllReqList))
	for index := range t.Result.AllReqList {
    
    
		todoFilterAll[index] = t.Result.AllReqList[index]
	}

	t.Result.AllReqList = []*model.Request{
    
    }
	var simpleFilter filter2.SimpleFilter
	for _, req := range todoFilterAll {
    
    
		if !simpleFilter.UniqueFilter(req) {
    
    
			t.Result.AllReqList = append(t.Result.AllReqList, req)
		}
	}

	// 全部域名
	t.Result.AllDomainList = AllDomainCollect(t.Result.AllReqList)
	// 子域名
	t.Result.SubDomainList = SubDomainCollect(t.Result.AllReqList, t.RootDomain)
}

把任务添加到线程池

// task_main.go,t.addTask2Pool()位于 210 行
/*开始当前任务*/
func (t *CrawlerTask) Run() {
    
    
	……
	// 遍历初始化任务数组的请求对象,逐个添加到协程池
	for _, req := range initTasks {
    
    
		if !engine2.IsIgnoredByKeywordMatch(*req, t.Config.IgnoreKeywords) {
    
    
			t.addTask2Pool(req)
		}
	}


// 追溯 t.addTask2Pool(req),同文件代码 240 行
/**
添加任务到协程池
添加之前实时过滤
*/
func (t *CrawlerTask) addTask2Pool(req *model.Request) {
    
    

	// 协程相关代码
	t.taskCountLock.Lock()
	if t.crawledCount >= t.Config.MaxCrawlCount {
    
    
		t.taskCountLock.Unlock()
		return
	} else {
    
    
		t.crawledCount += 1
	}
	t.taskCountLock.Unlock()

	t.taskWG.Add(1)

	// 传入请求对象(字典类型),追溯 t.generateTabTask(req) ,发现根据请求列表生成tabTask协程任务列表
	/* 实际上又嵌套了一层字典,字典结构如下所示
		task := tabTask{
			crawlerTask: t,
			browser:     t.Browser,
			req:         req,
		}
	*/
	task := t.generateTabTask(req)
	
	// 测试时F7单步调试,发现跳过了该部分代码
	// 查询资料发现,该部分代码作用是:以并发的方式调用匿名函数func
	go func() {
    
    
		// 提交协程任务执行
		err := t.Pool.Submit(task.Task)
		if err != nil {
    
    
			t.taskWG.Done()
			logger.Logger.Error("addTask2Pool ", err)
		}
	}()
}


// 追溯关键代码 t.Pool.submit(task.Task)

	在 t.Pool.submit(task.Task) 后添加打印提示信息的代码,发现打印了提示信息
	
	在该处下断点,进行单步调试
		发现使用F7或F8进行单步调试时,不会在该段代码的断点停留
		使用"运行到光标处",才会在该段代码的断点停留,
	
	然后就会执行 Run() 里面的代码:t.taskWG.Wait(),此时请求已经发送
	单步调试跟踪 t.taskWG.Wait(),发现的函数调用如下:
		task.Run()								crawlergo_cmd.go
		->t.taskWG.Wait()						task_main.go
			-> Wait()							src/sync/waitgroup.go
			->runtime_Semacquire(semap)			src/sync/waitgroup.go
				->sync_runtime_Semacquire()		src/runtime/sema.go
				F7步入执行该代码,此时会运行t.Pool.submit()并发送请求。
	也就是说,执行 t.taskWG.Wait()函数过程中,会执行 t.Pool.submit(),然后会执行协程任务。

	插个眼:注意func (t *tabTask) Task()

重要函数表格

创建爬虫任务

所属文件 函数 说明
crawlergo_cmd.go task, err := pkg.NewCrawlerTask(targets, taskConfig) 创建爬虫任务
task_main.go engine2.InitBrowser(xx) 以无头模式启动浏览器 chrome.exe
- ants.NewPool(taskConf.MaxTabsCount) 创建协程池

接着梳理协程任务的执行

关键代码:task_main.go 文件 代码 253 行。

go func() {
    
    
		// 每个 goroutine 调用 task.Task() 函数,目前不清楚为什么不是使用 task.Task(),可能是语法
		err := t.Pool.Submit(task.Task)
		fmt.Println(task.Task)
		fmt.Println("t.Pool.Submit")
		if err != nil {
    
    
			t.taskWG.Done()
			logger.Logger.Error("addTask2Pool ", err)
		}
	}()

启动 goroutine 执行操作

参考《Go in Action》第二章:一个 goroutine 是一个独立于其他函数运行的函数。使用关键字 go 启动一个 goroutine,并对这个 goroutine 做并发调度。代码使用关键字 go 启动了一个匿名函数作为 goroutine,这样可以并发地独立处理每个数据源的数据。

匿名函数内部包含了每个 goroutine 要完成的任务,此处是 t.Pool.Submit(task.Task)。

参考文章:ants:在Submit中再调用当前Pool的Submit可能导致阻塞,关于协程池 Pool。
goroutine pool减小开销的主要思路就是复用。即创建出的goroutine在做完一个task后不退出,而是等待下一个task,这样来减少goroutine反复创建和销毁带来的开销。

goroutine 的操作:task.Task

查看每个 goroutine 执行的操作,分析代码部分见注释内容。

// task_main.go 代码 264 行
/**
单个运行的tab标签任务,实现了workpool的接口
*/
func (t *tabTask) Task() {
    
    
	// 《Go in Action》第二章:
	// 关键字 defer 会安排随后的函数调用在函数返回时才执行。
	// taskWG.Done():任务完成后计数器减一
	defer t.crawlerTask.taskWG.Done()
	
	// 调用 engine2.NewTab()配置 tab,然后执行 tab
	// 这些参数不是与请求强相关的参数,可以忽略不看
	tab := engine2.NewTab(t.browser, *t.req, engine2.TabConfig{
    
    
		TabRunTimeout:           t.crawlerTask.Config.TabRunTimeout,
		DomContentLoadedTimeout: t.crawlerTask.Config.DomContentLoadedTimeout,
		EventTriggerMode:        t.crawlerTask.Config.EventTriggerMode,
		EventTriggerInterval:    t.crawlerTask.Config.EventTriggerInterval,
		BeforeExitDelay:         t.crawlerTask.Config.BeforeExitDelay,
		EncodeURLWithCharset:    t.crawlerTask.Config.EncodeURLWithCharset,
		IgnoreKeywords:          t.crawlerTask.Config.IgnoreKeywords,
		CustomFormValues:        t.crawlerTask.Config.CustomFormValues,
		CustomFormKeywordValues: t.crawlerTask.Config.CustomFormKeywordValues,
	})
	
	// 执行 tab,具体分析查看本章第四小节:发送请求的 tab 任务
	tab.Start()

	// 收集结果
	t.crawlerTask.Result.resultLock.Lock()
	t.crawlerTask.Result.AllReqList = append(t.crawlerTask.Result.AllReqList, tab.ResultList...)
	t.crawlerTask.Result.resultLock.Unlock()

	for _, req := range tab.ResultList {
    
    
		if t.crawlerTask.Config.FilterMode == config.SimpleFilterMode {
    
    
			if !t.crawlerTask.smartFilter.SimpleFilter.DoFilter(req) {
    
    
				t.crawlerTask.Result.resultLock.Lock()
				t.crawlerTask.Result.ReqList = append(t.crawlerTask.Result.ReqList, req)
				t.crawlerTask.Result.resultLock.Unlock()
				if !engine2.IsIgnoredByKeywordMatch(*req, t.crawlerTask.Config.IgnoreKeywords) {
    
    
					t.crawlerTask.addTask2Pool(req)
				}
			}
		} else {
    
    
			if !t.crawlerTask.smartFilter.DoFilter(req) {
    
    
				t.crawlerTask.Result.resultLock.Lock()
				t.crawlerTask.Result.ReqList = append(t.crawlerTask.Result.ReqList, req)
				t.crawlerTask.Result.resultLock.Unlock()
				if !engine2.IsIgnoredByKeywordMatch(*req, t.crawlerTask.Config.IgnoreKeywords) {
    
    
					t.crawlerTask.addTask2Pool(req)
				}
			}
		}
	}
}

配置引擎的库:/pkg/engine

查看 engine 库的目录结构,结合网上搜索结果,推测是作者自己编写的库。简单查看后,发现该库不重要,接下来看 tab 任务。

在这里插入图片描述

查看 task_main.go 文件与 engine 相关的调用代码。

import (engine2 "crawlergo/pkg/engine")

type CrawlerTask struct {
    
    
	Browser       *engine2.Browser    //
}

type tabTask struct {
    
    
	crawlerTask *CrawlerTask
	browser     *engine2.Browser
}

// 代码147行
crawlerTask.Browser = engine2.InitBrowser(taskConf.ChromiumPath, taskConf.IncognitoContext, taskConf.ExtraHeaders, taskConf.Proxy, taskConf.NoHeadless)
	

发送请求的 tab 任务

结构体:tab

定义的 tab 结构体如下,先不管执行到 tab.Start() 时装配了哪些变量。

// task_main.go 文件
type tabTask struct {
    
    
	crawlerTask *CrawlerTask
	browser     *engine2.Browser
	req         *model.Request
	pool        *ants.Pool
}

执行操作函数:tab.Start()

函数位置:/pkg/engine/tab.go 代码 188 行。

处理返回结果的主要代码:

  • tab.collectLinkWG.Add(3)
  • go tab.collectLinks(),函数位于 colletc_links.go
  • tab.collectLinkWG.Wait()
func (tab *Tab) Start() {
    
    
	// 打印提示信息
	logger.Logger.Info("Crawling " + tab.NavigateReq.Method + " " + tab.NavigateReq.URL.String())
	
	// 执行之后调用 tab.Cancel()
	defer tab.Cancel()
	
	if err := chromedp.Run(*tab.Ctx,
		RunWithTimeOut(tab.Ctx, tab.config.DomContentLoadedTimeout, chromedp.Tasks{
    
    
			//
			runtime.Enable(),
			// 开启网络层API
			network.Enable(),
			// 开启请求拦截API
			fetch.Enable().WithHandleAuthRequests(true),
			// 添加回调函数绑定
			// XSS-Scan 使用的回调
			runtime.AddBinding("addLink"),
			runtime.AddBinding("Test"),
			// 初始化执行JS
			chromedp.ActionFunc(func(ctx context.Context) error {
    
    
				var err error
				_, err = page.AddScriptToEvaluateOnNewDocument(js.TabInitJS).Do(ctx)
				if err != nil {
    
    
					return err
				}
				return nil
			}),
			network.SetExtraHTTPHeaders(tab.ExtraHeaders),
			// 执行导航
			//chromedp.Navigate(tab.NavigateReq.URL.String()),
			chromedp.ActionFunc(func(ctx context.Context) error {
    
    
				_, _, _, err := page.Navigate(tab.NavigateReq.URL.String()).Do(ctx)
				if err != nil {
    
    
					return err
				}
				return waitNavigateDone(ctx)
			}),
		}),
	); err != nil {
    
    
		if err.Error() == "context canceled" {
    
    
			return
		}
		logger.Logger.Warn("navigate timeout ", tab.NavigateReq.URL.String())
	}

	go func() {
    
    
		// 等待所有协程任务结束
		tab.WG.Wait()
		tab.NavDone <- 1
	}()

	select {
    
    
	case <-tab.NavDone:
		logger.Logger.Debug("all navigation tasks done.")
	case <-time.After(tab.config.DomContentLoadedTimeout + time.Second*10):
		logger.Logger.Warn("navigation tasks TIMEOUT.")
	}

	// 等待收集所有链接
	logger.Logger.Debug("collectLinks start.")
	tab.collectLinkWG.Add(3)
	go tab.collectLinks()
	tab.collectLinkWG.Wait()
	logger.Logger.Debug("collectLinks end.")

	// 识别页面编码 并编码所有URL
	if tab.config.EncodeURLWithCharset {
    
    
		tab.DetectCharset()
		tab.EncodeAllURLWithCharset()
	}

	//fmt.Println(tab.NavigateReq.URL.String(), len(tab.ResultList))
	//for _, v := range tab.ResultList {
    
    
	//	v.SimplePrint()
	//}
	// fmt.Println("Finished " + tab.NavigateReq.Method + " " + tab.NavigateReq.URL.String())
}

处理响应信息:collect_links.go(分析方向的转折点)

查看相关的调用函数:tab.GetExecutor(),追溯到 tab.go 文件 327 行,看不太懂。

下列代码看了半天,没发现类似 http.Get() 的函数 调用。全文搜索 .Get(.Post(,发现只有获取 robots.txt 和 Fuzz字典时会用到这些请求。

简单看了一些对响应信息的处理,突然意识到以无头模式启动 Google 并且进行复杂的处理方式,是针对爬虫而言的,参数FuzzPoc验证 没必要使用无头模式,唯一需要的是借鉴项目的并发代码 。

/**
最后收集所有的链接
*/
func (tab *Tab) collectLinks() {
    
    
	go tab.collectHrefLinks()
	go tab.collectObjectLinks()
	go tab.collectCommentLinks()
}

func (tab *Tab) collectHrefLinks() {
    
    
	defer tab.collectLinkWG.Done()
	ctx := tab.GetExecutor()
	fmt.Println("打印信息:")
	// 打印结果:context.Background.WithCancel.WithValue(type chromedp.contextKey, val <not Stringer>).WithCancel.WithValue(type chromedp.contextKey, val <not Stringer>).WithCancel.WithValue(type chromedp.contextKey, val <not Stringer>).WithDeadline(2022-04-13 19:16:13.5023884 +0800 CST m=+20.528509501 [16.1644673s]).WithValue(type cdp.contextKey, val <not Stringer>)
	//fmt.Println(ctx)
	
	// 收集 src href data-url 属性值
	attrNameList := []string{
    
    "src", "href", "data-url", "data-href"}
	for _, attrName := range attrNameList {
    
    
		tCtx, cancel := context.WithTimeout(ctx, time.Second*1)
		var attrs []map[string]string
		_ = chromedp.AttributesAll(fmt.Sprintf(`[%s]`, attrName), &attrs, chromedp.ByQueryAll).Do(tCtx)
		/* 打印结果
		[map[src:../images/Less-1.jpg]]
		[]
		[]
		[]
		*/
		fmt.Println(attrs)
		cancel()
		for _, attrMap := range attrs {
    
    
			tab.AddResultUrl(config.GET, attrMap[attrName], config.FromDOM)
		}
	}
}

小结

简单看了一些对响应信息的处理,突然意识到以无头模式启动 Google 并且进行复杂的处理方式,是针对爬虫而言的,参数FuzzPoc验证 没必要使用无头模式,唯一需要的是借鉴项目的并发代码 。

接下来另开一篇文章,搞清除并发代码,然后应用到 参数Fuzz 模块即可。

猜你喜欢

转载自blog.csdn.net/soldi_er/article/details/124039411