日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

网易 android 加密,解析网易云音乐的加密方式

發(fā)布時間:2023/12/9 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 网易 android 加密,解析网易云音乐的加密方式 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

找到參數(shù)的加密方法

首先我們先看評論的加載方式,打開一首音樂的主頁,然后打開開發(fā)工具的Network選項,點擊評論的翻頁按鈕,可以看到第一個請求就是請求下一頁的評論:

comment.png

我們分析一下這個請求,先看它的url,請求多次之后發(fā)現(xiàn)R_SO_4_在請求評論時是固定的,483671599則是歌曲的id,url還有一個參數(shù)csrf_token,看這個名字像是防止跨站攻擊的,但是它一直是空的。然后就是POST里面的參數(shù)params和encSecKey,這兩個參數(shù)是關(guān)鍵,接下來我們要重點分析它。

我們在開發(fā)工具對encSecKey進(jìn)行全局搜索,發(fā)現(xiàn)它只出現(xiàn)在一個文件中:

search.png

點擊搜索結(jié)果,打開文件并美化后發(fā)現(xiàn),這2處地方,一個只是簡單對結(jié)果賦值,params通過bAQ8I.encText而來,encSecKey通過bAQ8I.encSecKey而來,而另一個則是有具體函數(shù)調(diào)用,而這個就是我們的突破口。

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

}

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()

}

function c(a, b, c) {

var d, e;

return setMaxDigits(131),

d = new RSAKeyPair(b, "", c),

e = encryptedString(d, a)

}

function d(d, e, f, g) {

var h = {},

i = a(16);

return h.encText = b(d, g),

h.encText = b(h.encText, i),

h.encSecKey = c(i, e, f),

h

}

我們先簡單分析一下這幾個函數(shù),可以看到最后的賦值是在d(d,e,f,g)這個函數(shù)內(nèi)完成的,它首先調(diào)用了a(a),可以看出這個函數(shù)的作用是生成一個長度為16的隨機(jī)字符串;然后encText這個參數(shù)通過2次調(diào)用b(a,b)完成,這個函數(shù)的作用是進(jìn)行AES加密;最后encSecKey是調(diào)用c(i,e,f)完成,這個函數(shù)的作用是進(jìn)行RSA加密。

通過上面的代碼可以看出,params的生成需要d, g,i這3個參數(shù),前2個是函數(shù)傳進(jìn)來的,最后一個是隨機(jī)生成的。而encSecKey的生成則需要e, f,i這3個參數(shù),前2個是函數(shù)傳進(jìn)來的,最后一個和前面相同。

所以理論上我們知道了d,e,f,g這4個參數(shù)就可以構(gòu)造請求了,我們在d函數(shù)加斷點,繼續(xù)點擊下一頁,可以在斷點處調(diào)試,看到傳入的參數(shù):

param.png

試了幾次后我們發(fā)現(xiàn),無論是同一會話的新請求,還是新會話中的請求,e,f,g的值都是不變的,所以可以初步斷定這3個值是固定的,唯一有改變的就是d的值,所以我們只需要在請求時構(gòu)造好就行了。

參數(shù)i的生成

只需要簡單的生成16位隨機(jī)字符串即可

import random

from string import ascii_letters, digits

_charset = ascii_letters + digits

def rand_char(num=16):

return ''.join(random.choice(_charset) for _ in range(num))

params的生成

從代碼可以看出,2次AES加密中,初始向量都是0102030405060708,加密模式都是CBC加密,不同的是第一次加密中,d作為message,g作為key來加密;第二次加密中,把第一次加密結(jié)果作為message,i作為key來加密。我們可以通過Crypto.Cipher中的AES實現(xiàn),

import base64

from Crypto.Cipher import AES

def aes_encrypt(msg, key, iv='0102030405060708'):

def padded(msg):

pad = 16 - len(msg) % 16

return msg + pad * chr(pad)

msg = padded(msg)

cryptor = AES.new(key, IV=iv, mode=AES.MODE_CBC)

text = cryptor.encrypt(msg)

text = base64.b64encode(text)

return text

def gen_params(d, g, i):

text = aes_encrypt(d, g)

text = aes_encrypt(text, i)

return text

encSecKey的生成

這個參數(shù)通過RSA算法生成,其中i作為message,e,f是加密時用到的參數(shù)。

在這里稍微解釋一下RSA算法,算法選取2個很大的質(zhì)數(shù)p,q,得到它們的乘積n,然后選取e,d滿足e*d = 1 mod (p-1)(q-1),加密時text=(msg^e)%n,解密時msg=(text^d)%n,在這個函數(shù)里e就相當(dāng)于算法里的e,f相當(dāng)于算法里的n。

還有一點需要注意,encSecKey是一個完全由16進(jìn)制數(shù)組成,但是在加密模塊中一般都是返回byte流,然后通過base64編碼(長度是原來的4/3),而像這種的應(yīng)該是把byte流通過16進(jìn)制表示出來(長度是原來的2倍)。

下面就是用python實現(xiàn)的時候了,我們可以通過Crypto.PublicKey的RSA的construct方法實現(xiàn)。

# 錯誤版本

import binascii

from Crypto.PublicKey import RSA

cryptor = RSA.construct((0x00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7, 0x10001L))

text = cryptor.encrypt(msg, '')[0]

text = binascii.b2a_hex(text) # byte流轉(zhuǎn)為16進(jìn)制

但是這時候問題出現(xiàn)了,上面的代碼加密出來的結(jié)果和實際不符合,這樣看來網(wǎng)易云的RSA加密和標(biāo)準(zhǔn)的有些不同,所以我們要深入到encryptedString這個方法進(jìn)行調(diào)試。

function encryptedString(a, b) {

for (var f, g, h, i, j, k, l, c = new Array, d = b.length, e = 0; d > e; )

c[e] = b.charCodeAt(e),

e++;

for (; 0 != c.length % a.chunkSize; )

c[e++] = 0;

for (f = c.length,

g = "",

e = 0; f > e; e += a.chunkSize) {

for (j = new BigInt,

h = 0,

i = e; i < e + a.chunkSize; ++h) // here

j.digits[h] = c[i++],

j.digits[h] += c[i++] << 8;

k = a.barrett.powMod(j, a.e),

l = 16 == a.radix ? biToHex(k) : biToString(k, a.radix),

g += l + " "

}

return g.substring(0, g.length - 1)

}

通過代碼可以看出,c數(shù)組是b字符串轉(zhuǎn)成的數(shù)組,然后在for循環(huán)中,c數(shù)組從左到右是從低位加到高位的,比如123456,1是加在低位,6是加在高位,這和平常有些不一樣。

這樣看來似乎需要把要加密的消息先翻轉(zhuǎn)一下,然后再進(jìn)行加密,測試之后發(fā)現(xiàn)也確實如此,實現(xiàn)如下:

import binascii

from Crypto.PublicKey import RSA

def rsa_encrypt(msg):

cryptor = RSA.construct((0x00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7, 0x10001L))

text = cryptor.encrypt(msg[::-1], '')[0]

text = binascii.b2a_hex(text)

return text

事實上,也可以自己來實現(xiàn)它的加密方式text=(msg^e)%n,只是自己實現(xiàn)的方式效率會比較低

def rsa_encrypt2(msg):

msg = binascii.b2a_hex(msg[::-1])

msg = int(msg, 16)

text = 1

for _ in range(0x10001):

text *= msg

text %= 0x00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7

return format(text, 'x')

compare.png

最終實現(xiàn)

import base64

import binascii

import json

import random

import requests

from Crypto.Cipher import AES

from Crypto.PublicKey import RSA

from string import ascii_letters, digits

_charset = ascii_letters + digits

def rand_char(num=16):

return ''.join(random.choice(_charset) for _ in range(num))

def aes_encrypt(msg, key, iv='0102030405060708'):

def padded(msg):

pad = 16 - len(msg) % 16

return msg + pad * chr(pad)

msg = padded(msg)

cryptor = AES.new(key, IV=iv, mode=AES.MODE_CBC)

text = cryptor.encrypt(msg)

text = base64.b64encode(text)

return text

def gen_params(d, i):

text = aes_encrypt(d, '0CoJUm6Qyw8W8jud')

text = aes_encrypt(text, i)

return text

def rsa_encrypt(msg):

cryptor = RSA.construct((0x00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7, 0x10001L))

text = cryptor.encrypt(msg[::-1], '')[0]

text = binascii.b2a_hex(text)

return text

def encrypt(query):

query = json.dumps(query)

rand_i = rand_char(16)

params = gen_params(query, rand_i)

enc_sec_key = rsa_encrypt(rand_i)

data = {

'params': params,

'encSecKey': enc_sec_key

}

return data

if __name__ == '__main__':

music_id = '483671599'

url = 'http://music.163.com/weapi/v1/resource/comments/R_SO_4_{}?csrf_token='.format(music_id)

headers = {

'Accept': '*/*',

'Accept-Encoding': 'gzip, deflate',

'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7',

'Connection': 'keep-alive',

'Content-Type': 'application/x-www-form-urlencoded',

'Host': 'music.163.com',

'Origin': 'http://music.163.com',

'Referer': 'http://music.163.com/',

'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36',

}

query = {

'rid': 'R_SO_4_{}'.format(music_id),

'offset': '0',

'total': 'true', # 第一頁時為true,其他頁為false

'limit': '20',

'csrf_token': ''

}

data = encrypt(query)

r = requests.post(url, data=data, headers=headers)

print(r.content)

for item in r.json()['comments']:

print(item['content'])

result.png

一個套路

通過代碼我們可以看見encSecKey是由i決定的,但是這個參數(shù)是瀏覽器這邊隨機(jī)生成的,所以其實是可以寫死的,這樣一來encSecKey就成了一個固定值,只需要處理params這個參數(shù),當(dāng)然,會不會因為encSecKey總是不變而被封IP什么的我就不知道了

其它

由于RSA是非對稱加密,我們無法通過encSecKey解密出i,沒有i也就無法解密params,所以也就只能對每個接口進(jìn)行斷點調(diào)試,觀察請求的構(gòu)造,這里提供幾個常用接口的參數(shù)

歌曲評論

url:http://music.163.com/weapi/v1/resource/comments/R_SO_4_483671599?csrf_token=

d: {"rid":"R_SO_4_483671599","offset":"20","total":"false","limit":"20","csrf_token":""}

歌曲歌詞

url:http://music.163.com/weapi/song/lyric?csrf_token=

d:{"id":"483671599","lv":-1,"tv":-1,"csrf_token":""}

歌單評論

url:http://music.163.com/weapi/v1/resource/comments/A_PL_0_2003824512?csrf_token=

d:{"rid":"A_PL_0_2003824512","offset":"0","total":"true","limit":"20","csrf_token":""}

搜索

url:http://music.163.com/weapi/cloudsearch/get/web?csrf_token=

搜索單曲:{"hlpretag":"","hlposttag":"","s":"愛","type":"1","offset":"0","total":"true","limit":"30","csrf_token":""}

搜索歌手:{"hlpretag":"","hlposttag":"","s":"愛","type":"100","offset":"0","total":"true","limit":"90","csrf_token":""}

搜索專輯:{"hlpretag":"","hlposttag":"","s":"愛","type":"10","offset":"0","total":"true","limit":"75","csrf_token":""}

搜索MV:{"hlpretag":"","hlposttag":"","s":"愛","type":"1004","offset":"0","total":"true","limit":"20","csrf_token":""}

搜索歌詞:{"hlpretag":"","hlposttag":"","s":"愛","type":"1006","offset":"0","total":"true","limit":"30","csrf_token":""}

搜索歌單:{"hlpretag":"","hlposttag":"","s":"愛","type":"1000","offset":"0","total":"true","limit":"30","csrf_token":""}

搜索主播電臺:{"hlpretag":"","hlposttag":"","s":"愛","type":"1009","offset":"0","total":"true","limit":"30","csrf_token":""}

搜索用戶:{"hlpretag":"","hlposttag":"","s":"愛","type":"1002","offset":"0","total":"true","limit":"30","csrf_token":""}

最后,文章僅供學(xué)習(xí)。

總結(jié)

以上是生活随笔為你收集整理的网易 android 加密,解析网易云音乐的加密方式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。