微信小程序-某某牛仔城

xiaoeryu Lv5

本文中所有内容仅供研究与学习使用,禁止用于任何商业用途和非法用途,否则后果自负!!!

本章来分析一个微信小程序的协议,这个小程序的协议本身比较简单。主要是来学习一下分析微信小程序的流程

0x00 环境

设备:pixel 5 Android11.0

抓包:Charles + Postern

发包工具:postman、APIfox

0x01 抓包

环境配置好了,那么先来抓个包吧

  • 随便选一个领取,这里我们以预览充值中心为例

    • 抓到的包比较多,找不到多翻翻

    • 分析这个协议

      member_no:会员编号

      type:操作目标的类型,例如这里33是“每日预览充值中心”

      store_no:字面翻译是商店编号,这里我们抓了几次包都没变过。暂时当它是个固定值

      sign:签名是本篇文章要分析的一个重点,看起来像是一个md5值

      time:时间戳

      • 经过分析,看起来只有这个sign需要我们进行分析,其它的都可以直接获取或者是固定值

0x02 分析小程序源码

定位

首先定位小程序的包

因为路径下小程序文件夹太多了,先清理一下

然后重新打开一遍小程序,用everything搜索.wxapkg定位时间最近那个

解包

小程序的解包工具有很多,在github上都可以找到。这里我们使用这个带界面的看起来比较清楚

解密成功后拖入wxapkgconvertor.exe ,反编译

  • 反编译成功之后用IDE打开,这里我们使用微信开发者工具打开
  • PS:里面有一个app-service.js文件特别大,建议复制下来在网上找工具格式化一下。本机直接格式化容易卡死

源码分析

根据前面对抓包协议的分析,我们只需要分析sign怎么获取的就好了。那就直接搜索sign

  • 运气比较好,第三个直接定位到了获取签名的函数
  • 其中var u = a.default.hexMD5(n + t + w + m + v + p);把一系列值组合然后进行了DM5计算,这就是我们要找的sign

计算sign

把代码抠出来,自己重新计算一下获取sign

  • 这里用到了一个hexMD5的外部方法,搜索一下把这个方法也抠出来
  • 这里又用到了一个o方法,也找到抠出来
  • 这里一堆互相调用的都抠出来
function getxSign() {
    var d = "511MrfpKD^",
    h = "GKXW58GHG",
    p = "#iki0Dhr",
    g = "ASF74XSR^",
    v = "@UnOaJYS",
    m = "KIoa7YXIaN",
    y = "648DCGFG^",
    w = "511MrfpK^";
    var e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {},
        t = arguments.length > 1 ? arguments[1] : void 0,
        r = e.query || {},
        n = "";
    console.log("getSign.times===>"), console.log(t), console.log("getSign.data===>"), console.log(r);
    for (var o = Object.keys(r).sort(), i = {}, s = 0; s < o.length; s++) i[o[s]] = r[o[s]];
    for (var l in console.log("data排序后===>"), console.log(i), i) n += l + i[l];
    var u = a.default.hexMD5(n + t + w + m + v + p);
    return console.log("getSign.sign===>"), console.log(u), u
}

function hexMD5(e) {
    return function(e) {
        for (var t = "0123456789abcdef", r = "", n = 0; n < 4 * e.length; n++) r += t.charAt(e[n >> 2] >> n % 4 * 8 + 4 & 15) + t.charAt(e[n >> 2] >> n % 4 * 8 & 15);
        return r
    }(function(e) {
        for (var t = 1732584193, n = -271733879, l = -1732584194, u = 271733878, c = 0; c < e.length; c += 16) {
            var f = t,
                d = n,
                h = l,
                p = u;
            t = o(t, n, l, u, e[c + 0], 7, -680876936), u = o(u, t, n, l, e[c + 1], 12, -389564586), l = o(l, u, t, n, e[c + 2], 17, 606105819), n = o(n, l, u, t, e[c + 3], 22, -1044525330), t = o(t, n, l, u, e[c + 4], 7, -176418897), u = o(u, t, n, l, e[c + 5], 12, 1200080426), l = o(l, u, t, n, e[c + 6], 17, -1473231341), n = o(n, l, u, t, e[c + 7], 22, -45705983), t = o(t, n, l, u, e[c + 8], 7, 1770035416), u = o(u, t, n, l, e[c + 9], 12, -1958414417), l = o(l, u, t, n, e[c + 10], 17, -42063), n = o(n, l, u, t, e[c + 11], 22, -1990404162), t = o(t, n, l, u, e[c + 12], 7, 1804603682), u = o(u, t, n, l, e[c + 13], 12, -40341101), l = o(l, u, t, n, e[c + 14], 17, -1502002290), t = i(t, n = o(n, l, u, t, e[c + 15], 22, 1236535329), l, u, e[c + 1], 5, -165796510), u = i(u, t, n, l, e[c + 6], 9, -1069501632), l = i(l, u, t, n, e[c + 11], 14, 643717713), n = i(n, l, u, t, e[c + 0], 20, -373897302), t = i(t, n, l, u, e[c + 5], 5, -701558691), u = i(u, t, n, l, e[c + 10], 9, 38016083), l = i(l, u, t, n, e[c + 15], 14, -660478335), n = i(n, l, u, t, e[c + 4], 20, -405537848), t = i(t, n, l, u, e[c + 9], 5, 568446438), u = i(u, t, n, l, e[c + 14], 9, -1019803690), l = i(l, u, t, n, e[c + 3], 14, -187363961), n = i(n, l, u, t, e[c + 8], 20, 1163531501), t = i(t, n, l, u, e[c + 13], 5, -1444681467), u = i(u, t, n, l, e[c + 2], 9, -51403784), l = i(l, u, t, n, e[c + 7], 14, 1735328473), t = a(t, n = i(n, l, u, t, e[c + 12], 20, -1926607734), l, u, e[c + 5], 4, -378558), u = a(u, t, n, l, e[c + 8], 11, -2022574463), l = a(l, u, t, n, e[c + 11], 16, 1839030562), n = a(n, l, u, t, e[c + 14], 23, -35309556), t = a(t, n, l, u, e[c + 1], 4, -1530992060), u = a(u, t, n, l, e[c + 4], 11, 1272893353), l = a(l, u, t, n, e[c + 7], 16, -155497632), n = a(n, l, u, t, e[c + 10], 23, -1094730640), t = a(t, n, l, u, e[c + 13], 4, 681279174), u = a(u, t, n, l, e[c + 0], 11, -358537222), l = a(l, u, t, n, e[c + 3], 16, -722521979), n = a(n, l, u, t, e[c + 6], 23, 76029189), t = a(t, n, l, u, e[c + 9], 4, -640364487), u = a(u, t, n, l, e[c + 12], 11, -421815835), l = a(l, u, t, n, e[c + 15], 16, 530742520), t = s(t, n = a(n, l, u, t, e[c + 2], 23, -995338651), l, u, e[c + 0], 6, -198630844), u = s(u, t, n, l, e[c + 7], 10, 1126891415), l = s(l, u, t, n, e[c + 14], 15, -1416354905), n = s(n, l, u, t, e[c + 5], 21, -57434055), t = s(t, n, l, u, e[c + 12], 6, 1700485571), u = s(u, t, n, l, e[c + 3], 10, -1894986606), l = s(l, u, t, n, e[c + 10], 15, -1051523), n = s(n, l, u, t, e[c + 1], 21, -2054922799), t = s(t, n, l, u, e[c + 8], 6, 1873313359), u = s(u, t, n, l, e[c + 15], 10, -30611744), l = s(l, u, t, n, e[c + 6], 15, -1560198380), n = s(n, l, u, t, e[c + 13], 21, 1309151649), t = s(t, n, l, u, e[c + 4], 6, -145523070), u = s(u, t, n, l, e[c + 11], 10, -1120210379), l = s(l, u, t, n, e[c + 2], 15, 718787259), n = s(n, l, u, t, e[c + 9], 21, -343485551), t = r(t, f), n = r(n, d), l = r(l, h), u = r(u, p)
        }
        return [t, n, l, u]
    }(function(e) {
        for (var t = 1 + (e.length + 8 >> 6), r = new Array(16 * t), n = 0; n < 16 * t; n++) r[n] = 0;
        for (n = 0; n < e.length; n++) r[n >> 2] |= (255 & e.charCodeAt(n)) << n % 4 * 8;
        return r[n >> 2] |= 128 << n % 4 * 8, r[16 * t - 2] = 8 * e.length, r
    }(e)))

    function r(e, t) {
        var r = (65535 & e) + (65535 & t);
        return (e >> 16) + (t >> 16) + (r >> 16) << 16 | 65535 & r
    }

    function n(e, t, n, o, i, a) {
        return r(function (e, t) {
            return e << t | e >>> 32 - t
        }(r(r(t, e), r(o, a)), i), n)
    }

    function o(e, t, r, o, i, a, s) {
        return n(t & r | ~t & o, e, t, i, a, s)
    }

    function i(e, t, r, o, i, a, s) {
        return n(t & o | r & ~o, e, t, i, a, s)
    }

    function a(e, t, r, o, i, a, s) {
        return n(t ^ r ^ o, e, t, i, a, s)
    }

    function s(e, t, r, o, i, a, s) {
        return n(r ^ (t | ~o), e, t, i, a, s)
    }
}
  • 最终我们抠出来的代码,反编译的代码比较混乱不好看懂。我们找人工智能工具重写一下getxSign函数,计算MD5那个函数就算了没必要重写。
function getxSign(data = {}, timestamp) {
    // 定义常量
    const d = "511MrfpKD^",
          h = "GKXW58GHG",
          p = "#iki0Dhr",
          g = "ASF74XSR^",
          v = "@UnOaJYS",
          m = "KIoa7YXIaN",
          y = "648DCGFG^",
          w = "511MrfpK^";
    
    // 提取查询参数并初始化拼接字符串
    const query = data.query || {};
    let concatenatedString = "";

    // 打印输入的时间戳和数据
    console.log("getSign.times===>", timestamp);
    console.log("getSign.data===>", query);

    // 将对象键按字母顺序排序
    const sortedKeys = Object.keys(query).sort();
    const sortedData = {};

    // 根据排序后的键重新构建对象
    sortedKeys.forEach(key => {
        sortedData[key] = query[key];
    });

    // 打印排序后的数据
    console.log("data排序后===>", sortedData);

    // 构建签名字符串
    for (const key in sortedData) {
        concatenatedString += key + sortedData[key];
    }

    // 使用 MD5 计算签名
    const signature = hexMD5(concatenatedString + timestamp + w + m + v + p);
    
    // 打印签名结果并返回
    console.log("getSign.sign===>", signature);
    return signature;
}
  • 现在看起来就比较清楚了

接下来我们尝试调用一下,跟抓包的协议比较一下计算的签名是否相同

  • 这里不知道应该怎么传递参数,问一下chatGPT

    getxSign() 函数需要两个参数:

    1. 第一个参数 e 是一个对象,通常包含 query 属性,query 是一个键值对对象。这个对象中的键值对会被用于生成签名(sign)。

      • **e.query**:包含要签名的数据,它的键会被按字母排序,然后与它的值拼接在一起。

      例如:

      const e = {
          query: {
              foo: "value1",
              bar: "value2"
          }
      };
      
    2. 第二个参数 t 是一个时间戳或某种密钥值,它将在签名的生成中用到。这个值会拼接在 query 的数据之后,用于生成最终的 MD5 哈希。

      例如:

      const t = "20240916"; // 假设这个是某个时间戳或密钥
      

    调用示例

    const e = {
        query: {
            foo: "value1",
            bar: "value2"
        }
    };
    
    const t = "20240916"; // 假设这是时间戳或密钥
    
    const sign = getxSign(e, t);
    console.log("Generated Sign:", sign);
    

    在这个例子中,getxSign 会对 query 对象中的键(bar, foo)按字母顺序排序,并与对应的值拼接成字符串,再加上时间戳 t 和其他固定值,生成最终的 MD5 哈希签名。

添加调用代码

const e = {
    query: {
        member_no: 973470,
        type: 33,
        store_no: 12
    }
};
// console.log(getxSign(e, t))
var sign = getxSign(e, "20240916222353")	// 时间戳传递协议中的时间戳
  • 跟抓包的sign对比一下,确定两个sign相同
  • 这里计算的结果相同,说明我们正确的获取到了sign

发包确认

接下来构建一下完整的协议,看看是否能获取服务器的返回结果

发包工具这里尝试了两种常用的工具,postman、APIfox。postman更轻量一些,APIfox的功能更强大更全面一些。用哪个都可以

另外还尝试了使用Python直接发包

postman

请求成功

  • 这里主要有几个地方需要我们设置

    1. 改为POST

    2. 在Charles中把URL拷贝过来

    3. 因为我们在代码中把最终的body保存为了Postman中的query变量,所以要在Body选项中设置一下

      image-20240916215209121

    4. 填入我们的代码

      function hexMD5(e) {
          return function (e) {
              for (var t = "0123456789abcdef", r = "", n = 0; n < 4 * e.length; n++) r += t.charAt(e[n >> 2] >> n % 4 * 8 + 4 & 15) + t.charAt(e[n >> 2] >> n % 4 * 8 & 15);
              return r
          }(function (e) {
              for (var t = 1732584193, n = -271733879, l = -1732584194, u = 271733878, c = 0; c < e.length; c += 16) {
                  var f = t,
                      d = n,
                      h = l,
                      p = u;
                  t = o(t, n, l, u, e[c + 0], 7, -680876936), u = o(u, t, n, l, e[c + 1], 12, -389564586), l = o(l, u, t, n, e[c + 2], 17, 606105819), n = o(n, l, u, t, e[c + 3], 22, -1044525330), t = o(t, n, l, u, e[c + 4], 7, -176418897), u = o(u, t, n, l, e[c + 5], 12, 1200080426), l = o(l, u, t, n, e[c + 6], 17, -1473231341), n = o(n, l, u, t, e[c + 7], 22, -45705983), t = o(t, n, l, u, e[c + 8], 7, 1770035416), u = o(u, t, n, l, e[c + 9], 12, -1958414417), l = o(l, u, t, n, e[c + 10], 17, -42063), n = o(n, l, u, t, e[c + 11], 22, -1990404162), t = o(t, n, l, u, e[c + 12], 7, 1804603682), u = o(u, t, n, l, e[c + 13], 12, -40341101), l = o(l, u, t, n, e[c + 14], 17, -1502002290), t = i(t, n = o(n, l, u, t, e[c + 15], 22, 1236535329), l, u, e[c + 1], 5, -165796510), u = i(u, t, n, l, e[c + 6], 9, -1069501632), l = i(l, u, t, n, e[c + 11], 14, 643717713), n = i(n, l, u, t, e[c + 0], 20, -373897302), t = i(t, n, l, u, e[c + 5], 5, -701558691), u = i(u, t, n, l, e[c + 10], 9, 38016083), l = i(l, u, t, n, e[c + 15], 14, -660478335), n = i(n, l, u, t, e[c + 4], 20, -405537848), t = i(t, n, l, u, e[c + 9], 5, 568446438), u = i(u, t, n, l, e[c + 14], 9, -1019803690), l = i(l, u, t, n, e[c + 3], 14, -187363961), n = i(n, l, u, t, e[c + 8], 20, 1163531501), t = i(t, n, l, u, e[c + 13], 5, -1444681467), u = i(u, t, n, l, e[c + 2], 9, -51403784), l = i(l, u, t, n, e[c + 7], 14, 1735328473), t = a(t, n = i(n, l, u, t, e[c + 12], 20, -1926607734), l, u, e[c + 5], 4, -378558), u = a(u, t, n, l, e[c + 8], 11, -2022574463), l = a(l, u, t, n, e[c + 11], 16, 1839030562), n = a(n, l, u, t, e[c + 14], 23, -35309556), t = a(t, n, l, u, e[c + 1], 4, -1530992060), u = a(u, t, n, l, e[c + 4], 11, 1272893353), l = a(l, u, t, n, e[c + 7], 16, -155497632), n = a(n, l, u, t, e[c + 10], 23, -1094730640), t = a(t, n, l, u, e[c + 13], 4, 681279174), u = a(u, t, n, l, e[c + 0], 11, -358537222), l = a(l, u, t, n, e[c + 3], 16, -722521979), n = a(n, l, u, t, e[c + 6], 23, 76029189), t = a(t, n, l, u, e[c + 9], 4, -640364487), u = a(u, t, n, l, e[c + 12], 11, -421815835), l = a(l, u, t, n, e[c + 15], 16, 530742520), t = s(t, n = a(n, l, u, t, e[c + 2], 23, -995338651), l, u, e[c + 0], 6, -198630844), u = s(u, t, n, l, e[c + 7], 10, 1126891415), l = s(l, u, t, n, e[c + 14], 15, -1416354905), n = s(n, l, u, t, e[c + 5], 21, -57434055), t = s(t, n, l, u, e[c + 12], 6, 1700485571), u = s(u, t, n, l, e[c + 3], 10, -1894986606), l = s(l, u, t, n, e[c + 10], 15, -1051523), n = s(n, l, u, t, e[c + 1], 21, -2054922799), t = s(t, n, l, u, e[c + 8], 6, 1873313359), u = s(u, t, n, l, e[c + 15], 10, -30611744), l = s(l, u, t, n, e[c + 6], 15, -1560198380), n = s(n, l, u, t, e[c + 13], 21, 1309151649), t = s(t, n, l, u, e[c + 4], 6, -145523070), u = s(u, t, n, l, e[c + 11], 10, -1120210379), l = s(l, u, t, n, e[c + 2], 15, 718787259), n = s(n, l, u, t, e[c + 9], 21, -343485551), t = r(t, f), n = r(n, d), l = r(l, h), u = r(u, p)
              }
              return [t, n, l, u]
          }(function (e) {
              for (var t = 1 + (e.length + 8 >> 6), r = new Array(16 * t), n = 0; n < 16 * t; n++) r[n] = 0;
              for (n = 0; n < e.length; n++) r[n >> 2] |= (255 & e.charCodeAt(n)) << n % 4 * 8;
              return r[n >> 2] |= 128 << n % 4 * 8, r[16 * t - 2] = 8 * e.length, r
          }(e)))
      
          function r(e, t) {
              var r = (65535 & e) + (65535 & t);
              return (e >> 16) + (t >> 16) + (r >> 16) << 16 | 65535 & r
          }
      
          function n(e, t, n, o, i, a) {
              return r(function (e, t) {
                  return e << t | e >>> 32 - t
              }(r(r(t, e), r(o, a)), i), n)
          }
      
          function o(e, t, r, o, i, a, s) {
              return n(t & r | ~t & o, e, t, i, a, s)
          }
      
          function i(e, t, r, o, i, a, s) {
              return n(t & o | r & ~o, e, t, i, a, s)
          }
      
          function a(e, t, r, o, i, a, s) {
              return n(t ^ r ^ o, e, t, i, a, s)
          }
      
          function s(e, t, r, o, i, a, s) {
              return n(r ^ (t | ~o), e, t, i, a, s)
          }
      }
      
      // 获取时间戳
      function getCurrentTimestamp() {
          var date = new Date();
          var year = date.getFullYear();
          var month = ('0' + (date.getMonth() + 1)).slice(-2);
          var day = ('0' + date.getDate()).slice(-2);
          var hours = ('0' + date.getHours()).slice(-2);
          var minutes = ('0' + date.getMinutes()).slice(-2);
          var seconds = ('0' + date.getSeconds()).slice(-2);
          return year + month + day + hours + minutes + seconds;
      }
      
      function getxSign(e = {}, t) {
          const w = "511MrfpK^",
                m = "KIoa7YXIaN",
                v = "@UnOaJYS",
                p = "#iki0Dhr";
      
          const r = e.query || {};
          let n = "";
      
          console.log("getSign.times ===>", t, "getSign.data ===>", r);
      
          // 对 query 对象的键按字母排序后拼接
          const sortedKeys = Object.keys(r).sort();
          sortedKeys.forEach(key => {
              n += key + r[key];
          });
      
          console.log("data 排序后 ===>", n);
      
          // 计算 MD5 签名
          const sign = hexMD5(n + t + w + m + v + p);
          console.log("getSign.sign ===>", sign);
      
          return sign;
      }
      
      var t = getCurrentTimestamp();
      var body = {
          "member_no": 973470,
          "type": 33,
          "store_no": 12
      };
      
      var sign = getxSign({
          query: {
              "member_no": 973470,
              "type": 33,
              "store_no": 12
          }
      }, t)
      
      body.sign = sign;
      body.time = t;
      
      console.log("final body ==> ",body)
      
      // 将 body 设置为 Postman 变量
      pm.variables.set("query", JSON.stringify(body));
      
      • 这是最终的完整代码

APIfox

跟postman用起来差不多一样, 只是有些设置的位置改变了点

代码还是一模一样的,填入前置操作中就可以

Python

使用Python脚本直接发包

import hashlib
import requests
import json
from datetime import datetime

# 常量定义
CONSTANTS = {
    'd': "511MrfpKD^",
    'h': "GKXW58GHG",
    'p': "#iki0Dhr",
    'g': "ASF74XSR^",
    'v': "@UnOaJYS",
    'm': "KIoa7YXIaN",
    'y': "648DCGFG^",
    'w': "511MrfpK^"
}

# 获取当前时间戳
def get_current_timestamp():
    now = datetime.now()
    return now.strftime('%Y%m%d%H%M%S')

# 计算签名的函数,使用 MD5
def getxSign(params, timestamp):
    query = params.get("query", {})
    sorted_keys = sorted(query.keys())
    
    concatenated_string = "".join([key + str(query[key]) for key in sorted_keys])
    concatenated_string += timestamp + CONSTANTS['w'] + CONSTANTS['m'] + CONSTANTS['v'] + CONSTANTS['p']
    
    # 生成 MD5 签名
    sign = hashlib.md5(concatenated_string.encode('utf-8')).hexdigest()
    return sign

# 发送请求的函数
def send_request():
    # 请求体中的参数
    body = {
        "member_no": 973470,
        "type": 33,
        "store_no": 12
    }

    # 获取当前时间戳
    time = get_current_timestamp()

    # 生成签名
    sign = getxSign({
        "query": body
    }, time)

    # 将签名和时间戳加入到请求体
    body['sign'] = sign
    body['time'] = time

    # 打印请求体,查看是否正确
    print("Final body: ", body)

    # 发送 POST 请求
    url = "https://mpg.avillage.com.cn/api/grow/doGrow"  # 替换为实际的API URL
    headers = {
        "Content-Type": "application/json"
    }

    # 发起 HTTP 请求
    response = requests.post(url, headers=headers, data=json.dumps(body))

    # 输出响应
    print("Response Status Code: ", response.status_code)
    print("Response JSON: ", response.json())

# 执行请求
send_request()

执行结果:

  • 标题: 微信小程序-某某牛仔城
  • 作者: xiaoeryu
  • 创建于 : 2024-09-16 22:14:02
  • 更新于 : 2024-09-16 23:29:00
  • 链接: https://github.com/xiaoeryu/2024/09/16/微信小程序-某某牛仔城/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论