CONEX 802.1x 核心源代码

学校在使用华为的虚拟播号客户端,买了账号后运行客户端程序,填上用户名密码就能用,10块/月。速度很不错。
可惜的是只有针对pc用户的客户端程序, 小弟一无名小卒,向学校提了1年了还没动静。一直没发上网。
转机是最近发现了有个叫thorqq网友用vc自己写了这个客户端程序的破解版,当然还是针对pc的,不过去掉了多网卡监视(一台机器挂两块网卡,共享连接以前是不行的,现在可以了。我当然试过了,可是宿舍的朋友都是玩魔兽世界的主,不是我没速度就是他没速度)还有一些其他限制(比如在线看电影老是吊线)。
小弟在想是不是能有人写个jave版的出来?当然不是为个人了,thorqq在他的blog里提到中国还有几所大学也在受苦中,嘿嘿

以下给出我能找到的所有资料,我是个编程白吃,但凡能会一点我也不会在这里“坐享其成”,在这里只能尽力而为之,也不知道这个程序有多难,要是不难的话,希望兴趣的朋友能帮到就帮帮,其实最多也就句谢谢,但确实是帮到大忙了。


thorqq的blog: http://blog.csdn.net/thorqq



能找到的一点可能有用的东西(其实我也不知道)
bool UserSended = false; //用于标记是否发送过密码
bool FirstPacket = true; //用于标记第一个包是否已经发送
bool online = false; // 是否在线的标记
HWND hOwner = 0; // 界面的窗体句柄
pcap_t *fp = NULL; // 网卡设备


初始化各种待发送的包,略(包结构参考有关资料)
unsigned char ConnectBuf[60] 请求连接包
unsigned char DisconnectBuf[60] 请求断开包
unsigned char UnknowBufA[67] 我不知道的包
unsigned char PasswordBuf[60] 包含密码、用户名的包
unsigned char KeepBuf[60] 保持连接的包,包含IP、用户名
unsigned char SendUsernameBuf[60] 发送IP、用户名的包


//打开网卡,用pcap_findalldevs()获得*device
bool OpenDevice(char* device)
{
char errbuf[PCAP_ERRBUF_SIZE];
//抓取的最大包的字节为60, 20ms抓取一次
if ((fp = pcap_open_live(device, 60, 1, 20, errbuf)) == NULL)
return false;

return true;
}


void CloseDevice()// 关闭网卡
{
if (fp != NULL)
{
pcap_close(fp);
}
}


void DeviceLoop()// 启动包处理循环


{
if (fp == NULL)
return;
pcap_loop(fp, 0, packet_handler, NULL);
}


// 分析包并产生相应动作
void packet_handler(unsigned char *param,
const struct pcap_pkthdr *header,
const unsigned char *pkt_data)
{
// 判断包的类型的标志
static unsigned char Type[4] = {0x88, 0x8e, 0x01, 0x00}; //协议类型
static unsigned char SessionFlagA[3] = {0x00, 0x05, 0x01}; //保持连接包
static unsigned char SessionFlagB[3] = {0x00, 0x05, 0x01};
static unsigned char Unknow[3] = {0x00, 0x05, 0x02};
static unsigned char RequestPwdA[3] = {0x00, 0x16, 0x01}; //请求密码包
static unsigned char RequestPwdB[3] = {0x00, 0x16, 0x04};
static unsigned char SuccessA[3] = {0x00, 0x04, 0x03}; //成功认证包
static unsigned char SuccessB[3] = {0x00, 0x04, 0x00};
static unsigned char ByeA[3] = {0x00, 0x06, 0x04}; //成功断线包
static unsigned char ByeB[3] = {0x00, 0x07, 0x08};


// 找包的大小为60个字节, 协议为EAPoL的包
if (header->len == 60 && !memcmp(pkt_data + 0x0c, Type, 4))
{
//获得以后我们所要回复的MAC地址,并放入包
if (FirstPacket)
{
FirstPacket = false;
ChangeDestMac(pkt_data + 0x06);
}

// 判断是否为对话维持包或要求发送用户名的包(这两种包的特征是一样的)
if (!memcmp(pkt_data + 0x10, SessionFlagA, 3) &&
!memcmp(pkt_data + 0x14, SessionFlagB, 3))
{
if (online)//已经上线,发送对话维持包
{
SetSessionID(pkt_data + 0x13); //设置标识位
pcap_sendpacket(fp, KeepBuf, 60);
if (online == false)
{
online = true;
ShowOnline(); //上线处理
}
}
else // 否则就发送用户名
{
SetUsernameID(pkt_data + 0x13); //设置标识位
pcap_sendpacket(fp, SendUsernameBuf, 60); //包含IP、用户名
UserSended = true;
}
return;
}


// 判断是否为未知类型的包
if (!memcmp(pkt_data + 0x10, SessionFlagA, 3) &&
!memcmp(pkt_data + 0x14, Unknow, 3))
{
pcap_sendpacket(fp, UnknowBufA, 67); //固定的包
return;
}


// 判断是否为要求发送密码的包
if (!memcmp(pkt_data + 0x10, RequestPwdA, 3) &&
!memcmp(pkt_data + 0x14, RequestPwdB, 3))
{
SetPasswordID(pkt_data + 0x13); //设置标识位
SetMd5Buf(pkt_data + 0x13, pkt_data + 0x18);//MD5加密
pcap_sendpacket(fp, PasswordBuf, 60); //发送包含密码和用户名的包
return;
}

// 判断是否为认证成功的包 code=3
if (!memcmp(pkt_data + 0x10, SuccessA, 3) &&
!memcmp(pkt_data + 0x14, SuccessB, 3))
{
if (online == false)
{
online = true;
ShowOnline(); //上线处理
}
return;
}


// failure包 code=4
if (!memcmp(pkt_data + 0x10, ByeA, 3))
{
// 判断是否为断线成功的包
if(!memcmp(pkt_data + 0x14, ByeB, 3))
{
if (online == true)
{
online = false;
ShowOffline(); //离线处理


UserSended = false; // 重置UserSended
}
return;
}
else// 其他离线包
{
ShowOffline();
{//提示出错原因
strMsg.Format("%s", (pkt_data+0x18));
MessageBox(hOwner, strMsg, "连接失败",MB_ICONWARNING);
}
return;
}
}
}
}
十分感谢cshacker的帮助













CONEX 802.1x 核心源代码
#ifndef __ONEX_H__
#define __ONEX_H__

#include "pcap.h"
#include "md5.h"

#include "stdafx.h"

#define WM_ONLINE (WM_USER + 101) // 通知界面已经上线的自定义消息
#define WM_OFFLINE (WM_USER + 102) // 通知界面已经下线的自定义消息

namespace cshacker
{
pcap_t *fp = NULL; // 网卡设备
bool UserSended = false; // 用于标记是否发送过密码
bool FirstPacket = true; // 用于标记第一个包是否已经发送

bool online = false; // 是否在线的标记
char Passwd[64]; // 存放用户的密码
u_int PasswdLen = 0; // 密码字符串长度(不是存储长度)
u_char HuaweiMAC[6] = { // 存放后面和你通讯的华为MAC地址, 这个地址对于每个用户都是不同的(应该是个虚拟的地址)
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
HWND Owner = 0; // 界面的窗体句柄(通知消息的时候需要)

// 下面是初始的各种包
u_char ConnectBuf[60] = { // 连接请求的包(发送目标为华为的组播地址01-80-c2-00-00-03)
0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x8e, 0x01, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

u_char DisconnectBuf[60] = { // 断开请求的包
0x00, 0xe0, 0xfc, 0x17, 0xb5, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x8e, 0x01, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

u_char UnknowBufA[67] = { // 未知类型的包
0x00, 0xe0, 0xfc, 0x17, 0xb5, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x8e, 0x01, 0x00,
0x00, 0x31, 0x02, 0x01, 0x00, 0x31, 0x02, 0x01, 0x16, 0x2e, 0x25, 0x4d, 0x3b, 0x5f, 0x43, 0x5f,
0x5d, 0x40, 0x5d, 0x5f, 0x5c, 0x5d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x02, 0x16, 0x5c,
0x5a, 0x5d, 0x5e, 0x54, 0x5e, 0x55, 0x5b, 0x5c, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d,
0x6d, 0x6d, 0x6d
};

u_char PasswordBuf[60] = { // 发送密码的包
0x00, 0xe0, 0xfc, 0x17, 0xb5, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x8e, 0x01, 0x00,
0x00, 0x1f, 0x02, 0x00, 0x00, 0x1f, 0x04, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

u_char SessionBuf[60] = { // 维持对话的包
0x00, 0xe0, 0xfc, 0x17, 0xb5, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x8e, 0x01, 0x00,
0x00, 0x15, 0x02, 0x00, 0x00, 0x15, 0x14, 0x00, 0x15, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

u_char SendUsernameBuf[60] = { // 发送用户名的包
0x00, 0xe0, 0xfc, 0x17, 0xb5, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x8e, 0x01, 0x00,
0x00, 0x14, 0x02, 0x00, 0x00, 0x14, 0x01, 0x15, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

void ChangeDestMac(const u_char *MAC) // 更改各种包的Destination MAC
{
memcpy(DisconnectBuf, MAC, 6);
memcpy(UnknowBufA, MAC, 6);
memcpy(PasswordBuf, MAC, 6);
memcpy(SessionBuf, MAC, 6);
memcpy(SendUsernameBuf, MAC, 6);
}

void ShowOnline() // 通知界面上线了
{
SendMessage(Owner, WM_ONLINE, 0, 0);
}

void ShowOffline() // 通知界面下线了
{
SendMessage(Owner, WM_OFFLINE, 0, 0);
}

bool OpenOneX(char* device) // 更加网卡的设备名打开网卡设备
{
char errbuf[PCAP_ERRBUF_SIZE];
// 我们只关心大小为508个字节的数据包, 所以抓取的最大包的字节为508
// 20ms抓取一次
if ((fp = pcap_open_live(device, 508, 1, 20, errbuf)) == NULL)
return false;

return true;
}

void CloseOneX() // 关闭网卡设备
{
if (fp != NULL)
{
pcap_close(fp);
}
}

int ShowDevs() // 显示网卡设备名称及描述列表
{
pcap_if_t *alldevs, *d;
u_int i=0;
char errbuf[PCAP_ERRBUF_SIZE];

if (pcap_findalldevs(&alldevs, errbuf) == -1)
{
fprintf(stderr,"Error in pcap_findalldevs: %s/n", errbuf);
exit(1);
}

for(d=alldevs; d; d=d->next)
{
printf("%d. %s", ++i, d->name);
if (d->description)
printf("/n/t%s/n", d->description);
else
printf("/n/tNo description available/n");
}

if(i==0)
{
printf("/nNo interfaces found! Make sure WinPcap is installed./n");
return -1;
}

pcap_freealldevs(alldevs);

return 0;
}

void SetPassword(const char *Password) // 设置密码
{
strcpy(Passwd, Password);
PasswdLen = strlen(Passwd);
}

bool SetSessionID(const u_char *SessionID) // 设置对话维持包的ID
{
if (SessionBuf == NULL)
return false;

memcpy(SessionBuf + 0x13, SessionID, 1);

return true;
}

void SetUsernameID(const u_char *UsernameID) // 设置发送用户名包的ID
{
memcpy(SendUsernameBuf + 0x13, UsernameID, 1);
}

void SetPasswordID(const u_char *PasswordID) // 设置发送密码包的ID
{
memcpy(PasswordBuf + 0x13, PasswordID, 1);
}

void ShowPacket(int id) // 显示包的内容(调试的时候可以使用)
{
u_char* Buf;
switch(id)
{
case 1: Buf = SessionBuf; break;
case 2: Buf = SendUsernameBuf; break;
case 3: Buf = ConnectBuf; break;
case 4: Buf = DisconnectBuf; break;
case 5: Buf = UnknowBufA; break;
case 6: Buf = PasswordBuf; break;
default: return;
}

int content = 0;
if (Buf == NULL)
{
printf("None/n");
return;
}

int ln = -1;
int j = 60;
if (id == 5)
j = 67;
for (int i = 0; i < j; ++i)
{
if (16 == ++ln)
{
printf("/n");
ln = 0;
}
content = (int)(*(Buf + i));
if (content > 15)
printf("%2X ", content);
else
printf("0%x ", content);
}
printf("/n/n");
}

void SetMd5Buf(const u_char *ID, const u_char *chap) // 产生加密后的回复密文
{
static u_char TmpBuf[1 + 64 + 16];
static MD5_CTX md5T;
static u_char digest[16];
memcpy(TmpBuf + 0x00, ID, 1);
memcpy(TmpBuf + 0x01, Passwd, PasswdLen);
memcpy(TmpBuf + 0x01 + PasswdLen, chap, 16);
md5T.MD5Update(TmpBuf, 17 + PasswdLen);
md5T.MD5Final(digest);
memcpy(PasswordBuf + 0x18, digest, 16);
}

void packet_handler( // 分析包并产生对应触发动作的函数(整个认证系统的核心)
u_char *param,
const struct pcap_pkthdr *header,
const u_char *pkt_data)
{
// 判断这种包的类型的标志
static u_char PType[4] = {0x88, 0x8e, 0x01, 0x00};
static u_char SessionFlagA[3] = {0x00, 0x05, 0x01};
static u_char SessionFlagB[3] = {0x00, 0x05, 0x14};
static u_char UnknowFlagA[3] = {0x00, 0x05, 0x02};
static u_char RequestPwdFlagA[3] = {0x00, 0x16, 0x01};
static u_char RequestPwdFlagB[3] = {0x00, 0x16, 0x04};
static u_char SuccessFlagA[3] = {0x00, 0x04, 0x03};
static u_char SuccessFlagB[3] = {0x00, 0x04, 0x00};
static u_char ByeFlagA[3] = {0x00, 0x06, 0x04};
static u_char ByeFlagB[3] = {0x00, 0x07, 0x08};

// 找包的大小为508个字节, 协议为EAPoL的包
if (header->len == 508 && !memcmp(pkt_data + 0x0c, PType, 4))
{
// 在此处我们获得以后我们所要回复的MAC地址
if (FirstPacket)
{
FirstPacket = false;
ChangeDestMac(pkt_data + 0x06);
}

// 判断是否为对话维持包或要求发送用户名的包(这两种包的特征是一样的)
if (!memcmp(pkt_data + 0x10, SessionFlagA, 3) &&
!memcmp(pkt_data + 0x14, SessionFlagB, 3))
{
// 如果发送过用户名则发送对话维持包
if (UserSended)
{
SetSessionID(pkt_data + 0x13);
pcap_sendpacket(fp, SessionBuf, 60);
if (online == false)
{
online = true;
ShowOnline();
}
}
else // 否则就发送用户名
{
SetUsernameID(pkt_data + 0x13);
pcap_sendpacket(fp, SendUsernameBuf, 60);
UserSended = true;
}
return;
}

// 判断是否为未知类型的包(这种包好像老版本没有,有点不规则)
if (!memcmp(pkt_data + 0x10, SessionFlagA, 3) &&
!memcmp(pkt_data + 0x14, UnknowFlagA, 3))
{
// 发送对应的回复包(这个包的内容是固定的,估计是版本协商)
pcap_sendpacket(fp, UnknowBufA, 60);
return;
}

// 判断是否为要求发送密码的包
if (!memcmp(pkt_data + 0x10, RequestPwdFlagA, 3) &&
!memcmp(pkt_data + 0x14, RequestPwdFlagB, 3))
{
SetPasswordID(pkt_data + 0x13);
SetMd5Buf(pkt_data + 0x13, pkt_data + 0x18);
pcap_sendpacket(fp, PasswordBuf, 60);
return;
}

// 判断是否为认证成功的包
if (!memcmp(pkt_data + 0x10, SuccessFlagA, 3) &&
!memcmp(pkt_data + 0x14, SuccessFlagB, 3))
{
if (online == false)
{
online = true;
ShowOnline();
}
return;
}

// 判断是否为断线成果的包
if (!memcmp(pkt_data + 0x10, ByeFlagA, 3) &&
!memcmp(pkt_data + 0x14, ByeFlagB, 3))
{
if (online == true)
{
online = false;
ShowOffline();

UserSended = false; // 重置UserSended
}
return;
}

}
}

void OneXLoop() // 启动包处理循环(关键)
{
if (fp == NULL)
return;

pcap_loop(fp, 0, packet_handler, NULL);
}

// 设置Source Mac
bool SetMacAddress(u_char *MacAddress)
{
if (SessionBuf == NULL)
return false;

memcpy(SessionBuf + 0x06, MacAddress, 6);
memcpy(SendUsernameBuf + 0x06, MacAddress, 6);
memcpy(ConnectBuf + 0x06, MacAddress, 6);
memcpy(DisconnectBuf + 0x06, MacAddress, 6);
memcpy(UnknowBufA + 0x06, MacAddress, 6);
memcpy(PasswordBuf + 0x06, MacAddress, 6);

return true;
}

bool SetIpAddress(u_char *IpAddress)
{
if (SessionBuf == NULL)
return false;

memcpy(SessionBuf + 0x1a, IpAddress, 4);
memcpy(SendUsernameBuf + 0x19, IpAddress, 4);

return true;
}

bool SetUsername(char *Username)
{
if (SessionBuf == NULL)
return false;

memcpy(SessionBuf + 0x1e, Username, strlen(Username));
memcpy(SendUsernameBuf + 0x1d, Username, strlen(Username));
memcpy(PasswordBuf + 0x28, Username, strlen(Username));

return true;
}

bool Connect() // 发送请求连接的包
{
if (fp == NULL)
return false;

return !pcap_sendpacket(fp, ConnectBuf, 60);
}

bool Disconnect() // 发送断开连接的包
{
if (fp == NULL)
return false;

return !pcap_sendpacket(fp, DisconnectBuf, 60);
}

};

#endif

 

这是刚翻出来的程序解释









系统平台:WinXP SP2,VC6,Winpcap3
相关软件:Sniffer 4.70530, H3C 802.1x客户端(V2.20-0210)
参考文档:IEEE Std 802.1X-2001

之所以要自己编这个软件,是因为使用华为3com 802.1x客户端有很大的限制。首先,它能够检查系统使用代理软件和多网卡情况,万一发现有这些情况,马上迫使客户机下线,使得一个帐号仅能供一台电脑上网。其次,网络依靠帐号和IP绑定进行认证上网,这就限定了一个帐号只能在一个网关下使用,如果在其他网段中使用该帐号,就会出现IP绑定失败的提示。

在编程之前,先要对原版客户端工作机制进行分析,这里使用Sniffer进行抓包分析数据。原版客户端工作流程如下:
1. 点击认证按钮,客户端发送EAPOL-START包,请求认证。
2. 交换机发送60字节包。
3. 客户端发送67字节包。对于2,3两步,每次认证都是固定不变的数据包,不知道是何意义,可能是版本协商。
4. 交换机发送请求用户名包。
5. 客户端发送含用户名和本机IP的包。
6. 交换机发送含MD5-Challenge的包。
7. 客户端发送含经MD5加密后的密码和用户名的包。
8. 如果帐号、IP合法,交换机发送成功认证包,和失败包(这个失败包可能和检查代理、多网卡有关,可以不考虑);如果不合法,交换机发送失败离线包,此包包含失败原因。
9. 若成功认证,每隔15秒重复第4,5步,以确认客户端是否在线。
10. 如果点击客户端下线按钮,客户端发送EAPOL-LOGOFF包。
11. 交换机发送成功下线包。
注意,以上发送的包中,除了第3步的包长度为67个字节,其他都是60个字节。
包结构为:(1)8字节 目标MAC(请求认证时为组播地址,其他时候为华为MAC地址)
(2)8字节 源MAC
(3)2字节 协议(88 8e)
(4)1字节 版本(01)
(5)1字节 包类型(01表示请求连接,02表示请求断开)
(6)2字节 包长度(按照标准协议是从下面一下字节开始计算,到最后一个字节,不过现在已经被华为改了)
(7)1字节 code(01:请求,02:回复,03:成功,04:失败)
(8)1字节 标识(从01开始,每成功收发一次,自动加一,到FF后清零重新开始)
(9)2字节 包长度(这是华为自定义的,不知道如何计算出来的)
(10)有用数据(华为自定义,可包含用户名、加密后的密码、IP、出错信息等)
具体包的结构可参看IEEE Std 802.1X-2001。也可用Sniffer抓包分析一下,很容易的。

经过分析之后就可以开始着手编程了。程序中使用Winpcap进行抓包发包,因此需下载Winpcap开发库,把wpcap.lib添加到VC中。同时还需要准备MD5加密算法类文件(网上很多下载的,如果你够强,也可以自己编一个,呵呵)。
程序中用到的Winpcap函数如下:
1. pcap_findalldevs()获得网卡名称
2. pcap_open_live()打开网卡,每20ms抓包一次,抓取的包的长度为60字节
3. pcap_loop()启动包处理循环
4. packet_handler()分析包,并触发动作。这是整个认证过程的核心,用来对接收到的包进行判断,并发送相应的回复包,同时控制程序的状态。
5. pcap_sendpacket()发送指定长度的包
6. pcap_close()关闭网卡

在编程中需要注意的地方:
1. 组播地址为01 80 c2 00 00 03。华为的MAC地址对于每个用户是不同的,可以在交换机回复的第一个包中得到。
2. 出错信息包含在包内,因此只需先判断是否为失败包(包结构第四条),然后从包中提取出错误信息即可。
3. 对于费用查询功能,我暂时还没使用过,应该也可以用sniffer分析出来

本人学VC才半年,对802.1x协议也只是草草地看了其中的一部分。在某些电脑上回出现关闭出错的情况。

下载地址 http://thorqq.ys168.com
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值