吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 6158|回复: 55
收起左侧

[Web逆向] [MD5-JS逆向]某高等继续教育平台自动化答题

  [复制链接]
fanssong 发表于 2024-4-9 19:56
本帖最后由 fanssong 于 2024-4-10 17:34 编辑

实现某高等继续教育平台自动化答题
一、前言-本期主要讲解实现自动化

最近考取一建,发现对口专业问题,增考一个成人学历,入学后发现需要完成学校规定的【在线】课件视频学习时长、平时作业、和期末考试。
视频课件相信有过经历的同学都知道:拖拽滑条不计算学习时长。
平时作业、期末考试 需要在线进行作答,提交后查看分数,可进行多次修改。
屏幕截图 2024-04-09 162759.png
ps:因为时间不允许花费大量时间在这上面,就想着能不能分析后台js代码实现自动答题和视频课件拖拽。
二、开始分析
1、从网页元素分析-从答案提交按钮查找线索,登录教学平台,进入考试页面,F12进入开发者页面,等待提交试卷查看数据流。
屏幕截图 2024-04-09 165647.png
提交后发现提交数据流中并未有任何关于题干信息,
猜测:答案是根据点击选项时即时上传到服务器的。
利用开发者面板的选择工具(Ctrl+Shift+C),定位元素再查看该元素的click点击绑定事件
屏幕截图 2024-04-09 170647.png
1.1进入javascript代码后,跟踪分析发现果然是点击选项即时上传服务器的操作
[JavaScript] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
$('.ui-question-options>li .ui-question-options-order')
    .on('click', function() {
        /*选择题选项处理*/
        var t = $(this)
            .parent(),
            parent = t.parent()
            .parent(),
            answer = '',
            isChanged = false;
        while (!!parent) {
            if (parent.hasClass('ui-question')) {
                break;
            } else {
                parent = parent.parent();
            }
        }
        if (!!parent) {
            if (parent.hasClass('undone')) {
                parent.removeClass('undone');
            }
            if (parent.hasClass('ui-question-1')) {
                /*单选题*/
                if (!t.hasClass('ui-option-selected')) {
                    $('.ui-question-options>li.ui-option-selected', parent)
                        .removeClass('ui-option-selected');
                    t.addClass('ui-option-selected');
                    answer = t.attr('code');
                    isChanged = true;
                }
            } else if (parent.hasClass('ui-question-2')) {
                /*多选题*/
                isChanged = true;
                t.toggleClass('ui-option-selected');
                $('.ui-question-options>li.ui-option-selected', parent)
                    .each(function() {
                        answer = answer + $(this)
                            .attr('code');
                    });
            }
            if (isChanged) {
                exam.SaveItem(parent.prop('id')
                    .substring(2), parent.attr('code')
                    .substring(4), answer, '');
            }
        }
    });

2、分析3-分析javascript代码,发现是用jQuery语法来完成选择器与事件绑定。
2.1发现选择器与事件绑定:
【重要部分,后续会用到】该方法指定了所有带有类名.ui-question-options-order的元素,并且筛选了必须是.ui-question-options的直接子元素<li>的直接子元素,然后为这些元素绑定了点击事件。
[JavaScript] 纯文本查看 复制代码
1
2
3
4
5
$('.ui-question-options>li .ui-question-options-order')
    .on(
        'click',
        function() { //这里存放了选择题答案对比部分
        }

跳过上面答案对比比较部分,继续往下直达分析查看是否有上传加密。
2.2该部分提示使用exam.SaveItem()方法上传了题干信息
[JavaScript] 纯文本查看 复制代码
1
2
3
4
5
if (isChanged) {
    exam.SaveItem(parent.prop('id')
        .substring(2), parent.attr('code')
        .substring(4), answer, '');
}

2.2从总代码向上代码可以找到exam的赋值部分
该部分定义了使用全局变量window.__RTE.SaveItem()链条的函数
[JavaScript] 纯文本查看 复制代码
1
var exam = window.__RTE;

2.3想要查看全局变量可以用我写得代码来实现
利用控制台输入以下代码
[JavaScript] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
if (window.__RTE && typeof window.__RTE === 'object') {
    // 遍历window.__RTE对象的所有属性
    for (let key in window.__RTE) {
        // 检查属性是否是函数
        if (typeof window.__RTE[key] === 'function') {
            // 输出函数名称
            console.log(key);
            // 如果你还想查看函数的定义或者其它信息,你可以这样做:
            // 输出函数的定义(函数体)
            console.log(window.__RTE[key].toString());
            // 或者输出函数的长度(参数个数)
            console.log(window.__RTE[key].length);
        }
    }
} else {
    console.log('window.__RTE is not defined or not an object.');
}

2.4以此获取到全局变量函数:SaveItem()
[JavaScript] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
function(qId, psqId, answer, attach) {
    function SaveItem(qId, psqId, answer, attach) {
        var self = this,
            callback = (arguments.length > 4 ? arguments[4] : null);
 
        // 客户端判卷用到的方法
        function saveAnswerAndStatus(qId, psqId, qa, rightAnswers, attach) {
            var rightAnswer, len = rightAnswers.length;
            for (var i = 0; i < len; i++) {
                rightAnswer = rightAnswers[i];
                if (rightAnswer.questionId == qId) {
                    break;
                }
            }
            var score = 0,
                right = false;
            if (rightAnswer) {
                if (rightAnswer.type == 1) {
                    /*单选题*/
                    if (rightAnswer.answer == qa) {
                        score = rightAnswer.score;
                        right = true;
                    }
                } else if (rightAnswer.type == 2) {
                    /*多选替*/
                    var tmpAnswer = rightAnswer.answer;
                    var breaked = false;
                    for (var i = 0; i < qa.length; i++) {
                        var a = qa.charAt(i);
                        if (tmpAnswer.indexOf(a) < 0) {
                            breaked = true;
                            break;
                        }
                        tmpAnswer = tmpAnswer.replace(a, '');
                    }
                    if (!breaked && tmpAnswer.length == 0) {
                        score = rightAnswer.score;
                        right = true;
                    }
                }
            }
            saveAnswerFunc({
                qId: qId,
                userExamId: self.userExamId,
                body: {
                    psqId: psqId,
                    answer: qa,
                    attach: attach,
                    score: score,
                    right: right
                }
            });
        }
 
        //保存答案
        function saveAnswerFunc(params) {
            const saveAnswerRemote = self.basePath + '/myanswer/clientSave/' + self.userExamId + '/' + params.qId;
            const saveAnswerService = self.basePath + '/myanswer/newSave/' + self.userExamId + '/' + params.qId;
            const bodyParams = params.body;
            bodyParams["m"] = encryption(params.userExamId, params.qId, bodyParams);
            try {
                $.post(saveAnswerService, bodyParams, function(data) {
                    if (data && data.success) {
                        if (callback && $.isFunction(callback)) {
                            callback.apply(self);
                        }
                        if (data.leftTime) {
                            self.leftTime = data.leftTime;
                        }
                        saveUserAnswerCallback(qId);
                    } else {
                        const message = "保存答案失败," + data.errMsg;
                        self.Message(message, 'err', [{
                            label: '刷新',
                            func: function() {
                                window.location.reload()
                            }
                        }], true);
                    }
                }, 'json')
            } finally {
                try {
                    bodyParams["httpUrl"] = saveAnswerRemote;
                    cefobj.localSaveAnswer(JSON.stringify(bodyParams))
                } catch (e) {
                    console.log("保存本地出错: " + e)
                }
            }
        }

检查发现,果然有涉及到MD5混淆加密部分
[JavaScript] 纯文本查看 复制代码
1
bodyParams["m"] = encryption(params.userExamId, params.qId, bodyParams);

全局搜索,可以发现此方法是单独注册的一个MD5.js文件。
三、思路-实现最终自动化
1、已经获取到的加密提交信息,只需要对应输入即可
[JavaScript] 纯文本查看 复制代码
1
bodyParams["m"] = encryption(params.userExamId, params.qId, bodyParams);

1.1、qId=题目id值,psqId=为题目考试编号,根据数据流分析得到userExamId=考试编号+用户编号+时间戳
1.2、bodyParams={psqId: psqId, answer: answer, attach: attach},其中answer为选项的小写英文编号(如abcd),attach为空文本
2、解决MD5.js注册引用
[JavaScript] 纯文本查看 复制代码
1
2
3
4
5
6
7
8
// 使用loadScript函数加载MD5.js
loadScript('https://XXX.XXX..com/exam/statics/scripts/MD5.js')
    .then(() => {
        console.log('https://XXX.XXX.com/exam/statics/scripts/MD5.js has been loaded and executed');
    })
    .catch(error => {
        console.error('There was an error loading https://XXX.XXX.com/exam/statics/scripts/MD5.js:', error);
    });

3、整合以上部分,编写自己的代码
利用在最初我标记重要的地方插入自己javascript代码来实现自动化答题了以下为我自己编写的javascript代码,用于和pc机进行通信和网页操作,达到自动答题的目的
[JavaScript] 纯文本查看 复制代码
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
//==================================以下代码为自创===================================
// 页面加载完成后初始化
 
window.onload = init;
var 是否主动断开 = false;
const serverIP = '192.168.1.90'; // IP地址,请替换为实际的IP
const serverPort = 8888; // 端口号,请替换为实际的端口号
const wsUri = `ws://${serverIP}:${serverPort}`;
var kehuduan = null; // 在全局作用域中定义       
function init() {
    kehuduan = new WebSocket(wsUri);
    kehuduan.onopen = onOpen;
    kehuduan.onclose = onClose;
    kehuduan.onmessage = onMessage;
    kehuduan.onerror = onError;
}
 
function onOpen(evt) {
    console.log("连接已打开!");
    var referrerURL = document.referrer;
    console.log(referrerURL);
    //向本地服务器发送url
    var data = {
        code: "0",
        url: referrerURL
    };
    var jsonString = JSON.stringify(data); /// 将对象转换为JSON字符串
    kehuduan.send(jsonString); // 发送JSON字符串到服务器
}
 
function onClose(evt) {
    console.log("连接已关闭!", evt);
}
 
function onError(evt) {
    console.log('WebSocket发生错误');
}
 
function onMessage(evt) {
    console.log('收到消息:' + evt.data);
    //code的值:0时为:上一页,1时为:下一页,2时为:刷新页面,3时为:发送url,4时为:收到答案
    var jsonString = evt.data;
    var jsonObject = JSON.parse(jsonString);
    var xiaoxi = jsonObject.code;
    var jsondata = jsonObject.data;
    console.log('检测到代码命令:' + xiaoxi);
    if (xiaoxi === "0") { //上一题https://www.52pojie.cn/index.php
        __offsetQuestion(-1);
 
    } else if (xiaoxi == 1) { //下一题       
        __offsetQuestion(1);
 
    } else if (xiaoxi == 2) { //刷新页面
        location.reload();
        //javascript:location.reload(location.href);
 
    } else if (xiaoxi == 3) { //服务器要主动断开连接!
        kehuduan.close();
        是否主动断开 = true;
        console.log("服务器解析到URL主动断开连接!");
 
    } else if (xiaoxi == 4) { //收到手动答案,开始自动答题                       
        ZhengliDaAm(jsondata);
 
    } else if (xiaoxi == 5) { //获取到解析的答案页面url,用于直接获取答案自动答题
        var DaAN_url = jsondata[0].url //解析出url
        HuoquDaAn(DaAN_url, '', '', 5);
 
    } else if (xiaoxi == 6) { //获取到解析的答案页面url,用于直接获取答案自动答题
        var DaAN_url = jsondata[0].url //解析出url
        var DaAN_basePath = jsondata[1].basePath //解析上传头url
        var DaAN_userExamId = jsondata[2].userExamId //解析用户id和时间戳
        HuoquDaAn(DaAN_url, DaAN_basePath, DaAN_userExamId, 6);
 
    } else if (xiaoxi == 7) { //自定义数据,用于测试代码
        if (window.__RTE && typeof window.__RTE === 'object') {
            // 遍历window.__RTE对象的所有属性
            for (let key in window.__RTE) {
                // 检查属性是否是函数
                if (typeof window.__RTE[key] === 'function') {
                    // 输出函数名称
                    console.log(key);
                    // 如果你还想查看函数的定义或者其它信息,你可以这样做:
                    // 输出函数的定义(函数体)
                    console.log(window.__RTE[key].toString());
                    // 或者输出函数的长度(参数个数)
                    console.log(window.__RTE[key].length);
                }
            }
        } else {
            console.log('window.__RTE is not defined or not an object.');
        }
 
    };
}
//HuoquDaAn(DaAN_url,DaAN_basePath,DaAN_userExamId,6);
function HuoquDaAn(DaAN_url, DaAN_basePath, DaAN_userExamId, DaAN_code) { //用于从学校服务器获取正确答案
    $.get(DaAN_url, {}, function(data) {
        if (data && data.success) {
            console.log(data.answers);
            console.log(data.success);
            var jsonString = data.answers;
            ZhengliDaAm(jsonString, DaAN_code, DaAN_basePath, DaAN_userExamId);
        } else {
            console.log('错误:从服务器端获取答案失败!');
        }
    }, 'json');
}
 
function ZhengliDaAm(jsondata, DaAN_code, DaAN_basePath, DaAN_userExamId) {
    var allspan = $('.ui-question-options>li>span.ui-question-options-order');
    console.log('所有直接子元素带有 ui-question-options-order 类的元素:', allspan);
    if (allspan.length > 0) {
        jsondata.forEach(item => { //使用JavaScript的forEach方法
            const id = item.questionId;
            const answer = item.answer;
            const score = item.score;
            // 使用.each()方法遍历allspan中的每一个元素       
            allspan.each(function(index, element) { //element是每次遍历到的值
                var t = $(element)
                    .parent();
                var parent = t.parent()
                    .parent();
                if (id == parent.prop('id')
                    .substring(2)) {
                    if (DaAN_code == 5) {
                        var exam = window.__RTE; //获取赋值全局变量window的rte函数给exam
                        exam.SaveItem(id, parent.attr('code')
                            .substring(4), answer, ''); //向学校服务器保送答案,保存答案
                    } else {
                        SaveItem(id, parent.attr('code')
                            .substring(4), answer, '', DaAN_basePath, DaAN_userExamId, score); //向学校服务器保送答案,保存答案
                    }
 
                    console.log('已向服务器提交数据:', id, parent.attr('code')
                        .substring(4), answer, '');
                    //console.log(parent.prop('id').substring(2), parent.attr('code').substring(4), answer);
                    return false; //跳出当前.each()遍历                                                       
                }
 
            });
        });
    } else {
        console.log('没有找到任何匹配的元素');
    }
}
 
function SaveItem(qId, psqId, answer, attach, DaAN_basePath, DaAN_userExamId, DaAN_score) {
    var right = true,
        callback = (arguments.length > 7 ? arguments[7] : null);
    saveAnswerFunc({
        qId: qId,
        userExamId: DaAN_userExamId,
        body: {
            psqId: psqId,
            answer: answer,
            attach: attach
        }
    }, DaAN_basePath);
    //body: {psqId: psqId, answer: answer, attach: attach, score: DaAN_score, right: right}
    //},DaAN_basePath);
}
 
function saveAnswerFunc(params, DaAN_basePath) {
    const saveAnswerRemote = DaAN_basePath + '/myanswer/clientSave/' + params.userExamId + '/' + params.qId;
    const saveAnswerService = DaAN_basePath + '/myanswer/newSave/' + params.userExamId + '/' + params.qId;
    const bodyParams = params.body;
    bodyParams["m"] = encryption(params.userExamId, params.qId, bodyParams);
    $.post(saveAnswerService, bodyParams, function(data) {
        if (data && data.success) {
            console.log('答案提交成功');
            if (data.leftTime) {
                self.leftTime = data.leftTime;
            }
        } else {
            console.log('保存答案失败', data.errMsg);
        }
    }, 'json')
}

屏幕截图 2024-04-09 194112.png
以上代码是根据原有代码进行了现状修改,增加了一些个人习惯的功能,参考使用
下一期将分享如何实现该网站拖拽视频进度条,或干脆逆向MD5进行直接模拟提交进度信息
ps:这样偷懒是不好的行为,哈哈,多学习才能学到更多有用的知识
喜欢的话可以给本帖免费评分++++

免费评分

参与人数 14威望 +1 吾爱币 +33 热心值 +12 收起 理由
笙若 + 1 + 1 谢谢@Thanks!
涛之雨 + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
lxzlxz + 1 鼓励转贴优秀软件安全工具和文档!
Kls673M + 1 + 1 热心回复!
笨笨家的唯一 + 1 + 1 我很赞同!
鸠山一茶 + 1 + 1 鼓励转贴优秀软件安全工具和文档!
haxcode + 1 + 1 用心讨论,共获提升!
shengruqing + 1 我很赞同!
xyzliuin + 1 + 1 谢谢@Thanks!
Yukin0shita + 1 + 1 我很赞同!
gqdsc + 1 真是高手啊
liuxuming3303 + 1 + 1 用心讨论,共获提升!
mizhi + 1 + 1 我很赞同!
wystudio + 2 + 1 用心讨论,共获提升!

查看全部评分

本帖被以下淘专辑推荐:

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

Kls673M 发表于 2024-4-10 11:47
感谢分享,
大佬这类的答案你在怎么搞到手的?
是提交cookie反向获取答案吗? 还是原有题目里面就包含答案了?
小人类 发表于 2024-4-10 15:14
眼熟这个网站,之前做过自动化处理,md5应该就是我刷太多才加的,后面也解了,这是一个系列的网站,很多地方的成人教育都是使用这一套模板,
狂侠先森 发表于 2024-4-9 20:50
Scan 发表于 2024-4-9 20:58
可以,感谢分享,学习思路了!
lemonatalk952 发表于 2024-4-9 21:49
不错嘛,给我就是找不到关键函数
q12569463 发表于 2024-4-9 22:56

感谢分享
qilin570 发表于 2024-4-9 23:25
咋用啊 我不会
52PJ070 发表于 2024-4-9 23:43
不错的,学习了,感谢分享!
Lty20000423 发表于 2024-4-10 07:40
哈哈,偷懒是社会进步的动力
jituidadada 发表于 2024-4-10 08:41
我喜欢你的注释
photocs 发表于 2024-4-10 08:41
思路不错,试试看先
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

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

GMT+8, 2025-4-21 15:25

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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