网易某歌曲参数JS逆向分析,多图预警

之前写过一篇网易云的文章,但是一直不过审,这几天搞QQ音乐的爬虫,JS逆向不是很顺利,有点忘了怎么搞了,所以今天打断点重搞一把网易云音乐,毕竟是之前搞过的

找加密函数

网易某歌曲参数JS逆向分析,多图预警
最下面有个url就是歌曲mp3文件的url,也就是目标url
接下来看这个请求url携带的俩个参数(post类型)网易某歌曲参数JS逆向分析,多图预警
记住他们俩的名字,开始整活
网易某歌曲参数JS逆向分析,多图预警
康康发起程序里面是什么,点第三个进去,这个界面网易某歌曲参数JS逆向分析,多图预警
点这个
网易某歌曲参数JS逆向分析,多图预警
然后是这个界面
网易某歌曲参数JS逆向分析,多图预警
这是我已经打了断点的结果,现在解释为什么要在这打断点

打断点

还记得第二个参数吧,ctrl+f搜索
网易某歌曲参数JS逆向分析,多图预警
第一个就是加密函数本身,不信看后面俩个匹配项,
网易某歌曲参数JS逆向分析,多图预警

可以看到 params , encSecKey 都是根据 bwv4z 来的,
网易某歌曲参数JS逆向分析,多图预警
那bwv4z 是什么函数?方法一:
网易某歌曲参数JS逆向分析,多图预警
上面那个core d84链接点进去
方法二:
直接搜索window.asrsea网易某歌曲参数JS逆向分析,多图预警

而bwv4z 是根据 window.asrsea() 函数来的,window.asrsea就是function d(d, e, f, g), 所以在这个 函数打断点
网易某歌曲参数JS逆向分析,多图预警
ctrl+r刷新等待一下
看这个右栏的作用域网易某歌曲参数JS逆向分析,多图预警
这就是函数d需要的几个参数,多刷新几次,发现后面三个参数固定不变,第一个参数我给打印出来"{“csrf_token”:“ccea8e32b28180ce17986067edb5efbf”}"这个是你的账号,判断是否登录
为什么参数和函数都是啊a,b,c,d,就是混淆,也算是反爬虫
按照上面的办法看看函数a,b,c是什么用处

函数a

function a(a) {
        var d, e, b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", c = "";
        for (d = 0; a > d; d += 1)
            e = Math.random() * b.length,
            e = Math.floor(e),
            c += b.charAt(e);
        return c
    }

网易某歌曲参数JS逆向分析,多图预警
看不懂,用python解释一下

import random
def make_random():
    str = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
    random_str = ''
    for i in range(16) :
        index = random.randint(0, len(str) - 1)
        random_str += str[index]
    return random_str

就是生成一个16位随机字符串

函数b

function b(a, b) {
        var c = CryptoJS.enc.Utf8.parse(b)
          , d = CryptoJS.enc.Utf8.parse("0102030405060708")
          , e = CryptoJS.enc.Utf8.parse(a)
          , f = CryptoJS.AES.encrypt(e, c, {
            iv: d,
            mode: CryptoJS.mode.CBC
        });
        return f.toString()
    }

网易某歌曲参数JS逆向分析,多图预警
已经把函数的名字写到明面上了,AES加密CBC模式
网易某歌曲参数JS逆向分析,多图预警

我上一篇文章讲过AES和RSA,自己去看,给个关注就更好了

函数c

网易某歌曲参数JS逆向分析,多图预警
https://editor.csdn.net/md/?articleId=113795115这是我的文章,讲的RSA加密,我觉得挺详细的
RSA加密
高能预警

上爬虫代码

import json,base64,random,requests,re,os
from Crypto.Util.Padding import pad
from Crypto.Cipher import AES
from binascii import b2a_hex, a2b_hex
from  binascii import hexlify
def add_to_16(text):
    # 补全成16的倍数
    if len(text.encode('utf-8')) % 16:
        add = 16 - (len(text.encode('utf-8')) % 16)
    else:
        add = 0
    text = text + ('\0' * add)
    return text.encode('utf-8')

def make_random():
    str = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
    random_str = ''
    for i in range(16) :
        index = random.randint(0, len(str) - 1)
        random_str += str[index]
    return random_str


def AESEncrypt(clear_text, key):
    """
    AES加密, 对应函数b
    :param clear_text: 需要加密的数据
    :return:
    """
    # 数据填充
    clear_text = pad(data_to_pad=clear_text.encode(), block_size=AES.block_size)
    key = key.encode()
    iv = '0102030405060708'
    iv = iv.encode()
    aes = AES.new(key=key, mode=AES.MODE_CBC, iv = iv)
    cipher_text = aes.encrypt(plaintext=clear_text)
    # 字节串转为字符串
    cipher_texts = base64.b64encode(cipher_text).decode()
    return cipher_texts
def make_params_encSecKey(text):
    # song_name = input('要下载歌曲的名字:')
    text = json.dumps(text)
    key = '0CoJUm6Qyw8W8jud'
    # key = key.encode('utf-8')
    frist_aes =AESEncrypt(text,key)
    random_str = make_random()
    encSecKey = make_encSecKey(random_str)
    # print(frist_aes)
    fan_key = frist_aes.encode()
    params = AESEncrypt(frist_aes,random_str)
    # print(params)
    data = {
        'params': params,
        'encSecKey': encSecKey
    }
    return data
    # fanl_text = str(frist_aes,encoding='utf-8')
    # parmas = aes_encrypt(fanl_text,random_str)

def make_encSecKey(text):
        text = text[::-1]
        modulus = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'
        pub_key = '010001'
        rs = pow(int(hexlify(text.encode('utf-8')), 16), int(pub_key, 16), int(modulus, 16))
        return format(rs, 'x').zfill(256)

def print_id_list(data):
    # data = make_params_encSecKey()
    headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
                             'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'}
    id_url = 'https://music.163.com/weapi/cloudsearch/get/web?csrf_token='
    response = requests.post(id_url,data,headers).text
    # print(response.text)
    # names = re.findall(r'"name":"(.*?)"',response)
    # ids = re.findall(r'","id":(.*?),"pst"',response)
    # i = 0
    # for id in ids :
    #     # a = int(id)
    #     # if a>10000 and a<100000 :
    #     print(i,id)
    #     i = i+1
    # print(i)
    ids_list = json.loads(response)['result']['songs']
    count = 0
    info_list = []
    print('只显示前20首歌,如果不够用换个带歌手的关键词搜索')
    print('{:-^80}'.format('-'))
    print('{0:{5}<5}{1:{5}<20}{2:{5}<10}{3:{5}<10}{4:{5}<20}'.format('序号', '歌名', '歌手', '时长(s)', '专辑', chr(12288)))
    print('{:-^84}'.format('-'))
    for id_info in ids_list:
        song_name = id_info['name']
        id = id_info['id']
        time = id_info['dt'] // 1000
        album_name = id_info['al']['name']
        picture_url = id_info['al']['picUrl']
        singer = id_info['ar'][0]['name']
        info_list.append([id, song_name, singer])
        print('{0:{5}<5}{1:{5}<20}{2:{5}<10}{3:{5}<10}{4:{5}<20}'.format(count, song_name, singer, time, album_name,                                                           chr(12288)))
        count += 1
        if count == 20:
            # 为了测试方便, 这里只显示了9条数据
            break
    print('{:-^80}'.format('-'))
    return info_list

if __name__ == '__main__':
    song_name = input("请输入要下载的歌曲,需要买专辑和无版权的不可下载")
    text = {"hlpretag": "<span class=s-fc7>", "hlposttag": "</span>", "s": song_name, "type": "1", "offset": "0",
            "total": "true", "limit": "50", "csrf_token": ""}
    data = make_params_encSecKey(text)
    # print(text)
    ids_list = print_id_list(data)
    while True:
        input_index = eval(input("请输入要下载歌曲的序号(按666退出): "))
        if input_index == 666:
            break
        download_info = ids_list[input_index]
        song_d = {
            "ids": str([download_info[0]]),
            "level": " ",
            "encodeType": "aac",
            "csrf_token": ""
        }
        # print(song_d)
        # 把song_d用AES和RSA加密以后变成params和en写进请求
        headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
                                 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'}
        data = make_params_encSecKey(song_d)
        # print(data)
        song_url = 'https://music.163.com/weapi/song/enhance/player/url/v1?csrf_token='
        song_response = requests.post(song_url,data,headers).text
        # print(song_response)
        inti_download_url = re.findall(r'"url":"(.*?)","', song_response,re.S)
        # print(inti_download_url[0])
        # download_url = re.findall(r'[(.*?)]', inti_download_url, re.S)
        # print(download_url)
        # download_url = 1+inti_download_url
        # print(download_url[0])
        download_location = r'D:'
        # os.mkdir('d:\网易云音乐下载')
        download_song = requests.get(inti_download_url[0],headers)
        name=str(song_name)+'.mp3'
        with open(name, 'wb') as f:
            f.write(download_song.content)
        print("下载成功")

上效果图
网易某歌曲参数JS逆向分析,多图预警
网易某歌曲参数JS逆向分析,多图预警

上一篇:2021-03-24


下一篇:Servlet(知识点+实例项目)