JS逆向 喜马拉雅 xm_sign

本文详细记录了一位初学者逆向分析JavaScript中喜马拉雅平台的xm_sign参数的过程,包括定位、分析xm-sign的构建,探讨了md5加密和随机数字与时间戳的组合,以及window.XM_SERVER_CLOCK的生成方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:

最近学习js逆向案例,本文仅以初学者思路记录学习,内容比较繁多~

目标声明:仅仅记录一下自己的学习方法,不作为学习参考、更不作为商业用途。如有侵犯请联系本人删除

1.分析需要逆向参数。

xm-sign值为

5bc0af13a02e5d1da409d0d8acb77c32(44)1703550877500(54)1703550877738

初步分析可能是md5+随机数字+时间戳+随机数字+时间戳构成。下面我们进一步分析xmsign值

2.定位、分析xm-sign

2.1定位

该案例直接全局搜索xm-sign即可。

var d = new i.default;
    a.interceptors.request.use((function(e) {
        return e.url.indexOf("/revision") > -1 && (e.headers = f(f({}, e.headers), {}, {
            "xm-sign": d.getSign()
        })),
        e
    }

2.2分析xm-sign构建

在此处打断点,进一步跟踪。

跳转到该代码块。

t.prototype[c("0x27")] = function() {
                    var t, e, r, n = 0;
                    return n = s() ? Date.now() : window.XM_SERVER_CLOCK || 0,
                    t = this[c("0x13")],
                    e = n,
                    r = Date[c("0x25")](),
                    ("{" + t + e + "}(" + u(100) + ")" + e + "(" + u(100) + ")" + r)[c("0xa")](/{([\w-]+)}/, (function(t, e) {
                        return a(e)
                    }
                    ))
                }

//上述代码经过还原后如下:

t.prototype["getSign"] = function() {
                    var t, e, r, n = 0;
                    return n = s() ? Date.now() : window.XM_SERVER_CLOCK || 0,
                    t = this["secretKey"],
                    e = n,
                    r = Date["now"](),
                    ("{" + t + e + "}(" + u(100) + ")" + e + "(" + u(100) + ")" + r)["replace"](/{([\w-]+)}/, (function(t, e) {
                        return a(e)
                    }
                    ))
                }

这里我们分析下这个函数。

很明显n是一个三元运算符。此处加断点后,分析s(),发现结果固定为false。

所以此处可以简化为 return n= window.XM_SERVER_CLOCK

window.XM_SERVER_CLOCK此处已经有值,这里需要注意,最后需要返回此处分析window.XM_SERVER_CLOCK的构建方式。此处暂时不做分析。

继续加断点分析:("{" + t + e + "}(" + u(100) + ")" + e + "(" + u(100) + ")" + r)[c("0xa")](/{([\w-]+)}/, (function(t, e) {
                        return a(e)
                    }
                    ))

此处返回的是xm-sign密文,可以知道密文一定是这一串代码构建的,只要分析这段代码即可。

t和e参数在函数中都已经赋值。

由此可知

t="himalaya-"    //固定值
e=window.XM_SERVER_CLOCK 
r=Date["now"]() //时间戳


//最后该函数优化为
("{" + t + e + "}(" + u(100) + ")" + e + "(" + u(100) + ")" + r)['replace'](/{([\w-]+)}/, (function(t, e) {
                        return a(e)
                    }
                    ))

其中/{([\w-]+)}/为正则表达式,/是转义符,简化后为{([\w-]+)}

即将{" + t + e + "}的值替换为a(e)

通过第一步生成的xm-sign加密参数,我们可以知道

5bc0af13a02e5d1da409d0d8acb77c32(44)1703550877500(54)1703550877738

可能是md5+随机数字+时间戳+随机数字+时间戳构成

即5bc0af13a02e5d1da409d0d8acb77c32猜测可能是md5加密。

通过控制台执行可以知道

e='himalaya-1703556671595'

a(e)='43aab441946e1db6f4656d1a0db29020'

我们找一个md5在线加密网站加密e值测试一下

OK!结果完全一致,可以确定a函数就是进行md5加密。

2.3进一步分析u(100)

通过random我们可以知道这里是返回0~100之间随机值。

在pycharm中创建js文件,并创建一个get_xmsign函数,并进行打印输出:

const crypto = require('crypto');
window=global;

function a(e){
        const md5 = crypto.createHash('md5');
        return md5.update(e).digest('hex')
}
function get_xmsign() {
                    var t, e, r, n = 0;
                    return n =window.XM_SERVER_CLOCK ,
                    t = this["secretKey"],
                    e = n,
                    r = Date["now"](),
                    ("{" + t + e + "}(" + 33 + ")" + e + "(" + 22 + ")" + r)["replace"](/{([\w-]+)}/, (function(t, e) {
                        return a(e)
                    }
                    ))
                }

                console.log(get_xmsign())
//其中a函数里面进行md5加密,网上搜一下crypto的md5加密就有代码。

执行结果为

e21cc36bf0d65014c717a481a3f8a468(74)undefined(20)1703558161144

其中undefined是n值,即window.XM_SERVER_CLOCK。

2.4分析window.XM_SERVER_CLOCK

从头开始分析"xm-sign": d.getSign()

刷新页面,断到此处后,在控制台执行,有返回结果。说明window.XM_SERVER_CLOCK这个值在调用d.getSign()这一步之前就已经生成了。

在d生成的地方加断点试一下,

这时候需要刷新页面,window.XM_SERVER_CLOCK值是在加载页面的时候生成的。

发现this[]中没有我们要的XM_SERVER_CLOCK值, 进一步分析。

 this[c("0x36")]()这里没进行赋值,只调用了函数。断点到此处,鼠标放在这里分析一下

return t[c("0x9")][c("0x36")] = function(t) {
                    var e = this;
                    void 0 === t && (t = !1),
                    s() || window.XM_SERVER_CLOCK && !t || this[c("0x44")]()[c("0x45")]((function(t) {
                        e[c("0x12")] = t,
                        window[c("0x20")] = t,
                        e[c("0x48")]()
                    }
                    ))
                }

window.XM_SERVER_CLOCK = t ,t就是这个时间戳。需要继续分析t是从哪里构建的。

this[c("0x44")]()[c("0x45")]((function(t) {
                        e[c("0x12")] = t,
                        window[c("0x20")] = t,
                        e[c("0x48")]()
                    }
                    ))




//解混淆后::

this["getServerDate"]()["then"]((function(t) {
                        e[c("0x12")] = t,
                        window[c("0x20")] = t,
                        e[c("0x48")]()
                    }
                    ))

//分析得知
异步调用this.getServerDate(),并将返回值作为参数t,调用后面的函数。

所以t=this.getServerDate()

分析this.getServerDate后发现是通过调用网址

获取时间戳,作为参数t传入。

到这里,大功告成!

js代码参考如下:

const crypto = require('crypto');


function a(e){
        const md5 = crypto.createHash('md5');
        return md5.update(e).digest('hex')
}
u = function(t) {
                return ~~(Math["random"]() * t)
            };
function get_xmsign(timestamp) {
                    var t, e, r, n = 0;
                    return n =timestamp ,
                    t = this["secretKey"],
                    e = n,
                    r = Date["now"](),
                    ("{" + t + e + "}(" + u(100) + ")" + e + "(" + u(100) + ")" + r)["replace"](/{([\w-]+)}/, (function(t, e) {
                        return a(e)
                    }
                    ))
                }

                console.log(get_xmsign())

Python代码如下:

import execjs
import requests


def get_timestamp():
    ##获取时间戳
    res = requests.get('https://www.ximalaya.com/revision/time',headers={
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
    }).text
    return res

def get_xm_sign(timestamp):


    js_Code=open('ximalaya.js').read()
    js_compile = execjs.compile(js_Code)

    xm_sign = js_compile.call('get_xmsign',timestamp)
    return xm_sign

def get_info(bookName):
    timestamp = get_timestamp()
    xm_sign=get_xm_sign(timestamp)
    cookies = {
        '_xmLog': 'h5&35b9cf13-8e2c-48eb-9dd4-a6cbe84a8919&process.env.sdkVersion',
        'xm-page-viewid': 'ximalaya-web',
        'impl': 'www.ximalaya.com.login',
        'x_xmly_traffic': 'utm_source%253A%2526utm_medium%253A%2526utm_campaign%253A%2526utm_content%253A%2526utm_term%253A%2526utm_from%253A',
        'wfp': 'ACM4MzFmMjY1NTM5NGY3ZGQ0POuVQ3wgf4B4bXdlYl93d3c',
        'Hm_lvt_4a7d8ec50cfd6af753c4f8aee3425070': '1703550655',
        'Hm_lpvt_4a7d8ec50cfd6af753c4f8aee3425070': '1703567472',
    }

    headers = {
        'Accept': '*/*',
        'Accept-Language': 'zh-CN,zh;q=0.9',
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive',
        'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
        # 'Cookie': '_xmLog=h5&35b9cf13-8e2c-48eb-9dd4-a6cbe84a8919&process.env.sdkVersion; xm-page-viewid=ximalaya-web; impl=www.ximalaya.com.login; x_xmly_traffic=utm_source%253A%2526utm_medium%253A%2526utm_campaign%253A%2526utm_content%253A%2526utm_term%253A%2526utm_from%253A; wfp=ACM4MzFmMjY1NTM5NGY3ZGQ0POuVQ3wgf4B4bXdlYl93d3c; Hm_lvt_4a7d8ec50cfd6af753c4f8aee3425070=1703550655; Hm_lpvt_4a7d8ec50cfd6af753c4f8aee3425070=1703567472',
        'Pragma': 'no-cache',
        'Referer': 'https://www.ximalaya.com/so/%E6%88%91%E7%9A%84%E8%80%81%E5%8D%83%E7%94%9F%E6%B6%AF',
        'Sec-Fetch-Dest': 'empty',
        'Sec-Fetch-Mode': 'cors',
        'Sec-Fetch-Site': 'same-origin',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
        'sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
        'sec-ch-ua-mobile': '?0',
        'sec-ch-ua-platform': '"Windows"',
        'xm-sign': xm_sign,
    }

    params = {
        'core': 'all',
        'kw': bookName,
        'spellchecker': 'true',
        'device': 'iPhone',
        'live': 'true',
    }

    response = requests.get('https://www.ximalaya.com/revision/search/main', params=params, cookies=cookies,
                            headers=headers)
    return response.json()
def download():
    pass


if __name__ == '__main__':

    bookName = input('请输入小说名称:::')
    res = get_info(bookName)
    books=[]
    for i in res['data']['album']['docs']:
        dic={}
        if i['vipType']!=0 or len(i['priceTypes'])>0:#收费和会员不要 #此处写的比较随意,可以不参考
            pass
        else:
            dic['title']=i['title']
            dic['url'] = 'https://www.ximalaya.com'+i['url']
            dic['priceTypes']=i['priceTypes']
            books.append(dic)
    print(books)
    # url = books[0]['url']
    # title=books[0]['title']
    # albumId = books[0]['url'].split('/')[-1]

#后面可以继续扩展,调用download函数,通过名称搜索所有书籍,并进入到书籍明细中,获取每一章节的mp3地址,进行下载。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值