诗林 发表于 2024-10-29 02:35:26

得物小程序sign、data参数加密、响应体解密算法还原

一、声明


本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于贸易用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除!

二、逆向目标


目标:得物小程序
目标接口:1.分类接口、2.分类接口商品信息
https://i-blog.csdnimg.cn/direct/ddd3370ad7be4a51ae47e9d07107f036.pnghttps://i-blog.csdnimg.cn/direct/2d16134a1ba7456c9597e89fc65ab81f.png

三、逆向分析

1、小程序抓包

保举WeChatOpenDevTools,WeChatOpenDevTools,window wechat历史版本相比之前的微信开发者工具,方便了许多,可以直接用浏览器调试
提醒:用微信小号举行测试

现在自用版本:
https://i-blog.csdnimg.cn/direct/80b7debf4c8c4d8eb173b2e42bf3b65c.png
https://i-blog.csdnimg.cn/direct/c39bec2c28814e9dbc7ac4ad381680df.png
https://i-blog.csdnimg.cn/direct/0bd796fca1914ecea548d8e9b07ee174.png
https://i-blog.csdnimg.cn/direct/2112f0824efc432b9118f64dea8dffc8.jpeg

2.小程序接口分析


2.1分类接口分析

https://i-blog.csdnimg.cn/direct/1c7617131fcc45d9a55130456c9aff6a.png

只有一个加密参数sign
长度:32位
猜测:md5加密

验证
1. 最快的方式在全局搜索sign,但是搜索过程中卡住,不绝没有出现搜索效果,在后续分析验证是有sign这个关键字
https://i-blog.csdnimg.cn/direct/132341cde5e84c6d9706f2b6831e33db.png

2. 搜索方式不可,我们直接堆栈追踪

https://i-blog.csdnimg.cn/direct/c54ebd894eee45c493a7e7b3cfee901d.png
case 3 加密参数sign还没有生成
case 5 已有响应数据生成
以是加密生成位置
i.postRequest("/api/v1/h5/commodity/fire/search/doCategoryDetail", {
                                    catId: n
                              } 进入postRequest函数内部
https://i-blog.csdnimg.cn/direct/1a2dcf0860fe49c0bd66e11c50676ccd.png
进入 w.fetch(r)
https://i-blog.csdnimg.cn/direct/9166ad44e895408eae66e4480dd18929.png
简单说明一下代码含义

是一个处理HTTP请求和响应拦截器的逻辑

this.interceptors.request.reducer 和 this.interceptors.response.reducer 是两个函数,分别用于处理请求拦截器和响应拦截器。

u 是一个数组,用于存储处理成功(fulfilled)和处理失败(rejected)的回调函数。这些回调函数将会在拦截器处理时添加到数组中。

在请求拦截器中,成功和失败的回调函数被分别放到 u 数组的前两个位置(使用 unshift 方法),而在响应拦截器中,它们被放到 u 数组的末尾两个位置(使用 push 方法)。

接下来的循环 while (u.length) 确保在 u 数组中还有回调函数时继续执行。在每次循环中,从 u 数组中依次取出两个回调函数,并将它们依次应用到变量 s 上,这个变量最初可能是一个 Promise 或者一个初始的状态。

s = s.then(u.shift(), u.shift()) 这一行代码的作用是,通过 then 方法将两个回调函数注册到 Promise 链中,u.shift() 会从 u 数组中取出并移除第一个元素(即成功回调),u.shift() 取出并移除第二个元素(即失败回调)。

最后,返回变量 s,这个变量代表了一个最终的 Promise 对象,它可能已经包含了一系列经过请求和响应拦截器处理后的结果。

综上所述,这段代码主要作用是使用拦截器函数来处理HTTP请求和响应,在这些操作中注册了一系列的处理函数,并且通过 Promise 链式调用确保了每个处理函数的顺序执行。 进入哀求拦截中对应的函数,所有的参数加密都在其中,包含sign,data哀求体加密,都打上断点
https://i-blog.csdnimg.cn/direct/23f0a18dc39f4a3c8502c829f47a9ae8.png

拦截器的利用情势xxx.interceptors.request.use,xxx.interceptors.response.use
有的时候我们为了更快更好的定位可以直接搜索相干关键词
https://i-blog.csdnimg.cn/direct/075646e069744bbc8a02ebcd684e9466.png




sign加密参数分析

https://i-blog.csdnimg.cn/direct/53f5061636ad477a8f5c2da192b1e30d.png
sign加密值:固定salt:19bc545a393a25177083d4a748807cc0 + 将字典按照key排序和对应的处理后的值拼接成一个字符串做md5
def get_sign(params):
    # MD5加密,params字典参数列表
    # 排序后的键列表
    sorted_keys = sorted(params.keys())
    # 拼接排序后的键和值
    kv = ''.join(f"{key}{params}" for key in sorted_keys)
    salt = '19bc545a393a25177083d4a748807cc0'
    str1 = f"{kv}{salt}"
    md5_str = hashlib.md5(str1.encode("utf-8")).hexdigest()
    return md5_str



data加密参数分析

https://i-blog.csdnimg.cn/direct/6a6b39b3802749d787a9516ce85d96c1.jpeg

Fun110: function (t, e, n) {
                            var i = function (t, e) {
                                    return t === e
                              },
                              o = function (t, e, n) {
                                    return t(e, n)
                              },
                              a = function (t, e) {
                                    return t / e
                              },
                              c = function (t, e) {
                                    return t >> e
                              },
                              s = function (t, e) {
                                    return t & e
                              },
                              u = function (t, e) {
                                    return t + e
                              },
                              f = function (t, e) {
                                    return t << e
                              };
                            n === "xw".split("").reverse().join("") ? wx : "qq" === n ? qq : i(n, "dy") ? tt : i(n, "bd") ? swan : n === g(193, -6, 289, -185, 244) && my;
                            var l = o(L, 48, 16),
                              d = "",
                              p = "",
                              h = "0",
                              v = t;

                            function g(t, e, n, r, i) {
                              return H(i - -695, e)
                            }
                            if ((e || g(-18, -816, -279, 6, -617)).toLocaleLowerCase() === H(971 - 75, 928))
                              try {
                                    var _ = [],
                                        y = JSON.parse(t);
                                    Object.keys(y).map((function (t) {
                                          return _.push(t + "=" + encodeURIComponent(y)),
                                                t
                                        })),
                                        v = _.join("&")
                              } catch (t) {}
                            if ("0" === String($t))
                              return {
                                    a: l,
                                    b: o(L, 48, 16),
                                    c: $t + "," + Ht,
                                    d: t,
                                    e: L(48, 16)
                              };
                            try {
                              if (v) {
                                    var m = q.enc.Utf8.parse(v);
                                    d = q.MM.encrypt(m, q.enc.Utf8.parse(l.substr(10, 16)), {
                                          iv: q.enc.Utf8.parse(l.substr(20, 16)),
                                          mode: q.mode.CBC,
                                          padding: q.pad.Pkcs7
                                        }),
                                        h = "1"
                              }
                              var b = l.split(""),
                                    k = Number(a((new Date).valueOf(), 1e3).toFixed(0)).toString(16).toUpperCase().split("");
                              b.splice.apply(b, .concat(r(k)));
                              var w = 0;
                              k.forEach((function (t, e) {
                                        e > 3 && (w += t.charCodeAt())
                                    })),
                                    b.splice(w % 10, 1, 2);
                              var S, x, O = St.getKey(Kt).encrypt(b.join("")),
                                    C = "";
                              for (S = 0; S + 3 <= O.length; S += 3)
                                    x = parseInt(O.substring(S, S + 3), 16),
                                    C += "" + Nt.charAt(c(x, 6)) + Nt.charAt(s(x, 63));
                              S + 1 == O.length ? (x = parseInt(O.substring(S, u(S, 1)), 16),
                                        C += Nt.charAt(x << 2)) : u(S, 2) == O.length && (x = o(parseInt, O.substring(S, u(S, 2)), 16),
                                        C += u(Nt.charAt(x >> 2), Nt.charAt(f(3 & x, 4)))),
                                    p = C
                            } catch (t) {
                              console.log(t),
                                    d = v,
                                    h = 2
                            }
                            var W = d ? d.ciphertext ? d.ciphertext.toString().toUpperCase() : d : "";
                            return {
                              a: l,
                              b: L(48, 16),
                              c: h + "," + Ht,
                              d: JSON.stringify({
                                    data: p + "​" + W
                              }),
                              e: o(L, 48, 16)
                            }
                        } 分析p  和 w的值
https://i-blog.csdnimg.cn/direct/0d02267cac544b8e994b7540a50ca192.png
p值

C赋值给p,C是通过O举行运算得到的
   var S, x, O = St.getKey(Kt).encrypt(b.join("")), C = "";
                        for (S = 0; S + 3 <= O.length; S += 3)
                            x = parseInt(O.substring(S, S + 3), 16),
                            C += "" + Nt.charAt(c(x, 6)) + Nt.charAt(s(x, 63));
                        S + 1 == O.length ? (x = parseInt(O.substring(S, u(S, 1)), 16),
                        C += Nt.charAt(x << 2)) : u(S, 2) == O.length && (x = o(parseInt, O.substring(S, u(S, 2)), 16),
                        C += u(Nt.charAt(x >> 2), Nt.charAt(f(3 & x, 4)))),
                        p = C  O是通过rsa加密得到的,传入参数是颠末一系列运算的随机生成的48位16进制数https://i-blog.csdnimg.cn/direct/7b3d926eaceb47d3b4822d7eece5507d.png

RSA公钥的 modulus:00ca8d9830ba538887a4a19d88c1d93b2f6a228dcb61cfb05672d5efd20c5ee1b3492458e4b65091350fc0ce874d6f404c99e26c282f8765d2ede9b266160e7763
公钥的指数:010001

w值

   d = q.MM.encrypt(m, q.enc.Utf8.parse(l.substr(10, 16)), {
                              iv: q.enc.Utf8.parse(l.substr(20, 16)),
                              mode: q.mode.CBC,
                              padding: q.pad.Pkcs7
                            }), w值是颠末aes 加密得到的
l 是 随机生成的48位16进制字符串
key:是从l中截取10至10+16
iv:是从l中截取20至20+16

p的值保持固定稳定也没有关系
只须要注意哀求参数data中aes中加密的key和iv的值和响应体解密aes中key和iv的值保持不绝就可以


响应体解密

key 和 iv与aes加密保持一致
https://i-blog.csdnimg.cn/direct/1799bf3e9b51465b9034c170f5dbc0cf.png
https://i-blog.csdnimg.cn/direct/08885966841f46cd9385e8463a5d12a5.png

四、效果 

https://i-blog.csdnimg.cn/direct/3a8f987fb74248f6960af8d455a19fbd.png
https://i-blog.csdnimg.cn/direct/90892137044240eca2a918e368c3724a.png

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 得物小程序sign、data参数加密、响应体解密算法还原