前言
最近,当我在知识星球上观看小组聊天时,我发现一些朋友正在讨论与酷音乐有关的问题。以前,一些行星成员还发送了私人消息来询问此情况。 K兄弟一直尽力满足粉丝的需求。本文将在该网站上进行反向研究。这个案例是相对新颖的,不仅满足了粉丝的需求,而且还可以补充并改善了WASM反向案例:
反向目标数据包捕获分析
输入主页,随机输入您的手机号码,单击以发送验证代码,验证代码的弹出窗口将弹出并抓住数据包,并且会有很多响应。经过分析后,有用的接口将被send_mobile和get_verfy_info:
在第一个send_mobile接口之后,SSA代码将在响应协议标题中返回:
同时,此参数将在get_verfy_info接口上携带:
最后,通过此界面返回sessionID,这意味着会话已创建,并且是全球唯一的标识符:
然后,通过yidun单击/滑块验证代码,发现它将通过v4/verify_user_info进行验证:
该界面中有许多参数,还有与WASM相关的单词:
最后,再次调用send_mobile接口,状态:1表示发送成功:
在数据包捕获分析之后,我们需要反向的参数为:中,UUID,签名,参数,PK,SID,EDT。
中和UUID参数的反向分析
单击发送按钮,然后查看发送堆栈并从第一个输入:
然后搜索中间:我发现有一些可疑的地方,在这些地方分开切断,最后在以下地方成功崩溃:
我们输入getKgmid方法,发现它已在cookie中采用kg_mid,并且该值与我们刚刚看到的值相同。如果此参数在cookie中不存在,则将调用以下方法以获取浏览器的指纹信息,并使用MD5算法生成此值:
因此,我们直接使用v_jstools钩住了如何生成cookie参数。挂钩脚本如下:
(function () {
'use strict';
var cookieTemp = '';
Object.defineProperty(document, 'cookie', {
set: function (val) {
if (val.indexOf('kg_mid') != -1) {
debugger;
}
console.log('Hook捕获到cookie设置->', val);
cookieTemp = val;
return val;
},
get: function () {
return cookieTemp;
},
});
})();
清除浏览器高速缓存,刷新并发现向下已成功切断,遵循堆栈并找到生成参数的位置如下:
根据分析,该参数是通过生成UUID然后使用MD5算法生成的。它遵循UUID生成功能,其生成逻辑如下:
Guid: function() {
function S4() {
return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
}
return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
}
在这一点上,已经分析了MID和UUID参数。
签名参数
签名参数与全局搜索签名相同。最后,它在这个地方成功中断了。一代逻辑如下:
用数据参数将PARM参数拼写为parm参数,然后分别调用k.unshift(y)和k.push(y),以在数组的开始和结尾插入固定的字符串,最后将阵列拼接到字符串中通过加入MD5加密生成签名参数。
参数,PK参数
参数是在MID参数下生成的,逻辑如下:
通过将要加密的OBJ对象传递到AES函数中,键和EncryptedStr参数最终返回。我们输入AES函数以查看其生成逻辑:
经过分析后,可以看出t是随机生成的,然后通过指定的处理逻辑将t处理到键和IV中,最后,加密参数与t一起返回,并以下复制:
function AES_Encrypt(data) {
// 将输入数据转换为JSON字符串
const jsonString = JSON.stringify(data);
// 生成随机密钥(Key)
const generateRandomKey = (length) => {
const chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
let randomKey = '';
for (let i = 0; i < length; i++) {
randomKey += chars.charAt(Math.floor(Math.random() * chars.length));
}
return randomKey;
};
// 如果未提供密钥,则生成一个16字符的随机密钥
key = generateRandomKey(16);
// 使用MD5对密钥进行编码,并截取前32字符作为实际密钥
const md5Key = md5_encode(key).substring(0, 32);
// 如果未提供向量(IV),则使用密钥的最后16字符
iv = md5Key.substring(md5Key.length - 16);
// 将密钥和向量转换为CryptoJS的Utf8对象
const cryptoKey = CryptoJS.enc.Utf8.parse(md5Key);
const cryptoIv = CryptoJS.enc.Utf8.parse(iv);
// 使用AES算法进行加密
const encrypted = CryptoJS.AES.encrypt(jsonString, cryptoKey, {
iv: cryptoIv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
// 将加密结果转换为Hex字符串
const encryptedHexStr = CryptoJS.enc.Hex.stringify(CryptoJS.enc.Base64.parse(encrypted.toString()));
return {
key: key,
encryptedStr: encryptedHexStr
};
}
然后继续下降,发现PK是由RSA生成的,主要是对密钥进行加密:
输入RSA.Encrypt函数,并发现它是带有10001的公共密钥索引的RSA加密和类型的nopadding:
每个加密的结果具有一个独特的值。通过引用库或WT-JS重现它后,我们发现最终生成的加密值与其不同。最后,我们拿走了所有代码,并将其放入nodepad:
我发现它是在一个大模块中,我们将其重写为自我执行的功能,并以模块的形式导入它:
const RSA = require('./rsa'); // 引入保存的rsa.js文件
function rsa_encode(word){
word = JSON.stringify(word)
// 使用的公钥
const publicKey = "B1B1EC76A1BBDBF0D18E8CD9A87E53FA3881E2F004C67C9DDA2CA677DBEFA3D61DF8463FE12D84FF4B4699E02C9D41CAB917F5A8FB9E35580C4BDF97763A0420A476295D763EE10174E6F9EBF7DF8A77BA5B20CDA4EE705DEF5BBA3C88567B9656E52C9CD5CD95CA735FF2D25F762B133273EEEB7B4F3EA8B6DA29040F3B67CD";
// 使用 encrypt 函数进行加密
const encryptedMessage = RSA.encrypt(word);
return encryptedMessage
}
最后,结果与网页一致:
SID,EDT参数
求解上述参数后,我们最终来到SID和EDT参数,并使用堆栈和搜索快速找到以下位置:
生成的逻辑如下:
var t = new wasm_bindgen.EData;
e.sid = t.get_sid(),
e.edt = c
我发现我在wasm_bindgen下首先有一个新的edata对象。我们去了Edata查看它,逻辑是:
我们在全球搜索WASM_BINDGEN,以查找分配变量的下一个断点,清除缓存,单击发送以成功地在初始化位置上打破:
然后,我逐步跟进,发现该方法初始化后,WASM文件开始在以下位置加载:
之后,我准备与WebAssembly(WASM)模块进行交互,具体来说,开始为WebAssembly实例配置导入对象。当然,这也是本文中最令人恶心的部分,并且在互动部分进行了许多检测。
原型链的检测:
DOM层相关检测:
WebGL原型链检测:
与WebGL相关的API操作检测:
如果我们仍然像以前一样使用WebAssembly将WASM加载到节点中,则不可避免地会出现错误。因为我们缺乏相应的环境,所以我们需要将所有验证code.js拉到当地,如下所示:
然后,我们仍然使用Mixue的代理框架:最新的Snow King Type__1286参数反向分析,K兄弟K将带您免费饮料〜。
将我们所有的陈词滥调文档,窗户,导航器,画布,位置等挂在代理上。
_instanceOf分析
原始代码如下:
function _instanceof(left, right) {
if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
return !!right[Symbol.hasInstance](left);
} else {
return left instanceof right;
}
}
如果未满足以前的条件(即,正确或没有符号。Hasinstance方法),则使用JavaScript的内置实例操作员执行类型检查。
因此,在补充时,您需要注意原型链的继承关系,例如窗口,导航器,画布等。以下是一些例子:
HTMLCanvasElement = function HTMLCanvasElement (){
}
canvas.__proto__=HTMLCanvasElement.prototype
function WebGLRenderingContext() {}
// 为 WebGLRenderingContext 添加 Symbol.hasInstance
Object.defineProperty(WebGLRenderingContext, Symbol.hasInstance, {
value: function (obj) {
return obj && typeof obj === 'object' && obj.drawingBufferWidth !== undefined && obj.drawingBufferHeight !== undefined;
}
});
补充时,您可以转到检测场所打印相关信息,也可以直接修改RET = true(前提是您必须知道检测点在哪里):
或者,您可以大胆地尝试修改_instanceOf函数。没有影响WASM加载,这不是使其检测返回的一部分的一种方法(当然,您必须知道要检测到什么)。返回不带大脑的真实可能会导致虚假价值或无法使用。
整个代码完成后,我们将其运行并在网页上调用它:
var t = new wasm_bindgen.EData;
console.log(t.get_sid())
console.log(t.get_edt())
发现以下错误:
提示:此模块在我们的WASM下不存在,但是当我们打印WASM时,我们发现WASM确实存在并且负载已完成。经过进一步的分析,我们发现WASM是通过fetch加载的,而获取是一种异步形式,因此,当我们直接称其为单位时,它绝对不会被调用。我们还需要写一个异步呼叫:
setTimeout(() => {
var t = new wasm_bindgen.EData;
console.log(t.get_sid())
console.log(t.get_edt())
setTimeout = function(){}
}, 1000);
最后,我们使用节点导出异步结果:
const express = require('express');
const app = express();
const port = 3000;
// 定义一个接口来获取get_sid和get_edt的值
app.get('/getValues', async (req, res) => {
// 模拟延时并获取值
setTimeout(() => {
try {
var t = new wasm_bindgen.EData();
const get_sid = t.get_sid();
const get_edt = t.get_edt();
res.json({ get_sid, get_edt });
} catch (error) {
res.status(500).json({ error: error.message });
}
}, 1000);
});
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});
有关Yidun的相关说明,您可以参考以前的文章:[验证代码识别列] Tongkill空间推理验证验证代码培训和识别。
结果验证
最终的实施过程如下:
本网站每日更新互联网创业教程,一年会员只需98,全站资源免费下载点击查看会员权益