webpack逆向

一、webpack大致处理流程

  • webpack打包代码结构示例:

    window.cache_obj = {} // 用于存储所需要的模块对象
    
    !function (n) { // 参数n:包含所有模块的大数组或对象
        var r = {}; // 缓存
        window.loader_ = i; // 导出加载器
        // i是加载器:缓存机制,加载功能
        function i(t) { // t是功能id
            if (r[t]) // 先判断是否缓存了该功能
                return r[t].exports;  // 返回已加载的功能
            // 新建缓存(初始化,未添加功能)
            var e = r[t] = {
                i: t, l: !1, exports: {}
            };
            window.cache_obj[t] = n[t] // 缓存加载过的模块
            // 调用具体的模块(n[t]),向缓存中添加新功能,并返回该功能
            return n[t].call(e.exports, e, e.exports, i), //(4个参数:this指针,缓存,导出功能,加载器)
                e.l = !0, e.exports // 返回的导出功能
        }
        i.m = n, i.c = r, i.d = function (t, e, n) {
            i.o(t, e) || Object.defineProperty(t, e, {
                enumerable: !0, get: n
            })
        }
            , i.r = function (t) {
            "undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(t, Symbol.toStringTag, {
                value: "Module"
            }), Object.defineProperty(t, "__esModule", {
                value: !0
            })
        }
            , i.t = function (e, t) {
            if (1 & t && (e = i(e)), 8 & t) return e;
            if (4 & t && "object" == typeof e && e && e.__esModule) return e;
            var n = Object.create(null);
            if (i.r(n), Object.defineProperty(n, "default", {
                enumerable: !0, value: e
            }), 2 & t && "string" != typeof e) for (var r in e) i.d(n, r, function (t) {
                return e[t]
            }
                .bind(null, r));
            return n
        }
            , i.n = function (t) {
            var e = t && t.__esModule ? function () {
                return t.default
            } : function () {
                return t
            };
            return i.d(e, "a", e), e
        }
            , i.o = function (t, e) {
            return Object.prototype.hasOwnProperty.call(t, e)
        }
            , i.p = "/";
        // i(i.s = 50); // 大概率是初始化,可以注释掉,避免调用了bom、dom等,可以按需指定具体功能模块
    }([]) // 参数为大数组或对象,此处用空数组代替
    window.loader_(50) // 手动加载所需起始模块
    
    // 将缓存对象中的模块,格式化输出到文件中
    var file_content = 'var module = {'
    for (let key in window.cache_obj){
        file_content += `'${key}': ${window.cache_obj[key]+''},\r\n`
    }
    file_content = file_content.slice(0,-1) // 去掉末尾的逗号','
    file_content += '}'
    const fs = require('fs')
    fs.writeFileSync('./cache_obj.js', file_content, 'utf-8') // 脚本生成简化的模块代码
  • 方式一:手动处理

    • 先全局定义一个变量(比如aaa),用于接收加载器中的导出函数
    • 再定义一个全局变量(比如bbb),用于接收具体模块中的加密对象或方法
    • 补模块、环境(通过日志调试,根据报错信息去抠代码,技巧:数组索引操作可以转换为对象)
      • 对于数组类型模块,如何快速定位关键模块的索引
        ['各模块代码大数组'].forEach((ele, index) => {
            if(ele.toString().indexOf('模块中的特征代码') != -1){
                console.log(index);
            }
        })
    • 手动调用加载,使得全局定义的变量生效(可以打印aaa来查看)
    • 定义加密方法,供外部调用
  • 方式二:工具

    • 下载地址:https://gitcode.net/zjq592767809/webpack_ast
    • 使用命令行方式:
      node webpack_mixer.js -l loader.js -m module1.js -m module2.js -o output.js
    • 参数说明

      • -l:指定加载器的js路径

        • 特征:一般以自执行函数开头,并且定义了导出函数,并为导出函数定义了多个方法
      • -m:指定函数模块的js路径

        • 一般以window.webpackJsonp开头,如果有多个(或者不确定具体哪一个),可以多次-m指定
      • -o:指定导出文件
    • 注意:需要自行安装babel相关插件

二、案例

  • 案例1:某壳

    • https://bj.ke.com/?utm_source=baidu&utm_medium=pinzhuan&utm_term=biaoti&utm_content=biaotimiaoshu&utm_campaign=wybeijing
    • 代码示例
      var window = {}
      window.location = {
              href: 'https://bj.ke.com/'
          }
      var document = {}
      var navigator = {
          'appName': 'Netscape'
      }
      var aaa; // 定义全局变量,接收加载器中导出函数
      var ddd; // 定义全局变量,接收模块中的加密对象
      
      // 加载器
      !function (n){...}({
          50: function (t, e, n){...}, // 内部通过ddd接收了加密对象
          ... // 根据调试信息,补充后续各模块
      })
      
      // 加载模块中代码
      aaa(50).default();
      
      // 定义加密方法,供外部调用
      function getNewPwd(pwd){
          // 需要额外补上设置公钥相关代码
          let publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCJxBJn2gY+D2OdldUxpsNwIGyKc/QRvqbWWGIdIewE7SxyyGHNcLdT+2bb6E6Ko7jBlEElUBkKJJ93G761dp6pXu7ORTjJ1mta99Bjud7+u/3473mG+QReoH4ux8idsd+E0TW0HWUP6zyfYy42HPSaN3pjetM30sVazdWxpvAH6wIDAQAB"
          ddd.ec.setPublicKey(publicKey)
          ddd.publicKey = publicKey
          return ddd.ec.encrypt(pwd)
      }
  • 案例2:酷我音乐

    • 手动抠示例
      function c(t, e) {
          var n = ['00','01','02','03','04','05','06','07','08','09','0a','0b','0c','0d','0e','0f','10','11','12','13','14','15','16','17','18','19','1a','1b','1c','1d','1e','1f','20','21','22','23','24','25','26','27','28','29','2a','2b','2c','2d','2e','2f','30','31','32','33','34','35','36','37','38','39','3a','3b','3c','3d','3e','3f','40','41','42','43','44','45','46','47','48','49','4a','4b','4c','4d','4e','4f','50','51','52','53','54','55','56','57','58','59','5a','5b','5c','5d','5e','5f','60','61','62','63','64','65','66','67','68','69','6a','6b','6c','6d','6e','6f','70','71','72','73','74','75','76','77','78','79','7a','7b','7c','7d','7e','7f','80','81','82','83','84','85','86','87','88','89','8a','8b','8c','8d','8e','8f','90','91','92','93','94','95','96','97','98','99','9a','9b','9c','9d','9e','9f','a0','a1','a2','a3','a4','a5','a6','a7','a8','a9','aa','ab','ac','ad','ae','af','b0','b1','b2','b3','b4','b5','b6','b7','b8','b9','ba','bb','bc','bd','be','bf','c0','c1','c2','c3','c4','c5','c6','c7','c8','c9','ca','cb','cc','cd','ce','cf','d0','d1','d2','d3','d4','d5','d6','d7','d8','d9','da','db','dc','dd','de','df','e0','e1','e2','e3','e4','e5','e6','e7','e8','e9','ea','eb','ec','ed','ee','ef','f0','f1','f2','f3','f4','f5','f6','f7','f8','f9','fa','fb','fc','fd','fe','ff'];
          var i = e || 0
              , r = n;
          return [r[t[i++]], r[t[i++]], r[t[i++]], r[t[i++]], "-", r[t[i++]], r[t[i++]], "-", r[t[i++]], r[t[i++]], "-", r[t[i++]], r[t[i++]], "-", r[t[i++]], r[t[i++]], r[t[i++]], r[t[i++]], r[t[i++]], r[t[i++]]].join("")
      }
      
      // 加密函数
      function reqId() {
          var t = {};
          var e = undefined;
          var n = undefined;
          var r = [91, 94, 187, 123, 214, 240];
          var o = 7611;
          var h = 0;
          var d = Date.now() - Math.round(Math.random()*1000+1);
          var i = e && n || 0
              , b = e || []
              , f = (t = t || {}).node || r
              , v = void 0 !== t.clockseq ? t.clockseq : o;
          if (null == f || null == v) {
              var m = l();
              null == f && (f = r = [1 | m[0], m[1], m[2], m[3], m[4], m[5]]),
              null == v && (v = o = 16383 & (m[6] << 8 | m[7]))
          }
          var y = void 0 !== t.msecs ? t.msecs : (new Date).getTime()
              , w = void 0 !== t.nsecs ? t.nsecs : h + 1
              , dt = y - d + (w - h) / 1e4;
          if (dt < 0 && void 0 === t.clockseq && (v = v + 1 & 16383),
          (dt < 0 || y > d) && void 0 === t.nsecs && (w = 0),
          w >= 1e4)
              throw new Error("uuid.v1(): Can't create more than 10M uuids/sec");
          d = y,
              h = w,
              o = v;
          var x = (1e4 * (268435455 & (y += 122192928e5)) + w) % 4294967296;
          b[i++] = x >>> 24 & 255,
              b[i++] = x >>> 16 & 255,
              b[i++] = x >>> 8 & 255,
              b[i++] = 255 & x;
          var _ = y / 4294967296 * 1e4 & 268435455;
          b[i++] = _ >>> 8 & 255,
              b[i++] = 255 & _,
              b[i++] = _ >>> 24 & 15 | 16,
              b[i++] = _ >>> 16 & 255,
              b[i++] = v >>> 8 | 128,
              b[i++] = 255 & v;
          for (var A = 0; A < 6; ++A)
              b[i + A] = f[A];
          return e || c(b)
      }
      
      console.log(reqId())
    • 使用工具,输出代码示例
      var export_function;
      !function (e){...}({
          0: function (t, e, n){...},
          ...
          109: function (t, e, n){...},
          ...
      })
      
      module.exports = export_function;
      
      // 调用具体加密模块
      console.log(export_function(109)())
  • 案例3:产业政策大数据平台

    • 链接地址:http://www.spolicy.com/
    • 提交的post参数经过了特殊编码,需要逆向
    • 难点:
      • 利用hook脚本过debugger
      • 定位加密函数位置:
        • 搜索 r = r.then(s.shift(), s.shift())
        • 查看s第一个元素函数位置
    • 代码示例:
      var commonjsGlobal
      var protobuf_min = {
          exports: {}
      };
      var aaa;
      
      var data = {
          "policyType": '6',
          "province": "",
          "city": "",
          "downtown": "",
          "garden": "",
          "centralId": "",
          "sort": 0,
          "homePageFlag": 1,
          "pageNum": 1,
          "pageSize": 7
      }
      
      // 加载器
      (function (module){...})(protobuf_min)
      
      function PolicyInfoByTypeIdParam$encode(m, w) {
          if (!w)
              w = aaa.Writer.create()
          if (m.policyType != null && Object.hasOwnProperty.call(m, "policyType"))
              w.uint32(10).string(m.policyType)
          if (m.centralId != null && Object.hasOwnProperty.call(m, "centralId"))
              w.uint32(18).string(m.centralId)
          if (m.province != null && Object.hasOwnProperty.call(m, "province"))
              w.uint32(26).string(m.province)
          if (m.city != null && Object.hasOwnProperty.call(m, "city"))
              w.uint32(34).string(m.city)
          if (m.downtown != null && Object.hasOwnProperty.call(m, "downtown"))
              w.uint32(42).string(m.downtown)
          if (m.garden != null && Object.hasOwnProperty.call(m, "garden"))
              w.uint32(50).string(m.garden)
          if (m.sort != null && Object.hasOwnProperty.call(m, "sort"))
              w.uint32(56).uint32(m.sort)
          if (m.pageNum != null && Object.hasOwnProperty.call(m, "pageNum"))
              w.uint32(64).uint32(m.pageNum)
          if (m.pageSize != null && Object.hasOwnProperty.call(m, "pageSize"))
              w.uint32(72).uint32(m.pageSize)
          if (m.homePageFlag != null && Object.hasOwnProperty.call(m, "homePageFlag"))
              w.uint32(80).uint32(m.homePageFlag)
          return w
      }
      
      // 注意data中的policyType值需要改为字符串形式
      function getEncodeData(data) {
          var result = PolicyInfoByTypeIdParam$encode(data).finish().slice()
          console.log(result)
          return result
      }
      getEncodeData(data)

       

posted @ 2023-03-31 19:02  eliwang  阅读(333)  评论(0编辑  收藏  举报