首页 > 其他分享 >网易云JS逆向分析

网易云JS逆向分析

时间:2024-09-18 15:34:45浏览次数:8  
标签:逆向 网易 加密 get 16 JS return data id

网易云JS逆向分析

前言

本文章中所有内容仅供学习交流,严禁用于商业用途和非法用途,否则由此产生的一切后果均与文章作者无关,若有侵权,请联系我立即删除!

阅读本篇文章,需要一定的爬虫基础,和js逆向思维,否则无法继续

首先找到一个歌单

image-20240904214816055

找到这个api接口

image-20240904185750624

api传参为这两个

image-20240904185839399

找到启动器

image-20240904190017126

发现这两个参数和刚刚api接口是一模一样的,那么大概率源码就在这里

image-20240904190134236

打断点,分析加密参数

image-20240904190337254

刷新页面,已经卡在这里了

image-20240904190425077

分析每个api参数

image-20240904190518973

X1x为后端api地址

i1x为csrf_token

e1x为请求方式,该值为post

我们的目标是抓取歌单评论信息,一直放行,直到这个接口

image-20240904190753973

分析这个参数,如下

image-20240904191316338

分析rid:A_PL_0_3865036,这后面的就是歌单id,threadId:A_PL_0_3865036,后面也是歌单id信息,后面的参数基本没用了,页面大小,偏移量和歌单属性

image-20240904191112608

那么就可以得到python代码向后端传的参数为这个

def get_data():
    id=3865036  # 歌单id
    url = "https://music.163.com/weapi/comment/resource/comments/get"
    data = {
        'csrf_token': "",
        "cursor": "-1",  # 0为精彩评论,-1为最新评论
        "offset": "0",
        "orderType": "1",
        "pageNo": "1",
        "pageSize": "20",
        "rid": f"A_PL_0_{id}",
        "threadId": f"A_PL_0_{id}"
    }
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36'
    }
    resp = requests.post(url, data=data, headers=headers)
    print(resp.text)

但是呢没有任何响应结果,说明参数加密了
image-20240904191651991

对比源参数即可看到,这些值都是乱码,显然是进行了加密的

image-20240904185839399

那么就要找js的加密节点,怎么找?回到这个界面

image-20240904192831183

把这段代码复制下来仔细分析

var bVi3x = window.asrsea(JSON.stringify(i1x), bse0x(["流泪", "强"]), bse0x(Qu1x.md), bse0x(["爱心", "女孩", "惊恐", "大笑"]));

分别对应如下值

image-20240904193330296

这里我们就得到了每个参数的值

使用变量进行替换

# 服务于函数d
g = "0CoJUm6Qyw8W8jud"   # bse0x(["爱心", "女孩", "惊恐", "大笑"])
f = "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
e = "010001"    # bse0x(["流泪", "强"])

再放行,找到加密节点

image-20240904195300825

鼠标移动到这里,即可看到,使用了一个方法,方法名称叫 d

image-20240904195731888

即可看到这样一串代码

image-20240904195755290

贴上源码

!function() {
    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")  // AES加密算法中需要的偏移量
          , 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
    }
    function e(a, b, d, e) {
        var f = {};
        return f.encText = c(a + e, b, d),
        f
    }
    window.asrsea = d,
    window.ecnonasr = e
}();

仔细分析AES加密特征,打断点

image-20240904203114032

放行

image-20240904203339782

打印这些值

image-20240904203323650

此时即可得到参数 i ,如果你多次执行就会发现这个 i 值是变化的,调用的是a函数传了个16

image-20240904203509021

a 函数这就是一个随机计算结果,可以直接定死

image-20240904203516923

而 i 已经定死了,i = "2l8EuJuvLirixMmc" ,这里我执行了第二遍,就用我第二遍的 i 值,得到了已有的值

g = "0CoJUm6Qyw8W8jud"   # bse0x(["爱心", "女孩", "惊恐", "大笑"])
f = "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
e = "010001"    # bse0x(["流泪", "强"])
i = "2l8EuJuvLirixMmc"  # 手动固定,函数中是随机的

偏移量之前已经在js代码的b函数找到了,这张图就可以很清楚的看到解密后的值就是 h.encText 的值

image-20240904204459458

image-20240904205241618

发现这个函数是RSA加密,我们已经得到了这个值,由上图可以看到,i 值为偏移量,e为 010001,f 未知,那么就先放一放

image-20240904205200570

image-20240904205735458

查看这个参数,断点

image-20240904210119630

记得放行查看值

image-20240904210638152

这样就能得到encSecKey的值,分析得到python逆向代码

def get_encSecKey():  # 由于i是固定的,所以经过c函数输出的encSecKey也是固定的
    return "288bca7fd6e42e24d4549f155b4eb6317ba323b914c9ee8aaccb786cabcd27773aea34d9413b8902f3a1a86e50863cec7e1f872092585cddd432951f210ae078e2f2c6b486c83c210820d5c09c73e2d3f225401ce1f5543f7a6f903c7bdc65b8ea27894c0299f52d801f05aaa6ec61e34082bec0135b9c74dabcabd1e1dba1ab"


# 把参数进行加密,相当于
def get_params(data):  # 默认收到的是字符串
    first = enc_paramas(data, g)  # 第一次加密,再将第二次结果与i一起加密
    second = enc_paramas(first, i)  # 第二次加密
    return second  # 返回的就是paramas


# 将数据长度转化为16的倍数, 为下面加密服务
def to_16(data):  # 
    pad = 16 - len(data) % 16
    data += chr(pad) * pad
    return data


def enc_paramas(data, key):    # 相当于js中的b(a, b)函数
    iv = "0102030405060708"  # AES加密算法中需要的偏移量
    data = to_16(data)
    aes = AES.new(key=key.encode('utf-8'), IV=iv.encode('utf-8'), mode=AES.MODE_CBC)  # 创建加密器
    bs = aes.encrypt(data.encode('utf-8'))  # 加密,加密的长度必须是16的倍数,”123456789abcchr(4)chr(4)chr(4)chr(4)"
    return str(b64encode(bs), "utf-8")  # 转化成字符串

此时将刚刚没有任何结果的代码进行传参即可,半成品代码如下

import json
from base64 import b64encode
from Crypto.Cipher import AES  # pip install  pycryptodome
import requests


# 设置好已知的值
g = "0CoJUm6Qyw8W8jud"   # bse0x(["爱心", "女孩", "惊恐", "大笑"])
f = "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
e = "010001"    # bse0x(["流泪", "强"])
i = "2l8EuJuvLirixMmc"  # 手动固定,函数中是随机的



def get_data():
    id=3865036  # 歌单id
    url = "https://music.163.com/weapi/comment/resource/comments/get"
    data = {
        'csrf_token': "",
        "cursor": "-1",  # 0为精彩评论,-1为最新评论
        "offset": "0",
        "orderType": "1",
        "pageNo": "1",
        "pageSize": "20",
        "rid": f"A_PL_0_{id}",
        "threadId": f"A_PL_0_{id}"
    }
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36'
    }
    resp = requests.post(url, data={
        "params": get_params(json.dumps(data)),  # 通过函数的加密得到服务器需要的params参数
        "encSecKey": get_encSecKey()
    }, headers=headers)
    print(resp.text)
    
if __name__ == '__main__':
    get_data()

完整数据清洗后的代码

import random
import json
from base64 import b64encode
from Crypto.Cipher import AES  # pip install  pycryptodome
import requests


# 服务于函数d
g = "0CoJUm6Qyw8W8jud"
f = "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
e = "010001"
i = "2l8EuJuvLirixMmc"  # 手动固定,函数中是随机的


def get_encSecKey():  # 由于i是固定的,所以经过c函数输出的encSecKey也是固定的
    return "288bca7fd6e42e24d4549f155b4eb6317ba323b914c9ee8aaccb786cabcd27773aea34d9413b8902f3a1a86e50863cec7e1f872092585cddd432951f210ae078e2f2c6b486c83c210820d5c09c73e2d3f225401ce1f5543f7a6f903c7bdc65b8ea27894c0299f52d801f05aaa6ec61e34082bec0135b9c74dabcabd1e1dba1ab"


# 把参数进行加密
def get_params(data):  # 默认收到的是字符串
    first = enc_paramas(data, g)  # 第一次加密,再将第二次结果与i一起加密
    second = enc_paramas(first, i)  # 第二次加密
    return second  # 返回的就是paramas


# 将数据长度转化为16的倍数, 为下面加密服务
def to_16(data):
    pad = 16 - len(data) % 16
    data += chr(pad) * pad
    return data


def enc_paramas(data, key):
    iv = "0102030405060708"  # AES加密算法中需要的偏移量
    data = to_16(data)
    aes = AES.new(key=key.encode('utf-8'), IV=iv.encode('utf-8'), mode=AES.MODE_CBC)  # 创建加密器
    bs = aes.encrypt(data.encode('utf-8'))  # 加密,加密的长度必须是16的倍数,”123456789abcchr(4)chr(4)chr(4)chr(4)"
    return str(b64encode(bs), "utf-8")  # 转化成字符串



def get_data():
    id=3865036  # 歌单id
    url = "https://music.163.com/weapi/comment/resource/comments/get"
    data = {
        'csrf_token': "",
        "cursor": "-1",  # 0为精彩评论,-1为最新评论
        "offset": "0",
        "orderType": "1",
        "pageNo": "1",
        "pageSize": "20",
        "rid": f"A_PL_0_{id}",
        "threadId": f"A_PL_0_{id}"
    }
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36'
    }
    resp = requests.post(url, data={
        "params": get_params(json.dumps(data)),  # 通过函数的加密得到服务器需要的params参数
        "encSecKey": get_encSecKey()
    }, headers=headers)
    print(resp.text)
    dict = json.loads(resp.text)  # 将得到的内容转化为字典模式
    user_obj = dict['data']['hotComments']
    comment_data = []
    comment_sum = dict['data']['totalCount']
    if not user_obj:
        user_obj = dict['data']['comments']
    for _ in user_obj:
        content = _['content']
        nickname = _['user']['nickname']
        photo = _['user']['avatarUrl']
        timeStr = _['timeStr']
        like_count = _['likedCount']
        addres = _['ipLocation']['location']
        list = {
            'nickname': nickname,
            'content': content,
            'photo': photo,
            'timeStr': timeStr,
            'addres': addres,
            'like_count': like_count,
            'com_count': comment_sum
        }
        comment_data.append(list)
    print(comment_data)


if __name__ == '__main__':
    get_data()

得到评论结果

image-20240904200835693

其他api接口也是如此,还可以搞一个扫码登录的,下面是我23年8月写的项目

项目推荐

这是我写的一个完整的爬虫django项目,集成了网易云扫码登录,歌单信息,歌曲/歌单评论,歌单收藏,歌曲播放等等运用了aiohttp异步爬虫,requests库,IP池,线程池,redis,中间件mysql数据库,等等等等项目仅供学习使用严禁商业用途!!!!记得点个关注,感谢

扫码登录

image-20240904182237838

网易云首页

image-20240904214654409

我的首页

image-20240904181934755

歌单列表

image-20240904182014204

源列表

image-20240904182045727

评论

image-20240904182030903

image-20240904201537098

歌曲播放效果

image-20240904201657253

。。。。。。。

总结

本文章中所有内容仅供学习交流,严禁用于商业用途和非法用途,否则由此产生的一切后果均与文章作者无关,若有侵权,请联系我立即删除!,重要的事儿说三遍!!!

回忆往昔

111

标签:逆向,网易,加密,get,16,JS,return,data,id
From: https://www.cnblogs.com/xiaoyus/p/18418650

相关文章

  • 488.中国风中秋节专题网页 大学生期末大作业 Web前端网页制作 html+css+js
    目录一、网页概述二、网页文件 三、网页效果四、代码展示1.html2.CSS3.JS五、总结1.简洁实用2.使用方便3.整体性好4.形象突出5.交互式强六、更多推荐欢迎光临仙女的网页世界!这里有各行各业的Web前端网页制作的案例,样式齐全新颖,并持续更新!感谢CSDN,提供了这......
  • Electron加载服务器端JS:高效打造跨平台桌面应用与本地 API 交互
    在现代桌面应用开发中,使用Electron加载远程服务器托管的前端资源,再与本地API交互,能够带来灵活的部署和强大的本地功能支持。这种方式不仅提升了开发效率,还能充分利用PC端的资源和性能。本文将深入解析如何使用Electron实现这一架构,并探讨其背后的关键技术,包括ipcMain和......
  • 阅读周·深入浅出的Node.js | Node产品化,异构共存,发挥产品适应与创新的新效能
    背景去年下半年,我在微信书架里加入了许多技术书籍,各种类别的都有,断断续续的读了一部分。没有计划的阅读,收效甚微。新年伊始,我准备尝试一下其他方式,比如阅读周。每月抽出1~2个非连续周,完整阅读一本书籍。这个“玩法”虽然常见且板正,但是有效。已读完书籍:《架构简洁之道》。当前阅读......
  • js Stream API简单使用
    ReadableStream<script>//TextDecoder将字节转换为字符串,默认utf-8编码letuint8Array=newUint8Array([72,101,108,108,111]);console.log(newTextDecoder().decode(uint8Array));//Helloletuint8Array1=newUint8Array([228,189,160,229,165......
  • commonJs和ESModule的区别
    1.规范的区别ESM模块的导入使用Import关键字,导出使用export关键字。commonJs导入使用require关键字,导出使用module.export 2.文件名后缀不一样(这一点不用记,针对Node)在node.js,默认将.js后缀文件识别为CJS模块,.cjs也是CJS模块,.mjs文件识别为esm模块。 3.模块加载时机......
  • Node.js 版本管理工具对比总结
    Node.js版本管理工具用于帮助开发者在不同项目中灵活切换Node.js和npm版本。常见的工具有nvm、n、nvs、fnm和Volta。以下是它们的优缺点、常用命令及对比总结。nvm(NodeVersionManager)优点:支持macOS和Linux。可以灵活地安装、切换和卸载不同版本的Node.js。......
  • ExtJs获取记录(Record)
    想要通过特定条件获取当前Store的某个Record一般常用两个方法findRecord和findNode这两个方法都有两个必选参数fieldName和value参数说明:fieldName:需要查找的record的字段名称,value:字段值带入sql语句会比较好理解select*from[表]{store}where[字段]{fieldName}......
  • 前端开发中的JS调试技巧
    调试技巧,在任何一项技术研发中都可谓是必不可少的技能。掌握各种调试技巧,必定能在工作中起到事半功倍的效果。譬如,快速定位问题、降低故障概率、帮助分析逻辑错误等等。而在互联网前端开发越来越重要的今天,如何在前端开发中降低开发成本,提升工作效率,掌握前端开发调试技巧尤为重......
  • Vue.js入门系列(三十一):Element-UI的基本使用与按需引入、Vue 3简介及使用 Vue CLI 与 V
    个人名片......
  • Vue.js入门系列(三十):深入理解独享路由守卫、组件内路由守卫、History模式与Hash模式
    个人名片......