JS逆向:极验滑块验证码加密分析

本文仅供学习交流使用,请勿用于商业用途或不正当行为

如果侵犯到贵公司的隐私或权益,请联系我立即删除

搞爬虫的没有不知道极验的吧,看下官网的介绍

JS逆向:极验滑块验证码加密分析_第1张图片

卧槽,牛逼!

嗯,这就是读书少的缘故吧,除了卧槽牛逼,其他也不会讲了…

不说了,直接看干货吧

AST反混淆还原代码

极验的关键JS文件有两个:fullpage.8.9.5.js和slide.7.7.2.js(貌似这是最新的版本?)

前一篇文章JS逆向:AST还原极验混淆JS实战使用AST还原了fullpage.8.9.5.js,另外的slide.7.7.2.js文件还原方法与fullpage.8.9.5.js一样的,还原代码基本上是通用的,这里就不多说了。

乱序验证码图片还原

极验的验证码背景图片分两张,一张带滑块缺口的图片,一张不带滑块的完整图片。

两张都被分割成52分,上下两部分各26分,然后乱序排列,通过css将乱序的图片重组起来,显示在网页里

JS逆向:极验滑块验证码加密分析_第2张图片

JS逆向:极验滑块验证码加密分析_第3张图片

插播一下:我写文章一般是边分析边记录,本来是准备搞B站的,结果搞一半,B站给换了极验的文字点选验证码,雷佳音版:我尼玛!!!

JS逆向:极验滑块验证码加密分析_第4张图片

刚开始还以为是B站是分时间段展示不同的验证码,结果两天过去了,发现的确给换了文字点选,小伙伴给了个其他使用极验的站,所以接下来的截图会跟之前的不一样,但都是极验的将就看吧。

因为验证码图片是乱序的,所以首选需找到图片还原的算法,对乱序图片还原后才能进行缺口位置识别

通过观察能够看到,图片是用canvas画出来的,如下

JS逆向:极验滑块验证码加密分析_第5张图片

canvas绘图前肯定需要先对图片还原再进行绘图,这时候可以使用油候脚本在canvas绘图前HOOK住,定位到对应的canvas代码位置,再进行分析,脚本如下:

// hook canvas
(function() {
    'use strict';
    let create_element = document.createElement.bind(doument);

    document.createElement = function (_element) {
        console.log("create_element:",_element);
        if (_element === "canvas") {
            debugger;
        }
        return create_element(_element);
    }
})();

添加HOOK脚本后,点击【获取验证码】按钮触发极验滑块验证码,脚本就会在canvas调用时暂停。

由于网页内除了验证码图片外还有其他图片也使用了canvas绘制,所以脚本会Hook住每一个canvas调用,可以使用F8跳过其他图片,一直跳过到页面快要显示出验证码图片时

JS逆向:极验滑块验证码加密分析_第6张图片

这里大概跳过了11个拦截的canvas吧,开始单步进入到以下位置

JS逆向:极验滑块验证码加密分析_第7张图片

图片还原的算法就这里了,for循环里的常量SEQUENCE在这里

JS逆向:极验滑块验证码加密分析_第8张图片

按照上述算法编写python代码,然后就可以拿到还原后的图片了

JS逆向:极验滑块验证码加密分析_第9张图片

缺口位置识别

这种识别比较简单,思路是按列遍历两张图片的每个像素点位置,比较两张图片素点的RGB值,对比出来的第一个不同的像素点即为滑块位置

def is_px_equal(self, img1, img2, x, y):
    """
    判断两个像素是否相同
    :param img1: 图片1
    :param img2:图片2
    :param x:位置1
    :param y:位置2
    :return:像素是否相同
    """
    pix1 = img1.load()[x, y]
    pix2 = img2.load()[x, y]
    threshold = 60

    if abs(pix1[0] - pix2[0]) < threshold and abs(pix1[1] -pix2[1]) < threshold and abs(pix1[2] - pix2[2]) < threshold:
        return True
    else:
        return False

def get_gap(self, img1, img2):
    """
    获取缺口偏移量
    :param img1: 不带缺口图片
    :param img2: 带缺口图片
    :return:
    """
    left = 0
    for row in range(left, img1.size[0]):
        for col in range(img1.size[1]):
            if not self.is_px_equal(img1, img2, row, col):
                left = row
                return left
    return left

load() 方法可以获取[x,y]位置的RGB值size() 方法的返回值为图片宽度和高度值

JS逆向:极验滑块验证码加密分析_第10张图片

轨迹数据加密

在提交验证的请求里跟踪调用栈,在$_DADP找到加密的关键位置

JS逆向:极验滑块验证码加密分析_第11张图片

JS逆向:极验滑块验证码加密分析_第12张图片

var i = {
  "lang": o["lang"] || "zh-cn",
  "userresponse": $_CFn(t, o["challenge"]),
  "passtime": n,
  "imgload": r["$_CJFt"],
  "aa": e,
  "ep": r["$_DAEP"]()
};

t的值是传进来的,o[“challenge”]是获取图片信息请求返回的challenge

函数$_CFn(),没啥好说的直接扣

JS逆向:极验滑块验证码加密分析_第13张图片

passtime的值是n,n是传进来的

aa的值为e,e也是传进来的

ep的值是$_DAEP的返回结果

JS逆向:极验滑块验证码加密分析_第14张图片

te的值为false,me的值为true,可以是固定的

img

$_FHx()函数经过AST还原后为空函数

img

$_CCDO调用的是window.performance.timing里的属性值

JS逆向:极验滑块验证码加密分析_第15张图片

返回结果为

JS逆向:极验滑块验证码加密分析_第16张图片

i["rp"] = $_DDF(o["gt"] + o["challenge"]["slice"](0, 32)+ i["passtime"]);

这里传入了前面的几个值到$_DDF里进行了计算

$_DDF也是整个直接扣

到现在还是不清楚传进来的t、e,、n分别是什么,必须先搞清楚才能进行加密

跟踪到上一步,$_DADP的调用处,看下传入的参数都是什么

注:为了便于调试同时能看起来清晰点,所以改成了以下这样

t1 = n["$_DDFQ"]["$_BCBz"](),
t2 = n["$_IFM"]["c"],
t3 = n["$_IFM"]["s"],
l = n["$_DDFQ"]["$_BHBs"](t1, t2, t3);

c是拖动滑块时在x轴的最后一个点,也就是滑块缺口的距离n["$_CGJL"] 其实是拖动滑块的总耗时

接下来看t1,先看一下n["$_DDFQ"]

JS逆向:极验滑块验证码加密分析_第17张图片

跟进$_BCBz

JS逆向:极验滑块验证码加密分析_第18张图片

这个$_BCJN就是滑动轨迹数据,最后通过

r["join"]("") + "!!" + o["join"]("") + "!!" + i["join"("");

分隔拼接后返回结果

img

t2和t3是请求图片信息时返回的c和s值

img

JS逆向:极验滑块验证码加密分析_第19张图片

接着将t1、t2、t3传入$_BHBs进行处理得到l的值

JS逆向:极验滑块验证码加密分析_第20张图片

搞清楚了传入的t、e,、n后,接下看这一段

var s = r["$_DAFB"](),
    a = AES["encrypt"](gjson["stringify"](i), r["$_DAGa"()),
    _ = Base64["$_BCCY"](a),
    u = {
      "gt": o["gt"],
      "challenge": o["challenge"],
      "lang": i["lang"],
      "pt": r["$_CDHf"],
      "w": _ + s
    };

先看:

s = r["$_DAFB"]()

$_DAFB里面是进行了RSA加密

img

这是RSA的秘钥

JS逆向:极验滑块验证码加密分析_第21张图片

代码太长了显示不全

this['setPublic']('00C1E3934D1614465B33053E7F48EE4EC87B14B95EF88947713D25EECBFF7E74C7977D02DC1D9451F79DD5D1C10C29ACB6A9B4D6FB7D0A0279B6719E1772565F09AF627715919221AEF91899CAE08C0D686D748B20A3603BE2318CA6BC2B59706592A9219D0BF05C9F65023A21D2330807252AE0066D59CEEFA5F2748EA80BAB81','10001');

RSA传入的值为$_DAGa的返回值

img

看下$_DJj

img

img

简单说就是通过$_DJj生成一个随机字符作为RSA的key,然后进行RSA加密,得到的值给s

再分析:

a = AES["encrypt"](gjson["stringify"](i), r["$_DAGa"]())

stringify是将对象转换为字符串AES[“encrypt”] 传入了两个值:i 和 $_DAGa的返回值(刚才分析过)AES的秘钥为"0000000000000000"

JS逆向:极验滑块验证码加密分析_第22张图片

_ = Base64["$_BCCY"](a)

最后将a的值进行base64编码,这里不多说也是整体扣下来

至此极验滑块的加密已经分析完了,只需将生成的轨迹数据用上述加密方法进行加密,然后发送给服务端校验,至于生成轨迹的思路,网上一搜一大把。

最后测试了一下貌似有80%-90%的成功率,感觉还行~

JS逆向:极验滑块验证码加密分析_第23张图片

极验滑块的破解难度相对还是稍微高一些,需要的就是耐心。

这一篇写的没有之前那么全面,但写了一些主要关键点。由于极验的复杂度稍高,写起来也要花一些时间,最近忙着找工作,精力有限,就这样吧。

另外,本文使用的代码为个人学习成果,暂不对外公开,请小伙伴们理解

再次声明,本文仅为个人学习记录分享,请勿用于商业用途或者不正当行为。

你可能感兴趣的:(爬虫,JS逆向,python,js)