某网站心跳包参数加密的wasm分析
@(某网站心跳包参数加密的wasm分析)## js层动态分析
网站地址:aHR0cHM6Ly9saXZlLmJpbGliaWxpLmNvbS8=
首先打开任意直播,然后进行抓包
这里可以看到,s参数就是加密的,不多说,直接下个XHR断点
这时s参数已经生成,发现有很多的异步,不要紧,一个一个调用堆栈往前找
在找到到这个调用堆栈的时候,这个函数名称引起了我的注意,在这行下一个断点,然后继续让代码执行
断点再次断下时,发现是调用的上面的函数,继续在关键的callbacks.sign处下断点
callbacks.sign来到这里继续在t.sign下断点
来到这里进行单步调试,可以发现r.spyder就是计算签名的函数,在控制台尝试运行一下,然后继续跟进去
这个名称,很明显就是调用了wasm,抓包中搜索wasm
搜索到有两个wasm,那么就在wasm的文本格式中继续搜索spyder这个关键词,因为这个是导出函数,一定会出现在wasm的文本格式中
其中一个可以搜索到结果,样品地址:aHR0cHM6Ly9pMC5oZHNsYi5jb20vYmZzL2xpdmUvZTc5MTU1NjcwNmY4OGQ4OGI0ODQ2YTYxYTU4M2IzMWRiMDA3ZjgzZC53YXNt
## nodejs调用wasm与绕过dom环境检测
这篇文章将不直接分析算法,而是先尝试使用nodejs调用wasm的方式来获取结果
一般的加载顺序是先找到导入函数,然后加载wasm,最后获取导出函数
导入函数直接白给,那么就复制粘贴好了,初步代码如下
```javascript
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);
```
直接运行发现报错
那就直接给个空函数
```javascript
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);
```
这时已经可以加载成功,并且成功获取内存
然后就是根据js的逻辑,来调用wasm即可。不过这个wasm有两个坑,一个是数据类型的处理,另一个是dom环境的检测
第一点:在prepare_any_arg函数里面有个from_js函数
这里可以看到对传进来的数据类型不同,会做不同的处理。例如字符串的类型是4,数值的类型是2或者3,null的类型为1,void 0 的类型为0,布尔值为5或者6,以及其他类型,所以要改写一下js处理数据的逻辑,同时修改倒数函数中的函数名,下面是数据处理的相关函数
```javascript
var to_js = function (r) {
var t = (new Uint8Array(wasmmemory.buffer));
if (t !== 0){
if(t === 1){
return null;
}else if(t === 2){
return (new Int32Array(wasmmemory.buffer));
}else if(t === 3){
return (new Float64Array(wasmmemory.buffer));
}else if(t === 4){
var _ = (new Uint32Array(wasmmemory.buffer))
, 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))]
}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)) = c;
(new Uint32Array(wasmmemory.buffer))[(t + 4) / 4] = a;
};
var acquire_rust_reference = function (r) {
var c = last_refid++;
id_to_ref_map = r;
return c
};
var serialize_array = function (r, t) {
var _ = t.length
, n = wasmobject.exports.__web_malloc(16 * _);
(new Uint8Array(wasmmemory.buffer)) = 7;
(new Uint32Array(wasmmemory.buffer)) = n;
(new Uint32Array(wasmmemory.buffer))[(r + 4) / 4] = _;
for (var a = 0; a < _; ++a)
from_js(n + 16 * a, t)
};
var from_js = function (r, t) {
var _ = Object.prototype.toString.call(t);
if ("" === _){
(new Uint8Array(wasmmemory.buffer)) = 4;
to_utf8_string(r, t);
}
else if ("" === _)
t === (0 | t) ? ((new Uint8Array(wasmmemory.buffer)) = 2,
(new Int32Array(wasmmemory.buffer)) = t) : ((new Uint8Array(wasmmemory.buffer)) = 3,
(new Float64Array(wasmmemory.buffer)) = t);
else if (null === t)
(new Uint8Array(wasmmemory.buffer)) = 1;
else if (void 0 === t)
(new Uint8Array(wasmmemory.buffer)) = 0;
else if (!1 === t)
(new Uint8Array(wasmmemory.buffer)) = 5;
else if (!0 === t)
(new Uint8Array(wasmmemory.buffer)) = 6;
else if ("" === _) {
var n = register_raw_value(t);
e.HEAPU8 = 15,
e.HEAP32 = n
} else {
var a = acquire_rust_reference(t);
(new Uint8Array(wasmmemory.buffer)) = 9;
(new Int32Array(wasmmemory.buffer)) = a;
}
};
var prepare_any_arg = function(r){
var t = wasmobject.exports.__web_malloc(16);
from_js(t, r);
return t
};
```
第二点:然后设置参数尝试进行调用
```javascript
var arge1 = '{"id":"","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, );
```
此时可以发现其对bom和dom都有检测,这个问题不大,简单的补头就可以解决,报错什么就补什么
```javascript
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:
}
};
```
加上上面的代码,就可以绕过检测,最终的完整代码
```javascript
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:
}
};
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 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;
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));
if (t !== 0){
if(t === 1){
return null;
}else if(t === 2){
return (new Int32Array(wasmmemory.buffer));
}else if(t === 3){
return (new Float64Array(wasmmemory.buffer));
}else if(t === 4){
var _ = (new Uint32Array(wasmmemory.buffer))
, 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))]
}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)) = c;
(new Uint32Array(wasmmemory.buffer))[(t + 4) / 4] = a;
};
var acquire_rust_reference = function (r) {
var c = last_refid++;
id_to_ref_map = r;
return c
};
var serialize_array = function (r, t) {
var _ = t.length
, n = wasmobject.exports.__web_malloc(16 * _);
(new Uint8Array(wasmmemory.buffer)) = 7;
(new Uint32Array(wasmmemory.buffer)) = n;
(new Uint32Array(wasmmemory.buffer))[(r + 4) / 4] = _;
for (var a = 0; a < _; ++a)
from_js(n + 16 * a, t)
};
var from_js = function (r, t) {
var _ = Object.prototype.toString.call(t);
if ("" === _){
(new Uint8Array(wasmmemory.buffer)) = 4;
to_utf8_string(r, t);
}
else if ("" === _)
t === (0 | t) ? ((new Uint8Array(wasmmemory.buffer)) = 2,
(new Int32Array(wasmmemory.buffer)) = t) : ((new Uint8Array(wasmmemory.buffer)) = 3,
(new Float64Array(wasmmemory.buffer)) = t);
else if (null === t)
(new Uint8Array(wasmmemory.buffer)) = 1;
else if (void 0 === t)
(new Uint8Array(wasmmemory.buffer)) = 0;
else if (!1 === t)
(new Uint8Array(wasmmemory.buffer)) = 5;
else if (!0 === t)
(new Uint8Array(wasmmemory.buffer)) = 6;
else if ("" === _) {
var n = register_raw_value(t);
e.HEAPU8 = 15,
e.HEAP32 = n
} else {
var a = acquire_rust_reference(t);
(new Uint8Array(wasmmemory.buffer)) = 9;
(new Int32Array(wasmmemory.buffer)) = a;
}
};
var prepare_any_arg = function(r){
var t = wasmobject.exports.__web_malloc(16);
from_js(t, r);
return t
};
var arge1 = '{"id":"","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, );
console.log(tmp);
```
这时已经可以得到结果,经测试与浏览器计算结果一致
## IDA分析与python算法还原
关于wasm转o文件的操作在本篇就忽略了,详细内容参考上一篇:https://www.52pojie.cn/thread-1461335-1-1.html
IDA打开处理好的o文件,找到spyder方法,进入伪代码,函数的开头就出现了熟悉的case,没错,这个就是在js层设置的各种数据类型对应的数字。但是这个不太要管,根据前面nodejs调用wasm可知,前面会有一大段检测环境的代码,我们应该跳过这段检测的代码后再分析,进入图标概括,如下图
点击这一个块,然后再f5
一进去答案就出来了,连函数名都没有混淆,简直是白给
因为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的算法,根据文章(https://www.cnblogs.com/shoshana-kong/p/11497676.html)
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函数看一看
可以看到创建了两个sha256函数,进行了4次input,两次digest,与上面的算法是一致的,那么只需要直到input的内容,那么就可以还原算法了,在wasm中的hmac_sha256下一个断点,断下后,在函数内所有的input下一个断点
断下来后右下角的小按钮很有用,可以直接查看内存视图
根据OpenSSL的源码https://github.com/openssl/openssl/blob/master/crypto/hmac/hmac.c中的函数定义
第一个参数是结构体,第二个参数是内容,第三个参数是内容的长度
直接跳转到地址,可以看到这个就是64字节长度的i key pad,全部与0x36进行异或,就可以得到密钥为【seacasdgyijfhofiuxoannn】,实际就是参数的benchmark的值,继续执行
这时可以看到o key pad,继续往下
这时就可以看到实际签名的内容了,长度是199字节,是一段json的字符串,尝试使用python还原
```python
def hmac_sha256(key, data):
key = bytearray(key.encode())
while len(key) < 64:
key.append(0)
i_key_pad = bytes()
o_key_pad = bytes()
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函数都可以这样获取密钥和内容,最后可以得出签名的算法
```python
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)
```
后面就没有做请求测试了。理论上是没有问题的了,完结 漁滒 发表于 2021-8-3 23:41
对的,查找参数来源或者有特殊意义的函数,例如文章中讲到的sign函数,看到的时候就应该多注意
嗯,那个看明白了,主要看一些比较敏感的函数。
我自己有时候也是看到了敏感的函数,问题不大就直接下断点来看,再一个一个排除。
现在就是麻烦的如果js的代码太多了,然后一个一个筛选去定位就太麻烦了,一下字来个几万行几十万行,那真的是体力活。 howsk 发表于 2021-8-3 23:39
XHR断点下的是URL包含,包含的是一个request的URL,然后断下来了之后再一点一点往上翻吗?
对的,查找参数来源或者有特殊意义的函数,例如文章中讲到的sign函数,看到的时候就应该多注意 好帖!分析详尽! 请教楼主,刚开始的s是加密的数据,请问如何快速定位下断点的位置呀。 howsk 发表于 2021-8-3 23:20
请教楼主,刚开始的s是加密的数据,请问如何快速定位下断点的位置呀。
下XHR断点,在函数调用堆栈中往回找 漁滒 发表于 2021-8-3 23:30
下XHR断点,在函数调用堆栈中往回找
XHR断点下的是URL包含,包含的是一个request的URL,然后断下来了之后再一点一点往上翻吗?{:1_889:} 看起来不错,支持 感谢分享,论坛有你真好 厉害,值得的学习