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

JS混淆分析初步理论研究篇三

[复制链接]
  • TA的每日心情
    慵懒
    2024-10-28 07:07
  • 签到天数: 193 天

    [LV.7]常住居民III

    712

    主题

    5960

    回帖

    6759

    积分

    管理员

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

    积分
    6759

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

    发表于 2022-10-19 20:52:02 | 显示全部楼层 | 阅读模式

    之前我们已经解密到了

    let a = 0x1;
    alert(a);
    (function (_0xc2361a, _0x49f6b2, _0x4c5d49) {
      var _0x4bbdd6 = {
        PTyWI: "ert",
        CFOau: function _0x2e9886(_0x27d2c4, _0x233ed3) {
          return _0x27d2c4 !== _0x233ed3;
        },
        yGZoO: "undefined",
        UQhqT: function _0xe650a3(_0x4590a1, _0x160226) {
          return _0x4590a1 === _0x160226;
        },
        TtTMq: "jsjiami.com.v5",
        FQggo: function _0x47c22c(_0x2fe61e, _0x50381a) {
          return _0x2fe61e + _0x50381a;
        },
        FTCAn: "删除版本号,js会定期弹窗",
      };
      _0x4c5d49 = "al";
      try {
        _0x4c5d49 += _0x4bbdd6["PTyWI"];
        _0x49f6b2 = encode_version;
        if (
          !(
            _0x4bbdd6["CFOau"](typeof _0x49f6b2, _0x4bbdd6["yGZoO"]) &&
            _0x4bbdd6["UQhqT"](_0x49f6b2, _0x4bbdd6["TtTMq"])
          )
        ) {
          _0xc2361a[_0x4c5d49](
            _0x4bbdd6["FQggo"]("删除", "版本号,js会定期弹窗,还请支持我们的工作")
          );
        }
      } catch (_0x2d647c) {
        _0xc2361a[_0x4c5d49](_0x4bbdd6["FTCAn"]);
      }
    })(window);
    encode_version = "jsjiami.com.v5";

    我们接下来来处理花指令
    根据观察可以发现其特征都是访问对象.成员的形式,我们先丢Ast里看一下
    图片.png
    丢到Ast explorer看一下
    可以发现成员.属性是一个MemberExpression结构
    object.name是对象名
    property.value是属性名
    图片.png
    那我们可以假设一下
    我们可以遍历MemberExpression对象
    如果找到,就根据对象名找到对应的对象,然后查找属性
    判断是不是字符串
    如果是字符串则直接替换
    如果是函数则判断是否只有一行,并且有两个关键词,且有一个操作符
    理论建立完毕,实战开始!
    我们先替换字符串
    首先判断属性是否是一个string字符串,如果是则判断可能是一个对象.属性的花指令

    traverse(ast, {
      MemberExpression(path) {
        let obj_name = path.node.object.name;
        let obj_value =
          path.node.property.type === "StringLiteral"
            ? path.node.property.value
            : undefined;
        if (obj_name && obj_value) {
          console.log("obj_name", obj_name, obj_value);
        }
      },
    });

    然后找到对象,我们可以判断obj_name && obj_value是否都符合条件
    如果符合则开始找对象
    通过path.scope.getBinding找
    path表示两个节点的链接,而scope表示该属性所在的函数作用域
    找到函数作用域后通过getBinding获取该函数作用域绑定的哪个值

        if (obj_name && obj_value) {
          let obj_binding = path.scope.getBinding(obj_name);
        }

    那么这句话的代码就是
    找到我们所用的path的函数作用域,并找到对应的绑定(Binding)
    也就是说我们从一个节点,跳到了这个函数的范围,再找到该函数内对应的绑定字符串
    图片.png
    找到的是一个Bingding对象
    Identifier表示声明得时候所用的标识符
    path表示该ast的path对象
    kind表示var还是let等
    referenced表示是否被引用
    references表示引用的次数
    constant表示是否是常量
    constantViolations表示有谁修改了这个变量
    referencePaths表示有谁引用了这个变量
    图片.png
    通过打印我们确定了找到的binding在这里
    图片.png
    所以我们可以继续写
    找到binding后,遍历他的init的properties,也就是对象里声明的属性
    判断有没有与我们MemberExpression的属性相等
    如果相等,且该属性的初始化值为String类型,则直接进行替换

          if (obj_binding) {
            let prop_list = obj_binding.path.node.init.properties;
            for (let index = 0; index < prop_list.length; index++) {
              let item_prop = prop_list[index];
              let prop_name = item_prop?.key?.name;
              if (prop_name === obj_value) {
                if (item_prop.value.type === "StringLiteral") {
                  path.replaceInline(item_prop.value);
                  return;
                }
              }
            }
          }

    可以看到已经还原了字符串
    图片.png
    完整代码

    traverse(ast, {
      MemberExpression(path) {
        let obj_name = path.node.object.name;
        let obj_value =
          path.node.property.type === "StringLiteral"
            ? path.node.property.value
            : undefined;
        if (obj_name && obj_value) {
          let obj_binding = path.scope.getBinding(obj_name);
          if (obj_binding) {
            let prop_list = obj_binding.path.node.init.properties;
            for (let index = 0; index < prop_list.length; index++) {
              let item_prop = prop_list[index];
              let prop_name = item_prop?.key?.name;
              if (prop_name === obj_value) {
                if (item_prop.value.type === "StringLiteral") {
                  path.replaceInline(item_prop.value);
                  return;
                }
              }
            }
          }
        }
      },
    });

    那我们就搞定了属性的获取
    下节课尝试恢复混淆花指令的函数部分!

    结语

    撒花~

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

    入驻了爱发电https://afdian.net/a/lihengdao666
    个人宣言:この世界で私に胜てる人とコードはまだ生まれていません。死ぬのが怖くなければ来てください。

    发表回复

    本版积分规则

    快速回复 返回顶部 返回列表