美团滑块-[behavior] 加密分析

美团滑块-[behavior] 加密分析

文章仅为学习交流,如有侵权请联系删除

定位加密

  • 直接搜索关键字即可
  • 源JS也是做了一层OB混淆,建议先进行AST解混淆会对会面的分析有很大帮助
  • 没用很难的检测点但很多,补接混淆的话很费心力
  • 怎么解自行研究一下、我对这方面研究也不深
  • 解后的样子
  • 触发看一下参数
  • 三个参数
    • data就是滑动生成的轨迹信息
    • jc是request_code
    • this.config.isDegrade是返回的
  • 可以在前面的包返回中找到
  • 整个JS扣下来补环境即可

JS分析

  • 下面都以解OB混淆后的JS演示

  • 上面定位到了生成位置、分析一下生成逻辑

  • 处于自执行底部,赋值 window.Yoda.slider = HR;

  • HR 实际调用到是jg, 在jg处打个断点看看参数

  • 这里的参数还是前面page_data返回的data

  • 这里可以大概知道调用方式为:window.Yoda.slider(page_data);

  • 继续看jg这个对象

  • 大概可以猜测处这里是滑块相关的配置和调用函数

  • 滑倒末尾调用stopDrag

  • 然后调用dealMove -> onStop,就到了前面定位的加密位置

  • 可以看到data的初始化的对象

  • 轨迹部分最简单的办法,拿伪代码让AI大模型写个伪造轨迹结果的代码就搞定了

  • 这里把jg的伪代码贴出来,方便测试

    复制代码
    function jg(jf) {
      var jI = jW.call(this, jf) || this;
      jI.doms = {};
      jI.count = 0;
      jI.globalTimer = 0;
      jI.timeoutCount = 0;
      jI.firstTimeStamp = 0;
      jI.moveingBarX = 0;
      jI.moveingBarY = 0;
      jI.maxLeft = 0;
      jI._x = 0;
      jI._y = 0;
      jI.actualMove = 0;
      jI.initTimeStamp = Date.now();
      jI.isDrag = false;
      jI.data = {
        trajectory: [],
        env: {
          Type: 0,
          Return: 0,
          zone: [],
          client: [],
          Timestamp: [],
          count: 0,
          timeout: 0
        }
      };
      jI.customStyle = {};
      jI.ids = {
        help: "yodaHelp",
        boxWrapper: "yodaBoxWrapper",
        box: "yodaBox",
        moveingbar: "yodaMoveingBar",
        tip: "yodaSliderTip"
      };
      jI.whiteDuration = 0;
      jI.sliderMaxLenth = 100;
      jI.sliderType = 0;
      jI.sliderReturn = 0;
      jI.oceanPoint = [];
      jI.init = function () {
        var jM = jI.config;
        var jK = jM.action;
        var jF = jM.type;
        var jP = jM.style;
        var jN = jM.requestCode;
        var jJ = jM.root;
        var jO = jM.t;
        var jo = jM.display;
        jI.customStyle = jP || {};
        if (aV(jI.customStyle.slider) === "object") {
          jI.customStyle = jI.customStyle.slider;
        }
        try {
          var jQ = new aY(jI.config);
          var jS = jQ.template(jI.ids, jI.customStyle || {}, vX(jI.config));
          vx(jJ, jS, jo);
        } catch (jp) {
          vE(jp, jK, jF);
          return;
        }
        jI.whiteDuration = Date.now() - jI.config.yodaInitTime;
        jI.doms = vC(jI.ids);
        if (typeof jO === "number") {
          jI.sliderType = jO;
        }
        var jA = function jU(jD, jX) {
          return Math.round(Math.random() * (jX - jD)) + jD;
        };
        if (jO === 2) {
          var jk = jI.doms.boxWrapper;
          var jY = jk.getBoundingClientRect().width;
          var js = Math.ceil(jY / 100 * 70);
          var jZ = Math.floor(jY / 100) * 100;
          var jB = jA(js, jZ);
          jk.style.width = jB + "px";
        }
        if (jO === 3) {
          var je = jA(50, 70);
          jI.sliderMaxLenth = je;
        }
        jI.initSlider(jI.doms.box, jI.doms.boxWrapper);
        var jn = {
          duration: jI.whiteDuration,
          method: bT.SLIDER,
          mtaction: "loading",
          action: jK,
          requestCode: jN
        };
        vK(jn);
        if (jI.config.mounted) {
          vz(jI.config.mounted);
        }
        // change
        // jI.createbgImage("https://s3plus.meituan.net/v1/mss_f231eb419c414559a1837748d11d4312/yoda-resources/slider/m_loading.png", jK).then(function () {});
        // TOLOOK
        setTimeout(function () {
          try {
            RQ("slider", jN);
          } catch (jX) {
            var jD = jX;
            window.Yoda.CAT.sendLog(location.href, "jsError", jD.message, jD.stack || "", "info");
          }
        }, 0);
      };
      jI.initSlider = function (jM, jK) {
        jI.drag = jM;
        jI.moveingBar = jI.doms.moveingbar;
        jI.maxContainer = jK;
        H8(jI.doms.box, "mousedown", jI.startDrag);
        H8(jI.doms.box, "touchstart", jI.startDrag);
        H8(jI.doms.box, "touchstart", function () {
          window.Yoda.CAT.sendLog(location.href, "jsError", "PC上显示了i版的滑动", "使用了touchstart事件触发了滑块", "info");
        });
        var jF = {
          action: jI.config.action,
          type: jI.config.type,
          yodaInitTime: jI.config.yodaInitTime,
          whiteDuration: jI.whiteDuration
        };
        Hy(jF);
        if (typeof jI.config.mounted === "function") {
          jI.config.mounted();
        }
      };
      jI.help = function () {
        var jM = "https://verify.inf.test.meituan.com/feedback/manmachine/#/?requestCode=" + jI.config.requestCode;
        window.open(jM);
      };
      jI.startDrag = function (jM) {
        jI.count++;
        clearTimeout(jI.globalTimer);
        jI.timeoutListen();
        if (!jI.firstTimeStamp) {
          jI.firstTimeStamp = Date.now();
        }
        jI.moveingBarX = jI.moveingBar.clientWidth;
        jI.maxLeft = jI.maxContainer.clientWidth - jI.drag.offsetWidth;
        if (jM.clientX) {
          jI._x = jM.clientX;
          jI._y = jM.clientY;
        } else {
          jI._x = jM.targetTouches[0].clientX;
          jI._y = jM.targetTouches[0].clientY;
        }
        H8(document, "mousemove", jI.moveDrag);
        H8(document, "mouseup", jI.stopDrag);
        H8(document, "touchmove", jI.moveDrag);
        H8(document, "touchend", jI.stopDrag);
        H8(document, "mousemove", jI.sliderMoveDrag);
        H8(document, "mouseup", jI.sliderStopDrag);
        H8(document, "touchmove", jI.sliderMoveDrag);
        H8(document, "touchend", jI.sliderStopDrag);
        H9(jI.doms.box, "mousedown", jI.startDrag);
        H9(jI.doms.box, "touchstart", jI.startDrag);
        var jK = jI.maxContainer;
        var jF = {
          startX: vM(jI._x),
          startY: vM(jI._y),
          w: vM(jK.clientWidth),
          h: vM(jK.clientHeight),
          clientX: vM(jK.getBoundingClientRect().left),
          clientY: vM(jK.getBoundingClientRect().top)
        };
        jI.onStart(jF);
        Hm(jM);
      };
      jI.timeoutListen = function () {
        jI.globalTimer = window.setTimeout(function () {
          clearTimeout(jI.globalTimer);
          if (!jI.isDrag) {
            jI.stopDrag();
            if (jI.data) {
              jI.delLastItem(jI.data.trajectory);
            }
            jI.timeoutCount++;
          }
        }, 3000);
      };
      jI.move = function (jM) {
        var jK = 0;
        var jF = 0;
        if (jM.clientX) {
          jK = jM.clientX;
          jF = jM.clientY;
        } else if (jM.targetTouches) {
          jK = jM.targetTouches[0].clientX;
          jF = jM.targetTouches[0].clientY;
        }
        var jP = {
          clientX: jK,
          clientY: jF
        };
        return jP;
      };
      jI.sliderMoveDrag = function (jM) {
        var jK = jI.move(jM);
        var jF = jK.clientX;
        var jP = jK.clientY;
        jI.oceanPoint.push([0, jF, jP, Date.now() - jI.initTimeStamp]);
      };
      jI.sliderStopDrag = function (jM) {
        var jK = jI.move(jM);
        var jF = jK.clientX;
        var jP = jK.clientY;
        jI.oceanPoint.push([0, jF, jP, Date.now() - jI.initTimeStamp]);
        H9(document, "touchmove", jI.sliderMoveDrag);
        H9(document, "touchend", jI.sliderStopDrag);
        H9(document, "mousemove", jI.sliderMoveDrag);
        H9(document, "mouseup", jI.sliderStopDrag);
        jI.sliderPCPoint(JSON.stringify(jI.oceanPoint));
        jI.oceanPoint.splice(0);
      };
      jI.moveDrag = function (jM) {
        var jK = jI.move(jM);
        var jF = jK.clientX;
        var jP = jK.clientY;
        var jN = jF - jI._x;
        var jJ = jP - jI._y;
        if (Math.abs(jN) < 6 && Math.abs(jJ) < 6) {
          return false;
        }
        if (jN < 0) {
          jN = 0;
        }
        if (jN > jI.maxLeft) {
          jN = jI.maxLeft;
        }
        if (jI.sliderMaxLenth !== 100 && jN / jI.maxContainer.clientWidth * 100 > jI.sliderMaxLenth) {
          jI.sliderReturn = jN;
          jI.sliderMaxLenth = 100;
          jN = 0;
          Hm(jM);
          jI.stopDrag();
        }
        jI.setBoxPosition(jN);
        jI.onMove(vM(jF), vM(jP));
        if (jN === jI.maxLeft) {
          jI.stopDrag();
        }
        Hm(jM);
      };
      jI.stopDrag = function () {
        H9(document, "mousemove", jI.moveDrag);
        H9(document, "mouseup", jI.stopDrag);
        H9(document, "touchmove", jI.moveDrag);
        H9(document, "touchend", jI.stopDrag);
        jI.dealMove();
      };
      jI.setBoxPosition = function (jM) {
        jI.drag.style.left = jM + "px";
        jI.moveingBar.style.width = jI.moveingBarX + jM + "px";
        jI.actualMove = jM;
      };
      jI.dealMove = function () {
        if (jI.actualMove === jI.maxLeft) {
          jI.isDrag = true;
          H9(jI.drag, "mousedown", jI.startDrag);
          H9(jI.drag, "touchstart", jI.startDrag);
          jI.actualMove = 0;
          jI.drag.className = "boxLoading " + (jI.customStyle.boxLoading || "");
          jI.onStop();
          return false;
        }
        jI.backToStart();
      };
      jI.boxOk = function () {
        jI.drag.className = "boxOk " + (jI.customStyle.boxOk || "");
      };
      jI.boxStatic = function () {
        jI.drag.innerHTML = "";
        jI.drag.className = "boxStatic " + (jI.customStyle.boxStatic || "");
        jI.moveingBar.className = "moveingBar " + (jI.customStyle.moveingBar || "");
      };
      jI.boxError = function () {
        jI.drag.className = "boxError " + (jI.customStyle.boxError || "");
        jI.moveingBar.className = "moveingBarError " + (jI.customStyle.moveingBarError || "");
      };
      jI.backToStart = function () {
        var jM = 0;
        var jK = // TOLOOK
        setInterval(function () {
          var jF = vw.easeOutCubic(jM * 17, 0, jI.actualMove, 500);
          var jP = jI.actualMove - jF;
          jI.drag.style.left = jP + "px";
          jI.drag.style.left = jP + "px";
          jI.moveingBar.style.width = jI.moveingBarX + jP + "px";
          if (jP <= 0) {
            jI.drag.style.left = "0px";
            jI.drag.style.left = "0px";
            jI.moveingBar.style.width = jI.moveingBarX + "px";
            jI.actualMove = 0;
            clearInterval(jK);
            H8(jI.drag, "mousedown", jI.startDrag);
            H8(jI.drag, "touchstart", jI.startDrag);
          }
          jM++;
          jI.boxStatic();
        }, 17);
      };
      jI.onStart = function (jM) {
        var jK = jM.startX;
        var jF = jM.startY;
        var jP = jM.w;
        var jN = jM.h;
        var jJ = jM.clientX;
        var jO = jM.clientY;
        jI.data.env.zone = [jP, jN];
        jI.data.env.client = [jJ, jO];
        jI.data.trajectory.push({
          point: [[0, jK, jF, Date.now() - jI.initTimeStamp]],
          vector: {
            orientation: "h"
          }
        });
        jI.oceanPoint.push([0, jK, jF, Date.now() - jI.initTimeStamp]);
        var jQ = {
          action: jI.config.action,
          method: "71",
          requestCode: jI.config.requestCode
        };
        vN(jQ);
      };
      jI.onMove = function (jM, jK) {
        var jF = jI.data.trajectory;
        if (Array.isArray(jF) && jF.length) {
          jF[jF.length - 1].point.push([0, jM, jK, Date.now() - jI.initTimeStamp]);
        }
      };
      jI.showMessage = function (jM) {
        jI.doms.tip.textContent = jM;
        Ha(jI.doms.tip);
        var jK = window.setTimeout(function () {
          clearTimeout(jK);
          Hh(jI.doms.tip);
        }, 3000);
      };
      jI.config = jf;
      var jd = jf.theme || "meituan";
      if (typeof window.yodaTheme === "function") {
        window.yodaTheme(jd);
      }
      jI.init();
      return jI;
    }
    jg.prototype.onStop = function () {
      var jf = this;
      var jI = this.data.trajectory.length - 3;
      this.data.trajectory = this.data.trajectory.slice(jI > 0 ? jI : 0);
      this.data.env.Timestamp = [this.initTimeStamp, this.firstTimeStamp];
      this.data.env.count = this.count;
      this.data.env.timeout = this.timeoutCount;
      this.data.env.Type = this.sliderType;
      this.data.env.Return = Number(this.sliderReturn.toFixed(0));
      var jc = this.config.requestCode;
      var jz = {
        id: bT.SLIDER,
        request_code: jc,
        behavior: Rx(this.data, jc, this.config.isDegrade),
        fingerprint: "",
        action: this.config.action
      };
      this.verify(jc, jz).then(function (jE) {
        if (jE) {
          var jd = jE.message;
          var jM = jE.code;
          var jK = vn.isNeedSwap(jM);
          jf.boxError();
          if (!jK) {
            jf.showMessage(jd);
          }
          if (jM !== "jump" && jM !== 121056 && !jK) {
            // TOLOOK
            setTimeout(function () {
              jf.backToStart();
            }, 1000);
          }
        } else {
          jf.boxOk();
        }
      });
    };
    jg.prototype.delLastItem = function (jf) {
      if (Array.isArray(jf) && jf.length) {
        jf.length = jf.length - 1;
      }
    };
  • 好在轨迹路径不多、伪代码也还算清晰、构造伪造函数应该难度不大

  • 可以提交几段生成的data数据辅助生成

  • 贴个例子

  • 这里应该不算做难点

  • 参数部分都搞明白了,接着看加密部分

  • behavior: Rx(this.data, jc, this.config.isDegrade),

  • 原代码是滑块滑到末尾后调用加密、发送请求,没有返回

  • 可以定义个公共变量赋值加密函数、方便在外部调用

  • 后面就常规补环境就行了

  • 代理监控函数不会写可以去前面的文章找找,这个没啥特别的、用着方便就行。

  • 后面其实没啥特别难的环境、但是用了很多很多环境。。。

  • 不再一步步跟着补,还是贴一些关键点

  • 插件取name值



  • canvas画图部分就取了这么多属性。。。

  • 并且是验证了两次、也就取了两次toDataURL

  • 因为两次的结果是不一样的,我的处理方案是定义个全局公共变量做判断

  • 其实这里我认为不重要,正常todataIRL是不会验证结果的、只会验证有或没有、区别就在生成的加密,应该是不会影响使用的(只是想法未测试,为了对比结果我是返回的正确dataURL)

  • 然后还有个window.f

  • 一个自执行函数,直接扣下来放在顶部就行了

  • 其它照常补就行了、缺啥补啥、日子打清晰就不会漏

  • 对了、如果补着没日志输出了,就要处理一下 console, 被覆盖修改了、不能正常打印

  • 处理方法也很简单

  • Object.freeze(console);

  • 对象冻结

  • 环境部分就这些。

  • 补充一点参数的处理

  • config就是page_data返回的,替换掉options中requestCode就行了

  • 然后调用就是result = window.Rx(data,window.seed.config.request_code, window.seed.config.isDegrade);

  • 公共函数 Yoda

总结-验证

  • 补环境不是难度活而是体力活!!!
  • 心细+心累=成功
  • 最后来个验证
    • 请求滑块,得到page_data
    • 替换到补的环境中window.seed.config
    • 滑动滑块触发加密
    • 拷贝data值做结果对比测试
    • 对比结果
    • 因为结果是固定的、所以可以对比
    • 结果一致、可用性就不用考虑了吧
  • 再测试自生成轨迹、打断点替换生成值
    • 还是先拿到page_data
    • 生成加密值
    • 然后替换掉behavior
    • 注意看替换的值和页面生成的值是不一样的
    • 提交看结果
    • 正常返回request_code
  • 结尾说下_token参数,也是轨迹加密、之所以这么长是因为轨迹数据比较大,没啥特别的,另外这个参数没用到、不用管。
相关推荐
翟天保Steven2 小时前
ITK-基于欧拉变换与质心对齐的二维刚性配准算法
算法
Simucal3 小时前
基于物理引导粒子群算法的Si基GaN功率器件特性精准拟合
人工智能·算法·生成对抗网络
weixin_439647793 小时前
JavaScript性能优化实战:从指标到落地的全链路方案
开发语言·javascript·性能优化
玄魂4 小时前
一键生成国庆节祝福海报,给你的朋友圈上点颜色
前端·javascript·数据可视化
2501_916008894 小时前
JavaScript调试工具有哪些?常见问题与常用调试工具推荐
android·开发语言·javascript·小程序·uni-app·ecmascript·iphone
zero13_小葵司4 小时前
在不同开发语言与场景下设计模式的使用
java·开发语言·javascript·设计模式·策略模式
烦躁的大鼻嘎4 小时前
【Linux】深入探索多线程编程:从互斥锁到高性能线程池实战
linux·运维·服务器·开发语言·c++·算法·ubuntu
今后1234 小时前
【数据结构】快速排序与归并排序的实现
数据结构·算法·归并排序·快速排序
南莺莺5 小时前
树的存储结构
数据结构·算法·