与校园网斗智斗勇(三)之制作第三方安朗客户端

发布时间 2023-08-06 19:30:11作者: 一克猫

前言

  贪欲是人类与生俱来的陋习,但正是因为有贪欲,人类才能进步,“贪”才是我们探索精神的领袖。

  在前两篇博文中,我对安朗(安腾)宽带认证客户端(以下统称蝴蝶)进行了简单的爆破,达到了无障碍使用共享Wifi软件的目的,并且分析和试译了Swiftz协议(以下统称蝴蝶协议),但这并不能满足我的贪欲,所以这一篇记录了我如何从零制作一个属于自己的蝴蝶

准备

说明

  在你开始看这一篇博文之前,请务必看懂蝴蝶协议,并在动手制作之间实时查阅,这会让你的编程效率大大提高。

  根据蝴蝶协议的分析,整个蝴蝶客户端,我们需要实现的是四个主要功能:搜索、上线、心跳、下线。

  • 搜索:构建搜索包,发送搜索包从本机IP:38481.1.1.8:3850,接收返回包。
  • 上线:构建上线包,发送上线包从本机IP:3848服务器IP:3848,接收返回包。
  • 心跳:构建心跳包,循环发送心跳包从本机IP:3848服务器IP:3848(间隔30秒),循环接收返回包。
  • 下线:构建下线包,发送下线包从本机IP:3848服务器IP:3848,接收返回包。

实现

  重点提一下构建数据包,以构建搜索服务器的数据包为例(构建上线、心跳、下线数据包大同小异),至于发送接收数据包则非常简单,利用Socket发报收报即可,但要注意是基于UDP协议的。
  另外,在发送和接收之前,需要加密和解密数据包。即:

  构建数据包——加密数据包——发送数据包——接收数据包——解密数据包

  构建搜索服务器数据包部分示例代码:

  • [C]
void search_server(int sockfd,struct infoset * const pinfo){
    char md5[0x10] = {0x0};
    int  md5len = 0x10;
    char *pkt, *ppkt, *tmphost;
    struct usrinfoSet *psu = pinfo -> psu;
    struct sockaddr_in *pss = pinfo -> pss;
    int sendbytes = 51;
    
    int iplen = strlen(psu -> local_ip), maclen = 0x6;
    pkt = (char *)calloc(sendbytes, sizeof(char));
    ppkt = pkt;
    *ppkt++ = 0x0c;
    *ppkt++ = sendbytes;
    ppkt += 0x10;
    
    *ppkt++ = 0x08;
    *ppkt++ = 0x07;
    *ppkt++ = 0x00;
    *ppkt++ = 0x01;
    *ppkt++ = 0x02;
    *ppkt++ = 0x03;
    *ppkt++ = 0x04;
    ……
    ……
    ……
  • [Python]
def search_server_ip(ip, mac):
    packet = []
    packet.append(0x0c)
    packet_len = 1 + 1 + 16 + 1 + 1 + 5 + 1 + 1 + 16 + 1 + 1 + 6
    packet.append(packet_len)
    packet.extend([i * 0 for i in range(16)])
    packet.append(0x08)
    packet.append(0x07)
    packet.extend([i * 1 for i in range(5)])
    packet.append(0x09)
    packet.append(0x12)
    packet.extend([ord(i) for i in ip])
    packet.extend([i * 0 for i in range(16 - len(ip))])
    packet.append(0x07)
    ……
    ……
    ……
  • [Java]
private void searchServerIp() {
    byte packet_len = 1 + 1 + 16 + 1 + 1 + 5 + 1 + 1 + 16 + 1 + 1 + 6;
    byte[] packet = new byte[packet_len];
    byte i = -1;
    packet[++i] = 0x0c;
    packet[++i] = packet_len;
    for (; i < 17;) {
        packet[++i] = 0;
    }
    packet[++i] = 0x08;
    packet[++i] = 0x07;
    for (byte j = 0; j < 5; j++) {
        packet[++i] = j;
    }
    packet[++i] = 0x09;
    packet[++i] = 0x12;
    byte[] bytes = LOCAL_IP.getBytes();
    for (byte b : bytes) {
        packet[++i] = b;
    }
    for (int j = 0; j < 16 - bytes.length; j++) {
        packet[++i] = 0;
    }
    packet[++i] = 0x07;
    packet[++i] = 0x08;
    ……
    ……
    ……
  • [C#]
public class SearchSend
{
    public byte[] SessionBytes;
    public string IP;
    public string Mac;
    public SearchSend(string ip, string mac)
    {
        IP = ip;
        Mac = mac;
        SessionBytes = new byte[5]
        {
            0,1,2,3,4
            };
    }
    public byte[] ToBytes()
    {
        int offset = 0;
        int iLen = 51;
        byte[] bts = new byte[iLen];
        bts[offset] = 0x0C;
        offset++;
        bts[offset] = 51;
        for (int i = 1; i < 16; i++)
        {
            bts[++offset] = 0;
        }
        offset++;
        offset++;
        bts[offset] = 8;
        ……
        ……
        ……

抓包

  在构建数据包完成后,我们需要利用抓包工具来测试我们构建的数据包是否正确、发送和接收数据包是否正常,而Wireshark这款工具是一个不错的选择。

1.jpg

  我们主要检查的是包的长度、收发端口、以及最重要的Data

2.jpg

  抓包之前,我们要利用蝴蝶协议中提到的3848端口解密算法额外写一个解密程序,用来打印数据包被解密后的数据,以便我们能够更直观地检查。下图以上线数据包为例:

3.jpg

后记

  在编写过程中,一定会遇到许许多多的问题(大神忽略),路漫漫其修远兮,你要上下而求索,要时刻铭记。因为在你想要放弃的时候,其实距离终点只有一步之遥。

  下面说一下当初编写这个程序我的一些经验:

  • 登录确认包可以完全省略。
  • 如果你想节省时间,其实搜索包也可以省略,学校不会频繁更换服务类型和服务器IP地址,记下来填上即可。
  • 服务器是固定三个端口384838493850,但是本地端口是随机的话也能正常认证,绑定同样的端口是为了防止多个认证客户端同时工作,这可能会造成一些错误。
  • 上线包和下线包只发送一次,可以容许一点瑕疵,只要能成功接收到服务器返回的数据包,提取到需要的内容就行。
  • 心跳包的构建必须完全正确,因为是循环地发送和接收,其过程必须稳定,这关系到你能否顺畅地使用校园网,在调试过程中可以拿学校官方的蝴蝶数据进行比对查错。
  • 心跳包发送间隔是30秒,也可以小于30秒,如果心跳包不稳定可以尝试适当调整。
  • 在构建数据包之前要把需要填充的数据整理打包好,例如类型的转换,以及空字符的处理等等,以减少不必要的程序异常。
  • 编写这个程序很简单,但是错误处理和代码优化有点繁琐,将自己当成使用者,并不断测试程序的极限状态,你会发现bug比你头发还多(当然,其根本原因还是我学艺不精)。

  完成程序后,还可以考虑增加一点自己常用的功能,例如集成无线热点,增加NAT穿透等等……

  成品效果图:

4.jpg