吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 4831|回复: 47
收起左侧

[Web逆向] [Web逆向] 智慧职教资源库-刷课分析

  [复制链接]
wuye4 发表于 2024-4-26 22:04

只适用于https://zyk.icve.com.cn/

已更新至Githubhttps://github.com/wuye4/zyk.icve.com.cn

1.目前暂时不支持讨论、作业、测验、考试。
2.该网站的新课与旧课会有参数的值有多种形式需要一定时间适配。

一、找接口

1.登录接口https://sso.icve.com.cn/prod-api/data/userLoginV2
会响应一个token

Snipaste_2024-04-26_20-54-37.jpg

2.access_token接口https://zyk.icve.com.cn/prod-api/auth/passLogin?token={}
传入1.登录接口响应的token

Snipaste_2024-04-26_21-02-10.jpg

3.myCourseList接口https://zyk.icve.com.cn/prod-api/teacher/courseList/myCourseList?pageNum=1&pageSize=6&flag=1
获取所选课程courseId、courseInfoId、id

Snipaste_2024-04-26_21-06-05.jpg

4.studyMoudleList接口 https://zyk.icve.com.cn/prod-api/teacher/courseContent/studyMoudleList?courseInfoId={}
传入3.myCourseList接口获取的courseId 拿到一级标题与courseId、courseInfoId、id、level

Snipaste_2024-04-26_21-08-42.jpg

5.studyList接口https://zyk.icve.com.cn/prod-api/teacher/courseContent/studyList?level={}&parentId={}&courseInfoId={}
传入4.studyMoudleList接口获取的courseInfoId、id拿到二级标题与courseId、courseInfoId、id、level

Snipaste_2024-04-26_21-16-10.jpg

继续往下点后发现还是请求studyList接口拿到最终课程的参数

Snipaste_2024-04-26_21-19-02.jpg

6.studyRecord接口 https://zyk.icve.com.cn/prod-api/teacher/studyRecord
随便点击一个课程过一会就会请求studyRecord接口,也就是完成该课程的接口,以下以视频举例totalNum代表视频总时长,actualNum代表实际时长,studyTime代表学习时长,每个视频的时长是不固定的我们如何动态获取呢。

image-20240426213357989.png

7.status接口 https://upload.icve.com.cn/doc/{fileUrl}/status
5.studyList接口中有一个fileUrl参数将其传入就可以获取视频时长参数duration,最后将其转化为秒数再请求6.studyRecord接口就可以完成刷视频

image-20240426214133320.png

二、效果图

image-20240426215015774.png

image-20240426215025025.png

三、python代码实现

import requests
from datetime import datetime

requests = requests.session()

def getcookie():
    headers = {
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
        'Accept-Language': 'zh-CN,zh;q=0.9',
        'Connection': 'keep-alive',
        'Referer': 'https://zyk.icve.com.cn/',
        'Sec-Fetch-Dest': 'document',
        'Sec-Fetch-Mode': 'navigate',
        'Sec-Fetch-Site': 'same-site',
        'Sec-Fetch-User': '?1',
        'Upgrade-Insecure-Requests': '1',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
        'sec-ch-ua': '"Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"',
        'sec-ch-ua-mobile': '?0',
        'sec-ch-ua-platform': '"Windows"',
    }

    response = requests.get('https://sso.icve.com.cn/sso/auth?mode=simple&source=14&redirect=https://zyk.icve.com.cn/',
                            headers=headers)

# 登录
def userLoginV2(userName, password):
    headers = {
        'Accept': 'application/json, text/plain, */*',
        'Accept-Language': 'zh-CN,zh;q=0.9',
        'Connection': 'keep-alive',
        'Content-Type': 'application/json;charset=UTF-8',
        'Language': 'cn',
        'Origin': 'https://sso.icve.com.cn',
        'Referer': 'https://sso.icve.com.cn/sso/auth?mode=simple&source=14&redirect=https%3A%2F%2Fzyk.icve.com.cn%2F',
        '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/124.0.0.0 Safari/537.36',
        'sec-ch-ua': '"Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"',
        'sec-ch-ua-mobile': '?0',
        'sec-ch-ua-platform': '"Windows"',
    }

    json_data = {
        'type': 1,
        'userName': userName,
        'password': password,
        'webPageSource': 1,
    }

    response = requests.post('https://sso.icve.com.cn/prod-api/data/userLoginV2', headers=headers,
                             json=json_data)

    if len(response.json()["data"]["displayName"]) > 0:
        print(response.json()["data"]["displayName"] + "登录成功")
    return response.cookies.get("token")

# 获取access_token
def access_token(token):
    headers = {
        'Accept': 'application/json, text/plain, */*',
        'Accept-Language': 'zh-CN,zh;q=0.9',
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive',
        'Pragma': 'no-cache',
        'Referer': 'https://zyk.icve.com.cn/',
        '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/124.0.0.0 Safari/537.36',
        'isToken': 'false',
        'sec-ch-ua': '"Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"',
        'sec-ch-ua-mobile': '?0',
        'sec-ch-ua-platform': '"Windows"',
    }

    params = {
        'token': token,
    }

    response = requests.get('https://zyk.icve.com.cn/prod-api/auth/passLogin', params=params,
                            headers=headers)

    return response.json()["data"]["access_token"]

# 课程
def courseList():
    headers = {
        'Accept': 'application/json, text/plain, */*',
        'Accept-Language': 'zh-CN,zh;q=0.9',
        'Authorization': f'Bearer {accesstoken}',
        'Connection': 'keep-alive',
        '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/124.0.0.0 Safari/537.36',
        'sec-ch-ua': '"Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"',
        'sec-ch-ua-mobile': '?0',
        'sec-ch-ua-platform': '"Windows"',
    }

    params = {
        'pageNum': '1',
        'pageSize': '6',
        'flag': '1',
    }

    response = requests.get('https://zyk.icve.com.cn/prod-api/teacher/courseList/myCourseList', params=params,
                            headers=headers)
    info = []
    for i in response.json()["rows"]:
        info.append((i["id"], i["courseId"], i["courseInfoId"], i["studentId"], i["courseName"]))
    return info

# 一级标题
def studyMoudleList(courseInfoId):
    headers = {
        'Accept': 'application/json, text/plain, */*',
        'Accept-Language': 'zh-CN,zh;q=0.9',
        'Authorization': f'Bearer {accesstoken}',
        'Connection': 'keep-alive',
        '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/124.0.0.0 Safari/537.36',
        'sec-ch-ua': '"Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"',
        'sec-ch-ua-mobile': '?0',
        'sec-ch-ua-platform': '"Windows"',
    }

    params = {
        'courseInfoId': courseInfoId,
    }

    response = requests.get('https://zyk.icve.com.cn/prod-api/teacher/courseContent/studyMoudleList', params=params,
                            headers=headers)
    studymoudlelist = []
    for i in response.json():
        studymoudlelist.append((i["id"], i["courseId"], i["courseInfoId"], i["name"]))

    return studymoudlelist

# 二级标题
def studyList1(level, parentId, courseInfoId):
    headers = {
        'Accept': 'application/json, text/plain, */*',
        'Accept-Language': 'zh-CN,zh;q=0.9',
        'Authorization': f'Bearer {accesstoken}',
        'Connection': 'keep-alive',
        '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/124.0.0.0 Safari/537.36',
        'sec-ch-ua': '"Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"',
        'sec-ch-ua-mobile': '?0',
        'sec-ch-ua-platform': '"Windows"',
    }

    params = {
        'level': level,
        'parentId': parentId,
        'courseInfoId': courseInfoId,
    }

    response = requests.get('https://zyk.icve.com.cn/prod-api/teacher/courseContent/studyList', params=params,
                            headers=headers)

    for i in response.json():

        if i["level"] is not None:
            studyList1(i["level"], i["id"], i["courseInfoId"])

        else:
            geturl(i)

# 获取最总课程网址
def geturl(i):
    if i["fileType"] == 'docx' or i["fileType"] == 'doc':
        if i["studentStudyRecord"] is None:
            docx.append((i["id"], i["courseId"], i["courseInfoId"], i["name"], i["parentId"], i["fileUrl"]))
    elif i["fileType"] == 'pdf' or i["fileType"] == 'pptx':
        if i["studentStudyRecord"] is None:
            pdf.append((i["id"], i["courseId"], i["courseInfoId"], i["name"], i["parentId"], i["fileUrl"]))
    elif i["fileType"] == 'mp4':
        if i["studentStudyRecord"] is None:
            mp4.append((i["id"], i["courseId"], i["courseInfoId"], i["name"], i["parentId"], i["fileUrl"]))
    elif i["fileType"] == 'jpg':
        if i["studentStudyRecord"] is None:
            jpg.append((i["id"], i["courseId"], i["courseInfoId"], i["name"], i["parentId"]))

def getdocorpdfxnum(courseInfoId, parentId, sourceId, studentId, fileUrl):
    headers = {
        'Accept': 'application/json, text/plain, */*',
        'Accept-Language': 'zh-CN,zh;q=0.9',
        'Authorization': f'Bearer {accesstoken}',
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive',
        'Pragma': 'no-cache',
        '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/123.0.0.0 Safari/537.36',
        'sec-ch-ua': '"Google Chrome";v="123", "Not:A-Brand";v="8", "Chromium";v="123"',
        'sec-ch-ua-mobile': '?0',
        'sec-ch-ua-platform': '"Windows"',
    }

    params = {
        'fileUrl': fileUrl,
    }

    response = requests.get(
        'https://zyk.icve.com.cn/prod-api/teacher/oss/getUrlPngs',
        params=params,
        headers=headers,
    )
    totalNum = len(response.json()["data"])
    tapjpganddocxandpdf(courseInfoId, parentId, sourceId, studentId, totalNum)

# 刷课
def tapjpganddocxandpdf(courseInfoId, parentId, sourceId, studentId, totalNum):
    headers = {
        'Accept': 'application/json, text/plain, */*',
        'Accept-Language': 'zh-CN,zh;q=0.9',
        'Authorization': f'Bearer {accesstoken}',
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive',
        'Content-Type': 'application/json;charset=UTF-8',
        'Origin': 'https://zyk.icve.com.cn',
        'Pragma': 'no-cache',
        '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/123.0.0.0 Safari/537.36',
        'sec-ch-ua': '"Google Chrome";v="123", "Not:A-Brand";v="8", "Chromium";v="123"',
        'sec-ch-ua-mobile': '?0',
        'sec-ch-ua-platform': '"Windows"',
    }

    json_data = {
        'courseInfoId': courseInfoId,
        'id': '',
        'parentId': parentId,
        'studyTime': 60,
        'sourceId': sourceId,
        'studentId': studentId,
        'actualNum': totalNum,
        'lastNum': totalNum,
        'totalNum': totalNum,
    }

    response = requests.put('https://zyk.icve.com.cn/prod-api/teacher/studyRecord', headers=headers,
                            json=json_data)

# 视频状态
def videostatus(courseInfoId, parentId, sourceId, studentId, fileUrl):
    headers = {
        'accept': 'application/json, text/plain, */*',
        'accept-language': 'zh-CN,zh;q=0.9',
        'cache-control': 'no-cache',
        # 'content-length': '0',
        'origin': 'https://zyk.icve.com.cn',
        'pragma': 'no-cache',
        'sec-ch-ua': '"Google Chrome";v="123", "Not:A-Brand";v="8", "Chromium";v="123"',
        'sec-ch-ua-mobile': '?0',
        'sec-ch-ua-platform': '"Windows"',
        'sec-fetch-dest': 'empty',
        'sec-fetch-mode': 'cors',
        'sec-fetch-site': 'same-site',
        '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',
    }

    response = requests.post(f'https://upload.icve.com.cn/{fileUrl}/status',
                             headers=headers)
    time = str(response.json()["args"]["duration"])
    duration_str_no_micro = time.split('.')[0]
    duration_obj = datetime.strptime(duration_str_no_micro, "%H:%M:%S")
    total_seconds = duration_obj.hour * 3600 + duration_obj.minute * 60 + duration_obj.second
    tapjpganddocxandpdf(courseInfoId, parentId, sourceId, studentId, total_seconds)

if __name__ == '__main__':
    username = input("账号")
    pwd = input("密码")
    getcookie()

    token = userLoginV2(username, pwd)
    accesstoken = access_token(token)
    # 所有课程信息
    info = courseList()
    # print(info)
    for i in info:
        print(i[4] + "开始刷课")
        studymoudlelist = studyMoudleList(i[2])
        # print(studymoudlelist)
        docx = []
        pdf = []
        mp4 = []
        jpg = []
        # id courseInfoId
        for k in studymoudlelist:
            studyList1(1, k[0], k[2])
            # print(len(docx))
            # print(len(pdf))
            # print(len(mp4))
            # print(len(jpg))
            for i in docx:
                print(i[3] + "观看完毕")
                getdocorpdfxnum(i[2], i[4], i[0], info[0][4], i[5])
            for i in jpg:
                print(i[3] + "观看完毕")
                tapjpganddocxandpdf(i[2], i[4], i[0], info[0][4], 1)
            for i in pdf:
                print(i[3] + "观看完毕")
                getdocorpdfxnum(i[2], i[4], i[0], info[0][4], i[5])
            for i in mp4:
                print(i[3] + "观看完毕")
                videostatus(i[2], i[4], i[0], info[0][4], i[5])

免费评分

参与人数 16吾爱币 +21 热心值 +15 收起 理由
jin1231 + 1 + 1 谢谢@Thanks!
XXXXXBully + 1 + 1 我很赞同!
che_shen + 1 + 1 用心讨论,共获提升!
ooHHoo + 1 谢谢@Thanks!
lenxueyan + 1 + 1 我很赞同!
mushan2000 + 1 + 1 我很赞同!
涛之雨 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
liuxuming3303 + 1 + 1 谢谢@Thanks!
西枫游戏 + 1 谢谢@Thanks!
edge + 1 + 1 谢谢@Thanks!
yangyih + 1 + 1 谢谢@Thanks!
zermx + 1 + 1 用心讨论,共获提升!
随遇而安8 + 1 + 1 厉害,希望出更多学习教程
jy138290 + 1 + 1 谢谢@Thanks!
lingyun011 + 1 + 1 用心讨论,共获提升!
SVIP9大会员 + 1 + 1 用心讨论,共获提升!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

SVIP9大会员 发表于 2024-4-26 23:05
这个代码过程以及分析过程简直就是范例啊 受教了
shiyun01 发表于 2024-4-28 11:22
binbinyouli521 发表于 2024-11-24 07:34
学习一下😁😁😁😁😁😁
wantwill 发表于 2024-11-23 14:42
感谢分享
李玉风我爱你 发表于 2024-11-5 21:39
haohaovision 发表于 2024-9-18 06:28
大佬,截止24年9月18日,发现studyRecord这个接口的payload似乎是加密的(如图)

今天晚上睡不着没事干 ...

你好 可以参考这段代码 360截图20241105213853939.jpg
haohaovision 发表于 2024-9-18 06:28
大佬,截止24年9月18日,发现studyRecord这个接口的payload似乎是加密的(如图)
SCR-20240918-ggug.png
今天晚上睡不着没事干起来写,从Github一路搜过来的,发现似乎接口有改动.直接按原样不加密提交上去不会报错,但是也没有更新进度。 无奈个人能力有限,js代码有混淆,无法确定是不是加密,是怎么加密的,望大佬更新!
THqqqqp 发表于 2024-5-16 16:53
代码分析过程简直就是范例啊 受教了
eacpjls 发表于 2024-5-8 14:38
wuye4 发表于 2024-5-7 16:15
根据文件的网址,写就行了

希望大佬有时间出个教程~
 楼主| wuye4 发表于 2024-5-7 16:15
eacpjls 发表于 2024-5-6 08:52
是的 下载到本地

根据文件的网址,写就行了
eacpjls 发表于 2024-5-6 08:52
wuye4 发表于 2024-5-1 14:14
你是说下载到本地吗还是啥

是的 下载到本地
 楼主| wuye4 发表于 2024-5-1 14:14
eacpjls 发表于 2024-4-29 16:41
ppt咋下载啊大佬

你是说下载到本地吗还是啥
 楼主| wuye4 发表于 2024-5-1 14:14
icyou66 发表于 2024-4-29 15:11
有没有mooc的思路分析大佬。

有的,稍后再写
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-13 17:37

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表