本程序无需题库百分之百正确答题,程序仅供测试。我会分享思路及程序(程序写的比较烂但功能已经实现),推荐大家看完思路自己独立完成一下,我会把文章终点放在思路上,思路也就是结论,但是还有一些问题没有解决,我会在文章最后列举出来,希望能一起解决。
本文仅做知识分享,切勿用作商业用途。本人是一个大二业余编程爱好者,文章旨在抛砖引玉希望能结识志同道合的朋友,QQ2742431760。思路及程序如有不足之处欢迎指正。
用到的工具是:HttpCanary 和谷歌浏览器手机版
为了方便描述我对重要网址进行编号
1,打开网址http://dxs.moe.gov.cn/zx/xy/gxlb/-wszzczl-gxdjlhd.shtml -》进入答题-》
2,获得二维码的网址
"https://oauth.u.hep.com.cn/oauth/wxapp/qrcode/5f582dd3683c2e0ae3aaaceerandom=lmDtOozSzjJac3XiRB5GibqW&useSelfWxapp=true&enableFetchPhone=false(返回下面的二维码地址) 网址①
二维码地址 https://node2dpublic.hep.com.cn/wxapp/qrcode/2S9RnG85W.png 网址②
网站监控和二维码是否被扫描的网址https://oauth.u.hep.com.cn/oauth/wxapp/confirm/qr?random=lmDtOozSzjJac3XiRB5GibqW&useSelfWxapp=true 网址③
参数关系:
lmDtOozSzjJac3XiRB5GibqW和/2S9RnG85W的关系是:
lmDtOozSzjJac3XiRB5GibqW为一个随机的24位的字符串(0-9 a-z A-Z)。
2S9RnG85W为访问二维码一直监控的地址返回的一个特定的二维码文件名。也就是每个24位随机字符串都对应着一张png格式的二维码。这个二维码名称随意长短不一。
3,获得登陆的token,在这个包的返回值里(只管token无视refresh_token就行)
这个包访问的网址分析:https://ssxx.univs.cn/cgi-bin/authorize/token/?t=1614522688&uid=6031e31b3405f413462cd581&avatar=https:%2F%2Fnode2d-public.hep.com.cn%2Favatar-6031e31b3405f418062cd581-1613882139925&activity_id=5f71e934bcdbf3a8c3ba5061 网址④
t :t参数也是我没搞懂的,如果按随机数处理确实可以行得通。可是每20道题他就会随机出现验证码,这个验证码是跳不过去的(跳过去服务器会拒绝返回答题所获得的分值,下文我会提到验证码)。每个t参数都对应着特定的验证码,所以抓到一套包在用编程语言模拟发送的时候碰到t参数不要修改。
uid :应该是固定的在网址③的返回值中获得
avatar :也在网址③的返回值中获得 参数名称叫photo。
activity_id :应该是一个固定值。
注:此后的一切我提到的网址都要加上获得到的token去访问。
4,获得题目的race_code参数的地址:https://ssxx.univs.cn/cgi-bin/race/beginning/?t=1614522688&activity_id=5f71e934bcdbf3a8c3ba5061&mode_id=5f71e934bcdbf3a8c3ba51d5&way=1 网址⑤
至于race_code有啥用下文用到我会解释,这个网址也有接下来要答的题目的id,race_code个人理解就是20到题目综合的id。这个网址里我们要获取race_code以及20道题目的id
5,接下来是对答题时出现的验证码的处理了,我们会访问三个网址,这三个网址只是固定套路请不要变化。(有的同学会感觉验证码是随机出现的,在一开头就处理有用蛮?答:确实有用。经测试验证码仅与t参数有关只要自己抓到一个t参数的包,只要t参数不变就可以通用,用来解决验证码问题)
第一个网址https://ssxx.univs.cn/cgi-bin/base/public/key/?t=1614522688 (个人理解:获得公钥,以前从来没接触过我也不知道干啥用哈,只是按固定格式操作的)
第二个网址https://ssxx.univs.cn/cgi-bin/save/verification/code/(个人理解:获得验证码)
在用编程语言模拟访问的时候别把包里的请求信息拉了,每个t对应都是特定的,包的内容如下的格式
第三个网址:https://ssxx.univs.cn/cgi-bin/check/verification/code/(个人理解:验证验证码是否正确)同样不要忘了包里的内容,以及要原封不动的发送,仅和t参数有关。
最后需要注意的是 这三个包有过期时间(据我推测应该是三天),如果不能正常运行了请更新一下t参数和这三个包里的数据。
6,进行答题
获得题目:
https://ssxx.univs.cn/cgi-bin/race/question/?t=1614522690&activity_id=5f71e934bcdbf3a8c3ba5061&question_id=5f71e934bcdbf3a8c3ba52e8&mode_id=5f71e934bcdbf3a8c3ba51d5&way=1
回答问题:
https://ssxx.univs.cn/cgi-bin/race/answer/
题目可以重复提交比如一道题答错了,再复制他返回的正确答案重新提交就行了。
参数解释 :t上面说了,activity_id和mode_id是固定值,question_id为网址⑤中返回参数中获得。
需要注意的是回答问题的post请求中有内容别忘记了,他的返回值就是正确答案的id,好了循环20次就行了。经测试每道题目以及答案的id是变化的,也就是说同一道题目以及答案会有不同的id,这只是猜测,我爬取了很多题目id并没有爬取题目(因为爬取题目比较麻烦偷了个懒哈哈,因此我只能看到id所以猜测他是变化的,大家可以自行检测)
7,把自己的答案提交给服务器,这也是最后一步了,终于完成了哈。
https://ssxx.univs.cn/cgi-bin/race/finish/
提交的内容就是我们上面获取到的race_code了
碰到的问题不明白的:
1,验证码与t参数有关,t参数可以是随机值除了无法通过验证码的请求以及无法把答案提交给服务器其他都能正常,那么能否知道t参数和验证码的那三条请求的对应关系,进行自定义。
2,题目和答案的id是否一直在变化,如果一直在变化那么其和什么有关。(例如访问次数,时间。。。。)。
我写的代码不是很好我也不想整理了,如果对我的思路不太理解可以看看代码辅助理解一下把【狗头保命】,程序能跑哈哈哈。个人感觉如果你直接用我的代码去改的话,还不如自己写省时省力,切勿用于商业行为。 运行程序时如果一切正常但是无法把答案提交给服务器,也就是自己的分数不增加,那应该就是t参数对应的那三个包过期了,毕竟有效期仿佛是三天,自己再抓一个填上试试
import java.util.concurrent.TimeUnit;
public class ChinaStudent {
public static void main (String[] args) {
//生成24位随机字符串
char[] letters = {
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',
'1','2','3','4','5','6','7','8','9','0'};
MyRandom mr = new MyRandom();
String strrand = mr.random(letters,24);
//获得返回信息
String httpurl = "https://oauth.u.hep.com.cn/oauth/wxapp/qrcode/5f582dd3683c2e0ae3aaacee?random="+ strrand +"&useSelfWxapp=true&enableFetchPhone=false";
HttpClient hc = new HttpClient();
String targeturl = hc.doGet(httpurl,"!");
System.out.print(targeturl);
//处理返回信息
String imgurl;
Regex regex = new Regex();
imgurl = regex.regex(targeturl, "https://node2d-public.hep.com.cn/wxapp/qrcode/[\\w]+.png")[0];
//下载二维码
Download download = new Download();
download.down("F:\\", imgurl);
//一直检测二维码是否被扫描
boolean flag = true;
String examinestr = "空";
//需要监控的网址
String examineurl = "https://oauth.u.hep.com.cn/oauth/wxapp/confirm/qr?random="+strrand+"&useSelfWxapp=true";
//生成?t=1614057480类型的字符串
String tstr ="1614522690";
String uid;
String photo;
String tokenurl;
String token;
String tokenstr;
String qstr;
String[] str = new String[30];
String[] str2 = new String[30];
String race_code = "";
String[] qid =new String[30];
String wanswer;
String wastr;
String answerurl = "https://ssxx.univs.cn/cgi-bin/race/answer/";
String answerstr;
String[] answer=new String[30];
String str1="";
String str3 = "";
long startTime = 0;
long endTime = 0;
while(flag) {
examinestr = hc.doPost(examineurl,"!","!");
System.out.println(examinestr);
if(!examinestr.contains("500")) {
flag =false;
}
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
uid = regex.regex(examinestr, "_id\":\"[\\w]+")[0];
str = uid.split("\":\"");
uid = str[1];
photo = regex.regex(examinestr, "photo\":\"[\\S]+?\"")[0];
str = photo.split("\"");
photo = str[2];
tokenurl = "https://ssxx.univs.cn/cgi-bin/authorize/token/?t=" + tstr + "&uid=" + uid + "&avatar=" + photo + "&activity_id=5f71e934bcdbf3a8c3ba5061";
tokenstr = hc.doGet(tokenurl,"!");
token = regex.regex(tokenstr, " \"token\": \"[\\S]+\"")[0];
str = token.split("\"");
token = str[3];
hc.doGet("https://ssxx.univs.cn/cgi-bin/base/public/key/?t="+tstr, token);
while(true) {
startTime = System.currentTimeMillis();
qstr = hc.doGet("https://ssxx.univs.cn/cgi-bin/race/beginning/?t="+tstr+"&activity_id=5f71e934bcdbf3a8c3ba5061&mode_id=5f71e934bcdbf3a8c3ba51d5&way=1",token);
race_code = regex.regex(qstr, "race_code\": \"[\\S]+\"")[0];
str = race_code.split("\"");
race_code = str[2];
qid[0] = regex.regex(qstr, "\\[\"[\\S\\s]*\\]")[0];
str2 = qid[0].split("\"");
for(int i=0;i<20;i++) {
System.out.println("答题第: "+(i+1)+"道");
qid[i] = str2[1+i*2];
String qurl = "https://ssxx.univs.cn/cgi-bin/race/question/?t="+tstr+"&activity_id=5f71e934bcdbf3a8c3ba5061&question_id="+qid[i]+"&mode_id=5f71e934bcdbf3a8c3ba51d5&way=1";
wastr = hc.doGet(qurl, token);
wanswer = regex.regex(wastr, "id\"[\\S\\s]+?\",")[1];
str = wanswer.split("\"");
wanswer = str[2];
str1 = "{\"activity_id\":\"5f71e934bcdbf3a8c3ba5061\",\"question_id\":\""+qid[i]+"\",\"answer\":[\""+wanswer+"\"],\"mode_id\":\"5f71e934bcdbf3a8c3ba51d5\",\"way\":\"1\"}";
answerstr = hc.doPost(answerurl, token,str1);
answer[0] = regex.regex(answerstr, "\\[\"[\\s\\S]+\"]")[0];
if(answerstr.contains("false"))
{
str3 = "{\"activity_id\":\"5f71e934bcdbf3a8c3ba5061\",\"question_id\":\""+qid[i]+"\",\"answer\":"+answer[0]+",\"mode_id\":\"5f71e934bcdbf3a8c3ba51d5\",\"way\":\"1\"}";
hc.doPost(answerurl, token, str3);
}
}
System.out.println(hc.doPost("https://ssxx.univs.cn/cgi-bin/save/verification/code/", token, "{\"activity_id\":\"5f71e934bcdbf3a8c3ba5061\",\"mode_id\":\"5f71e934bcdbf3a8c3ba51d5\",\"way\":\"1\",\"code\":\"e+Zmj8PZZOvDhleZ9llPSVwsq4ehzXRpoGfXucUR+AHltpZwAMO6LoZz3Zly1g0E5wSdybFS1lOpjQoCBMatqQp7jwQFGes5nJ4muiOrUyDm92S75gAP7YQ5TiD2oxpptyACA3MXm+idb8HetZV8FN1pHPIVgDvRUMHBiRLoOis=\"}"));
System.out.println(hc.doPost("https://ssxx.univs.cn/cgi-bin/check/verification/code/", token, "{\"activity_id\":\"5f71e934bcdbf3a8c3ba5061\",\"mode_id\":\"5f71e934bcdbf3a8c3ba51d5\",\"way\":\"1\",\"code\":\"hWQ9JQZedZWpkTWUiACPXr1JiRnuPHEg8Xy64Lj6XwiFCp9rBrmpqDPhxzG2qnBNy7c0mm1DSVnUKMJHfdyEG33UT366lhSMnxJxxZyf19tLMua8u0TUI9Z61xM7qYC1nzbzkMjOmULldJ5LUrsgTh2kA0IoKjbZLJWGYh2nVoU=\"}"));
System.out.println(hc.doPost("https://ssxx.univs.cn/cgi-bin/race/finish/", token, "{\"race_code\":\""+race_code+"\"}"));
endTime = System.currentTimeMillis();
System.out.println("运行时间: "+(endTime-startTime)/1000+"秒");
}
}
}
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
public class Download {
public void down(String file,String url) {
InputStream in = null;
String filename;
filename = url.substring(url.lastIndexOf("/")+1);
try {
URL url1 = new URL(url);
URLConnection uc = url1.openConnection();
in = uc.getInputStream();
FileOutputStream out = new FileOutputStream(file + filename);
int j = 0;
while((j = in.read()) != -1) {
out.write(j);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
try {
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Set;
public class HttpClient {
public String doGet(String httpurl,String token) {
HttpURLConnection connection = null;
InputStream is = null;
BufferedReader br = null;
String result = null;
Set<String> keys;
String cookie = "空";
try {
URL url = new URL(httpurl);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
if(!token.contains("!"))
connection.setRequestProperty("Authorization", "Bearer "+token);
//设置连接主机的时间
connection.setConnectTimeout(15000);
//设置读取远程返回的数据时间
connection.setReadTimeout(60000);
connection.connect();
if(connection.getResponseCode() == 200) {
is = connection.getInputStream();
br = new BufferedReader(new InputStreamReader(is,"UTF-8"));
StringBuffer sbf = new StringBuffer();
String temp = null;
while((temp = br.readLine())!= null) {
sbf.append(temp);
sbf.append("\r\n");
}
result = sbf.toString();
/* Map headers = connection.getHeaderFields();
keys = headers.keySet();
for(String key:keys) {
String val = connection.getHeaderField(key);
System.out.println(key+" "+val);
}*/
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
if(null != br) {
try {
br.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(null!=is) {
try {
is.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
connection.disconnect();
}
return result + cookie;
}
public String doPost(String httpurl,String token,String params) {
HttpURLConnection connection = null;
InputStream is = null;
OutputStream os = null;
BufferedReader br = null;
String result = null;
Set<String> keys;
String cookie = "空";
try {
URL url = new URL(httpurl);
connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setRequestMethod("POST");
connection.setUseCaches(false);
connection.setInstanceFollowRedirects(true);
connection.setRequestProperty("Content-Type", "application/json");
if(!token.contains("!")) {
connection.setRequestProperty("Authorization", "Bearer "+token);
}
//设置连接主机的时间
connection.setConnectTimeout(15000);
//设置读取远程返回的数据时间
connection.setReadTimeout(60000);
connection.connect();
if(!params.contains("!")) {
OutputStream out = connection.getOutputStream();
out.write(params.getBytes("UTF-8"));
out.flush();
out.close();
}
//添加内容
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String lines;
StringBuffer sbf = new StringBuffer();
while((lines = reader.readLine()) != null) {
lines = new String(lines.getBytes(),"utf-8");
sbf.append(lines);
}
result = sbf.toString();
connection.disconnect();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
if(null != br) {
try {
br.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(null != is) {
try {
is.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(null != os) {
try {
os.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
return result;
}
}
import java.util.Random;
public class MyRandom {
public String random(char[] letters,int j) {
Random random = new Random();
String str = "";
int index = 0;
for(int i=0;i<j;i++) {
index = random.nextInt(letters.length);
str += letters[index];
}
return str;
}
}
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Regex {
public String[] regex(String str,String rex) {
Pattern p;
Matcher m;
String[] strs=new String[100];
int index=0;
p = Pattern.compile(rex);
m = p.matcher(str);
while(m.find()) {
strs[index] = m.group();
index++;
}
return strs;
}
}