声明
原创文章,请勿转载!
本文内容仅限于安全研究,不公开具体源码。维护网络安全,人人有责。
环节概述
- 使用任意请求转发工具,将页面原本的代码文件的请求转发到本地文件上,也就是我们上一节内容中通过反混淆得到的可阅读代码文件
- 分析请求和代码,找到合适的验证码相关的图片
- 使用 opencv 分析图片,计算滑动距离
环境准备
-
准备 python 环境
我们这次需要准备 python 的环境。可以通过百度搜索下如何安装 python,然后再安装下下面几个库:
pip install numpy requests opencv-python numpy
,这些库是用来帮助我们做一些图像处理的操作。安装 python 依赖的方式推荐使用 pipenv,这个工具可以独立每一个开发环境,而不是把依赖通通安装到全局,来避免全局被污染。
-
准备请求转发工具。
能做请求转发的软件有很多,比如抓包软件:whistle、fiddler、charles,或者浏览器的插件:modheader、reres 等。
挑一个喜欢的即可。
请求转发
使用准备好的转发工具,把链接 static.geetest.com/static/js/slide.7.8.8.js
转发到本地的文件地址即可(例如 file:///xxxx
),具体链接的版本号可以根据实际情况做下修改,或者直接通过通配符略过版本号。
分析请求和代码
-
找到图片文件链接
在官网的在线测试页面中尝试触发滑动验证可以发现,页面会发起一个请求
https://api.geetest.com/get.php
,里面的返回结果是这样的。对照浏览器发起的图片请求我们发现,验证码图片涉及到的是图中箭头所指的四个字段。其中:
- static_servers 是图片地址的可选域名,实际上采用是内部的第一个值
- bg 是验证码的背景图,并且内部含有灰色空缺滑块
- fullbg 也是验证码的背景图,但是内部不含有灰色空缺滑块
- slice 是滑块图
-
还原验证码背景图
观察验证码背景图,我们可以发现它其实是原本的的图片乱序打乱后的结果。
所以我们需要先还原这张图。观察页面,这张图是通过 canvas 画出来的:
所以我们打开 devtolls,先在 canvas 事件上打个断点看看。
一眼可得,这个函数内的很多变量就是刚才我们看到的图片的链接,看来就是这里了。我们简单分析下这里的代码:
function $_BEr(t, e) { t = t.$_CGQ e = e.$_CGQ // 312 var n = t.width // 160 var r = t.height // h 就是 document,i 是创建的 canvas 对象 var i = h.createElement('canvas') // 设置 canvas 宽高 i.width = n i.height = r // 新创建的 i 和原本的 e 是两个 canvas 对象,目的是对图像做一些操作 // 一个用来把图片裁剪出来,一个用来把裁剪出来的图片拼接起来 var o = i.getContext('2d') o.drawImage(t, 0, 0) var s = e.getContext('2d') e.height = r // 160 e.width = 260 var a = r / 2 // 80 for (var _ = 0; _ < 52; _ += 1) { // Ut 是一个数组,里面包含 52 个值 // 也就是说图片被切分为了 52 块,这 52 块会按照下面的方式去还原 // 也就是说原本 312x160 的图被切分为 52块 10x80,最后一共拼成一个 260x160 的图 // 宽度的相对位置 var c = (Ut[_] % 26) * 12 + 1 // 高度的相对位置 var u = 25 < Ut[_] ? a : 0 // 把对应位置裁剪下来 var l = o.getImageData(c, u, 10, a) // 把裁剪下来的部分画到另一个 canvas 内 s.putImageData(l, (_ % 26) * 10, 25 < _ ? a : 0) } }
为了方便逻辑处理,以及和后面使用 opencv 的逻辑对齐,这里选择使用 python 来处理图像。那么刚才还原图片的逻辑可以简单改写为:
from PIL import Image import requests # 刚才的数组扣过来 IMG_SHUFFLE_ORDER = [ 39, 38, 48, 49, 41, 40, 46, 47, 35, 34, 50, 51, 33, 32, 28, 29, 27, 26, 36, 37, 31, 30, 44, 45, 43, 42, 12, 13, 23, 22, 14, 15, 21, 20, 8, 9, 25, 24, 6, 7, 3, 2, 0, 1, 11, 10, 4, 5, 19, 18, 16, 17, ] # 图片会被切分为 10x80 的小图 IMG_SHUFFLE_X_STEP = 10 IMG_SHUFFLE_Y_STEP = 80 # 最后图片的宽高 IMG_WIDTH = 260 IMG_HEIGHT = 160 # 根据链接下载图片 def downloadImg(url: str): r = requests.get(url) return Image.open(io.BytesIO(r.content)) def spliceImg(img: Image.Image): # 创建一个图对象片 newImg = Image.new('RGB', (IMG_WIDTH, IMG_HEIGHT)) # 按照顺序循环 52 次 for i in range(len(IMG_SHUFFLE_ORDER)): x = IMG_SHUFFLE_ORDER[i] % 26 * 12 + 1 y = IMG_SHUFFLE_Y_STEP if IMG_SHUFFLE_ORDER[i] > 25 else 0 # 根据刚才 JS 的逻辑,把图片裁剪出一小块儿 cut = img.crop((x, y, x + IMG_SHUFFLE_X_STEP, y + IMG_SHUFFLE_Y_STEP)) # 根据刚才的逻辑,确定新图片的位置 newX = i % 26 * 10 newY = IMG_SHUFFLE_Y_STEP if i > 25 else 0 # 把新图片拼接过去 newImg.paste(cut, (newX, newY)) return newImg if __name__ == '__main__': img = sliceImg(downloadImg('img url')) img.show()
效果:
分析滑动距离
我们通过前面的操作可以得到两张图片,一张是被还原的背景图,一张是滑块本身的图片(slice)。现在我们要通过一些操作,来找到缺口位置。
整个匹配逻辑是这样的:
-
将图片置灰,减少干扰
-
对图片做边缘检测,进一步降低干扰
-
做模板匹配,找到缺口位置
-
得出滑动距离
import io from PIL import Image import cv2 import numpy as np # 将 Image 转换为 Mat,通过 flag 可以控制颜色 def pilImgToCv2(img: Image.Image, flag=cv2.COLOR_RGB2BGR): return cv2.cvtColor(np.asarray(img), flag) # 弹窗查看图片 def showImg(bg: cv2.Mat, name='test', delay=0): cv2.imshow(name, bg) cv2.waitKey(delay) cv2.destroyAllWindows() def getDistance(img: Image.Image, slice: Image.Image): # 通过 pilImgToCv2 将图片置灰 # 背景图和滑块图都需要做相同处理 grayImg = pilImgToCv2(img, cv2.COLOR_BGR2GRAY) # showImg(grayImg) # 可以通过它来看处理后的图片效果 graySlice = pilImgToCv2(slice, cv2.COLOR_BGR2GRAY) # 做边缘检测进一步降低干扰,阈值可以自行调整 grayImg = cv2.Canny(grayImg, 255, 255) # showImg(grayImg) # 可以通过它来看处理后的图片效果 graySlice = cv2.Canny(graySlice, 255, 255) # 通过模板匹配两张图片,找出缺口的位置 result = cv2.matchTemplate(grayImg, graySlice, cv2.TM_CCOEFF_NORMED) maxLoc = cv2.minMaxLoc(result)[3] # 匹配出来的滑动距离 distance = maxLoc[0] # 下面的逻辑是在图片画出一个矩形框来标记匹配到的位置,可以直观的看到匹配结果,去掉也可以的 sliceHeight, sliceWidth = graySlice.shape[:2] # 左上角 x, y = maxLoc # 右下角 x2, y2 = x + sliceWidth, y + sliceHeight resultBg = pilImgToCv2(img, cv2.COLOR_RGB2BGR) cv2.rectangle(resultBg, (x, y), (x2, y2), (0, 0, 255), 2) # showImg(resultBg) # 可以通过它来看处理后的图片效果 return (distance, resultBg)
总结
这一节内容里,我们简单分析了下网络请求和代码逻辑,搭建了 python 环境并通过 opencv 处理图片得到了所需的滑动距离。
下一节我们将继续分析代码和请求的逻辑,来自己构造请求通过验证
本期文章到这里就结束了,如果对您有帮助,记得收藏关注,有什么想法也可以联系我哦。
后续内容持续更新中。。。