GJ航司,验证码网易易盾 JS逆向 纯算轨迹

技术背景与目标

目标网址:aHR0cHM6Ly93d3cubG9vbmdhaXIuY24vIy9sb2dpbg==

逆向目标与技术难点

JS混淆和ASCALL编码是逆向阅读的难点,本文主要讲轨迹的生成。个别接口会一并带过

JS逆向分析流程
初始化

打开网址,输入手机号点击获取验证码。会触发风控,加密参数有dt,irToken,cb,fp

打上断点,此时数据已经生成

继续跟栈,这里actions里面的_0x8f2900存放值,

搜索一下,在cb打上断点

进入该方法,前面三个是定值,可以写死,分别是suffix :m25b40 code:vfnv46和一个定值数组

_0x58ee17 = _0x11cfc7a0_0x1e60(496)(32);这个是uuid,控制台输出一下a0_0x1e60(496)是uuid

然后for循环对数三个值进行charat拼接,剩下的进入下一个方法进行操作。这个扣代码就可以。不详细讲

javascript 复制代码
var _0x11cfc7 = {
    uuid: function(length) {
        var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
        var result = '';
        for (var i = 0; i < length; i++) {
            result += chars.charAt(Math.floor(Math.random() * chars.length));
        }
        return result;
    }
};

        function _0x62692() {
          var _0x28d664 = {};
//          _0x28d664['suffix'] = a0_0x1e60(1387),
//          _0x28d664['code'] = a0_0x1e60(1388),
          _0x28d664['suffix'] = "m25b40",
          _0x28d664['code'] = "vfnv46",
          _0x28d664["pos"] = [
            1,
            10,
            12,
            13,
            26,
            31
          ];
          var _0x183b74 = _0x28d664 ||
          {
          },
          _0x86f269 = _0x183b74["code"],
          _0x22d54d = _0x183b74["pos"],
          _0x58ee17 = _0x11cfc7["uuid"](32);
          if (_0x86f269 && _0x22d54d && Array['isArray'](_0x22d54d)) {
            for (
              var _0x56eeac = _0x58ee17['split'](''),
              _0x3d43fc = 0;
              _0x3d43fc < _0x22d54d['length'];
              _0x3d43fc++
            ) _0x56eeac[_0x22d54d[_0x3d43fc]] = _0x86f269['charAt'](_0x3d43fc);
            _0x58ee17 = _0x56eeac['join']('');

          }
          return _0x3ebd00(_0x58ee17);
        }



a=_0x62692()
console.log("生产的cb值为"+a)
up,conf接口

初始化接口的别的参数,fp扣代码复现,dt conf接口返回,irToken up接口返回

getconf接口:可固定,直接默认请求,

up接口:参数固定,除了n 是一个uuid,可以python复现之后动态替换

check验证接口(主要讲解)

触发一下接口验证,里面除了data是轨迹生成。别的参数都是扣代码。别的接口返回。cb和fp特别注意!!!由于python的字节码和js生成的字节码有出入,这两个值不能转python。只能node生成

打上断点

继续跟栈,data在这生成。打上断点继续跟栈

继续跟栈这里存放data里面各种属性的生成这里的d值就是轨迹真实的值

轨迹算法(主要讲解)
traceData逻辑

打上断点,走到轨迹生成的步骤,这里的a0_0x1e60(611)是traceData,这个是真实的帧轨迹,也是加密的。然后组合到一起,通过d声明的方法来整体加密

搜一下traceData打上断点,此时帧轨迹加密生成。

将代码还原一下

javascript 复制代码
   'onMouseMove': function (_0x240a05) {
              var _0x466917 = _0x240a05['clientX'],
              _0x1e9e11 = _0x240a05["clientY" ],
              _0x557353 = this['drag'],
              _0x550d04 = _0x557353["status" ],
              _0xea33d8 = _0x557353["beginTime" ],
              _0x3b3633 = _0x557353['startX'];
              if (
                _0x557353["status" ] = _0xea33d8 &&
                _0x466917 - _0x3b3633 > 3 &&
                'dragend' === _0x550d04 ? 'dragstart' : _0x550d04,
                "dragend"  !== _0x557353['status']
              ) {
                !(
                  _0x240a05['type']["indexOf" ]("touch" ) !== - 1 &&
                  _0x4332c6["supportPassive" ] ||
                  _0x240a05["event"]["cancelable" ] !== !1
                ) &&
                _0x240a05["preventDefault"](),
                Object['assign'](
                  _0x557353,
                  {
                    'clientX': _0x466917,
                    'clientY': _0x1e9e11,
                    'dragX': _0x466917 - _0x557353["startX" ]
                  }
                );
                var _0x2c044c = this["$store" ]["state" ]['token'],
                _0x1d0d76 = [
                  Math["round" ](_0x557353['dragX'] < 0 ? 0 : _0x557353["dragX"]),
                  Math['round'](_0x557353['clientY'] - _0x557353["startY" ]),
                  _0x1b7568[now"]() - _0x557353['beginTime'],
                  null == _0x240a05["isTrusted" ] ? 0 : _0x240a05["isTrusted" ] ? 1 : 2
                ];
                this["atomTraceData" ]['push'](_0x1d0d76);
                var _0x4cde17 = _0x564a9f(_0x2c044c, _0x1d0d76 + '');
                this["traceData" ]['push'](_0x4cde17),
                "dragstart"  === _0x557353["status" ] &&
                this['onMouseMoveStart'](_0x240a05),
                'dragging' === _0x557353["status" ] &&
                this["onMouseMoving" ](_0x240a05);
              }
            },

_0x2c044c之前的操作是为了声明变量

_0x1d0d76:首先判断移动距离dragX是正向还是反方向,如果反方向就为0,正方向就四舍五入取整

其次点击的y值和开始的y值相减(因为是滑块,所以只有x变化,这个可以默认写0)

紧接着是当前时间戳和开始时间戳相减的差值

最后是判断是真人还是脚本触发,真人是1,脚本是2,其它为0

然后放入atomTraceData数组,经过_0x564a9f通过up接口返回的token进行加密处理。最后放入traceData

重新触发断点进入_0x564a9f方法:将参数进行了ASCII 码转换并进入_0x228898c方法

跟栈进入_0x228898c方法:对数据进行加密处理,根据data和key来进行xor加密

加密完之后进入另一个方法里面声明了两个定值变量去访问_0x4c4b3e方法

进入_0x4c4b3e方法

将代码还原一下,这个方法做了自定义base64:将字节组每三个调用_0x2d84c6方法编码,最后拼接返回

javascript 复制代码
   function _0x4c4b3e(_0x368459, _0xcf7f71, _0x4a9800) {
          if (!_0x368459 || 0 === _0x368459["length" ]) return '';
          try {
            for (var _0x402ac6 = 0, _0x373bfd = []; _0x402ac6 < _0x368459['length']; ) {
              if (!(_0x402ac6 + 3 <= _0x368459["length" ])) {
                var _0x38562e = _0x368459['slice'](_0x402ac6);
                _0x373bfd['push'](_0x2d84c6(_0x38562e, _0xcf7f71, _0x4a9800));
                break;
              }
              var _0x2627da = _0x368459["slice"](_0x402ac6, _0x402ac6 + 3);
              _0x373bfd['push'](_0x2d84c6(_0x2627da, _0xcf7f71, _0x4a9800)),
              _0x402ac6 += 3;
            }
            return _0x373bfd['join']('');
          } catch (_0xce575b) {
            return '';
          }
        }
响应接密:和加密一样使用同样代码,只需要进行xor解密即可
部分代码
纯算轨迹代码
python 复制代码
   def get_track(distance):
        """生成轨迹"""
        # 73.5px   22.8125   300 x 160  6.5
        base_track = [
            [4, 0, 94], [6, 0, 102], [9, 0, 111], [13, 0, 118], [18, 0, 126], [22, 0, 134], [28, 0, 140], [32, 0, 148],
            [35, 0, 156], [40, 0, 164], [42, 1, 172], [45, 2, 180], [46, 2, 189], [47, 3, 196], [49, 4, 204],
            [50, 4, 212], [51, 4, 220], [52, 4, 237], [53, 4, 244], [54, 4, 252], [55, 4, 260], [57, 4, 268],
            [58, 4, 276], [60, 4, 294], [62, 4, 373], [62, 4, 380], [63, 4, 388], [65, 4, 396], [66, 4, 405],
            [67, 4, 412], [68, 4, 421], [70, 4, 428], [73, 5, 437], [74, 5, 444], [75, 6, 452], [78, 6, 460],
            [80, 7, 468], [82, 8, 477], [84, 8, 485], [86, 8, 492], [90, 8, 501], [94, 8, 509], [95, 8, 518],
            [98, 8, 525], [102, 9, 533], [105, 10, 541], [106, 10, 588], [107, 10, 604], [109, 10, 612], [110, 10, 620],
            [110, 10, 628], [113, 11, 636], [115, 11, 644], [116, 11, 653], [118, 11, 660], [118, 11, 668],
            [120, 11, 676], [122, 11, 684], [122, 11, 692], [123, 11, 700], [124, 12, 764], [125, 12, 772],
            [126, 12, 788], [128, 12, 804], [129, 12, 812], [130, 12, 1190], [130, 12, 1252], [131, 12, 1268],
            [132, 12, 1340], [134, 12, 1710]
        ]
        random_y = random.randint(-3, 5)
        radio = distance / (base_track[-1][0] - base_track[0][0])
        new_track = []
        for x, y, t in base_track:
            y = y + random_y if y else 0
            point = [round(x * radio), y, round(t * radio)]
            new_track.append(point)
        return new_track
Tips

cb和fp特别注意!!!由于python的字节码和js生成的字节码有出入,这两个值不能转python。只能node生成

技术验证与测试
法律与伦理声明
  • 强调技术研究边界
  • 注明实际应用需获得授权
  • 个人观点,禁止商业行为,侵权请联系下架
相关推荐
L-影1 小时前
FastAPI 请求头与 Cookie:Web 世界的“身份证”与“通行证”
前端·fastapi
IT_陈寒1 小时前
Vue的响应式更新把我坑惨了,原来是这个原因
前端·人工智能·后端
铁皮饭盒1 小时前
Bun.js 集成了 SQL/Redis/S3,为什么 Java/Go/Python 不集成?
javascript
人道领域1 小时前
【LeetCode刷题日记】贪心算法理论与实战:455.分发饼干最优解
java·开发语言·数据结构·算法·leetcode·贪心算法
年轻的砖头1 小时前
以Ajax方式提交整个表单
前端·javascript·ajax
xin_yao_xin1 小时前
Conda 环境的 CUDA PATH 配置指南
开发语言·python·conda·cuda
he___H2 小时前
基于LCEL的联想
开发语言·python·langchain
L-影2 小时前
FastAPI CORS 跨域:Web 世界的“小区门禁”与“访客通行证”
前端·javascript·fastapi