【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"})
相关推荐
思则变1 小时前
[Pytest] [Part 2]增加 log功能
开发语言·python·pytest
漫谈网络1 小时前
WebSocket 在前后端的完整使用流程
javascript·python·websocket
try2find3 小时前
安装llama-cpp-python踩坑记
开发语言·python·llama
泡泡以安4 小时前
安卓高版本HTTPS抓包:终极解决方案
爬虫·https·安卓逆向·安卓抓包
博观而约取4 小时前
Django ORM 1. 创建模型(Model)
数据库·python·django
精灵vector5 小时前
构建专家级SQL Agent交互
python·aigc·ai编程
q567315235 小时前
Java Selenium反爬虫技术方案
java·爬虫·selenium
Zonda要好好学习6 小时前
Python入门Day2
开发语言·python
Vertira6 小时前
pdf 合并 python实现(已解决)
前端·python·pdf
太凉6 小时前
Python之 sorted() 函数的基本语法
python