QQ Music sign value reverse engineering practice: cracking the signature algorithm packaged by Webpack

🎯 Actual goal: capture QQ Music PC TOP list page (https://y.qq.com/n/ryqq/toplist/62)中,接口请求携带的`sign`parameters and restore its generation process. 🧩 Applicable scenarios: front-end applications using modular packaging tools such as Webpack, a general reverse analysis method that hides core logic.

1. Preliminary analysis: looking for encryption entrance

1.1 Network request positioning

Open the Network panel of Chrome DevTools and refresh the list page. Enter in the filter boxmusics.fcgQuickly filter the request and you can see that this interface returns list data. Click on the request details and view the Payload tag. You will find several key parameters:

Parameter nameFunction
signCore encryption value of this reverse engineering
dataJSON string of business parameters
g_tk_newIdentifier related to login status

Obviously,signis a dynamically generated check value, and our goal is to find its generating function and simulate it.

Traditional reverse engineering usually searches globally directly in the Sources panel.sign, or try printing possible global variables in the Console. Try this first:

// 方法1:Chrome DevTools 搜索栏输入 = sign (搜索全局赋值语句)
// 可能返回大量干扰结果

// 方法2:尝试猜测变量名
console.log(window._P); // undefined,说明 _P 没有直接挂在 window 上
console.log(window.ddd); // 返回了一个函数!疑似模块入口

window.dddexists and is a function, but_PBut couldn't find it. This implies that QQ Music uses modular packaging and the core functions are encapsulated internally.

💡 Practical Tips: The difficulty in reversing modern front-end frameworks often lies not in the algorithm itself, but in how to extract the target function from the "black box" packaged by Webpack.

2. General identification and analysis of Webpack architecture

2.1 Quick judgment of Webpack features

QQ Music uses the extremely common Webpack modular packaging. It can be quickly identified through these characteristics:

  1. Open the JS file loaded by the page (usually large in size, hundreds of KB to several MB) and search__webpack_require__or an older version of the keywordwebpackJsonp
  2. Enter in the Consolewindow.webpackJsonp, if it returns an array/function, it can basically be determined to be packaged by Webpack;
  3. If none of the above are found, pay attention to see if there are similarwindow.ddd(0)This custom exposed loading entry.

2.2 Customize the Webpack structure of the exposed entry

QQ Music does not use the defaultwebpackJsonpglobal array, but exposedwindow.dddas a loader. After restoration, its core structure is roughly as follows (this is a simplified version refined during reverse engineering, not the original compressed code):

!function(t) {
    // 模块缓存对象,防止重复加载
    var n = {};

    // 核心 Webpack 模块加载器(对应 QQ音乐 的 window.ddd 函数)
    function r(e) {
        // 如果模块已加载,直接返回导出
        if (n[e]) return n[e].exports;

        // 创建模块对象并存入缓存
        var o = n[e] = {
            i: e,         // 模块 ID
            l: !1,        // 是否已加载标记
            exports: {}   // 模块导出内容
        };

        // 执行模块自身的代码,把导出挂到 o.exports 上
        t[e].call(o.exports, o, o.exports, r);
        o.l = !0;

        // 返回模块最终导出
        return o.exports;
    }

    // 关键一步:将加载器暴露到全局,方便外部调用
    window.ddd = r;
}([
    /* 模块数组:包含了所有压缩后的业务和工具模块 */
    /* 0 */ function(module, exports, r) { /* 初始化环境 */ },
    /* 1 */ function(module, exports, r) { /* 工具函数 */ },
    /* 2 */ function(module, exports, r) { /* 这里可能就包含了 _P 签名函数 */ }
]);

After understanding this structure, we know that as long as the Hook loader is used, we can "peep" what's inside the module when it is exported.

3. Reverse positioning_PSteps to Signature Function

3.1 Find and trigger module loading

Confirm firstwindow.dddactual usage. In the Network panel,musics.fcgHit an XHR breakpoint at the location before launching (Sources > XHR/fetch Breakpoints Add/cgi-bin/musics.fcg), the page will stop before the request is sent after refreshing. Observing the call stack, you can find calls like this:

window.ddd(0); // 必须先加载模块 0,它会初始化内部环境,并可能间接导出 _P

In other words, the entire module system needs to load the module with ID 0 for initialization before other modules can be used.

3.2 Hook loader, tracking exported modules

Now, we write a simple Hook script in the Console to letwindow.dddPrint the log every time the module is loaded, and check whether the export contains what we are thinking about_P

// 1. 先保存原始的加载器
const originalDdd = window.ddd;

// 2. 重写加载器,加上我们的“监控”
window.ddd = function(moduleId) {
    console.log(`正在加载模块 ID: ${moduleId}`);
    const moduleExports = originalDdd.call(this, moduleId);
    // 检查导出对象里是否有 _P
    if (moduleExports && moduleExports._P) {
        console.warn(`✅ 找到 _P 签名函数所在模块!模块 ID: ${moduleId}`);
        console.log('模块导出内容:', moduleExports);
        debugger; // 自动断点,方便进一步分析
    }
    return moduleExports;
};

// 3. 手动触发模块初始化,让加载器跑起来
window.ddd(0);

After running, if the console appears✅ 找到 _P 签名函数所在模块!warning indicates that the positioning is successful. At this point you can directly view the exported objects of the module, which include_Pmethod. Next, you only need to dig out the relevant code and analyze the algorithm logic.

4. Frequently asked questions and solutions

4.1 Incomplete environment completion

when you try to put_PWhen a function is simulated and called in Node.js, there is a high probability of encounteringxxx is not definederror. This is because the module uses global objects in the browser environment (such aswindownavigatordocumentwait).

The solution is in two steps:

  1. Locate missing items: Find the missing browser objects or methods one by one based on the error message;
  2. Construct simulation object: Add the corresponding simulation code at the top of the script. For example:
// 简单的 window 模拟
const window = {
    location: {
        hostname: "y.qq.com",
        protocol: "https:"
    },
    navigator: {
        userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ..."
    },
    // 如果用到了 document,可以这样简陋模拟
    document: {
        createElement: () => ({})
    }
};

💡 Advanced skills: can be usedProxyProxy an empty object and intercept access to unknown properties, so that you can quickly know what properties the code reads and avoid writing a bunch of useless completions.

5. Summary

The core of this reverse engineering practice is not how complex a certain encryption algorithm is, but rather demonstrates a set of general reverse process for Webpack packaging architecture:

  1. Locate the core interface and encryption parameters through the Network panel;
  2. Global search to find the custom module loader (herewindow.ddd);
  3. Hook loader, checks whether the target function is included when the module is exported (_P);
  4. Extract the module code, complete the necessary browser environment, and finally reproduce the signature in any JS environment.

As long as you master this idea, you can handle it calmly whether it is QQ Music or other front-end applications that are also packaged with Webpack and only expose custom loading entries. I wish you success on your reverse journey!