本次分享仅作为合理分析及技术研究,请勿有其他大胆的想法。

准备工作

  1. 安装MuMu模拟器 (模拟器内需要安装微信&RE文件管理器)下载

    wxapkg文件路径:/data/data/com.tencent.mm/MicroMsg/{32位字符串 如4c8188dbf07eec893d0ac90974657aeb}/appbrand/pkg

  2. 安装mp-unpack 下载

  3. 安装微信开发者工具下载

  4. 解压wxapkg文件并导出到一个文件夹

  5. 使用微信开发者工具导入项目

  6. 到这一步,你应该得到了一个可以正常展示的小程序了

image-20220114230228227

分析XHR

image-20220114230525415
image-20220114230721138
image-20220114230743444

分析接口请求可以得出,有一些参数是公共字段,每个接口都会带上,列表如下:

disseminatorUid:
pid:1196
platform:2
taskId:
timestamp:1642172654893
token:
ukey:058ea05ce8534af6964e696eba228bc9
version:V5.7.1
sign:1fbf7b840e70ca79d3783831339b9ecf60a95877

开始分析

  1. 回顾一下webpack产物

    https://hellogithub2014.github.io/2019/01/02/webpack-bundle-code-analysis/

    http://www.ptbird.cn/umd-module-webpack-build-vue.html

  2. 通过全局搜索,找到接口定义的地方

    getHsbAddr: function() {
      return p.a.post(u.c.apiBase + "api/common/getDeliveryAddress");
    }
    
  3. 找到p.a.post/p.a/p中任意一个的函数定义

    {
      qzrq: function(e, t, n) {
        // e: module
        // t: __webpack_exports__
        // n: __webpack_require__ 
      }
    }
    
    /******/     function __webpack_require__(moduleId) {
    /******/
    /******/         // Check if module is in cache
    /******/         if(installedModules[moduleId]) {
    /******/             return installedModules[moduleId].exports;
    /******/         }
    /******/         // Create a new module (and put it into the cache)
    /******/         var module = installedModules[moduleId] = {
    /******/             i: moduleId,
    /******/             l: false,
    /******/             exports: {}
    /******/         };
    /******/
    /******/         // Execute the module function
    /******/         modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
    /******/
    /******/         // Flag the module as loaded/*  */
    /******/         module.l = true;
    /******/
    /******/         // Return the exports of the module
    /******/         return module.exports;
    /******/     }
    
    // __webpack_require__这个函数的入参是一个moduleId 
    // function __webpack_require__(moduleId) {} 这个方法是通过moduleId拿到属性值,属性值对应的是一个函数,然后通过call去执行这个函数,并且将 module, __webpack_exports__, __webpack_require__ 这三个参数值再传递给这个方法
    
    image-20220114231555406
  4. 找到p的定义
    p = n("Vo7i")

    image-20220114214112883
  5. 找到n("Vo7i")

    image-20220114214431053

    看到了p.interceptors.request.usep.interceptors.response.use,是不是很像axios?盲猜一波,这是个中间件看看里面有没有什么重要的信息?

  6. 发现了刚才接口上的那些公共参数
    image-20220114214735178

  7. 分析这些参数的来源

    var n = wx.getStorageSync("userInfo"), // wx原生api 获取storage里面缓存的"userInfo"
        r = wx.getStorageSync("env") || "ziyou", // wx原生api 获取storage里面缓存的"env"
        o = wx.getLaunchOptionsSync().query, // wx原生api 获取小程序启动时落地页携带的url query
        i = n && n.token || "", // 同userInfo.token
        a = n && n.disseminatorUid || "", // 同userInfo.disseminatorUid
        u = o.taskId || "", // 同wx.getLaunchOptionsSync().query.taskId 获取落地页携带的query中的taskId
        p = Object(s.c)(), // s是啥?c是啥?为啥
        d = "app" // 固定字符串
    
    e.url = Object(s.a)(e.url, "token", i) // 
    e.url = Object(s.a)(e.url, "disseminatorUid", a),
    e.url = Object(s.a)(e.url, "taskId", u),
    e.url = Object(s.a)(e.url, "version", c.c.version),
    e.url = Object(s.a)(e.url, "pid", h),
    e.url = Object(s.a)(e.url, "platform", c.c.platform),
    e.url = Object(s.a)(e.url, "timestamp", +new Date()),
    e.url = Object(s.a)(e.url, "page", d),
    e.url = Object(s.a)(e.url, "ukey", p), // 至此 仅ukey没有找到咋生成的
    e.url = Object(s.m)(e); 
    
    // https://segmentfault.com/q/1010000002736664
    
  8. e.url = Object(s.a)(e.url, "ukey", p)这段代码到底在干什么?

    s.a是什么?

    ss = n("0xDb")

    n("0xDb")

    "0xDb": function(t, e, n) {
       e.a = function(t, e, n) {
         return -1 === t.indexOf("?") ? t += "?" : t += "&", t += e + "=" + n;
       }
     }
    

    所以 s.a得到的是个function

    Objcet(s.a)是什么?
    image-20220114221516141
  9. 由此可以得到

  10. // webpack
    e.url = Object(s.a)(e.url, "ukey", p)
    
    // coder
    function concatString(origin: string, key: string, val: string): string;
    e.url = concatString(e.url, key, val)
    
去看看作为val的p咋来的吧

p = Object(s.c)()

e.c = function() {
            return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(t) {
                var e = 16 * Math.random() | 0;
                return ("x" === t ? e : 3 & e | 8).toString(16);
            }).toLowerCase().replace(/-/g, "");

很好 标准的uuid生成

得到了ukey的生成方法

最后 e.url = Object(s.m)(e)调用了s.m, s.m是什么?

e.m = function(t) {
  for (var e = {}, n = t.url.split("?")[1].split("&"), r = 0; r < n.length; r++) {
    var o = n[r].split("=");
    "page" !== o[0] && (e[o[0]] = o[1]);
  }
  e = f()(e, t.body);
  var i = "", a = "?", s = c()(e);
  s = (s = s.filter(function(t) {
    return "sign" !== t;
  })).sort();
  for (var u = 0; u < s.length; u++) i += s[u] + e[s[u]], a += s[u] + "=" + e[s[u]] + "&";
  return (
	  i += "b7cab12b2b81385dd2cccb8ce67e4998",
    a += "sign=" + (i = v()(i)), 
    t.url.split("?")[0] + a;
  )
}

"sign=" + (i = v()(i)) v是啥?

v太乱了 看不懂

sha1加密

常见加密方式的长度

md5: 16位
md2: 32位
md4: 40位
快速识别方法:加密12345 
123456——16位加密以49开头
123456——32位加密以e10开头

sha1——40位
sha256——62位
sha512——128位

看看能否请求的通?

SUCCESS!~