入微-极验第四代滑块验证码系列二

该系列文章仅限于某验滑块研究,不会公开具体算法源码,欢迎讨论
本文关联文章:
纵观
入微
芥子
浩瀚

一. 观察verify请求

  1. 我们看一下verify请求的Initiator,可以观察到全是来自于gcaptcha4.js这个文件,所以该文件也是我们此次的目标所在
  2. 点开gcaptcha4.js这个文件,格式化完,13339行,嗯,真的是混淆他妈给混淆开门,混淆到家了
  3. 这时候我们有两种选择,硬刚或者AST,我这里用的是AST还原js文件,然后再使用chrome伤的reres插件把混淆的js替换掉,再来一步一步调试。这里的AST代码我放在了github上面,插件reres的安装以及使用各位自行谷歌。AST还原代码
  4. 做到下面这个效果,就算替换成功了

二. 定位w参数加密位置

  1. 搜索w,我们这里根据经验可以搜索以下关键词来定位
w 或 .w 或 'w' 或 "w"
  1. 这里搜索"w"有我们想要的返回,我们在2527行打上断点,可以看到w的值r在2525行定义,我们也打上断点,刷新一下,成功断上,至此我们已完成w参数的加密位置定位

三. 分析w参数加密算法

  1. 简化参数w的值r的定义
var r = (0, d["default"])(l["default"]["stringify"](e), a)
等价于,其原理是js的逗号操作符对它的每个操作对象求值(从左至右),然后返回最后一个操作对象的值
var r = d["default"](l["default"]["stringify"](e), a)
等价于,其原理均是js获取对象的属性
var r = d.default(l.default.stringify(e), a)
  1. 分析l.default.stringify(e)含义

    可以看出此处就是对e进行json序列化操作

  2. 分析d.default(l.default.stringify(e), a)含义

    跟进d函数
    我们先对里面的几个条件判断处打上断点,

    调试到最后一处,前往console处打印一下值,可以得出下面结论
function a(e, t) {
    var s = t["options"];
    // !s["pt"] || "0" === s["pt"] 为false,恒不执行
    if (!s["pt"] || "0" === s["pt"])
        return r["default"]["urlsafe_encode"](e);

    // "1" === s["pt"]为true,恒执行
    if ("1" === s["pt"]) {
        var n = (0,
        c["guid"])()
          , a = new _["default"]()["encrypt"](n);

        // !a || 256 !== a["length"]为false,恒不执行
        while (!a || 256 !== a["length"])
            n = (0,
            c["guid"])(),
            a = new _["default"]()["encrypt"](n);

        var o = i["default"]["encrypt"](e, n);
        return (0,
        c["arrayToHex"])(o) + a;
    }
}
等价于
function a(e, t) {
    var n = c["guid"])(), 
        a = new _["default"]()["encrypt"](n);
        o = i["default"]["encrypt"](e, n);
    return c["arrayToHex"])(o) + a;
}

我们到这里可以确定参数t并没有被使用,参数e就是w的明文,我们接着分析json序列化之前e的成分

  1. 继续分析e的组成
{
    'device_id': 'A8A0', # 固定值
    'em': { 
        'cp': 0,
        'ek': '11',
        'nt': 0,
        'ph': 0,
        'sc': 0,
        'si': 0,
        'wd': 1,
    }, # 固定值
    'ep': '123', # 固定值
    'geetest': 'captcha', # 固定值
    'fq6a': '1925502591', # 固定值
    'lang': 'zh', # 固定值
    'lot_number': '7e22264d4f3e4dd8a6ffbf6e82e1122d', # load请求返回值
    'passtime': 166, # 通过时间
    'pow_msg': '1|0|md5|2022-03-25T14:23:36.364152+08:00|24f56dc13c40dc4a02fd0318567caef5|7e22264d4f3e4dd8a6ffbf6e82e1122d||29f07cebf938aa4e', # load请求返回加上某算法返回值,后面再分析
    'pow_sign': '2b47a3a9425dd19dd5abf902c8bb0763', # pow_msg的md5值,为何是md5,写在了pow_msg上
    'setLeft': 88, # 滑动距离
    'track': [[38, 18, 0], [1, 0, 33]......], # 轨迹
    'userresponse': 87.47978686742837 # 某算法返回值
}

4.1 pow_msg和pow_sign我们放一起分析

搜索pow_msg,全部打上断点,刷新,然后保留成功断上的断点
u = n + "|" + a + "|" + s + "|" + o + "|" + t + "|" + e + "|" + r + "|"
# 上面n、a、s、o、t、e、r的值则来自load请求的response中
p = (0, b["guid"])()
g = u + p
pow_msg = u + p
pow_sign = md5(g)

我们还需要分析下b.guid()是什么算法,继续跟进

var a = function () {
    function e() {
      return (65536 * (1 + Math["random"]()) | 0)["toString"](16)["substring"](1);
    }

    return function () {
      return e() + e() + e() + e();
    };
}();

t["guid"] = a;

4.2. set_left、track、passtime、userresponse

set_left为滑块移动距离取整
track为移动轨迹,从第二步开始,是上一步的相对移动距离(x, y, t)
passtime为总移动时间
userresponse为set_left / (0.8876 * 340 / 300)

  1. 到这里我们已经把w的明文分析出来了,又到了show code环节了

四. 结语

我们还剩set_left、track、passtime三部分,也就是滑块的缺口距离识别,滑动轨迹的构造,滑动耗时,这个我们放到下一篇文章再分析,当然也包括函数a(e, t)中的重头戏:c.guid()、_.encrypt()、i.encrypt()、c.arrayToHex()四个函数
芥子

你可能感兴趣的:(入微-极验第四代滑块验证码系列二)