【某购物网站】请求头参数、请求参数、响应数据加密分析

在这里插入图片描述


分析目标

Cmh0dHBzOi8vd3d3LnpraC5jb20v

1. 接口分析

  抓包数据详情页面价格接口,可以看到加密的参数非常之多,首先是请求头里面的X-Akc、X-Rgn参数,如下所示:

在这里插入图片描述

接着是接口响应数据的加密,如下所示:

在这里插入图片描述

最后是请求参数加密,如下图所示的两个参数traceId、ciphertraceId是一段随机的字符串,每次请求都需要带上。cipher参数的话就是请求价格的商品id

在这里插入图片描述

2. 断点分析

  接下来先从请求头参数X-Akc、X-Rgn分析,两个是headers里面的加密参数,直接先字段名称关键词搜索,这里能够直接搜索到加密位置,如下所示:

在这里插入图片描述

点击进入到JS文件,这里分析发现X-Akc、X-Rgn参数需要先访问rsaKey接口,这个接口请求后会返回rsaKey、rsaGroup字段信息,如下所示:

在这里插入图片描述

rsaGroup字段就是请求头内X-Rgn参数的值,请求上面rsaKey接口需要带上traceId参数,这里我们就可以断点继续分析traceId参数的生成,如下所示:

在这里插入图片描述

经分析,traceId参数的生成代码如上图,直接拿下来修改一下,代码如下:

function TraceId(e = 8, t = !0) {
    
    
            var n = ""
              , n = Math.ceil(1e14 * Math.random()).toString().substr(0, e || 4);
            return t && (n += Date.now()),
            n

}

拿到控制台测试一下,看起来应该是没错的,至此请求头内X-Rgn参数已经解决,如下所示:

在这里插入图片描述

继续分析请求头内X-Akc参数的加密,还是下面这部分JS代码:

在这里插入图片描述

JS代码中现实需要公钥publicKey,在上面rsaKey接口的返回数据中rsaKey字段就是公钥!截图发现一摸一样~是吧?公钥已经有了,再看上面这段JS代码,用了JSEncrypt加密库,这里还原一下加密逻辑,可以就是如下这样:

const jsencrypt = require('jsencrypt')
o = rsaKey # 接口返回的公钥串
c = rsaGroup # traceId

let s = new jsencrypt();
s.setPublicKey(o)
const encryptedData = s.encrypt(N)

const headers = {
    
    
    'x-akc': encryptedData,
    'x-rgn': c.toString() 
};

3. 扣代码

  接下来分析完以后,就是扣代码环节。cipher参数也是里面稍难的,比较复杂!JS往下接着看cipher用到的b.a.encrypt来自Webpack打包的代码,k.a直接修改使用crypto-js的包:

在这里插入图片描述

这里没猜错的话,runtime…应该是Webpack的加载器代码

在这里插入图片描述

接下来找b.a,JS往上追溯尝试去找到b的定义处,找到之后需要注意t = n(399),什么意思?意思就是说b是webpack中索引399的代码,这个时候我们需要开始构造,在构造之前需要先找Webpack的加载器,如下所示:

在这里插入图片描述

点击上图runtime…跳转,之前分析的时候已经猜测它是一个加载器,这个JS文件,所以直接先扣出来,如下所示:

!function(l) {
    
    
    function e(e) {
    
    
        for (var r, t, n = e[0], o = e[1], u = e[2], i = 0, f = []; i < n.length; i++)
            t = n[i],
            c[t] && f.push(c[t][0]),
            c[t] = 0;
        for (r in o)
            Object.prototype.hasOwnProperty.call(o, r) && (l[r] = o[r]);
        for (s && s(e); f.length; )
            f.shift()();
        return p.push.apply(p, u || []),
        a()
    }
    function a() {
    
    
        for (var e, r = 0; r < p.length; r++) {
    
    
            for (var t = p[r], n = !0, o = 1; o < t.length; o++) {
    
    
                var u = t[o];
                0 !== c[u] && (n = !1)
            }
            n && (p.splice(r--, 1),
            e = i(i.s = t[0]))
        }
        return e
    }
    var t = {
    
    }
      , c = {
    
    
        76: 0
    }
      , p = [];
    function i(e) {
    
    
        var r;
        return (t[e] || (r = t[e] = {
    
    
            i: e,
            l: !1,
            exports: {
    
    }
        },
        l[e].call(r.exports, r, r.exports, i),
        r.l = !0,
        r)).exports
    }
    i.m = l,
    i.c = t,
    i.d = function(e, r, t) {
    
    
        i.o(e, r) || Object.defineProperty(e, r, {
    
    
            enumerable: !0,
            get: t
        })
    }
    ,
    i.r = function(e) {
    
    
        "undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, {
    
    
            value: "Module"
        }),
        Object.defineProperty(e, "__esModule", {
    
    
            value: !0
        })
    }
    ,
    i.t = function(r, e) {
    
    
        if (1 & e && (r = i(r)),
        8 & e)
            return r;
        if (4 & e && "object" == typeof r && r && r.__esModule)
            return r;
        var t = Object.create(null);
        if (i.r(t),
        Object.defineProperty(t, "default", {
    
    
            enumerable: !0,
            value: r
        }),
        2 & e && "string" != typeof r)
            for (var n in r)
                i.d(t, n, function(e) {
    
    
                    return r[e]
                }
                .bind(null, n));
        return t
    }
    ,
    i.n = function(e) {
    
    
        var r = e && e.__esModule ? function() {
    
    
            return e.default
        }
        : function() {
    
    
            return e
        }
        ;
        return i.d(r, "a", r),
        r
    }
    ,
    i.o = function(e, r) {
    
    
        return Object.prototype.hasOwnProperty.call(e, r)
    }
    ,
    i.p = "//static.zkh360.com/file/resource/official/";
    var r = (n = window.webpackJsonp = window.webpackJsonp || []).push.bind(n);
    n.push = e;
    for (var n = n.slice(), o = 0; o < n.length; o++)
        e(n[o]);
    var s = r;
    a()
}([]); // 放索引的JS代码

加载器代码内[]是什么?是399索引的JS代码,需要继续接着扣,扣完再填充到加载器内!399代码如下:

在这里插入图片描述

399代码中还引用了其他代码,需要依次导入。看上图n(237),这种就接着扣就完事了:

在这里插入图片描述

另外,部分代码是没有写索引的,如上图,可根据列表中下标的位置找对应的代码!还需要扣上面引用的n(893)、n(894)、n(624)、n(896)

剩余需要扣的代码这里不详细描述了,感兴趣的可以自己根据上面的方式去慢慢扣

代码扣全之后,就可以直接调用生成请求头加密参数跟解密接口响应的加密数据:

/**
 * @returns {
    
    {"x-akc": "", "x-rgn": "", "cipher": ""}}
 */
get_cipher_and_headers = function (traceId, data, rsaKey, rsaGroup, N, E) {
    
    
    o = rsaKey
    c = rsaGroup
    let s = new jsencrypt();
    s.setPublicKey(o)
    const encryptedData = s.encrypt(N)
    // 准备HTTP请求的headers
    const headers = {
    
    
        'x-akc': encryptedData,
        'x-rgn': c.toString() // 这里填写对应的区域值
    };


    // 使用ECB模式和PKCS7填充加密请求数据
    a = loader(398)

    let cipher = a.encrypt(JSON.stringify(data), E, {
    
    
        mode: crypto_js.mode.ECB,
        padding: crypto_js.pad.Pkcs7
    }).toString()
    headers["cipher"] = cipher
    return headers
}

/**
 * 解密返回的数据
 */
decrypt = function (data, E){
    
    

    // 使用ECB模式和PKCS7填充加密请求数据
    a = loader(398)

    let n = a.decrypt(data, E, {
    
    
        mode: crypto_js.mode.ECB,
        padding: crypto_js.pad.Pkcs7
    })
    return JSON.parse(n.toString(crypto_js.enc.Utf8))
}
data = '' // 密文数据
let res = decrypt(data, {
    
    'sigBytes': 32, 'words': [825700400, 943273273, 825833778, 959526704, 808991029, 959527225, 842608951, 808466489]});

console.log(res);

4. 测试结果

在这里插入图片描述

  好了,到这里又到了跟大家说再见的时候了。创作不易,帮忙点个赞再走吧。你的支持是我创作的动力,希望能带给大家更多优质的文章

猜你喜欢

转载自blog.csdn.net/qiulin_wu/article/details/134831368
今日推荐