上一主题 下一主题
ScriptCat,新一代的脚本管理器脚本站,与全世界分享你的用户脚本油猴脚本开发指南教程目录
返回列表 发新帖
楼主: 李恒道 - 

抖音 bogus jsvmp纯算

[复制链接]
  • TA的每日心情

    2025-8-16 01:57
  • 签到天数: 196 天

    [LV.7]常住居民III

    771

    主题

    6816

    回帖

    7536

    积分

    管理员

    非物质文化遗产社会摇传承人

    积分
    7536

    荣誉开发者油中2周年生态建设者

    发表于 2 小时前 | 显示全部楼层 | 阅读模式

    根据代码进行调试可以发现J是vmp的装载器

          function J(t, r, e, n) {
            return (
              Z.length ||
                (function (t) {
                  var r = (function (t) {
                    for (var r = atob(t), e = 0, n = 4; n < 8; ++n)
                      e += r.charCodeAt(n);
                    return { d: C(Uint8Array.from(r.slice(8), _, e % 256)), i: 0 };
                  })(t);
                  ((Z.length = 0), (z.length = 0), V.clear());
                  for (var e = W(r), n = 0; n < e; ++n) Z.push(K(r));
                  var o = W(r);
                  for (n = 0; n < o; ++n) {
                    for (
                      var i = W(r),
                        u = Boolean(W(r)),
                        s = new Array(),
                        c = W(r),
                        a = 0;
                      a < c;
                      ++a
                    )
                      s.push([W(r), W(r), W(r), W(r)]);
                    for (var f = new Array(), l = W(r), p = 0; p < l; ++p)
                      f.push(W(r));
                    z.push([f, i, u, s]);
                  }
                })(
                  "字符串",
                ),
              X(z[t], r, e, n)
            );

    实际的执行器在function X(t, r, e, n),看代码首先内部会执行g函数进行初始化

    o为指令流,i为严格模式判断,s是变量表,数组0是外层作用域,1是arguments,2开始拷贝参数

    u,异常处理表,a,pc指针,f,异常处理状态,l异常对象

    function g(t, r, e, n) {
                        var p = Math.min(e.length, t[1])
                          , v = {};
                        Object.defineProperty(v, "length", {
                            value: e.length,
                            writable: !0,
                            enumerable: !1,
                            configurable: !0
                        }),
                        o = t[0],
                        i = t[2],
                        u = t[3],
                        s = [n, v];
                        for (var h = 0; h < p; ++h)
                            s.push(e[h]);
                        if (i)
                            for (c = r,
                            h = 0; h < e.length; ++h)
                                v[h] = e[h];
                        else {
                            c = null == r ? globalThis : Object(r);
                            var g = function(t) {
                                t < p ? Object.defineProperty(v, t, {
                                    get: function() {
                                        return s[t + 2]
                                    },
                                    set: function(r) {
                                        s[t + 2] = r
                                    },
                                    enumerable: !0,
                                    configurable: !0
                                }) : v[t] = e[t]
                            };
                            for (h = 0; h < e.length; ++h)
                                g(h)
                        }
                        a = 0,
                        f = 0,
                        l = void 0
                    }

    初始化后开始进入d函数进行执行,这里vmp为了混淆判断分支流,用了大量的if,我们可以用ast拍平变成switch-case结构

    // 根据指定 opcode,从原始 if/else 分发树里取出真正会执行的 handler。
    function resolveStatementForOpcode(statementNode, opcodeValue) {
      if (types.isIfStatement(statementNode)) {
        let matched;
    
        try {
          matched = evaluateTest(statementNode.test, opcodeValue);
        } catch {
          // handler 内部也可能有普通业务判断,例如:
          //
          //   if ("function" != typeof n) ...
          //
          // 这种判断和 opcode t 无关,不能静态选择分支,必须原样保留。
          return [types.cloneNode(statementNode, true)];
        }
    
        const next = matched ? statementNode.consequent : statementNode.alternate;
    
        if (!next) {
          return [];
        }
    
        return resolveStatementForOpcode(next, opcodeValue);
      }
    
      if (types.isBlockStatement(statementNode)) {
        return statementNode.body.flatMap((item) =>
          resolveStatementForOpcode(item, opcodeValue),
        );
      }
    
      if (types.isExpressionStatement(statementNode)) {
        return [
          types.expressionStatement(
            simplifyExpression(statementNode.expression, opcodeValue),
          ),
        ];
      }
    
      if (types.isReturnStatement(statementNode)) {
        const cloned = types.cloneNode(statementNode, true);
    
        if (cloned.argument) {
          cloned.argument = simplifyExpression(cloned.argument, opcodeValue);
        }
    
        return [cloned];
      }
    
      // 其他语句类型暂时原样克隆。
      // 如果后续遇到不准确的 case,再针对那个结构补处理逻辑。
      return [types.cloneNode(statementNode, true)];
    }
    function evaluateTest(testNode, opcodeValue) {
      if (!types.isBinaryExpression(testNode)) {
        throw new Error(`暂不支持的条件类型:${testNode.type}`);
      }
    
      const left = testNode.left;
      const right = testNode.right;
      const operator = testNode.operator;
    
      // 支持 t < number。
      if (isOpcodeIdentifier(left)) {
        const rightValue = numericValue(right);
    
        if (rightValue === undefined) {
          throw new Error(`条件右侧不是数字:${generator(testNode).code}`);
        }
    
        switch (operator) {
          case "<":
            return opcodeValue < rightValue;
          case "<=":
            return opcodeValue <= rightValue;
          case ">":
            return opcodeValue > rightValue;
          case ">=":
            return opcodeValue >= rightValue;
          case "===":
          case "==":
            return opcodeValue === rightValue;
          case "!==":
          case "!=":
            return opcodeValue !== rightValue;
          default:
            throw new Error(`暂不支持的操作符:${operator}`);
        }
      }
    
      // 支持 number === t / number !== t。
      if (isOpcodeIdentifier(right)) {
        const leftValue = numericValue(left);
    
        if (leftValue === undefined) {
          throw new Error(`条件左侧不是数字:${generator(testNode).code}`);
        }
    
        switch (operator) {
          case "<":
            return leftValue < opcodeValue;
          case "<=":
            return leftValue <= opcodeValue;
          case ">":
            return leftValue > opcodeValue;
          case ">=":
            return leftValue >= opcodeValue;
          case "===":
          case "==":
            return leftValue === opcodeValue;
          case "!==":
          case "!=":
            return leftValue !== opcodeValue;
          default:
            throw new Error(`暂不支持的操作符:${operator}`);
        }
      }
    
      throw new Error(`条件里没有 opcode 变量 t:${generator(testNode).code}`);
    }

    处理完成后得到了

    function d() {
      for (;;) {
        // 读取当前 opcode,并让 pc 后移一位。
        var t = o[a++];
    
        switch (t) {
          case 0:
            // opcode 0: handler 逻辑省略
            break;
    
          case 1:
            // opcode 1: handler 逻辑省略
            break;
    
          case 2:
            // opcode 2: handler 逻辑省略
            break;
    
          case 3:
            // opcode 3: handler 逻辑省略
            break;
    
          case 4:
            // opcode 4: handler 逻辑省略
            break;
    
          case 5:
            // opcode 5: handler 逻辑省略
            break;
    
          case 6:
            // opcode 6: handler 逻辑省略
            break;
    
          case 7:
            // opcode 7: handler 逻辑省略
            break;
    
          // ...
    
          case 75:
            // opcode 75: handler 逻辑省略
            break;
    
          default:
            // 未知 opcode / 默认处理逻辑
            break;
        }
      }
    }

    可以看到一共75个opcode+fallback code,这个时候一定要替换文件测试一下,确保算法依然是正常的

    关于ast处理vmp目前主要分为三类

    1.纯静态生成,读取opcode+对应参数,直接脱离网页的代码和执行逻辑,静态decompile,给到夯

    2.解释器改造为生成器,会处理opcode的时候直接修改原stack代码,实现还原,破坏了原来的基本流程,需要较高的代码能力去核对

    3.采用旁路式,在执行代码的同时执行ast代码,更利于调试和逆向分析

    这里采用旁路式,那我们需要在浏览器环境下引入一个ast的sdk

    import * as types from "@babel/types";
    import generateModule from "@babel/generator";
    
    const generator = generateModule.default || generateModule;
    
    globalThis.AST = {
      types,
      generator,
      generate(node, options) {
        return generator(node, options).code;
      },
    };

    构建出ast_bundle.js,这里推荐制作一个脚本猫脚本来自动注入提供环境

    构造完验证一下

    window.__AST_SDK_INJECTED__
    AST.generate(AST.types.stringLiteral("dfp"))
    '"dfp"'

    接下来处理opcode,大部分都是比较简单的,例如opcode 24是v[p] = typeof v[p];,从堆栈取出来一个值进行typeof然后塞回去,我们就写一下对应的ast代码就可以

                  case 24:
                    // opcode 24
                    // 原逻辑:对当前栈顶做 typeof,结果覆盖当前栈顶。AST:生成 UnaryExpression 'typeof'。
                    v[p] = typeof v[p];
                    if (__astTypes) {
                      var __astArg = __astStack[__astP];
                      __astStack[__astP] = __astTypes.unaryExpression("typeof", __astArg, true);
                    }
                    break;

    同样类似的还有

                  case 21:
                    // opcode 21
                    // 原逻辑:弹出右值 E,对当前栈顶做减法 -=。AST:生成 BinaryExpression '-'。
                    E = v[p--], v[p] -= E;
                    if (__astTypes) {
                      var __astRight = __astStack[__astP--];
                      var __astLeft = __astStack[__astP];
                      __astStack[__astP] = __astTypes.binaryExpression("-", __astLeft, __astRight);
                    }
                    break;

    一些基础的CRUD的opcode大约40个,全部处理完还有30多个没处理,我们要靠debugger去分析对应代码,可以把没处理的顶部都加一个debugger,以便调试

    这里有一个需要注意的就是case 63是一个递归调用,这里我们主要目的是跟踪,所以不进行处理,只制造一个空function表示,如果有需求后续考虑加强代码递归调用

    E = D(o[a++], s), v[++p] = E;

    这些都是比较简单的,剩下的一些就是cfg bound的处理了

    目前也是有两种处理方案

    1.按看雪的https://bbs.kanxue.com/thread-276268-1.htm 文章,如果遇到if就开始创建一个block

    同样结构是这样的

    如果 xxx >10 为false则跳转语句B
        语句A
        跳转语句C
        语句B
    语句C

    所以无论如何,读取跳转语句的last语句,即可直到该分支的结束位置,为了防止递归,最好用stack处理

    2.运行时收集数据结构,生成完整pc状态机,后续可以识别各种分支以便进行美化,例如

    function _vm_func_12(args_0, args_1) {
      var pc = 0;
      var stack = [];
      var sp = -1;
    
      while (true) {
        switch (pc) {
          case 0:
            ...
            pc = 8;
            break;
    
          case 8:
            ...
            if (cond) pc = 20;
            else pc = 10;
            break;
    
          case 20:
            ...
            return stack[sp];
        }
      }
    }

    我们其实这里也可以更折中一点,PC状态机的方案可以无视各种分支流的处理,也具有可读性,所以直接在执行过程中生成pc状态机,但不追求构建完整状态机,仅保证最小可用,缺点就是不会去判断什么时候分支流程结束,但是因为我们目标是还原,而非完美还原,所以到这里已经可以满足需求

    例如

                    var x = o[a++],
                      S = v[p--],
                      P = [];
                    for (var j in S) P.push(j);
                    s[x] = [P, S];

    我们可以构造出来

                    var __astOpcodePc = a - 1;
                    if (__astTypes) {
                      __astBeginOpcode(__astOpcodePc);
                    }
                    // opcode 3
                    var x = o[a++],
                      S = v[p--],
                      P = [];
                    for (var j in S) P.push(j);
                    s[x] = [P, S];
                    if (__astTypes) {
                      --__astP;
                    }
                    if (__astTypes) {
                      __astRecordNormalExit(a);
                    }

    其中

            function __astBeginOpcode(pc) {
              __astCurrentPc = pc;
              __astGetBlock(pc).executed = true;
            }
           function __astRecordNormalExit(nextPc) {
              if (__astCurrentPc < 0) return;
              var block = __astGetBlock(__astCurrentPc);
              var lastExit = block.exits[block.exits.length - 1];
              if (!lastExit) {
                block.exits.push({
                  type: "next",
                  to: nextPc
                });
              }
            }
    

    这里注意当处理状态机的时候,那所有的opcode都要加上对应的信息桩

     while (true) {
        switch (pc) {
          case 0:
            pc = 1;
            break;
          case 1:
            var _s0_3 = this;
            _s0_2 = false;
            pc = 4;
            break;
          case 2:
            var _s0_2 = 1;
            _s0_4 = [];
            _s0_4 = "=";
            _s0_3 = "";
            _s0_3 = [];
            _s0_3 = [];
            _s0_3 = [];
            _s0_4 = [];
            _s0_4 = "=";
            pc = 4;
            break;
          case 3:
            Object.defineProperty({}, "name", {
              value: "Other",
              writable: true,
              configurable: true,
              enumerable: true
            });
            Object.defineProperty({}, "name", {
              value: "Other",
              writable: true,
              configurable: true,
              enumerable: true
            });
            Object.defineProperty({}, "name", {
              value: "Other",
              writable: true,
              configurable: true,
              enumerable: true
            });
            Object.defineProperty({}, "name", {
              value: "Other",
              writable: true,
              configurable: true,
              enumerable: true
            });
            Object.defineProperty({}, "name", {
              value: "Other",
              writable: true,
              configurable: true,
              enumerable: true
            });
            Object.defineProperty({}, "name", {
              value: "Other",
              writable: true,
              configurable: true,
              enumerable: true
            });
            if (_s1_8) {
              pc = 12;
            } else {
              --sp;
              pc = 5;
            }
            break;
          case 4:
            _s0_2 = location.href;
            _s0_2 = window.outerWidth;
            _s0_2 = window.outerWidth;
            if (navigator.storage) {
              --sp;
              pc = 6;
            } else {
              pc = 16;
            }
            break;
          case 5:
            if (null == _s0_3) {
              pc = 16;
            } else {
              --sp;
              pc = 7;
            }
            break;
          case 1827:
            var _s0_93 = _s2_1.apply(undefined, [_s0_92, "s4"]);
            pc = 1830;
            break;
          case 1833:
            controlState = 2;
            controlValue = _s0_93;
            return;

    生成了这样的状态机代码,然后根据返回值判断可以确定是bogus,接下来根据_s2_1的函数来看,因为目前没有做opcode 63 vm递归生成的映射处理,所以我们需要再处理一下,这些都是纯体力话,就不多赘述了,我们可以利用之前的opcode的执行记录pc来回溯目前首条执行时到底时哪个stack来形成关联

    目前还有许多空跳,以及对普通流进行合并,这里我的方法是把所有的pc指针的case都塞到一个map里,然后进行排序,此时如果某一pc有多入口,则视为非普通语句,存在控制流,如果存在单一入口则视为普通语句,属于比较基本的呆呆的CFG Bound处理方法,这里要注意不能把存在两个入口的case进行合并

          case 1820:
            pc = 1823;
            break;
          // frame z[150]
          // frame z[150]
          case 1823:
            pc = 1825;
            break;
          // frame z[150]
          // frame z[150]
          case 1825:
            pc = 0;
            break;
          // frame z[150]
          // frame z[150]

    全部处理完就得到了类似于

          case 1745:
            var _s0_90 = _s1_20.apply(undefined, [_s0_88]);
            pc = 0;
            break;
          // flow block 1788 [z[150]] merged=1788,1789,1792,1795
          // frame z[150]
          case 1788:
            pc = 0;
            break;
          // flow block 1797 [z[150]] merged=1797,1799,1801
          // frame z[150]
          case 1797:
            pc = 0;
            break;
          // flow block 1803 [z[150]] merged=1803,1806,1809,1812,1813,1816,1817,1820,1823,1825
          // frame z[150]
          case 1803:
            var _s0_91 = _s2_2.apply(undefined, [String.fromCharCode.apply.apply(String.fromCharCode, [null, [211]]), String.fromCharCode.apply.apply(String.fromCharCode, [null, [].concat.apply([], [_s1_6.apply(undefined, [_s0_86]), _s1_6.apply(undefined, [_s0_90])])])]);
            var _s0_92 = _s0_89 + _s0_91;
            pc = 0;
            break;
          // flow block 1827 [z[150]] merged=1827,1830
          // frame z[150]
          case 1827:
            var _s0_93 = _s2_1.apply(undefined, [_s0_92, "s4"]);
            pc = 1833;
            break;
          // flow block 1833 [z[150]] merged=1833
          // frame z[150]
          case 1833:
            controlState = 2;
            controlValue = _s0_93;
            return;
            break;
        }
      }

    这样的格式,接下来开始处理cfg bound,结合一下,我们就可以得到,这里是一个纯耐心的工作,需要尝试自己找规律,建 CFG,算支配树,找段内回边,然后把原来的代码拼回块里,因为代码多到爆炸,所以这里就

    function recovered_z150_structured() {
      // block pc 0
      var _s0_12 = 11;
      // block pc 14
      if (window.onwheelx) {
        // block pc 16
        // block pc 22
        if (window.onwheelx._Ax) {
          // block pc 24
            xxxxxxx
            // block pc 52
            if (_s0_13 === void 0) {
              // goto pc 54 (external frame)
            } else {
              // block pc 59
              // block pc 66
              if (_s0_13.writable === false) {
                // block pc 68
                _s0_12 = 3;
                // block pc 77
                var _s0_14 = Date.now.apply(Date, []);
                var _s0_15 = new _s2_3();
                _s0_16 = _s2_4.apply(undefined, []);
                // block pc 114
                if (_s2_5.track.mode !== 0) {
                  // goto pc 116 (external frame)
                } else {
                  // block pc 120
                  xxxxx
                  // block pc 250
                  if (navigator.vendorSubs === null) {
                    // block pc 259
                    if (_s0_11 === void 0) {
                      // goto pc 261 (external frame)
                    } else {
                      // block pc 266
                      // block pc 271
                      if (_s0_11.ink) {
                        // block pc 275
                        var _s0_22 = _s0_11.ink;
                        var _s0_23 = [3, 82];
                        var _s0_24 = 41;
                        var _s0_25 = _s0_10.split.apply(_s0_10, ["."]).map.apply(_s0_10.split.apply(_s0_10, ["."]), [_vm_func_151]);
                        var _s0_26 = (new Date().getTime.apply(new Date(), []) - 1721836800000) / 1000 / 60 / 60 / 24 / 14 >> 0;
                        var _s0_27 = _s1_21.apply(undefined, []);
                        var _s0_28 = 2;
                        // block pc 365
                        if (_s1_3 > 0) {
                          // block pc 367
                          _s0_28 = _s0_14 - _s1_3 + 3 & 255;
                          // block pc 383
                          var _s0_29 = _s0_14 & 255;
                          var _s0_30 = _s0_14 >> 8 & 255;
                          var _s0_31 = _s0_14 >> 16 & 255;
                          var _s0_32 = _s0_14 >> 24 & 255;
                          var _s0_33 = _s0_14 / 256 / 256 / 256 / 256 & 255;
                          var _s0_34 = _s0_14 / 256 / 256 / 256 / 256 / 256 & 255;
                          var _s0_35 = _s0_16 % 256 & 255;
                          var _s0_36 = _s0_16 / 256 & 255;
                          var _s0_37 = _s2_13.apply(undefined, [1, 0, 8, _s0_6, _s0_7, _s0_8, _s2_18.pageId, _s2_18.aid, "1.0.1.19-fix.01"]);
                          var _s0_38 = _s0_37["4"] & 255;
                          var _s0_39 = _s0_37["4"] >> 8 & 255;
                          xxxxxx
                          var _s0_50 = 3;
                          var _s0_51 = _s0_18[_s0_50];
                          // loop header pc 639, back-edge from pc 663
                          while (_s0_51 === 11) {
                            // block pc 647
                            // block pc 661
                            if (_s0_50 < _s0_19.length) {
                              // block pc 663
                              _s0_51 = _s0_18[_s0_50];
                              continue; // -> pc 639
                            } else {
                              break; // -> pc 675
                            }
                          }
                          // block pc 682
                          // block pc 690
                          if (_s0_37["4"] & 2) {
                            // goto pc 692 (external frame)
                          } else {
                            // block pc 697
                            var _s0_52 = _s0_19["10"];
                            var _s0_53 = _s0_19["19"];
                            var _s0_54 = 4;
                            var _s0_55 = _s0_19[_s0_54];
                            // block pc 734
                            if (_s0_55 === 8) {
                              // goto pc 736 (external frame)
                            } else {
                              // block pc 771
                              // block pc 779
                              if (_s0_37["4"] & 4) {
                                // goto pc 781 (external frame)
                              } else {
                                // block pc 786
                                var _s0_56 = _s0_21["11"];
                                var _s0_57 = _s0_21["21"];
                                var _s0_58 = 5;
                                var _s0_59 = _s0_21[_s0_58];
                                // block pc 823
                                if (_s0_59 === 12) {
                                  // goto pc 825 (external frame)
                                } else {
                                  // block pc 860
                                  // block pc 868
                                  if (_s0_37["4"] & 8) {
                                    // goto pc 870 (external frame)
                                  } else {
                                    // block pc 875
                                    var _s0_60 = _s0_22 & 255;
                                    var _s0_61 = _s0_22 >> 8 & 255;
                                    var _s0_62 = _s0_22 >> 16 & 255;
                                    var _s0_63 = _s0_22 >> 24 & 255;
                                    var _s0_64 = _s0_22 / 256 / 256 / 256 / 256 & 255;
                                    var _s0_65 = _s0_22 / 256 / 256 / 256 / 256 / 256 & 255;
                                    var _s0_66 = _s0_12;
                                    xxxxxx
    
                                    var _s0_75 = {};
                                    var _s0_76 = _s1_13.apply(undefined, [_s0_75]);
                                    var _s0_77 = _s1_12.apply(undefined, [_s0_76]);
                                    var _s0_78 = _s0_77.length;
                                    var _s0_79 = _s0_78 & 255;
                                    var _s0_80 = _s0_78 >> 8 & 255;
                                    var _s0_81 = (_s0_14 + 3 & 255) + ",";
                                    var _s0_82 = _s1_12.apply(undefined, [_s0_81]);
                                    var _s0_83 = _s0_82.length;
                                    var _s0_84 = _s0_83 & 255;
                                    var _s0_85 = _s0_83 >> 8 & 255;
                                    var _s0_86 = _s2_37.apply(undefined, [_s0_8.searchParams.toString.apply(_s0_8.searchParams, []), _s0_2]);
                                    var _s0_87 = _s0_86["0"] ^ _s0_86["1"] ^ _s0_86["2"] ^ _s0_86["3"] ^ _s0_86["4"] ^ _s0_86["5"] ^ _s0_86["6"] ^ _s0_86["7"] ^ _s0_24 ^ _s0_26 ^ _s0_27 ^ _s0_28 ^ _s0_29 ^ _s0_30 ^ _s0_31 ^ _s0_32 ^ _s0_33 ^ _s0_34 ^ _s0_35 ^ _s0_36 ^ _s0_38 ^ _s0_39 ^ _s0_40 ^ _s0_41 ^ _s0_42 ^ _s0_43 ^ _s0_44 ^ _s0_45 ^ _s0_56 ^ _s0_47 ^ _s0_48 ^ _s0_49 ^ _s0_51 ^ _s0_52 ^ _s0_53 ^ _s0_55 ^ _s0_56 ^ _s0_57 ^ _s0_59 ^ _s0_60 ^ _s0_61 ^ _s0_62 ^ _s0_63 ^ _s0_64 ^ _s0_65 ^ _s0_66 ^ _s0_67 ^ _s0_68 ^ _s0_69 ^ _s0_70 ^ _s0_71 ^ _s0_72 ^ _s0_73 ^ _s0_74 ^ _s0_79 ^ _s0_80 ^ _s0_84 ^ _s0_85;
                                    var _s0_88 = [_s0_34, _s0_44, _s0_56, _s0_61, _s0_73, _s0_29, _s0_70, _s0_45, _s0_35, _s0_49, _s0_38, _s0_66, _s0_51, _s0_68, _s0_28, _s0_48, _s0_64, _s0_47, _s0_30, _s0_71, _s0_26, _s0_55, _s0_31, _s0_69, _s0_59, _s0_40, _s0_62, _s0_63, _s0_27, _s0_72, _s0_41, _s0_74, _s0_57, _s0_52, _s0_42, _s0_39, _s0_33, _s0_67, _s0_53, _s0_43, _s0_65, _s0_46, _s0_36, _s0_24, _s0_60, _s0_32, _s0_79, _s0_80, _s0_84, _s0_85].concat.apply([_s0_34, _s0_44, _s0_56, _s0_61, _s0_73, _s0_29, _s0_70, _s0_45, _s0_35, _s0_49, _s0_38, _s0_66, _s0_51, _s0_68, _s0_28, _s0_48, _s0_64, _s0_47, _s0_30, _s0_71, _s0_26, _s0_55, _s0_31, _s0_69, _s0_59, _s0_40, _s0_62, _s0_53, _s0_27, _s0_72, _s0_41, _s0_74, _s0_57, _s0_52, _s0_42, _s0_39, _s0_33, _s0_67, _s0_53, _s0_43, _s0_65, _s0_46, _s0_36, _s0_24, _s0_60, _s0_32, _s0_79, _s0_80, _s0_84, _s0_85], [_s1_6.apply(undefined, [_s0_77]), _s1_6.apply(undefined, [_s0_82]), [_s0_87]]);
                                    var _s0_89 = String.fromCharCode.apply.apply(String.fromCharCode, [String, _s1_6.apply(undefined, [_s1_18.apply(undefined, [_s0_23, 1])])]);
                                    var _s0_90 = _s1_20.apply(undefined, [_s0_88]);
                                    var _s0_91 = _s2_2.apply(undefined, [String.fromCharCode.apply.apply(String.fromCharCode, [null, [211]]), String.fromCharCode.apply.apply(String.fromCharCode, [null, [].concat.apply([], [_s1_6.apply(undefined, [_s0_86]), _s1_6.apply(undefined, [_s0_90])])])]);
                                    var _s0_92 = _s0_89 + _s0_91;
                                    var _s0_93 = _s2_1.apply(undefined, [_s0_92, "s4"]);
                                    // block pc 1833
                                    controlState = 2;
                                    controlValue = _s0_93;
                                    return;
                                  }
                                }
                              }
                            }
                          }
                        } else {
                          // goto pc 383 (already emitted)
                        }
                      } else {
                        // goto pc 273 (external frame)
                      }
                    }
                  } else {
                    // block pc 252
                    // goto pc 259 (already emitted)
                  }
                }
              } else {
                // goto pc 72 (external frame)
              }
            }
          } else {
            // block pc 45
            // goto pc 52 (already emitted)
          }
        } else {
          // goto pc 77 (already emitted)
        }
      } else {
        // goto pc 22 (already emitted)
      }
    }
    

    类似对其他函数进行同样的处理,在合适的地方下断点进行分析,最后将所有代码聚集到一起进行分析

    可以知道bogus采用了SM3 + 字节抽样 → 拼环境字节 → 随机展开 → RC4 → s4 base64 → 188 字符

    实现bogus纯算
    image.png

    混的人。
    ------------------------------------------
    進撃!永遠の帝国の破壊虎---李恒道

    入驻了爱发电https://afdian.com/a/lihengdao666

    发表回复

    本版积分规则