接入“教学管理信息服务平台”,实现模拟登陆

本文最后更新于2021年4月3日,可能由于时间等因素导致内容失效,请自行辨别或联系作者。

一、前言

在构思毕设的时候,为了提升用户的体验,心想能不能接入学校的“教学管理信息服务平台”,这样就可以让用户无需注册,也无需再多记一个单独的账号和密码。当然,学校肯定不会给接口,只能是自己分析登录的过程,用代码实现模拟登陆。

话不多说,开搞,本篇文章没有仔细整理,而是边分析,边记录下来,重在跟大家分享我分析思考的过程。之前也写了一篇更基础的,如果你没有看过,可以先看之前的文章。

小白也能看得懂的——新版学校电费查询接口抓包思路分享

二、分析过程

访问首页,看到发起了一次请求,传入了一个time的参数,应该是个时间戳,我们解析看看。

确实是个时间戳,再看看返回了内容。

通过请求的名字,猜测是获取公钥,返回的是两个不知道什么意思的值,不管,先登录看看。

登录到后台之后,这期间只发起了一次请求。传入了学号,看这命名,再结合登录后的页面,应该是“查询我的应用”。返回的是一些路径,没什么用。

到这,似乎没有什么有用的请求了。目前可以说是什么都没挖掘出来,不知道login_getPublicKey是干嘛的,也不知道返回的参数有什么用,就连登陆调用了哪个接口也不知道。

既然从请求上分析不出来,那再看看js文件和网页的源代码。

这部分的js文件,是在登录前请求的,出去jquery相关的,框框内的比较可疑,依次看一下。

jsbn.js

看不懂,不过从路径上来看,猜测是跟加密有关的库,不过这肯定不是加密的核心文件/逻辑。

prng4.js

同上,也看不懂,估计也是跟加密有关的库。

rng.js

看到这,已经能够确定这几个js,肯定是跟随机数生成、加密相关的库。

剩下的这两个,应该是比较熟悉的名词了。

不过,这些可以说是第三方的库,对于我们没有任何帮助,还得接着看。需要找到如何加密的逻辑。最后的希望是login.js了,若里面也没有相关的逻辑,那可能就没法继续了。

好在,看到login.js之后,这就是核心的加密代码了。

那接下来的目标,就是分析这个代码,弄清楚加密(一开始的那两个参数是干嘛的,如何使用等)、登录的整个流程。我们尽可能详细的贴出我分析的过程,跟大家一起交流。

后续为了方便阅读代码,我先下载下来,在IDE中打开。

到这,我们就知道了前面login_getPublicKey获取的值是用来干嘛的了,以及,知道了密码的加密过程:将modulus和exponent分别转换为16进制,作为RSA的公钥,然后对密码进行加密,再转换为Base64。

分析到这,上面的代码应该是请求进行校验身份了,但是从我们之前浏览器显示的请求来看,并没有上述截图里面的请求,那继续看看点击登录之后,下半部分请求的JS,如果还是没有,可以尝试用抓包工具,把完整的请求抓下来,看看漏了没。

但是,分析下来,后续的请求中,的确没有login_cxDlxgxx的,这个时候,我想到一般这种教务系统都比较老了,有不少仍在使用struts和jsp,可能会返回整个html,所以再看看其他的请求类型中有没有。

通过这个请求,把我们之前的一系列参数都传入了,同时返回的状态码是302,这应该是真正的登录接口,那js文件中的那个接口又是怎么回事呢?重新看了一遍Js代码,发现只有$(“#mmsrddcshkzfs”).val()==”1″时才会走此处的逻辑,光顾着分析去了,忘了仔细看逻辑….然后我页面看了一下,发现是”0″,话说mmsrddcshkzfs这个变量名我是真的看不懂….密码输入等待测试看再发送??

算了,不管了,继续看Js。

嗯,直接提交表单的方式,用action登录,如果我没记错的话,后台是struts吧?

分析一圈,还是通过浏览器,才找到的登录请求,不过总算是知道了加密的方式了,这波也不亏。那接下来仔细看登录的请求。

  • csrftoken,到目前为止,还不知道这个值是怎么来的,待会儿再分析。
  • yhm,为输入的用户名
  • mm,为加密后的密码

在js和请求中,都没有发现csrftoken的身影,那我们再去登录页面的源代码搜一下。

成功找到了,并且每重新访问(不是刷新)一次,都不同,应该是根据time这个时间戳生成的。这么做的目的,应该是为了防止跨站域请求伪造。

到此,登录所需的几个参数全部知道是如何来的了,接下来只需要凑齐这几个参数,调用请求就行。再开始撸代码之前,我们先整理一下流程。

三、代码

这里用Springboot来实现,使用jsoup这个库,具体使用可以见末尾的链接。实现步骤:①请求login_slogin.html,获取csrftoken和Cookies;②请求login_getPublicKey.html,获取公钥,并加密密码;③请求login_slogin.html,进行登录。

public class EasService {

    private final String url = "http://xxxxx.edu.cn";
    private Map<String,String> cookies = new HashMap<>();
    private String modulus;
    private String exponent;
    private String csrftoken;
    private Connection connection;
    private Connection.Response response;
    private Document document;

    private String stuNum;
    private String password;

    public EasService(){

    }

    public ResponseCode login(String stuNum,String password) throws Exception{
        log.info("通过教务系统登录:学号={}",stuNum);
        this.stuNum = stuNum;
        this.password = password;
        getCsrftoken();
        getRSApublickey();
        return beginLogin();
    }

    // 获取csrftoken和Cookies
    public void getCsrftoken(){
        try{
            connection = Jsoup.connect(url+ "/xtgl/login_slogin.html?language=zh_CN&_t="+new Date().getTime());
            connection.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36");
            response = connection.execute();
            cookies = response.cookies();
            //保存csrftoken
            document = Jsoup.parse(response.body());
            csrftoken = document.getElementById("csrftoken").val();
        }catch (Exception ex){
            ex.printStackTrace();
        }
    }

    // 获取公钥(是通过modulus和exponent加密而来的)并加密密码
    public void getRSApublickey() throws Exception{
        connection = Jsoup.connect(url+ "/xtgl/login_getPublicKey.html?" +
                "time="+ new Date().getTime());
        connection.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36");
        response = connection.cookies(cookies).ignoreContentType(true).execute();
        JSONObject jsonObject = JSON.parseObject(response.body());
        modulus = jsonObject.getString("modulus");
        exponent = jsonObject.getString("exponent");
        String mmPassword = RSAEncoderUtil.RSAEncrypt(password, Base64Util.b64tohex(modulus), Base64Util.b64tohex(exponent));
        password =  Base64Util.hex2b64(mmPassword);
    }

    //登录
    public ResponseCode beginLogin() throws Exception{
        connection = Jsoup.connect(url+ "/xtgl/login_slogin.html");
        connection.header("Content-Type","application/x-www-form-urlencoded;charset=utf-8");
        connection.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36");

        connection.data("csrftoken",csrftoken);
        connection.data("yhm",stuNum);
        connection.data("mm",password);
        connection.data("mm",password);
        connection.cookies(cookies).ignoreContentType(true)
                .method(Connection.Method.POST).execute();
        response = connection.execute();
        document = Jsoup.parse(response.body());
        if(document.getElementById("tips") == null){
            return ResponseCode.success(cookies);
        }else{
            return ResponseCode.error(document.getElementById("tips").text());
        }
    }
}

其中,Cookies是用来判断用户登录状态的,具体就不再分析了,相信各位如果掌握了上面的步骤,剩下的也就很简单了。

后续我会把成绩查询和课表查询的也抓一下,课设中需要用到这两个接口。如果你也有兴趣,可以把它开发为一个完整的项目,可以加入抢课等功能。

四、其他知识

1、RSA加密详解:https://juejin.im/post/5d74581de51d453bb13b66a1

2、Jsoup库:https://cloud.tencent.com/developer/article/1508640

五、后续

使用上述的分析和代码,确实能够登录成功,进入到后台的页面,但是,当我拿到JSSESSIONID和insert_cookie之后,用postman去请求查询成绩的页面,发现会被拦截,返回的是登录页面。

仔细观察后才发现,当我们访问登录页面,就会生成JSSESSIONID和insert_cookie,一直到登录到后台,都是不变的,但是,在我们调用login_slogin.html登录成功后,后台会进行重定向,具体可以看下图。

既然这样,那直接获取Set-Cookie不就好了嘛?顺着这个思路,很快有了下面的代码。

Jsoup在解决302的问题上,似乎没有找到好的方式,网上找到唯一相关的是一个禁止跳转的参数,但加了也没用。这就导致我们在登录成功后,Cookies仍然是默认的,所以这个时候调用其他页面自然就行不通了。

随后我在登录的位置,打断点,去看下response对象里面的内容,结果还真被我发现了一些端倪。

兴奋的我立马用req下面的JSSESSIONID和insert_cookie用postman进行请求,成了~~ 接下来,只需要进行判断登录成功与否,然后获取req下面的cookie了~

But!我搜了一下,也看了一下相关的方法,似乎没有办法获取到….网上找不到,那就自己折腾看看,先看看源码。

发现其实是Connection.Request,那就好办了,我们学Connection.Response新加一个变量就行了,Response这里就用不到了,随后,我们再来看下Request的内容。

最后,拿着这两个cookie,想干啥就干啥~

[copyright][/copyright]

 

为TA充电
共{{data.count}}人
人已赞赏
技术交流破解

Jetbrains全家桶永久破解方法(2019、2020)

2020-1-4 12:49:39

NAS技术交流

纯小白从零搭建一套属于自己的NAS——总篇

2020-5-7 15:06:54

11 条回复 A文章作者 M管理员
  1. jackzhong

    感谢分享

  2. 发福利

    看不懂

  3. 纯黑洒洒水

    学习一下,不懂就学

  4. octopus

    学习了解一下。

  5. 另外在浏览器那里截图有时需要把网址带上导致让收藏夹都截上不太好的话,可以按下CtrlShiftB临时隐藏此区域,就不用后期打码去了

    • get!

  6. 另外mmsrddcshkzfs 这个应该是是 密码输入得到参数后控制发送,哎前公司给研究所做事天天是这我练出来了 ✗粑粑✗

    • 牛批!!! ✗棒棒的✗

  7. 让我理理逻辑: 1.首先抓包逆向到前端加密逻辑(加密方式比较常规也无混淆) 2抓包response对象模拟插入正常cookie 最后模拟登录成功 之后开始使用各类api操作数据

    • 其实,通过第五部分的后续可以看出,前期登录的cookie可以不用传,因为登录之后,会更换cookie,之后的cookie才是有效了。同时,登录的时候,应该用request,具体分析见第五部分内容。所以逻辑大概是这样:1、抓包逆向到前端加密逻辑获取加密后的密码,即mm入参。2、获取csrftoken。3、请求login_slogin.html,传入相关参数,通过request进行请求,进而获取页面的某个可以用来判断是否登录成功的DOM节点,进行判断是否登录成功。4、登录成功后,保存有效的cookies即可。

    • 轻尘

      可以的 思路理清了 ✗微笑✗

个人中心
今日签到
有新私信 私信列表
搜索