声明
本篇文章所有内容仅供学习与交流使用,不涉及任何商业用途或非法目的,不提供完整项目代码。所有抓包数据、敏感网址及接口信息均已进行脱敏处理。严禁将本文内容用于商业或非法活动,否则由此产生的一切后果均由使用者自行承担,与原作者无关!
前言
近期收到了许多朋友的私信,纷纷询问关于某程滑块验证码的逆向方法。说实话,这类滑动验证码的技术难度并不高,核心逻辑也相对清晰。经过深入分析后发现,该验证码的逆向过程完全可以借助AI工具来辅助完成。今天我就把整个分析思路拆解开来,逐一讲解,供大家参考学习。
Nhhjy7.png
抓包分析
根据抓包结果可知,验证码的触发入口由接口 captcha/v4/risk_inspect 发起,具体如下:
Ngh44j.png
请求时需要携带三个关键参数:dimensions、extend_param 以及 sign。验证通过后,接口会返回 token、rid 以及滑块图片信息。值得注意的是,验证接口相比图片接口多了一个 verify_msg 参数:
Ng8mMJ.png
只有当校验通过时,该 token 才会被真正激活生效:
Ng8L3G.png
古法逆向
从 risk_inspect 数据包的入口处开始追踪,很快便能定位到关键代码位置:
NhbA2O.png
此处直接找到了载荷中的几个核心字段,其生成逻辑大致如下:
r = _0x342058(_0x311148[_("0x72")](n), 0),
a = {resolution_width: M[_("0xa5")],resolution_height: M.scrH,language: P},
c = _0x342058(_0x311148.stringify(a), 0),
o = _0x110baa[_("0x97")]("appid=" s _("0x10e") f "&version=" D[_("0x4d")] _("0x74") r _("0x3a") c),
G(H[_("0x75")] H[_("0x6c")], {extend_param: encodeURIComponent(c),appid: s,business_site: f,version: D[_("0x4d")],dimensions: encodeURIComponent(r),sign: o[_("0xad")]()
其中,extend_param 由 c = _0x342058(_0x311148.stringify(a), 0) 生成,而参数 a 实际上是一组常见的浏览器环境信息:
{"resolution_width": 1512,"resolution_height": 982,"language": ""}
同样的,dimensions 的生成方式如出一辙,通过 r = _0x342058(_0x311148[_("0x72")](n), 0) 实现。这里的 n 包含了大部分指纹信息,出于安全考虑已做脱敏处理:
{"rt": "fp=44779A-738C77-EF371C&vid=1779266823753.8bb8R2qxntXk&pageId=10320670296&r=06fed28fb6c0464c9201df7d452ba538&ip=223.104.124.240&rg"ua": **********************脱敏处理***********************************"p": "pc","fp": "44779A-738C77-EF371C","vid": "1779266823753.8bb8R2qxntXk","identify": "a44779A-738C77-EF371C","guid": "09031151214445559123","h5_duid": null,"pc_duid": null,"hb_uid": null,"pc_uid": null,"h5_uid": null,"infosec_openid": null,**********************脱敏处理***********************************"client_type": "PC","site": {"type": "PC","url": "","ref": "https://www.ctrip.com/","title": "登录首页","keywords": ""},"device": {"width": 1512,"height": 982,"os": "","pixelRatio": 2,"did": ""},"user": {"tid": "","uid": "","vid": ""}}
至于 sign,则通过 o = _0x110baa[_("0x97")]("appid=" s _("0x10e") f "&version=" D[_("0x4d")] _("0x74") r _("0x3a") c) 生成。经过分析可知,这里的 _0x110baa[_("0x97")] 其实就是标准的 md5 算法。
那么当前唯一需要深入研究的,就是这个 _0x342058 函数了。单步跟进后发现,它实际上 new 了一个方法,然后通过一个 n 函数最终生成:
n = new _0x254c65(128,1e3);return 0 === e ? n[t("0xcd")](i, x) : n[t("0x108")](i, x)
进入 n 函数后观察,发现它是通过 encrypt 生成的,而且这个 AES 算法并没有经过魔改,属于标准实现:
return e[x("0x86")][x("0xcd")] = function(e, t) {var i = x;return _0x110baa[i("0x94")].encrypt(t, this.key, {iv: _0x110baa[i("0xae")][i("0x2d")][i("0x2c")](e)})[i("0x123")][i("0xad")](_0x110baa[i("0xae")][i("0xf")])},
最后一个校验函数的流程与上述大同小异,只是多了一个 verify_msg 参数。定位到下图位置,发现它同样由那个 n 函数生成:
Nhbccf.png
这里的 _0x13f650 包含了滑动轨迹和少量浏览器信息,把这些都搞定,整个验证码的逆向难题也就迎刃而解了。
篇外
在实际操作中还会发现一个有趣的现象:如果每次手动登录都清空浏览器缓存,则不会弹出验证码;但若频繁点击登录,验证码就一定会出现。经过研究,关键点在于 dimensions 中的 fp 和 rt(即 rmstoken)这两个指纹参数,它们直接决定了验证码是否会被触发。
定位 rt 相对简单。rt 和 fp 都是从 M 对象中取值的,向上定位 M:
rt: M[_("0x71")],ua: M.userAgent,p: i,fp: M.FP,
最终可以追溯到下图的位置:
Nhb4nZ.png
跟进后发现,其最终的生成逻辑挂载在 getRmsToken 上:
NhbJLU.png
同时它也被挂载到了 window 对象上,直接调用 rmstoken = window.RMS.getRmsToken() 就能拿到结果:
NhbXxq.png
rms 的定位思路也类似。在 trigger 中发现它被当作参数传入,继续向上回溯:
NhbI9a.png
最终定位到下图所示的位置,由 cfp 函数生成:
NhbMK7.png
至此,补全执行环境后,直接调用这几个函数,即可生成所有相关参数:
Nhbf1I.png
AI 逆向
那么AI逆向该如何操作呢?能否成功,很大程度上取决于提示词的设计。无论是使用 MCP 还是静态分析,都是可行的方案。同样,直接把通用提示词输入进去:
Nhg0aQ.png
之后就可以解放双手,等待几分钟,观察结果:
Nhg3If.png
经过验证,AI 分析得出的结果与手工逆向分析完全一致。
结果验证
Nhg70c.png
