【2024-01-22】某极验3流程分析-滑块验证码

声明:该专栏涉及的所有案例均为学习使用,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!如有侵权,请私信联系本人删帖!

文章目录

一、前言

看一下极验3的流程分析

javascript 复制代码
aHR0cHM6Ly93d3cuZ2VldGVzdC5jb20vU2Vuc2Vib3Q=

二、抓包流程分析

1.刷新页面

①第1个包,register-slide 注册滑块条

返回gt和challenge

②第2个包,gettype.php 获取验证码

携带①包的gt值

③第3个包,get.php

携带的值:

bash 复制代码
gt: ①返回的
challenge: ①返回的
lang: zh-cn
pt: 0
client_type: web
w:加密值,可为空
callback: geetest_1705890896070

而第二个get请求是携带gt和challenge和一个加密值w,经测试w值可以不带

2.点击按钮进行验证

④第4个包,ajax.php

携带的值

bash 复制代码
gt: 第1个包返回
challenge: 第1个包返回
lang: zh-cn
pt: 0
client_type: web
w: 可以置空
callback: geetest_1705891494273

返回

⑤第五个包,get.php 获取图片和新challenge

携带参数:

bash 复制代码
is_next: true
type: slide3
gt: 第1个包返回
challenge: 第1个包返回
lang: zh-cn
https: true
protocol: https://
offline: false
product: embed
api_server: api.geetest.com
isPC: true
autoReset: true
width: 100%
callback: geetest_1705891493735

返回了图片的下载地址,其中包含打乱顺序的底图和滑块图,还有新的challenge

3.滑动验证码

⑥第6个包,ajax.php 校验滑块,w参数包含轨迹,携带gt和challenge

成功返回

失败返回

像前边除了校验的,我们都可以直接请求出来

然后获取到图片后进行图片的还原

三、图片还原

在我们抓包的过程中,我们抓到了图片的包,是一个打乱的图片,一共有两个打乱的图片,一个是完整的底图,一个是有滑块缺口的底图

我们写完前四个流程的代码,第四个包返回的图片的url也是这样

而页面中的图片也是通过canvas渲染上去的

那我们就要想办法还原底图,我们直接勾选canvas断点

点击加载图片按钮,会直接断住,经过跟值会锁定的这个位置,是底图还原的关键代码

这里会得到一个正确顺序的数组,只要根据上诉算法还原即可。这里直接用十一姐大佬的代码

无序图:

还原后

然后识别出距离即可,这里还是用的ddddocr,需要距离-10,因为比实际多算了10px

四、w值

搞完前几部分,只剩下w值,这个w值应该会和滑动的轨迹相关。那我们分析一下这个w值,从滑动的包的第一个堆栈进入

打上断点,然后断住往上找堆栈,很快就会找到这个位置

f值中的\u0077是w的Unicode编码,它在这里生成

javascript 复制代码
w = h + u
u = r[$_CAHJS(737)]()
l = V[$_CAHJS(392)](gt[$_CAIAK(254)](o), r[$_CAIAK(744)]())
h = m[$_CAIAK(792)](l)

由于极验是动态js,这里的h和u名称是动态变化的,这里暂时是u和h

①u值

先来看u值,进入u值的断点,看到返回的是e

大概就是将this[$_CBGAK(744)](t)生成的16位字符串,用new U()['encrypt']进行了加密,我们看一下这个16位的字符串是怎么生成的

进入后

再进,挂住断点发现断不住,需要重新刷新页面可以断住

该值就是一个随机数拼接,自己创建个函数。

创建出16位的字符串后,被new U()['encrypt']加密,我们看一下new U(),能看到setPublic,一般RSA才有这些类似方法

而rsa需要两个参数:加密文本+公钥。获取公钥进入setPublic的函数

在这里打断点,刷新一下页面

这个就是公钥

我们用python库实现

javascript 复制代码
import rsa
from binascii import b2a_hex


def rsa_encrypt_text(key, _text: str):
    """
    RSA加密
    :param key: 公钥的参数
    :param _text: 待加密的明文
    :return: 加密后的数据
    """
    e = int('010001', 16)
    n = int(key, 16)
    pub_key = rsa.PublicKey(e=e, n=n)
    return b2a_hex(rsa.encrypt(_text.encode(), pub_key)).decode()


if __name__ == '__main__':
    key = '00C1E3934D1614465B33053E7F48EE4EC87B14B95EF88947713D25EECBFF7E74C7977D02DC1D9451F79DD5D1C10C29ACB6A9B4D6FB7D0A0279B6719E1772565F09AF627715919221AEF91899CAE08C0D686D748B20A3603BE2318CA6BC2B59706592A9219D0BF05C9F65023A21D2330807252AE0066D59CEEFA5F2748EA80BAB81'
    _text = 'd4e0165efa3c7712'
    result = rsa_encrypt_text(key, _text)
    print(result)

②l值

再看h值,h值参数是l,需要先看l

控制台输出一下各个值,清晰一点

javascript 复制代码
l = V["encrypt"](gt["stringify"](o), r["$_CCEV"]())
h = m["$_FEE"](l)

相当于在 V["encrypt"]中传入了两个参数,一个是那一个json串,估计是加密值,另一个经过跟栈就是上边生成的随机16个字符串,最后结果是得到一个数组

o值应该包含轨迹加密,暂时先放放 ,我们先看看 V["encrypt"]

在这里看到iv关键字,是AES加密,那么需要找到key,iv,加密内容

经上边看到iv是0000000000000000,key是随机16位字符串(与u值中使用的字符串要相等),这里用js实现

javascript 复制代码
var CryptoJS = require("crypto-js");

// 随机生成16位字符串
function get_random_str() {
    var random_str = '';
    for (var i = 0; i < 4; i++) {
        random_str += (65536 * (1 + Math['random']()) | 0)['toString'](16)['substring'](1);
    }
    return random_str;
}

// aes
function aes_encrypt(key, _iv, value) {
    var e = CryptoJS.enc.Utf8.parse(key);
    var iv = CryptoJS.enc.Utf8.parse(_iv);
    var l = CryptoJS.enc.Utf8.parse(value);
    return CryptoJS.AES.encrypt(l, e, {
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7,
        iv: iv
    })
};


var get_arry_i = function (i, a) {
    var o = [];
    for (var s = 0; s < a; s++) {
        var c = i[s >>> 2] >>> 24 - s % 4 * 8 & 255;
        o["push"](c);
    }
    return o;
};

var value = '{"lang":"zh-cn","userresponse":"cccccc66c6c6688d75","passtime":350,"imgload":64,"aa":"O!)(!!H(xysyttsysAt(!!(Zb--5-Y7*7*96$*8_","ep":{"v":"7.9.0","$_BIo":false,"me":true,"tm":{"a":1680165934423,"b":1680165934777,"c":1680165934777,"d":0,"e":0,"f":1680165934429,"g":1680165934433,"h":1680165934440,"i":1680165934440,"j":1680165934535,"k":1680165934442,"l":1680165934535,"m":1680165934772,"n":1680165934774,"o":1680165934779,"p":1680165935041,"q":1680165935041,"r":1680165935044,"s":1680165935070,"t":1680165935070,"u":1680165935071},"td":-1},"h9s9":"1816378497","rp":"56f9ced1740a4656d430a1dfa0bd697b"}';
var aes_result = aes_encrypt(get_random_str(), '0000000000000000', value);
var u = get_arry_i(aes_result["ciphertext"]["words"], aes_result['ciphertext']['sigBytes']);

console.log(u);

③h值

javascript 复制代码
h = m[$_CAIAj(783)](l)

断点进入

再进去

这里我们抠出这个函数即可,把这个函数对象的方法都扣下来

运行一下,缺什么补什么

修改一下获取h的方法,变量先用浏览器抠的固定值
都抠出来

运行一下

④l中的o值

看刚才l的o值

示例:

javascript 复制代码
 {
        "lang": "zh-cn",
        "userresponse": "cccccc66c6c6688d75",
        "passtime": 350,
        "imgload": 64,
        "aa": "O!)(!!H(xysyttsysAt(!!(Zb--5-Y7*7*96$*8_",
        "ep": {
            "v": "7.9.0",
            "$_BIo": false,
            "me": true,
            "tm": {
                "a": 1680165934423,
                "b": 1680165934777,
                "c": 1680165934777,
                ...省略
            },
            "td": -1
        },
        "h9s9": "1816378497",
        "rp": "56f9ced1740a4656d430a1dfa0bd697b"
    }
  • h9s9:【可以写死】随机参数影响不大
  • imgload:【可以写死】图片加载时间,随机生成
  • ep:【可以写死】涉及v和tm,v是js文件的版本,tm和window["performance"]["timing"]相关,可以根据Performance.timing 时间产生的先后顺序以及时间间隔,用当前的时间戳减去相应的值来模拟。
  • aa:对轨迹和响应的c和s参数进行了加密
  • passtime:轨迹滑动总时长
  • rp://gt + 32 位 challenge + passtime,再经过 MD5 加密
  • userresponse: 滑动距离x + challenge 的值

这些参数往上翻一点,可以看到生成的地方

aa参数

这里看一下aa,是第二个参数,追上一个栈

就是这里的l,
l是由上一行的函数生成,需要三个参数,其中后两个是我们曾经抓包的响应c和s

第一个参数就是加密后的轨迹,而前边的部分就是轨迹数组,应该是x,y,t

进去这个函数看看怎么加密的轨迹

进来到这里使用了轨迹列表

看最后跟到这里变成了加密

我们直接把这个方法抠出来,传一个轨迹

把使用的地方,改为我们的轨迹

然后抠一个浏览器的轨迹运行,缺什么补什么,注意抠ct的时候有个坑需要补一下

最后抠出来这几个

拿浏览器的轨迹试验对比一下

到此轨迹的加密就获取出来了

接下来继续,后边的参数是c和s,直接断点抠下来就可以

至此这个l就是我们的aa参数

passtime参数

取轨迹的最后一个列表的的最后一位数

bash 复制代码
passtime = track[-1][-1] #滑动时间

userresponse参数

t为我们轨迹的滑动x距离,另外一个为challenge,然后H函数

抠出来,修改亿点点

bash 复制代码
function H(t, e) {
     var $_CJES = mwbxQ.$_Cg
        , $_BEGI_ = ['$_BEHCp'].concat($_CJES)
        , $_BEHAO = $_BEGI_[1];
    $_BEGI_.shift();

    var $_DAJEI = mwbxQ.$_DW()[9][13];
    for (; $_DAJEI !== mwbxQ.$_DW()[0][11];) {
        switch ($_DAJEI) {
            case mwbxQ.$_DW()[6][13]:
                for (var n = e[$_CJES(187)](-2), r = [], i = 0; i < n[$_CJES(192)]; i++) {
                    var o = n["charCodeAt"](i);
                    r[i] = 57 < o ? o - 87 : o - 48;
                }
                n = 36 * r[0] + r[1];
                var s, a = Math[$_CJES(158)](t) + n, _ = [[], [], [], [], []], c = {}, u = 0;
                i = 0;
                $_DAJEI = mwbxQ.$_DW()[6][12];
                break;
            case mwbxQ.$_DW()[0][12]:
                for (var l = (e = e[$_CJES(187)](0, -2))['length']; i < l; i++)
                    c[s = e[$_CJES(125)](i)] || (c[s] = 1,
                        _[u][$_CJES(137)](s),
                        u = 5 == ++u ? 0 : u);
                var h, f = a, d = 4, p = '', g = [1, 2, 5, 10, 50];
                while (0 < f)
                    0 <= f - g[d] ? (h = parseInt(Math['random']() * _[d]['length'], 10),
                        p += _[d][h],
                        f -= g[d]) : (_[$_CJES(115)](d, 1),
                        g[$_CJES(115)](d, 1),
                        d -= 1);
                return p;
                break;
        }
    }
}

rp参数

这个值在w生成的上一点点

结果32位

有点像md5,试验一下

至此,所有参数搞定

五、结果

效果展示

其他情况

bash 复制代码
// challenge 不对
geetest_xxxxxxxxxxxxx({"status": "error", "error": "illegal challenge", "user_error": "网络不给力", "error_code": "error_23"})
// w 生成不对
geetest_xxxxxxxxxxxxx({"status": "error", "error": "param decrypt error", "user_error": "网络不给力", "error_code": "error_03"})
// 滑动验证没有轨迹
geetest_xxxxxxxxxxxxx({"status": "error", "error": "not proof", "user_error": "网络不给力", "error_code": "error_21"})
// 轨迹、缺口距离、参数问题
geetest_xxxxxxxxxxxxx({"success": 0, "message": "fail"})
geetest_xxxxxxxxxxxxx({"success": 0, "message": "forbidden"})
相关推荐
JovaZou22 分钟前
[Python学习日记-75] 计算机基础与网络
开发语言·网络·python·网络协议·学习·tcp/ip·计算机网络
深栈24 分钟前
Tkinter组件-Button按键
python·gui·tkinter
QQ27437851091 小时前
django在线考试系统
后端·python·django
数据小爬虫@2 小时前
运行爬虫时可能遇到哪些常见问题?
爬虫
程序员非鱼3 小时前
深度学习中常见的激活函数详解
人工智能·python·深度学习·神经网络·机器学习·激活函数
Ckyeka3 小时前
Leetcode刷题笔记—栈与队列
数据结构·python·算法·leetcode
Q_27437851093 小时前
django基于 Python 的考研学习系统的设计与实现
java·python·考研·django
白雪公主的后妈3 小时前
Python爬虫基础——IP反爬虫的应对
爬虫·python·random·requests模块·parsel
Jelena技术达人4 小时前
利用 Python 爬虫获取 1688 关键字 API 接口
开发语言·爬虫·python
白雪公主的后妈4 小时前
Python爬虫进阶——案例:模拟bilibili登录)
爬虫·python·selenium模块