【JS逆向百例】酷某音乐 wasm 逆向
字数 1085 2025-08-20 18:18:40

酷某音乐WASM逆向分析教学文档

1. 逆向目标分析

目标网站:酷某音乐(aHR0cHM6Ly93d3cua3Vnb3UuY29tLw==)

主要逆向参数:

  • mid
  • uuid
  • signature
  • params
  • pk
  • sid
  • edt

2. 抓包分析流程

  1. 进入首页,输入手机号点击发送验证码
  2. 关键接口分析:
    • send_mobile:首次请求后在响应头返回ssa-code
    • get_verfy_info:携带ssa-code参数,返回全局唯一的sessionid
    • v4/verify_user_info:易盾点选/滑块验证码验证接口
    • 再次调用send_mobilestatus:1表示发送成功

3. 参数逆向详解

3.1 mid和uuid参数

生成逻辑:

  1. 从cookie中获取kg_mid
  2. 若不存在则生成浏览器指纹信息并通过md5算法生成

Hook方法:

(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生成算法:

Guid: function() {
    function S4() {
        return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
    }
    return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
}

3.2 signature参数

生成逻辑:

  1. 将url中的parm参数与data参数拼接
  2. 调用K.unshift(y)K.push(y)在数组开头和结尾插入固定字符串
  3. 通过join拼接成字符串
  4. 进行md5加密生成signature

3.3 params和pk参数

AES加密流程:

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加密:

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
}

3.4 sid和edt参数

生成逻辑:

var t = new wasm_bindgen.EData;
e.sid = t.get_sid(), 
e.edt = c

WASM环境检测点:

  1. 原型链检测
  2. DOM层相关检测
  3. WebGL原型链检测
  4. WebGL相关API操作检测

环境补全要点:

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;
    }
});

异步调用解决方案:

setTimeout(function(){
    var t = new wasm_bindgen.EData;
    console.log(t.get_sid())
    console.log(t.get_edt())
}, 1000);

Node.js接口实现:

const express = require('express');
const app = express();
const port = 3000;

app.get('/getValues', async (req, res) => {
    setTimeout(function(){
        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}`);
});

4. 关键函数解析

_instanceof函数分析

function _instanceof(left, right) {
    if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
        return !!right[Symbol.hasInstance](left);
    } else {
        return left instanceof right;
    }
}

功能说明:

  1. 使用Symbol.hasInstance进行类型检查
  2. 如果rightSymbol.hasInstance方法,则调用并将left传递给right[Symbol.hasInstance]
  3. 使用!!将结果强制转为布尔值
  4. 如果不满足条件,则使用JavaScript内置的instanceof操作符

5. 实现流程总结

  1. 获取miduuid参数
  2. 生成signature参数
  3. 生成paramspk参数
  4. 处理WASM环境并获取sidedt参数
  5. 完成验证码验证流程
  6. 最终调用send_mobile接口发送验证码

6. 注意事项

  1. WASM加载是异步过程,需要正确处理异步调用
  2. 环境检测严格,需要完整补全浏览器环境
  3. 原型链继承关系需要正确处理
  4. 建议使用代理框架处理环境检测问题
  5. 关键检测点可以通过插桩打印或直接修改返回值进行调试
酷某音乐WASM逆向分析教学文档 1. 逆向目标分析 目标网站:酷某音乐(aHR0cHM6Ly93d3cua3Vnb3UuY29tLw==) 主要逆向参数: mid uuid signature params pk sid edt 2. 抓包分析流程 进入首页,输入手机号点击发送验证码 关键接口分析: send_mobile :首次请求后在响应头返回 ssa-code get_verfy_info :携带 ssa-code 参数,返回全局唯一的 sessionid v4/verify_user_info :易盾点选/滑块验证码验证接口 再次调用 send_mobile , status:1 表示发送成功 3. 参数逆向详解 3.1 mid和uuid参数 生成逻辑: 从cookie中获取 kg_mid 若不存在则生成浏览器指纹信息并通过md5算法生成 Hook方法: uuid生成算法: 3.2 signature参数 生成逻辑: 将url中的 parm 参数与 data 参数拼接 调用 K.unshift(y) 和 K.push(y) 在数组开头和结尾插入固定字符串 通过 join 拼接成字符串 进行md5加密生成 signature 3.3 params和pk参数 AES加密流程: pk参数RSA加密: 3.4 sid和edt参数 生成逻辑: WASM环境检测点: 原型链检测 DOM层相关检测 WebGL原型链检测 WebGL相关API操作检测 环境补全要点: 异步调用解决方案: Node.js接口实现: 4. 关键函数解析 _instanceof 函数分析 功能说明: 使用 Symbol.hasInstance 进行类型检查 如果 right 有 Symbol.hasInstance 方法,则调用并将 left 传递给 right[Symbol.hasInstance] 使用 !! 将结果强制转为布尔值 如果不满足条件,则使用JavaScript内置的 instanceof 操作符 5. 实现流程总结 获取 mid 和 uuid 参数 生成 signature 参数 生成 params 和 pk 参数 处理WASM环境并获取 sid 和 edt 参数 完成验证码验证流程 最终调用 send_mobile 接口发送验证码 6. 注意事项 WASM加载是异步过程,需要正确处理异步调用 环境检测严格,需要完整补全浏览器环境 原型链继承关系需要正确处理 建议使用代理框架处理环境检测问题 关键检测点可以通过插桩打印或直接修改返回值进行调试