js逆向-喜某拉雅Xm-Sign参数解密

url:www.ximalaya.com/channel/11/

分析过程

  1. 抓包,关注有页面数据回显的数据包。
    该url的请求头中有个加密的参数,找到该参数的加密过程。
  2. 由于该参数名比较不常见,可以直接全局搜索这个参数名。
    只有一处,打断点。
  3. 切换页码,触发断点。
  4. 非常直接,xm-sign是由d.getSign()生成的,直接找到getSign函数的定位。
  5. 在该函数的第一行打断点,让程序运行到该断点处。
    单步调试,看看每个变量的值是什么。
    n = s() ? Date.now() : window.XM_SERVER_CLOCK || 0,由于s()的值恒为false,故n = window.XM_SERVER_CLOCK。但window.XM_SERVER_CLOCK的值和Date.now()的值相差不多,猜测估计是服务器上的时间戳,就先不用管了。

t = this[c("0x13")],c("0x13")的值为secretKey,故t的值为himalaya-

e = n这个就不解释了。
r = Date[c("0x25")]()c("0x25")的值为now,故Date[c("0x25")]()相当于Date.now(),获取时间戳。

这里涉及到了c,其是用于获取s中的某个值。
s的值如下。
那么c("0x")这样形式的就是去获取s中的某一个值。

继续往下看。
("{" + t + e + "}(" + u(100) + ")" + e + "(" + u(100) + ")" + r),这就是一个字符串的拼接,涉及到了u函数,看看u函数是啥。
c("0x28")的值为random,故Math[c("0x28")]()相当于Math.random()获取一个随机值,所以本质上u函数就是生成一个随机数。
[c("0xa")](/{([\w-]+)}/, (function(t, e) {return a(e) }))[c("0xa")]的值是replace,那么就是将前面拼接的字符串中符合/{([\w-]+)}/格式的替换成(function(t, e) {return a(e) }),那么关键点就在a函数上了,找到定义处。
打断点。
单步调试。由于t不为null,故if语句不执行,直接执行r[c("0x3c")](u(t, e))c("0x3c")的值为wordsToBytes
r[c("0x3c")]的值如下。
u函数的定义如下。
这里面的代码不需要完全看懂,最后只需要复制该代码进行调用即可。
return e && e[c("0x2")] ? n : e && e[c("0x2a")] ? s[c("0x18")](n) : r[c("0x21")](n),由于e的值为undefined,故最后返回的是r[c("0x21")](n),该函数就是将字节转换成16进制。
整个加密过程分析完了,只需要将相关代码复制到js中即可。(注意,由于其中很多变量名都是一样的,所以在复制的时候需要对其进行对应的区分,这里很容易出错)

完整的js代码如下:

ini 复制代码
var s = [
    "xmSign", "slice", "asBytes", "object", "readFloatLE", "Illegal argument ", "charCodeAt", "send", "himalaya-", 
    "prototype", "replace", "join", "clockTimer", "_gg", "isBuffer", "initServerTimeUrl", "stringToBytes", "_hh", 
    "INISTAL_TIME", "secretKey", "_ii", "push", "responseText", "GET", "bytesToString", "binary", "indexOf", "length", 
    "toString", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", "endian", "exports", "XM_SERVER_CLOCK", 
    "bytesToHex", "floor", "call", "_blocksize", "now", "CLOCK_UPDATE_INTERVAL", "getSign", "random", "protocol", "asString", 
    "amd", "pow", "https:", "_instance", "_ff", "bytesToWords", "https://www.ximalaya.com/revision/time", "host", 
    "onreadystatechange", "encoding", "isArray", "start", "function", "URL", "charAt", "bin", "constructor", "wordsToBytes", 
    "fromCharCode", "location", "undefined", "readyState", "rotl", "/revision/time", "_isBuffer", "getServerDate", "then", 
    "test", "substr", "updateClock"
];

var c = function(t, e) { 
    return s[t - 0]; 
};

var u = function(t) { 
    return ~~(Math[c("0x28")]() * t); 
};

var r = {
    rotl: function(t, e) {
        return t << e | t >>> 32 - e;
    },
    rotr: function(t, e) {
        return t << 32 - e | t >>> e;
    },
    endian: function(t) {
        if (t[c("0x3b")] === Number)
            return 16711935 & r[c("0x41")](t, 8) | 4278255360 & r[c("0x41")](t, 24);
        for (var e = 0; e < t[c("0x1b")]; e++) {
            t[e] = r.endian(t[e]);
        }
        return t;
    },
    randomBytes: function(t) {
        var e = [];
        for (var i = 0; i < t; i++) {
            e[c("0x15")](Math[c("0x22")](256 * Math[c("0x28")]()));
        }
        return e;
    },
    bytesToWords: function(t) {
        var e = [],
            r = 0,
            n = 0;
        for (r = 0; r < t[c("0x1b")]; r++, n += 8) {
            e[n >>> 5] |= t[r] << 24 - n % 32;
        }
        return e;
    },
    wordsToBytes: function(t) {
        var e = [];
        for (var r = 0; r < 32 * t[c("0x1b")]; r += 8) {
            e[c("0x15")](t[r >>> 5] >>> 24 - r % 32 & 255);
        }
        return e;
    },
    bytesToHex: function(t) {
        var e = [];
        for (var r = 0; r < t[c("0x1b")]; r++) {
            e );
            e );
        }
        return e[c("0xb")]("");
    },
    hexToBytes: function(t) {
        var e = [];
        for (var r = 0; r < t[c("0x1b")]; r += 2) {
            e[c("0x15")](parseInt(t[c("0x47")](r, 2), 16));
        }
        return e;
    },
    bytesToBase64: function(t) {
        var r = [],
            n = 0;
        for (n = 0; n < t[c("0x1b")]; n += 3) {
            for (var i = t[n] << 16 | t[n + 1] << 8 | t[n + 2], o = 0; o < 4; o++) {
                if (8 * n + 6 * o <= 8 * t[c("0x1b")]) {
                    r.push(e[c("0x39")](i >>> 6 * (3 - o) & 63));
                } else {
                    r[c("0x15")]("=");
                }
            }
        }
        return r[c("0xb")]("");
    },
    base64ToBytes: function(t) {
        t = t.replace(/[^A-Z0-9+\/]/gi, "");
        var r = [],
            n = 0,
            i = 0;
        for (n = 0; n < t[c("0x1b")]; i = ++n % 4) {
            if (0 !== i) {
                r.push((e[c("0x1a")](t.charAt(n - 1)) & Math[c("0x2c")](2, -2 * i + 8) - 1) << 2 * i | e[c("0x1a")](t.charAt(n)) >>> 6 - 2 * i);
            }
        }
        return r;
    }
};

var uuu = function(t, e) {
    var nn = {
        utf8: {
            stringToBytes: function(t) {
                return nn[c("0x3a")].stringToBytes(unescape(encodeURIComponent(t)));
            },
            bytesToString: function(t) {
                return decodeURIComponent(escape(nn[c("0x3a")][c("0x18")](t)));
            }
        },
        bin: {
            stringToBytes: function(t) {
                var e = [];
                for (var r = 0; r < t[c("0x1b")]; r++) {
                    e[c("0x15")](255 & t[c("0x6")](r));
                }
                return e;
            },
            bytesToString: function(t) {
                var e = [];
                for (var r = 0; r < t[c("0x1b")]; r++) {
                    e[c("0x15")](String[c("0x3d")](t[r]));
                }
                return e[c("0xb")]("");
            }
        }
    };
    
    var o = nn.utf8;
    t[c("0x3b")] === String ? t = e && e[c("0x34")] === c("0x19") ? s[c("0x10")](t) : o.stringToBytes(t) : a(t) ? t = Array[c("0x9")][c("0x1")][c("0x23")](t, 0) : Array[c("0x35")](t) || (t = t[c("0x1c")]());
    
    var n = r[c("0x30")](t),
        i = 8 * t[c("0x1b")],
        l = 1732584193,
        f = -271733879,
        d = -1732584194,
        h = 271733878,
        p = 0;
    
    for (p = 0; p < n[c("0x1b")]; p++) {
        n[p] = 16711935 & (n[p] << 8 | n[p] >>> 24) | 4278255360 & (n[p] << 24 | n[p] >>> 8);
    }
    
    n[i >>> 5] |= 128 << i % 32;
    n[14 + (i + 64 >>> 9 << 4)] = i;
    
    var g = function(t, e, r, n, i, o, a) {
        var s = t + (e & r | ~e & n) + (i >>> 0) + a;
        return (s << o | s >>> 32 - o) + e;
    };

    // MD5 steps continue...

    return r[c("0x1e")]([l, f, d, h]);
};

function a(t, e) {
    if (null == t)
        throw new Error(c("0x5") + t);
    var n = r[c("0x3c")](uuu(t, e));
    return e && e[c("0x2")] ? n : e && e[c("0x2a")] ? s[c("0x18")](n) : r[c("0x21")](n);
}

function getSign() {
    var t, e, r, n = 0;
    return n = Date.now(),
        t = "himalaya-",
        e = n,
        r = Date[c("0x25")]() + 9876, // Adjusting value as required
        ("{" + t + e + "}(" + u(100) + ")" + e + "(" + u(100) + ")" + r)[c("0xa")](/{([\w-]+)}/, function(t, e) {
            return a(e);
        });
}

测试一下。
得到了我们想要的结果,就可以编写python代码来调用getSign函数获取页面数据了。

ini 复制代码
import requests
import execjs

# 提示用户输入要查询的页面号
page_num = input("请输入您要查询第几页:")

# 构造带有页面号的URL
url = f"https://www.ximalaya.com/revision/metadata/v2/channel/albums?pageNum={page_num}&pageSize=50&sort=1&metadata=&groupId=11"

# 读取JavaScript文件中的代码
with open("解密.js", mode="r", encoding="utf-8") as file_object:
    exec_code = file_object.read()

# 编译JavaScript代码
exec_js = execjs.compile(exec_code)

# 调用JavaScript中的getSign方法获取xmSign值
xm_sign = exec_js.call("getSign")

# 定义请求头
headers = {
    "Xm-Sign": xm_sign,  # 设置签名
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36"
}

# 发送GET请求,获取响应
response = requests.get(url, headers=headers)

# 输出响应的文本内容
print(response.text)

运行,成功获取到数据。

相关推荐
夏天想6 分钟前
vant4+vue3上传一个pdf文件并实现pdf的预览。使用插件pdf.js
开发语言·javascript·pdf·vant
....49211 分钟前
antvX6节点全选后鼠标通过拖拉调整视图的展示位置
javascript·计算机外设·数据中台·antvx6
samuel91841 分钟前
axios取消重复请求
前端·javascript·vue.js
滿1 小时前
Vue 3 中按照某个字段将数组分成多个数组
前端·javascript·vue.js
安分小尧1 小时前
[特殊字符] 使用 Handsontable 构建一个支持 Excel 公式计算的动态表格
前端·javascript·react.js·typescript·excel
好_快1 小时前
Lodash源码阅读-baseClone
前端·javascript·源码阅读
好_快1 小时前
Lodash源码阅读-baseToString
前端·javascript·源码阅读
好_快1 小时前
Lodash源码阅读-initCloneByTag
前端·javascript·源码阅读
好_快1 小时前
Lodash源码阅读-cloneSymbol
前端·javascript·源码阅读
好_快1 小时前
Lodash源码阅读-cloneRegExp
前端·javascript·源码阅读