skuukzky
文章9
标签0
分类2

文章分类

某东4.7jsvmp 算法还原

某东4.7jsvmp 算法还原

京东 h5st 4.7.4

H5st

以下参数皆为研究时随机参数 并不 是同一请求 全部参数 特别注意的是在 aes 加密过程中用的 fp 需要和第二段保持一致 否则将会请求失败。

1
2
3
4
5
6
7
8
9
20240721134400770;   第一段
5t96nmyigzygygy9;  第二段
74f42;   第三段
tk03wbb161bb218ny47exhUsIoE3D7lHbxbZgTik7YqisIFvWjBTQG8XlY2fmxSJ5sec_Q95KVeW9RG8w227CEONjo52;第四段
f4a8befb0820e360d42ba0a7805b81e0;  第五段  新增一段
4.7; 第六段
1721532832275; 第七段
UOWRm8fD2k3wKIklaB5h58P7JQx1oXrg_gB7U7jVoEs1NiltSlNQddHgLqVLdjHSiXGLgu72eJ7n13kJgBCBIIHceQiv3eNMhgHk1ZUbAPT4josbXzPcT4mD-ZqTokNYpDn-IifpL1SQ_X7FmySgoTTmntIOskhmlQfn2MvsQZJE6JK80ykJk3DjWsqWSKWJyzA0_NsXY2m5NBWtpTOQ9rljTB8IWGrJR9R_aH5RLHnbpaoXOrXHhE1oJ9IZc9zRYaKSXjaFhRqhCdGqq4wW9GASnk6ddS2HYMMfOYfCLVCQkWp6f725741DXfhF94h2Kssd4eedjcIl5HIlyOlFvHsFRTmsnIOKqSLln_6hbg04svF3qgBbQVAKsqCnsVCuVTB0QCJ4pBu3aWHAKjuvyYyDwxL3E2UTOq5A8_dGSqEiSU1MrfZy--K3O56hREYguSt09mu3Rb6RtORJu8aBFo5n; 第八段 魔改aes
4b8b4c86155f61276e7a8df6ffc8dcdc  第九段 新增一段

第一段

1
2
3
4
5
6
7
8
9
10
11
12
//根据当前时间戳转换的    1721540640770 -->20240721134400770
function formatTime(timestamp) {
  const date = new Date(timestamp);
  const year = date.getFullYear();
  const month = (date.getMonth() + 1).toString().padStart(2, "0");
  const day = date.getDate().toString().padStart(2, "0");
  const hours = date.getHours().toString().padStart(2, "0");
  const minutes = date.getMinutes().toString().padStart(2, "0");
  const seconds = date.getSeconds().toString().padStart(2, "0");
  const milliseconds = date.getMilliseconds().toString().padStart(3, "0");
  return `${year}${month}${day}${hours}${minutes}${seconds}${milliseconds}`;
}

第二段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//生成的 fingerprint
e.generateVisitKey = function () {
  let t = "1uct6d0jhq"; //改变了 还没找到
  let o = k(t, 5);
  let i = y();
  let c = _(t, o);
  let s = m({ size: i, num: c }) + o + m({ size: 16 - 5 - 1 - i, num: c }) + i;
  let u = s.split("");
  let l = u.slice(0, 15);
  let p = u.slice(15);

  let v = [];
  while (l.length > 0) {
    v.push((35 - parseInt(l.pop(), 36)).toString(36));
  }
  return v.concat(p).join("");
};

第三段

1
//appid, 每个接口都有一个对应的值

第四段

1
获取到的token

第五段

image-20240724162954831

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//item.jd.com
(function anonymous() {
  return function test(tk, fp, ts, ai, algo) {
    // ai 为73806 且为固定数
    var rd = "jr6qBP48qndB";
    var str = "".concat(tk).concat(fp).concat(ts).concat(ai).concat(rd);
    return algo.HmacSHA256(str, tk);
  };
});
/*
在经过t.algo.HMAC 时候 自写了 init和ekey 函数

其中
init函数应该是初始化字符串

ekey函数操作是
tk03w8c3e1c3e18nXPECn0kkSU01SK7QBOZ6CnS2ONny98eulgMemppU2VFRzTrfjau4E0ZztEMdpNbOHSCRv8SclwgS 反转字符串--->vmp操作后得到'kn3* 这个字符串暂时不知道怎么来的 但多次测试是固定的(包括更换设备更换浏览器更换账号等) 应该和某个固定字符串有关 'kn3*8c3e1c3e18nXPECn0kkSU01SK7QBOZ6CnS2ONny98eulgMemppU2VFRzTrfjau4E0ZztEMdpNbOHSCRv8SclwgS
*/
/*
[sign]  __makeSign, result:{
  "key": "1146b5b7dd7d22e0a54dbc15b88e3d3af128bbf4d9a7202f70bd17f648619b19f4a7bea82ac734a3e9846ddfa77889c82ae195fa659aab5dafcd9f39d0ca997d",
  "signStr": "cf39e022791e801e8e14831caf9ce9ba",
  "_stk": "r,t",
  "_ste": 1,
  "h5st": "20240724162804806;65i6tgyn9m5zzzi5;73806;tk03wb5891cc618nLyxfAEBtCpMCZjxBj2zClh3zmA66NEPAbCgNviddGCDhG71yVMBcjItUgiu_ZAc3Gywo8QbnjqWT;cf39e022791e801e8e14831caf9ce9ba;4.7;1721809684806;UOmmZVMpjhlF79xYgh738cOy_ODWEgb7Yup7dEol2JF27v4kqKSLKMqNG3pEHTFJRf-odLa1vnNCtjj9ElYBbCjkPbWYU7HJlGyhMiT-SHASJA-gEkuJTMM78rLGFZdgCbfMpt-sdfRCrw57yOVdgSiAf4q2r5igXFEmLeSWWXrlTdHAfIozsGdsOPp6_VL-ffPmbpCtne3f8eV94isG75oexYsT2BBJKhorpaTxIaUrc_pQDqu4JV3kSI7KkVVqCRyjBVi0M64P8BEq_eOzchggqqWSXPIYV0Lay933IbGUEiW62DNv6DQO_bjnmEauatShWIgckcVMADGRP0IUP2w6o_DL1BZHEVhWlt3rQEUEBluS6QH1to002ho4zGr6P-A80a6PXFzj0V2CWmsbxdtefFQBuzPcacrWVP2EapUUjYoXOrXHhE1oJ9IZc9zRYaKSXjaFhRqhCdGqq4wW9GASnk6ddS2HYMMfOYfCLVCQkWp6f725741DXfhF94h2Kssd4eedjcIl5HIlyOlFvHsFRTmsnIOKqSLln_6hbg04svF3qgBbQVAKsqCnsVCuVTB0QCJ4pBu3aWHAKjuvyYyDwxL3E2UTOq5A8_dGSqEiSU1MrfZy--K3O56hREYguSt09mu3Rb6RtORJu8aBFo5n;73b25807580cfabdd047818058c75e83"
}
hmac 如sha256  他的操作就是 先经过上述的ekey反转字符串 当作为hmac的key 密文和md5大同小异 而密文就是
tk03wb5891cc618nLyxfAEBtCpMCZjxBj2zClh3zmA66NEPAbCgNviddGCDhG71yVMBcjItUgiu_ZAc3Gywo8QbnjqWT 65i6tgyn9m5zzzi5 20240724162804806 47 73806 AfUt6ACvKwFn7n5<G*
tk拼接第二段在拼接第一段拼接47拼接appid拼接algo中的rd值拼接salt盐7n5<G*后续操作同下述操作*/
1
2
3
4
5
6
7
8
9
10
11
tk03wbb161bb218ny47exhUsIoE3D7lHbxbZgTik7YqisIFvWjBTQG8XlY2fmxSJ5sec_Q95KVeW9RG8w227CEONjo525t96nmyigzygygy9202407211509126964774f428egu1x3bttOY7n5<G* ---md5-> b8df766f084d15a71eb7422a2c999137

b8df766f084d15a71eb7422a2c999137appid:pc_home_page&client:pc&clientVersion:1.0.0&functionId:getAlwaysBuyGoods&t:1721545191402b8df766f084d15a71eb7422a2c9991377n5<G*  -->md5-> 810c4f5c06a1307bc27490e3ffe80fcf

总的来说 第五段的生成 是第四段token拼接第二段 在拼接第一段在拼接4774f428egu1x3bttOY加上salt值7n5<G* 生成md5值后
按键值对大小拼接body中的appid和client和clientVersion和functionid和t,最后在拼接一下生成的md5值和salt值7n5<G*生成的结果即为第五段结果
至于 4774f428egu1x3bttOY 怎么来的,还没能研究透彻,但多次测试发现是固定的,可能与个人账号有关。
algo:function test(tk,fp,ts,ai,algo){var rd='IuxIY6nBYZ8r';var str="".concat(tk).concat(fp).concat(ts).concat(ai).concat(rd);return algo.HmacMD5(str,tk);}

现在发现 47是固定的 而 74f42是appid即第三段 8egu1x3bttOY是algo中的盐 即rd的值
如果请求参数带body 则需要把body进行sha256加密 然后带入第五段流程加密

image-20240725133330290

第六段

1
版本号 4.7

第七段

1
与第一段相关 为时间戳

第八段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
//aes加密
key ='_M6Y?dvfN40VMF[X';
iv ='0102030405060708';
//魔改了aes 自定义了几个方法用于base64的编码设置
v.Utils = {
    toWordArray: function(e) {
        for (var r = [], n = 0; n < e.length; n++)
            r[n >>> 2] |= e[n] << 24 - n % 4 * 8;
        return t.lib.WordArray.create(r, e.length)
    },
    fromWordArray: function(e) {
        for (var t = new Uint8Array(e.sigBytes), r = 0; r < e.sigBytes; r++)
            t[r] = e.words[r >>> 2] >>> 24 - r % 4 * 8 & 255;
        return t
    }
};
//一段jsvmp
 _append: function(e) {
  for (var t, r, n = c, a = i, o = [], l = 0; ; )
      switch (a[l++]) {
      case 6:
          t = o[o.length - 1];
          break;
      case 13:
          o.push(this);
          break;
      case 21:
          o.push(k);
          break;
      case 32:
          o.push(o[o.length - 1]);
          break;
      case 35:
          e = o[o.length - 1];
          break;
      case 36:
          o[o.length - 2][s[a[l++]]] = o[o.length - 1],
          o[o.length - 2] = o[o.length - 1],
          o.length--;
          break;
      case 41:
          return;
      case 45:
          o.push(a[l++]);
          break;
      case 49:
          r = o.pop(),
          o[o.length - 1] += r;
          break;
      case 57:
          o.push(e);
          break;
      case 60:
          o.push(null);
          break;
      case 66:
          o[o.length - 1] = o[o.length - 1][s[a[l++]]];
          break;
      case 67:
          o.push(t);
          break;
      case 69:
          o[o.length - 1] = Dg(o[o.length - 1]);
          break;
      case 72:
          o.pop();
          break;
      case 73:
          o[o.length - 1] ? (++l,
          --o.length) : l += a[l];
          break;
      case 75:
          o.push(o[o.length - 1]),
          o[o.length - 2] = o[o.length - 2][s[a[l++]]];
          break;
      case 80:
          r = o.pop(),
          o[o.length - 1] = o[o.length - 1] == r;
          break;
      case 90:
          null != o[o.length - 2] ? (o[o.length - 3] = n.call(o[o.length - 3], o[o.length - 2], o[o.length - 1]),
          o.length -= 2) : (r = o[o.length - 3],
          o[o.length - 3] = r(o[o.length - 1]),
          o.length -= 2);
          break;
      case 95:
          o.push(u)
      }
},
  /*我们插装发现 他翻转了密钥 X[FMV04Nfvd?Y6M_
其实这个aes整体并没做太大的魔改,只是把密钥给反转了而已,算法是原生的aes 主要魔改了base64算法 才使其加密的结果不同*/
  //魔改base64算法 我们把所有运算符的操作给打上日志点,根据规律慢慢还原。
  encode: function (t) {
      let r = e.enc.Utils.fromWordArray(t)
      let array = new Array(0)
      let n = array.slice.apply(r)

      let a = new Array(0)
      Array.prototype.push.apply(a, n)

      for (let o = 3 - a.length % 3, u = 0; u < o; u++) {
          a.push(o)
      }

      let l = new Array(0)
      for (let i = a.length - 1; i >= 0; i = i - 3) {
          a.slice(i - 2, i + 1)
          Array.prototype.push.apply(l, a.slice(i - 2, i + 1))

      }
      let f = e.enc.Utils.toWordArray(l)
      let g = f.words
      let p = f.sigBytes
      let v = this._map1
      f.clamp(f)
      let d = new Array(0)
      for (let b = 0; b < p; b += 3) {
          let y = g[b >>> 2] >>> (24 - b * 8) & 255
          let k = g[(b + 1) >>> 2] >>> (24 - (b + 1) * 8) & 255
          let m = g[(b + 2) >>> 2] >>> (24 - (b + 2) * 8) & 255
          let w = y << 16 | k << 8 | m

          for (let _ = 0; _ < 4; _++) {
              if ((b + (_ * 0.75)) < p) {
                  d.push(v.charAt((w >>> 6 * (3 - _) & 63)))
              }
          }
      }
      let x = []
      for (let j = 0; j < d.length; j += 4) {
          x.push.apply(x, d.slice(j, j + 4).reverse())
      }
      return x.join("")
  }

第九段

1
2
3
4
5
6
7
8
tk03wbb161bb218ny47exhUsIoE3D7lHbxbZgTik7YqisIFvWjBTQG8XlY2fmxSJ5sec_Q95KVeW9RG8w227CEONjo525t96nmyigzygygy9202407211245081744774f428egu1x3bttOY7n5<G* --> a1957d7fbdd8687cc8a075743aae865a

a1957d7fbdd8687cc8a075743aae865aappid:pc_home_page&functionId:getAlwaysBuyGoodsa1957d7fbdd8687cc8a075743aae865a7n5<G* --->4b8b4c86155f61276e7a8df6ffc8dcdc

现在发现 47是固定的 而 74f42是appid即第三段 8egu1x3bttOY是algo中的盐
所以第8段的加密即为 第五段tk拼接第二段拼接第一段拼接4774f428egu1x3bttOY在拼接7n5<G* 得到的md5值
md5值拼接body中的appid值在拼body中的functionId值在拼接md5值在拼接7n5<G* 就得到了第9段的md5值
与第五段比较相近 但不用的在于 第八段之只用到了body中的appid和fucntionid这两个参数。

请求也是问题不大

image-20240725132955244

一些坑点

要想实现接口部署还需要部分细节修饰

algo

1
2
3
4
5
对于algo 主要有一个expandParams的参数问题 他和第七段一样也是个aes。主要问题在于他的key不同于第七段 是 wm0!@w-s#ll1flo(
加密参数是
'{\n  "wc": 0,\n  "wd": 0,\n  "l": "zh-CN",\n  "ls": "zh-CN,en,en-GB,en-US",\n  "ml": 2,\n  "pl": 2,\n  "av": "5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0",\n  "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0",\n  "sua": "Macintosh; Intel Mac OS X 10_15_7",\n  "pp": {\n    "p2": "桥到船头自然沉9060"\n  },\n  "extend": {\n    "wd": 0,\n    "l": 0,\n    "ls": 2,\n    "wk": 0,\n    "bu1": "0.1.5",\n    "bu2": 0,\n    "bu3": 50,\n    "bu4": 0,\n    "bu5": 0,\n    "bu6": 10\n  },\n  "pp1": "",\n  "w": 1728,\n  "h": 1117,\n  "ow": 1728,\n  "oh": 992,\n  "url": "https://paipai.m.jd.com/ppdbd/pages/detail/index?id=389626893&cprice=47&entryid=p0020003hd190531&showhead=no&entryid=p0020003hd190531&scene=null",\n  "og": "https://paipai.m.jd.com",\n  "pf": "MacIntel",\n  "pr": 2,\n  "re": "",\n  "random": "UhHAH4rSHNTC",\n  "referer": "",\n  "v": "h5_file_v4.7.4",\n  "bu2": "    at https://storage.360buyimg.com/webcontainer/js_security_v3_0.1.5.js:3833:21",\n  "canvas": "fa3fabf83a3dc96ca518dcdec635e8d9",\n  "webglFp": "e3873b69ce54d5eb4893fad2d0592dda",\n  "ccn": 14,\n  "ai": "86b9f",\n  "fp": "n5yi5yn5ytzg69i1"\n}'

主要是ai 也就是appid 和fp 的处理问题就可以实现动态请求 random其实可以处理可以不处理
本文作者:skuukzky
本文链接:https://lpy30m.github.io/skuukzky.github.io/2024/08/23/%E9%80%86%E5%90%91/%E6%9F%90%E4%B8%9C4-7jsvmp-%E7%AE%97%E6%B3%95%E8%BF%98%E5%8E%9F/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可