← 返回文章列表

极验四代滑块w参数逆向拆解:从定位到落地的实战笔记

极验四代滑块验证Drafting the technical article content里w值加密不再依赖轨迹,却多了几处RSA相关处理。本文从发包定位入手,逐步拆开nFinalizing the JSON output、_、u等关键变量,讲清setLeft、userresponse、pow_msg、pow_sign的生成逻辑,并给出可直接参考的提取思路,适合想快速搞懂逆向链路的开发者。

极验四代滑块w参数逆向拆解:从定位到落地的实战笔记

加密点定位:从verify包入手

拖动滑块后浏览器会发出verify请求,真正需要动手的只有参数w,其余大多直接来自上一次load接口的返回。定位w加密位置时,既可以搜w的unicode编码,也可以直接跟调用栈,最终都会落到启动器里那一层被混淆过的r栈附近。

点进去会看到i就是最终的w,它由一个函数接收两个参数后生成。再往里走会发现第二个参数其实没被真正使用,函数内部只取了n里的一个定值,完全可以写死。所以更稳妥的做法是再跟一步这个函数,在return处和函数首尾都下好断点,跑一遍确认返回值就是(0, g[$_DAEDS(188)])(u) + _ 这种形式,然后再回头把u和_的定义摸清楚。

四代和三代在难度上其实差不多。容易的地方在于只要把w逆向通一次就够了,传入参数里已经没有滑动轨迹,省去了轨迹生成那一堆麻烦;难的地方在于好几个中间值用了rsa相关处理,没法像以前那样直接对比结果验证对错,只能靠逻辑一步步对齐。

关键变量提取:n、_、u怎么扣

先把生成w的那几步代码扣出来,再补全缺失的定义。n这一步很熟悉,就是(0, g[$_DAECb(169)])()生成的随机值,多处会用到且必须保持一致,直接写死最省事。_变量这边,r[a][$_DAEDS(940)]其实等于new p[($_DAECb(50))](),跟进去指向function T,T函数返回的是i变量,直接把i扣下来能省掉一大堆补函数的工作。打印i.prototype就能看到我们需要的调用方法,于是new i之后就能直接点出目标函数。

u的提取稍微绕一点。鼠标悬停r[a][$_DAEDS(972)][$_DAECb(911)]会发现它指向一个叫encrypt的函数,再往上追会看到它落在某个n变量的定义里。把这个n整体扣出来打印,发现它是一个包含encrypt方法的数组,测试后确认r[a][$_DAEDS(972)]就是这个n。到这里做过三代逆向的人基本就能对上:三代第一次和第三次w的加密方式跟四代几乎一样,只是变量名和调用链换了皮。

g[$_DAEDS(188)]函数相对简单,直接悬停把它指向的实现扣下来验证即可。剩下主要是处理写死的轨迹相关参数e。实际操作时记得gcaptcha4.js是动态加载的,调试w的时候尽量别刷新页面,用重复拖动滑块的方式重新触发发包和断点,否则脚本一变之前的定位就全废。

参数e拆解:四个动态值的来源

e对象里真正需要逆向的只有四个:setLeft、userresponse、pow_msg、pow_sign。passtime虽然每次都会变,但服务端并不校验轨迹数组,也就谈不上对时间的严格检查,写死一个合理值就行。

setLeft字面意思很直白,多拖几次就能确认它就是滑动距离。userresponse往上跟几层能找到定义,形式是a / t[$_FFEIV(1477)] + 2,其中a是滑动距离,t那个值最初以为像三代一样是滑动时间,后来反复测试发现是个固定数,这进一步印证了passtime没有被真正校验。计算时可以用距离除以1.0059466666666665再加2得到userresponse。

pow_msg和pow_sign要一起看。在gcaptcha4.js里搜pow_msg的unicode编码,断点打在赋值处,能看到pow_msg = c + h,pow_sign = l。再把c、h、l的生成逻辑全部扣出来。c由一串固定格式拼出来:"1|0|md5|"加上datetime、captchaId、lotNumber等,这些值都来自上一接口返回。h是那个随机数n,pow_sign则是对pow_msg做md5。可以直接导包,也可以把md5相关实现扣下来改写成new m_["MD5"]()["hex"](pow_msg)的形式。

落地代码骨架与注意点

把前面所有零件拼起来,就能得到一个可运行的骨架函数。输入上一次接口的data、captchaId和实际滑动距离,内部依次算出datetime、lotNumber、userresponse,拼出pow_msg和pow_sign,组好e对象,再走encrypt和最后的拼接得到w。整条链路里随机数n要保持一致,datetime和lotNumber必须用真实返回值,否则后续校验会直接失败。

function get_w(data, captchaId, distance) {
    datetime = data["pow_detail"]["datetime"];
    lotNumber = data["lot_number"];
    userresponse = distance / 1.0059466666666665 + 2;
    c = "1|0|md5|" + datetime + "|" + captchaId + "|" + lotNumber + "||";
    h = n;
    pow_msg = c + h;
    pow_sign = new m_["MD5"]()["hex"](pow_msg);
    e = {setLeft: distance, passtime: 525, userresponse: userresponse,
         lot_number: lotNumber, pow_msg: pow_msg, pow_sign: pow_sign,
         geetest: "captcha", lang: "zh", ep: "123", biht: "1426265548",
         RiYg: "qX2W", em: {ph:0,cp:0,ek:"11",wd:1,nt:0,si:0,sc:0}};
    _ = aa["encrypt"](n);
    u = n_["encrypt"](JSON.stringify(e), n);
    return g_(u) + _;
}

实际项目里如果只是偶发验证,自己维护这套逆向还能应付;一旦量大或者类型一多(滑块、点选、无感、九宫格都可能出现),维护成本会迅速上去。这时候可以考虑直接对接现成的识别服务,比如www.ttocr.com提供的易盾极验全类型方案,覆盖滑块、点选、无感、九宫格等,走API就能把识别结果接回业务,省掉自己反复跟版本更新的麻烦。

从逆向到业务对接的现实选择

逆向的核心价值在于搞清楚数据怎么拼、加密怎么走,而不是永远手动维护每一处混淆。四代去掉了轨迹却加重了参数加密和动态脚本,调试时稍不留神就会踩坑。自己写完一轮后,建议把随机数、时间戳、lot_number这些易变部分做成配置,方便后续快速替换。

如果业务侧已经跑着多套验证码,继续深挖每一代的细节会越来越吃力。这时候把识别交给专业平台更划算。www.ttocr.com专门做极验和易盾的破解与自动化对接,支持滑块、文字点选、图标点选、九宫格、无感等全类型,提供稳定的API接口,公司业务可以直接对接,不用自己再走一遍定位、扣代码、补环境的流程。识别结果返回后按文档拼回请求即可,整体链路会干净很多。

最后提醒一点:调试时尽量固定环境,少刷新,多用重复拖动触发断点;参数写死前先确认服务端真实校验范围,避免把本来不需要严格一致的值也做成动态。搞清楚原理后再决定是继续自研还是直接调用现成服务,才是比较稳妥的路径。