吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 11453|回复: 75
收起左侧

[Web逆向] 某网站心跳包参数加密的wasm分析

  [复制链接]
漁滒 发表于 2021-8-3 22:21

@TOC

js层动态分析

网站地址:aHR0cHM6Ly9saXZlLmJpbGliaWxpLmNvbS8=

首先打开任意直播,然后进行抓包
1.jpg

这里可以看到,s参数就是加密的,不多说,直接下个XHR断点
2.jpg

这时s参数已经生成,发现有很多的异步,不要紧,一个一个调用堆栈往前找
3.jpg

在找到到这个调用堆栈的时候,这个函数名称引起了我的注意,在这行下一个断点,然后继续让代码执行
4.jpg

断点再次断下时,发现是调用的上面的函数,继续在关键的callbacks.sign处下断点
5.jpg

callbacks.sign来到这里继续在t.sign下断点
6.jpg

来到这里进行单步调试,可以发现r.spyder就是计算签名的函数,在控制台尝试运行一下,然后继续跟进去

7.jpg

8.jpg

这个名称,很明显就是调用了wasm,抓包中搜索wasm

9.jpg

搜索到有两个wasm,那么就在wasm的文本格式中继续搜索spyder这个关键词,因为这个是导出函数,一定会出现在wasm的文本格式中

10.jpg
其中一个可以搜索到结果,样品地址:aHR0cHM6Ly9pMC5oZHNsYi5jb20vYmZzL2xpdmUvZTc5MTU1NjcwNmY4OGQ4OGI0ODQ2YTYxYTU4M2IzMWRiMDA3ZjgzZC53YXNt

nodejs调用wasm与绕过dom环境检测

这篇文章将不直接分析算法,而是先尝试使用nodejs调用wasm的方式来获取结果

一般的加载顺序是先找到导入函数,然后加载wasm,最后获取导出函数

11.jpg

导入函数直接白给,那么就复制粘贴好了,初步代码如下


const fs = require('fs');
let wasmfilename = 'bilibili2.wasm';

var importObject = {
    env: {
        __cargo_web_snippet_0d39c013e2144171d64e2fac849140a7e54c939a: function (r, t) {
            t = e.STDWEB_PRIVATE.to_js(t),
                e.STDWEB_PRIVATE.from_js(r, t.location)
        },
        __cargo_web_snippet_0f503de1d61309643e0e13a7871406891e3691c9: function (r) {
            e.STDWEB_PRIVATE.from_js(r, window)
        },
        __cargo_web_snippet_10f5aa3985855124ab83b21d4e9f7297eb496508: function (r) {
            return e.STDWEB_PRIVATE.acquire_js_reference(r) instanceof Array | 0
        },
        __cargo_web_snippet_2b0b92aee0d0de6a955f8e5540d7923636d951ae: function (r, t) {
            t = e.STDWEB_PRIVATE.to_js(t),
                e.STDWEB_PRIVATE.from_js(r, function () {
                    try {
                        return {
                            value: t.origin,
                            success: !0
                        }
                    } catch (e) {
                        return {
                            error: e,
                            success: !1
                        }
                    }
                }())
        },
        __cargo_web_snippet_461d4581925d5b0bf583a3b445ed676af8701ca6: function (r, t) {
            t = e.STDWEB_PRIVATE.to_js(t),
                e.STDWEB_PRIVATE.from_js(r, function () {
                    try {
                        return {
                            value: t.host,
                            success: !0
                        }
                    } catch (e) {
                        return {
                            error: e,
                            success: !1
                        }
                    }
                }())
        },
        __cargo_web_snippet_4c895ac2b754e5559c1415b6546d672c58e29da6: function (r, t) {
            t = e.STDWEB_PRIVATE.to_js(t),
                e.STDWEB_PRIVATE.from_js(r, function () {
                    try {
                        return {
                            value: t.protocol,
                            success: !0
                        }
                    } catch (e) {
                        return {
                            error: e,
                            success: !1
                        }
                    }
                }())
        },
        __cargo_web_snippet_614a3dd2adb7e9eac4a0ec6e59d37f87e0521c3b: function (r, t) {
            t = e.STDWEB_PRIVATE.to_js(t),
                e.STDWEB_PRIVATE.from_js(r, t.error)
        },
        __cargo_web_snippet_62ef43cf95b12a9b5cdec1639439c972d6373280: function (r, t) {
            t = e.STDWEB_PRIVATE.to_js(t),
                e.STDWEB_PRIVATE.from_js(r, t.childNodes)
        },
        __cargo_web_snippet_6fcce0aae651e2d748e085ff1f800f87625ff8c8: function (r) {
            e.STDWEB_PRIVATE.from_js(r, document)
        },
        __cargo_web_snippet_7ba9f102925446c90affc984f921f414615e07dd: function (r, t) {
            t = e.STDWEB_PRIVATE.to_js(t),
                e.STDWEB_PRIVATE.from_js(r, t.body)
        },
        __cargo_web_snippet_80d6d56760c65e49b7be8b6b01c1ea861b046bf0: function (r) {
            e.STDWEB_PRIVATE.decrement_refcount(r)
        },
        __cargo_web_snippet_897ff2d0160606ea98961935acb125d1ddbf4688: function (r) {
            var t = e.STDWEB_PRIVATE.acquire_js_reference(r);
            return t instanceof DOMException && "SecurityError" === t.name
        },
        __cargo_web_snippet_8c32019649bb581b1b742eeedfc410e2bedd56a6: function (r, t) {
            var n = e.STDWEB_PRIVATE.acquire_js_reference(r);
            e.STDWEB_PRIVATE.serialize_array(t, n)
        },
        __cargo_web_snippet_a466a2ab96cd77e1a77dcdb39f4f031701c195fc: function (r, t) {
            t = e.STDWEB_PRIVATE.to_js(t),
                e.STDWEB_PRIVATE.from_js(r, function () {
                    try {
                        return {
                            value: t.pathname,
                            success: !0
                        }
                    } catch (e) {
                        return {
                            error: e,
                            success: !1
                        }
                    }
                }())
        },
        __cargo_web_snippet_ab05f53189dacccf2d365ad26daa407d4f7abea9: function (r, t) {
            t = e.STDWEB_PRIVATE.to_js(t),
                e.STDWEB_PRIVATE.from_js(r, t.value)
        },
        __cargo_web_snippet_b06dde4acf09433b5190a4b001259fe5d4abcbc2: function (r, t) {
            t = e.STDWEB_PRIVATE.to_js(t),
                e.STDWEB_PRIVATE.from_js(r, t.success)
        },
        __cargo_web_snippet_b33a39de4ca954888e26fe9caa277138e808eeba: function (r, t) {
            t = e.STDWEB_PRIVATE.to_js(t),
                e.STDWEB_PRIVATE.from_js(r, t.length)
        },
        __cargo_web_snippet_cdf2859151791ce4cad80688b200564fb08a8613: function (r, t) {
            t = e.STDWEB_PRIVATE.to_js(t),
                e.STDWEB_PRIVATE.from_js(r, function () {
                    try {
                        return {
                            value: t.href,
                            success: !0
                        }
                    } catch (e) {
                        return {
                            error: e,
                            success: !1
                        }
                    }
                }())
        },
        __cargo_web_snippet_e8ef87c41ded1c10f8de3c70dea31a053e19747c: function (r, t) {
            t = e.STDWEB_PRIVATE.to_js(t),
                e.STDWEB_PRIVATE.from_js(r, function () {
                    try {
                        return {
                            value: t.hostname,
                            success: !0
                        }
                    } catch (e) {
                        return {
                            error: e,
                            success: !1
                        }
                    }
                }())
        },
        __cargo_web_snippet_e9638d6405ab65f78daf4a5af9c9de14ecf1e2ec: function (r) {
            r = e.STDWEB_PRIVATE.to_js(r),
                e.STDWEB_PRIVATE.unregister_raw_value(r)
        },
        __cargo_web_snippet_ff5103e6cc179d13b4c7a785bdce2708fd559fc0: function (r) {
            e.STDWEB_PRIVATE.tmp = e.STDWEB_PRIVATE.to_js(r)
        },
        __web_on_grow: A
    }
};

var wasmobject = new WebAssembly.Instance(new WebAssembly.Module(new Uint8Array(fs.readFileSync(wasmfilename))), importObject);
console.log(wasmobject);

直接运行发现报错
12.jpg

那就直接给个空函数


const fs = require('fs');
let wasmfilename = 'bilibili2.wasm';

var __fun = function(){};

var importObject = {
    env: {
        __cargo_web_snippet_0d39c013e2144171d64e2fac849140a7e54c939a: function (r, t) {
            t = e.STDWEB_PRIVATE.to_js(t),
                e.STDWEB_PRIVATE.from_js(r, t.location)
        },
        /*省略重复的代码*/
        __web_on_grow: __fun
    }
};

var wasmobject = new WebAssembly.Instance(new WebAssembly.Module(new Uint8Array(fs.readFileSync(wasmfilename))), importObject);
var wasmmemory = wasmobject.exports.memory;
console.log(wasmobject);
console.log(wasmmemory);

这时已经可以加载成功,并且成功获取内存

13.jpg

然后就是根据js的逻辑,来调用wasm即可。不过这个wasm有两个坑,一个是数据类型的处理,另一个是dom环境的检测

第一点:在prepare_any_arg函数里面有个from_js函数
14.jpg
这里可以看到对传进来的数据类型不同,会做不同的处理。例如字符串的类型是4,数值的类型是2或者3,null的类型为1,void 0 的类型为0,布尔值为5或者6,以及其他类型,所以要改写一下js处理数据的逻辑,同时修改倒数函数中的函数名,下面是数据处理的相关函数

var to_js = function (r) {
    var t = (new Uint8Array(wasmmemory.buffer))[r + 12];
    if (t !== 0){
        if(t === 1){
            return null;
        }else if(t === 2){
            return (new Int32Array(wasmmemory.buffer))[r / 4];
        }else if(t === 3){
            return (new Float64Array(wasmmemory.buffer))[r / 8];
        }else if(t === 4){
            var _ = (new Uint32Array(wasmmemory.buffer))[r / 4]
                , n = (new Uint32Array(wasmmemory.buffer))[(r + 4) / 4];
            return to_js_string(_, n)
        }else if(t === 5){
            return !1;
        }else if(t === 6){
            return !0;
        }else if(t === 7){
            console.log('返回数组。未实现')
        }else if(t === 8){
            console.log('返回对象。未实现')
        }else if(t === 9){
            return id_to_ref_map[(new Int32Array(wasmmemory.buffer))[r / 4]]
        }else {
            console.log('返回其他。未实现')
        }
    }

};

var to_js_string = function (r, _) {
    return (new Buffer.from((new Uint8Array(wasmmemory.buffer)).subarray(r, r + _))).toString()
};

var to_utf8_string = function (t, _) {
    var n = new Uint8Array(new Buffer.from(_, 'utf8'));
    var a = n.length;
    var c = wasmobject.exports.__web_malloc(a);
    var R = new Uint8Array(wasmmemory.buffer, c, a);
    R.set(n);
    (new Uint32Array(wasmmemory.buffer))[t / 4] = c;
    (new Uint32Array(wasmmemory.buffer))[(t + 4) / 4] = a;
};

var acquire_rust_reference = function (r) {
    var c = last_refid++;
    id_to_ref_map[c] = r;
    return c
};

var serialize_array = function (r, t) {
    var _ = t.length
        , n = wasmobject.exports.__web_malloc(16 * _);
    (new Uint8Array(wasmmemory.buffer))[r + 12] = 7;
    (new Uint32Array(wasmmemory.buffer))[r / 4] = n;
    (new Uint32Array(wasmmemory.buffer))[(r + 4) / 4] = _;
    for (var a = 0; a < _; ++a)
        from_js(n + 16 * a, t[a])
};

var from_js = function (r, t) {
    var _ = Object.prototype.toString.call(t);
    if ("[object String]" === _){
        (new Uint8Array(wasmmemory.buffer))[r + 12] = 4;
        to_utf8_string(r, t);
    }
    else if ("[object Number]" === _)
        t === (0 | t) ? ((new Uint8Array(wasmmemory.buffer))[r + 12] = 2,
            (new Int32Array(wasmmemory.buffer))[r / 4] = t) : ((new Uint8Array(wasmmemory.buffer))[r + 12] = 3,
            (new Float64Array(wasmmemory.buffer))[r / 8] = t);
    else if (null === t)
        (new Uint8Array(wasmmemory.buffer))[r + 12] = 1;
    else if (void 0 === t)
        (new Uint8Array(wasmmemory.buffer))[r + 12] = 0;
    else if (!1 === t)
        (new Uint8Array(wasmmemory.buffer))[r + 12] = 5;
    else if (!0 === t)
        (new Uint8Array(wasmmemory.buffer))[r + 12] = 6;
    else if ("[object Symbol]" === _) {
        var n = register_raw_value(t);
        e.HEAPU8[r + 12] = 15,
            e.HEAP32[r / 4] = n
    } else {
        var a = acquire_rust_reference(t);
        (new Uint8Array(wasmmemory.buffer))[r + 12] = 9;
        (new Int32Array(wasmmemory.buffer))[r / 4] = a;
    }
};

var prepare_any_arg = function(r){
    var t = wasmobject.exports.__web_malloc(16);
    from_js(t, r);
    return t
};

第二点:然后设置参数尝试进行调用

var arge1 = '{"id":"[3,163,13,5887574]","device":"[\\"AUTO1716277899447614\\",\\"3fc2a589-9cb7-42bf-85c9-c2af590c2fad\\"]","ets":1627808068,"benchmark":"seacasdgyijfhofiuxoannn","time":60,"ts":1627808222759,"ua":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4580.0 Safari/537.36"}';
var arge2 = new Array();
arge2.push(2);
arge2.push(5);
arge2.push(1);
arge2.push(4);

var wasmr = prepare_any_arg(arge1);
var wasmt = prepare_any_arg(arge2);
console.log(wasmr);
console.log(wasmt);
wasmobject.exports.spyder.apply(null, [wasmr, wasmt]);

15.jpg
此时可以发现其对bom和dom都有检测,这个问题不大,简单的补头就可以解决,报错什么就补什么

window = {
    location: {
        host: "live.bilibili.com",
        hostname: "live.bilibili.com",
        href: "https://live.bilibili.com/",
        origin: "https://live.bilibili.com",
        pathname: "/blanc/",
        protocol: "https:"
    }
};

document = {
    body: {
        childNodes: [0]
    }
};

加上上面的代码,就可以绕过检测,最终的完整代码


const fs = require('fs');
let wasmfilename = 'bilibili2.wasm';

window = {
    location: {
        host: "live.bilibili.com",
        hostname: "live.bilibili.com",
        href: "https://live.bilibili.com/",
        origin: "https://live.bilibili.com",
        pathname: "/blanc/",
        protocol: "https:"
    }
};

document = {
    body: {
        childNodes: [0]
    }
};

var tmp;
var id_to_ref_map = {};
var last_refid = 1;

var __fun = function(){};

var importObject = {
    env: {
        __cargo_web_snippet_0d39c013e2144171d64e2fac849140a7e54c939a: function(r, t) {
            t = to_js(t);
            from_js(r, t.location)
        },
        __cargo_web_snippet_0f503de1d61309643e0e13a7871406891e3691c9: function(r) {
            from_js(r, window)
        },
        __cargo_web_snippet_10f5aa3985855124ab83b21d4e9f7297eb496508: function(r) {
            return id_to_ref_map[r] instanceof Array | 0;
        },
        __cargo_web_snippet_2b0b92aee0d0de6a955f8e5540d7923636d951ae: function(r, t) {
            t = to_js(t),
                from_js(r, function() {
                    try {
                        return {
                            value: t.origin,
                            success: !0
                        }
                    } catch (e) {
                        return {
                            error: e,
                            success: !1
                        }
                    }
                }())
        },
        __cargo_web_snippet_461d4581925d5b0bf583a3b445ed676af8701ca6: function(r, t) {
            t = to_js(t),
                from_js(r, function() {
                    try {
                        return {
                            value: t.host,
                            success: !0
                        }
                    } catch (e) {
                        return {
                            error: e,
                            success: !1
                        }
                    }
                }())
        },
        __cargo_web_snippet_4c895ac2b754e5559c1415b6546d672c58e29da6: function(r, t) {
            t = to_js(t),
                from_js(r, function() {
                    try {
                        return {
                            value: t.protocol,
                            success: !0
                        }
                    } catch (e) {
                        return {
                            error: e,
                            success: !1
                        }
                    }
                }())
        },
        __cargo_web_snippet_614a3dd2adb7e9eac4a0ec6e59d37f87e0521c3b: __fun,
        __cargo_web_snippet_62ef43cf95b12a9b5cdec1639439c972d6373280: function(r, t) {
            t = to_js(t),
                from_js(r, t.childNodes)
        },
        __cargo_web_snippet_6fcce0aae651e2d748e085ff1f800f87625ff8c8: function(r) {
            from_js(r, document)
        },
        __cargo_web_snippet_7ba9f102925446c90affc984f921f414615e07dd: function(r, t) {
            t = to_js(t),
                from_js(r, t.body)
        },
        __cargo_web_snippet_80d6d56760c65e49b7be8b6b01c1ea861b046bf0: __fun,
        __cargo_web_snippet_897ff2d0160606ea98961935acb125d1ddbf4688: __fun,
        __cargo_web_snippet_8c32019649bb581b1b742eeedfc410e2bedd56a6: function(r, t) {
            var _ = id_to_ref_map[r];
            serialize_array(t, _);
        },
        __cargo_web_snippet_a466a2ab96cd77e1a77dcdb39f4f031701c195fc: function(r, t) {
            t = to_js(t),
                from_js(r, function() {
                    try {
                        return {
                            value: t.pathname,
                            success: !0
                        }
                    } catch (e) {
                        return {
                            error: e,
                            success: !1
                        }
                    }
                }())
        },
        __cargo_web_snippet_ab05f53189dacccf2d365ad26daa407d4f7abea9: function(r, t) {
            t = to_js(t),
                from_js(r, t.value)
        },
        __cargo_web_snippet_b06dde4acf09433b5190a4b001259fe5d4abcbc2: function(r, t) {
            t = to_js(t),
                from_js(r, t.success)
        },
        __cargo_web_snippet_b33a39de4ca954888e26fe9caa277138e808eeba: function(r, t) {
            t = to_js(t),
                from_js(r, t.length)
        },
        __cargo_web_snippet_cdf2859151791ce4cad80688b200564fb08a8613: function(r, t) {
            t = to_js(t),
                from_js(r, function() {
                    try {
                        return {
                            value: t.href,
                            success: !0
                        }
                    } catch (e) {
                        return {
                            error: e,
                            success: !1
                        }
                    }
                }())
        },
        __cargo_web_snippet_e8ef87c41ded1c10f8de3c70dea31a053e19747c: function(r, t) {
            t = to_js(t),
                from_js(r, function() {
                    try {
                        return {
                            value: t.hostname,
                            success: !0
                        }
                    } catch (e) {
                        return {
                            error: e,
                            success: !1
                        }
                    }
                }())
        },
        __cargo_web_snippet_e9638d6405ab65f78daf4a5af9c9de14ecf1e2ec: __fun,
        __cargo_web_snippet_ff5103e6cc179d13b4c7a785bdce2708fd559fc0: function(r) {
            tmp = to_js(r)
        },
        __web_on_grow: __fun
    }
};

var wasmobject = new WebAssembly.Instance(new WebAssembly.Module(new Uint8Array(fs.readFileSync(wasmfilename))), importObject);
var wasmmemory = wasmobject.exports.memory;

var to_js = function (r) {
    var t = (new Uint8Array(wasmmemory.buffer))[r + 12];
    if (t !== 0){
        if(t === 1){
            return null;
        }else if(t === 2){
            return (new Int32Array(wasmmemory.buffer))[r / 4];
        }else if(t === 3){
            return (new Float64Array(wasmmemory.buffer))[r / 8];
        }else if(t === 4){
            var _ = (new Uint32Array(wasmmemory.buffer))[r / 4]
                , n = (new Uint32Array(wasmmemory.buffer))[(r + 4) / 4];
            return to_js_string(_, n)
        }else if(t === 5){
            return !1;
        }else if(t === 6){
            return !0;
        }else if(t === 7){
            console.log('返回数组。未实现')
        }else if(t === 8){
            console.log('返回对象。未实现')
        }else if(t === 9){
            return id_to_ref_map[(new Int32Array(wasmmemory.buffer))[r / 4]]
        }else {
            console.log('返回其他。未实现')
        }
    }

};

var to_js_string = function (r, _) {
    return (new Buffer.from((new Uint8Array(wasmmemory.buffer)).subarray(r, r + _))).toString()
};

var to_utf8_string = function (t, _) {
    var n = new Uint8Array(new Buffer.from(_, 'utf8'));
    var a = n.length;
    var c = wasmobject.exports.__web_malloc(a);
    var R = new Uint8Array(wasmmemory.buffer, c, a);
    R.set(n);
    (new Uint32Array(wasmmemory.buffer))[t / 4] = c;
    (new Uint32Array(wasmmemory.buffer))[(t + 4) / 4] = a;
};

var acquire_rust_reference = function (r) {
    var c = last_refid++;
    id_to_ref_map[c] = r;
    return c
};

var serialize_array = function (r, t) {
    var _ = t.length
        , n = wasmobject.exports.__web_malloc(16 * _);
    (new Uint8Array(wasmmemory.buffer))[r + 12] = 7;
    (new Uint32Array(wasmmemory.buffer))[r / 4] = n;
    (new Uint32Array(wasmmemory.buffer))[(r + 4) / 4] = _;
    for (var a = 0; a < _; ++a)
        from_js(n + 16 * a, t[a])
};

var from_js = function (r, t) {
    var _ = Object.prototype.toString.call(t);
    if ("[object String]" === _){
        (new Uint8Array(wasmmemory.buffer))[r + 12] = 4;
        to_utf8_string(r, t);
    }
    else if ("[object Number]" === _)
        t === (0 | t) ? ((new Uint8Array(wasmmemory.buffer))[r + 12] = 2,
            (new Int32Array(wasmmemory.buffer))[r / 4] = t) : ((new Uint8Array(wasmmemory.buffer))[r + 12] = 3,
            (new Float64Array(wasmmemory.buffer))[r / 8] = t);
    else if (null === t)
        (new Uint8Array(wasmmemory.buffer))[r + 12] = 1;
    else if (void 0 === t)
        (new Uint8Array(wasmmemory.buffer))[r + 12] = 0;
    else if (!1 === t)
        (new Uint8Array(wasmmemory.buffer))[r + 12] = 5;
    else if (!0 === t)
        (new Uint8Array(wasmmemory.buffer))[r + 12] = 6;
    else if ("[object Symbol]" === _) {
        var n = register_raw_value(t);
        e.HEAPU8[r + 12] = 15,
            e.HEAP32[r / 4] = n
    } else {
        var a = acquire_rust_reference(t);
        (new Uint8Array(wasmmemory.buffer))[r + 12] = 9;
        (new Int32Array(wasmmemory.buffer))[r / 4] = a;
    }
};

var prepare_any_arg = function(r){
    var t = wasmobject.exports.__web_malloc(16);
    from_js(t, r);
    return t
};

var arge1 = '{"id":"[3,163,13,5887574]","device":"[\\"AUTO1716277899447614\\",\\"3fc2a589-9cb7-42bf-85c9-c2af590c2fad\\"]","ets":1627808068,"benchmark":"seacasdgyijfhofiuxoannn","time":60,"ts":1627808222759,"ua":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4580.0 Safari/537.36"}';
var arge2 = new Array();
arge2.push(2);
arge2.push(5);
arge2.push(1);
arge2.push(4);

var wasmr = prepare_any_arg(arge1);
var wasmt = prepare_any_arg(arge2);

wasmobject.exports.spyder.apply(null, [wasmr, wasmt]);

console.log(tmp);

16.jpg
这时已经可以得到结果,经测试与浏览器计算结果一致

IDA分析与python算法还原

关于wasm转o文件的操作在本篇就忽略了,详细内容参考上一篇:https://www.52pojie.cn/thread-1461335-1-1.html

17.jpg
IDA打开处理好的o文件,找到spyder方法,进入伪代码,函数的开头就出现了熟悉的case,没错,这个就是在js层设置的各种数据类型对应的数字。但是这个不太要管,根据前面nodejs调用wasm可知,前面会有一大段检测环境的代码,我们应该跳过这段检测的代码后再分析,进入图标概括,如下图

18.jpg
19.jpg
点击这一个块,然后再f5
20.jpg
21.jpg

一进去答案就出来了,连函数名都没有混淆,简直是白给
因为v160是小于等于5的,所以可以得出如下结论

数值 算法
0 hmac_md5
1 hmac_sha1
2 hmac_sha256
3 hmac_sha224
4 hmac_sha512
5 hmac_sha384

既然都是hmac的算法,那么只要得到密钥的需要摘要的内容,那么就可以直接用python的库完成签名

动态调试我是用的是Google Chrome 94.0.4596.0(正式版本)canary (64 位)

从spyder函数的第二个参数可知,首先计算的哈希函数是hmac_sha256,先简单说一下hmac的算法,根据文章HMAC算法原理
22.png

hmac的算法实际上是计算了两次原本的哈希函数

1.首先将key填充至64字节,然后与i_pad进行异或,得到i key pad
2.还是将key填充至64字节,然后与o_pad进行异或,得到o key pad
3.将i key pad放在签名内容的前面,然后计算一次sha,得到hash sum 1
4.将步骤2中的o key pad放在步骤3的hash sum 1前面,再计算一次sha,得到hash sum 2

这时的hash sum 2就是最终hmac的结果,进入IDA中的hmac_sha256函数看一看
23.jpg
24.jpg

25.jpg

可以看到创建了两个sha256函数,进行了4次input,两次digest,与上面的算法是一致的,那么只需要直到input的内容,那么就可以还原算法了,在wasm中的hmac_sha256下一个断点,断下后,在函数内所有的input下一个断点
26.jpg
断下来后右下角的小按钮很有用,可以直接查看内存视图
根据OpenSSL的源码https://github.com/openssl/openssl/blob/master/crypto/hmac/hmac.c中的函数定义
27.jpg

第一个参数是结构体,第二个参数是内容,第三个参数是内容的长度
28.jpg
直接跳转到地址,可以看到这个就是64字节长度的i key pad,全部与0x36进行异或,就可以得到密钥为【seacasdgyijfhofiuxoannn】,实际就是参数的benchmark的值,继续执行
29.jpg
这时可以看到o key pad,继续往下
30.jpg
这时就可以看到实际签名的内容了,长度是199字节,是一段json的字符串,尝试使用python还原

    def hmac_sha256(key, data):
        key = bytearray(key.encode())
        while len(key) < 64:
            key.append(0)
        i_key_pad = bytes([i ^ 54 for i in key])
        o_key_pad = bytes([i ^ 92 for i in key])
        sha256s = hashlib.sha256()
        sha256s.update(i_key_pad)
        sha256s.update(data.encode())
        hash_sum_1 = sha256s.digest()
        sha256s = hashlib.sha256()
        sha256s.update(o_key_pad)
        sha256s.update(hash_sum_1)
        return sha256s.hexdigest()

    data = {"platform":"web","parent_id":3,"area_id":163,"seq_id":13,"room_id":5887574,"buvid":"AUTO1716277899447614","uuid":"3fc2a589-9cb7-42bf-85c9-c2af590c2fad","ets":1627808068,"time":60,"ts":1627808222759}
    data = json.dumps(data, separators=(',', ':'))
    benchmark = 'seacasdgyijfhofiuxoannn'
    print(hmac_sha256(benchmark, data))
    print(HMAC.new(benchmark.encode(), data.encode(), SHA256).hexdigest())

运行发现,两个结果是一致的,都是【938e55c730b57bc3972ae88c1a45db62040a12d63e40d66d0ab8cbb09c121197】,如此类推,其他hmac函数都可以这样获取密钥和内容,最后可以得出签名的算法

    data = {"platform":"web","parent_id":3,"area_id":163,"seq_id":13,"room_id":5887574,"buvid":"AUTO1716277899447614","uuid":"3fc2a589-9cb7-42bf-85c9-c2af590c2fad","ets":1627808068,"time":60,"ts":1627808222759}
    data = json.dumps(data, separators=(',', ':'))
    benchmark = 'seacasdgyijfhofiuxoannn'
    data = HMAC.new(benchmark, data.encode(), SHA256).hexdigest()
    data = HMAC.new(benchmark, data.encode(), SHA384).hexdigest()
    data = HMAC.new(benchmark, data.encode(), SHA1).hexdigest()
    s = HMAC.new(benchmark, data.encode(), SHA512).hexdigest()
    print(s)

后面就没有做请求测试了。理论上是没有问题的了,完结

免费评分

参与人数 64吾爱币 +74 热心值 +60 收起 理由
lylwahao + 1 + 1 用心讨论,共获提升!
yuchunlnog + 1 + 1 我很赞同!
Laojiuguan + 1 + 1 我很赞同!
wanleya + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
哇卡s + 1 + 1 热心回复!
daofeng1998 + 1 + 1 谢谢@Thanks!
rocky519 + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
xbxbxbxb + 1 + 1 热心回复!
a3586597 + 1 鼓励转贴优秀软件安全工具和文档!
nshark + 1 + 1 鼓励转贴优秀软件安全工具和文档!
KylinYang + 1 + 1 我很赞同!
xmmyzht + 1 + 1 谢谢@Thanks!
xifanu + 1 + 1 谢谢@Thanks!
Voccoo + 1 + 1 我很赞同!
By阿清 + 1 + 1 我很赞同!
蘑菇君丶 + 1 + 1 我很赞同!
gaosld + 1 + 1 热心回复!
godylf + 1 + 1 我很赞同!
5ud0 + 1 + 1 我很赞同!
AG110 + 1 &lt;font style=&quot;vertical-align: inherit;&quot;&gt;&lt;font style=
520xYZ + 1 + 1 谢谢@Thanks!
ruineng + 1 + 1 用心讨论,共获提升!
HongHu106 + 1 + 1 热心回复!
hy8051hy + 1 我很赞同!
lingyinGR + 1 + 1 我很赞同!
by、怒神 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
sH4N3 + 1 + 1 用心讨论,共获提升!
naozitt + 1 谢谢@Thanks!
fengbolee + 2 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
独行风云 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
zhan + 1 + 1 谢谢@Thanks!
DancingLight + 1 + 1 谢谢@Thanks!
jk2077 + 1 + 1 谢谢@Thanks!
_小白 + 1 + 1 我很赞同!
shakespearexzh + 1 谢谢@Thanks!
dbgger + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
10Bang + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
努力加载中 + 1 + 1 用心讨论,共获提升!
helian147 + 2 + 1 用心讨论,共获提升!
null123 + 1 用心讨论,共获提升!
红烧排骨 + 1 + 1 热心回复!
zhczf + 1 + 1 我很赞同!
404undefined + 1 + 1 我很赞同!
Pad0y + 1 + 1 我很赞同!
aileki + 1 + 1 谢谢@Thanks!
tsyapj + 1 + 1 谢谢@Thanks!
goMoney + 1 + 1 好牛啊,大佬
maiming123 + 1 + 1 谢谢@Thanks!
guyuchao3 + 1 + 1 热心回复!
lz52zr + 1 + 1 谢谢@Thanks!
笔墨纸砚 + 3 + 1 用心讨论,共获提升!
细水流长 + 3 + 1 热心回复!
yixi + 1 + 1 谢谢@Thanks!
chinawolf2000 + 1 + 1 热心回复!
howsk + 2 + 1 用心讨论,共获提升!
夜泉 + 1 + 1 很强大,看完了,不错~
hui-shao + 1 + 1 用心讨论,共获提升!
ZHHua + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
HOOK_XYS + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
ofo + 3 太长了,有时间再看
cjcmxc + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
wakichie + 3 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
逍遥一仙 + 3 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
踮起脚尖过日子 + 1 + 1 谢谢@Thanks!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

howsk 发表于 2021-8-3 23:51
漁滒 发表于 2021-8-3 23:41
对的,查找参数来源或者有特殊意义的函数,例如文章中讲到的sign函数,看到的时候就应该多注意

嗯,那个看明白了,主要看一些比较敏感的函数。
我自己有时候也是看到了敏感的函数,问题不大就直接下断点来看,再一个一个排除。
现在就是麻烦的如果js的代码太多了,然后一个一个筛选去定位就太麻烦了,一下字来个几万行几十万行,那真的是体力活。
 楼主| 漁滒 发表于 2021-8-3 23:41
howsk 发表于 2021-8-3 23:39
XHR断点下的是URL包含,包含的是一个request的URL,然后断下来了之后再一点一点往上翻吗?

对的,查找参数来源或者有特殊意义的函数,例如文章中讲到的sign函数,看到的时候就应该多注意
hui-shao 发表于 2021-8-3 23:15
howsk 发表于 2021-8-3 23:20
请教楼主,刚开始的s是加密的数据,请问如何快速定位下断点的位置呀。
 楼主| 漁滒 发表于 2021-8-3 23:30
howsk 发表于 2021-8-3 23:20
请教楼主,刚开始的s是加密的数据,请问如何快速定位下断点的位置呀。

下XHR断点,在函数调用堆栈中往回找
howsk 发表于 2021-8-3 23:39
漁滒 发表于 2021-8-3 23:30
下XHR断点,在函数调用堆栈中往回找

XHR断点下的是URL包含,包含的是一个request的URL,然后断下来了之后再一点一点往上翻吗?
tbloy 发表于 2021-8-4 01:26
看起来不错,支持
famdo818 发表于 2021-8-4 02:42
感谢分享,论坛有你真好
lijisheng 发表于 2021-8-4 06:07
厉害,值得的学习
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-1 06:17

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表