吵吵   2016-04-06  阅读:1,088

我是觉得写代码比写文档要容易,代码写起来,很多以前你就明白的东西,一笔就略过了,文档写起来,写的明白易懂就不容易。

我要是还不写完这篇文章的话,基本上我就快忘了我是怎么写代码的。

废话不说了,开始编程吧。

一、搜索服务器。

我们首先要向1.1.1.8广播一个包获取服务器的地址,那么这个包应该包括啥呢?

我们看它结构:(SERVER = 0X0C)

SERVER:
SESSION as string
IP as string(16)
MAC as data(16)
IP地址和MAC是很好办的,直接获取就好了,可是SESSION呢,我怎么知道这个代表会话的唯一ID到底是如何构成的?

没有关系,我们造不出来,我们可以用wireshark去抓官方程序发送的包,然后把SESSION元模原样的赋值过去就好了:

因此这个包的构造就是如下的代码(所有代码都用c#):

public class SearchSend
{

public byte[] SessionBytes;
public string IP;
public byte[] Mac;

public SearchSend(string ip,byte[] mac)
{
IP = ip;
Mac = mac;
SessionBytes = new byte[35]
{123,123,129,107,110,100,133,113,120,123,
106,108,102,117,135,118,130,106,119,135,
101,101,129,135,100,120,114,109,137,110,
110,101,133,114,103
};
}

public byte[] ToBytes()
{

//计算包的长度
int iLen = 55 + 8 + 2 + IP.Length;
byte[] bts = new byte[iLen];
bts[0] = 0x0C;
bts[1] = 81;
//填充MD5校验区
for (int i = 2; i < 18; i++) { bts[i] = 0; } bts[18] = 8; bts[19] = 37; //填充Sesssion SessionBytes.CopyTo(bts, 20); //填充IP地址 bts[55] = 9; bts[56] =Convert.ToByte(IP.Length); Encoding.ASCII.GetBytes(IP).CopyTo(bts, 57); int iOffset = 57 + IP.Length; //填充MAC地址 bts[iOffset] = 7; iOffset++; bts[iOffset] = 8; iOffset++; Mac.CopyTo(bts, iOffset++); //md5校验计算并且填入 MD5 md5 = new MD5CryptoServiceProvider(); byte[] output = md5.ComputeHash(bts); output.CopyTo(bts, 2); return bts; } } 当这个包被发送出去后,会1.1.1.8会返回服务器的ip地址。 public class SearchReceive { public string IP; static public SearchReceive FromBytes(byte[] bts) { SearchReceive search=new SearchReceive(); search.IP=bts[20].ToString()+"."; search.IP+=bts[21].ToString()+"."; search.IP+=bts[22].ToString()+"."; search.IP+=bts[23].ToString(); return search; } } 我们把20-23每个字节提取出来,再转换成整数,加上点就是IP地址了。 二、登录: 我们先看登录包包含了什么: LOGIN: MAC as data(6) 6字节的MAC地址 USERNAME as string 用户名转换为字节,编码的格式为ASCII PASSWORD as string 密码转化为字节,编码格式为ASCII IP as string IP地址,是字符串的IP地址,包含了点的,同样ASCII格式编码 ENTRY as string 认证方式,我们学校是internet了。 DHCP as boolean 是否DHCP VERSION as string 认证程序版本 好了,了解了包的所有意义后,我们构造认证包就是: public class OnlineSend { public byte[] Mac; public string User; public string Pass; public string IP; public string Entry="internet"; public int DHCP=1; public string Version="3.8.3"; public OnlineSend(byte[] mac,string user,string pass,string ip) { Mac=mac; User=user; Pass=pass; IP=ip; } public byte[] ToBytes() { int iLen=2+16+2+8+this.User.Length+2+this.Pass.Length+2+IP.Length+2+Entry.Length+3+2+Version.Length; byte[] bts = new byte[iLen]; bts[0] = 0x01; bts[1] = Convert.ToByte(iLen); for (int i = 2; i < 18; i++) { bts[i] = 0; } //mac bts[18]=7; bts[19]=8; Mac.CopyTo(bts,20); //用户名 bts[26]=1; bts[27]=Convert.ToByte(User.Length+2); ASCIIEncoding.ASCII.GetBytes(User).CopyTo(bts,28); int iOffset=28+User.Length; //密码 bts[iOffset]=2; iOffset++; bts[iOffset]=Convert.ToByte(Pass.Length+2); iOffset++; ASCIIEncoding.ASCII.GetBytes(Pass).CopyTo(bts,iOffset); iOffset+=Pass.Length; //IP地址 bts[iOffset]=9; iOffset++; bts[iOffset]=Convert.ToByte(IP.Length+2); iOffset++; ASCIIEncoding.ASCII.GetBytes(IP).CopyTo(bts,iOffset); iOffset+=IP.Length; //Entry bts[iOffset]=10; iOffset++; bts[iOffset]=Convert.ToByte(Entry.Length+2); iOffset++; ASCIIEncoding.ASCII.GetBytes(Entry).CopyTo(bts,iOffset); iOffset+=Entry.Length; //DHCP bts[iOffset]=14; iOffset++; bts[iOffset]=3; iOffset++; bts[iOffset]=1; iOffset++; //Version bts[iOffset]=31; iOffset++; bts[iOffset]=Convert.ToByte(Version.Length+2); iOffset++; ASCIIEncoding.ASCII.GetBytes(Version).CopyTo(bts,iOffset); iOffset+=Version.Length; MD5 md5 = new MD5CryptoServiceProvider(); byte[] output = md5.ComputeHash(bts); output.CopyTo(bts, 2); return bts; } } 登录包发送出去后,服务器会给我们返回认证是否成功的包: LOGIN_RET: SUCCESS as boolean SESSION as string UNKNOWN05 as char UNKNOWN06 as char MESSAGE as string UNKNOWN95 as data(24) UNKNOWN06 as char BLOCK34 as data(4) BLOCK35 as data(4) BLOCK36 as data(4) BLOCK37 as data(4) BLOCK38 as data(4) WEBSITE as string UNKNOWN23 as char UNKNOWN20 as char 返回来的信息有用的是SUCCESS(是否成功)、SESSION(这个是本次连接的唯一ID,需要记录下来,后面有用)、Message(不成功的话会有提示信息,比如账号或者密码错误,注意,一定是不成功才有Message). 其次,取MESSAGE的时候还应该注意长度,而且这个不是ASCII编码,而是GB2313编码的,c#编程如下: public class OnlineReceive { public byte[] Session; public int Success; public string Msg=""; static public OnlineReceive FromBytes(byte[] bts) { OnlineReceive online = new OnlineReceive(); online.Success = bts[20]; byte[] btsSession=new byte[bts[22]]; Array.Copy(bts,23,btsSession,0,btsSession.Length); online.Session = btsSession; if (online.Success != 1) { int iOffset = btsSession.Length + 23; if (bts[iOffset] == 0x0b) { int iMsgLen = bts[iOffset+1]; byte[] btMessage = new byte[iMsgLen]; Array.Copy(bts, iOffset + 2, btMessage, 0, btMessage.Length); online.Msg = Encoding.GetEncoding("GB2312").GetString(btMessage); } } return online; } } 有意思的是,我们发现这里的Session一般是固定长度的,即22字节,而我们去搜寻服务器时候用的Session是35个字节? 三、保持在线(呼吸包) 如果你认证收到的包是SUCESS,那么恭喜你,登录成功了,接下来的一个任务就是保持在线,这个包是间隔30秒向服务器发送的,成功与否服务器都会返回一个包。 其中有个计数器(index),如果呼吸成功,则Index加3再发送。 呼吸包结构为: BREATHE: SESSION as string 认证成功获取的SESSION填写到这里 IP as string(16) IP地址,字符串格式的,带点的。 MAC as data(16) MAC地址 INDEX as integer 计数器,初始值是0x01000000 BLOCK2A as data(4) BLOCK2B as data(4) BLOCK2C as data(4) BLOCK2D as data(4) BLOCK2E as data(4) BLOCK2F as data(4) 则c#的代码为: public class BreatheSend { public byte[] Session; public string IP; public byte[] Mac; public int Index; public byte[] BlockBytes; public BreatheSend(byte[] session,string ip,byte[] mac,int index) { Session = session; Mac = mac; IP = ip; Index = index; BlockBytes = new byte[36] { 42, 6, 0, 0, 0, 0, 43, 6, 0, 0, 0, 0, 44, 6, 0, 0, 0, 0, 45, 6, 0, 0, 0, 0, 46, 6, 0, 0, 0, 0, 47, 6, 0, 0, 0, 0 }; } public byte[] ToBytes() { int iLen = 2 + 16 +2+Session.Length+ 2+ this.IP.Length + 2 + this.Mac.Length + 2 + 4 + 36; //MessageBox.Show(iLen.ToString()); byte[] bts = new byte[iLen]; bts[0] = 0x03; bts[1] = Convert.ToByte(iLen); for (int i = 2; i < 18; i++) { bts[i] = 0; } int iOffset = 18; //session bts[iOffset] = 8; iOffset++; bts[iOffset] = Convert.ToByte(Session.Length + 2); iOffset++; Session.CopyTo(bts,iOffset); iOffset += Session.Length; //IP地址 bts[iOffset] = 9; iOffset++; bts[iOffset] = Convert.ToByte(IP.Length + 2); iOffset++; ASCIIEncoding.ASCII.GetBytes(IP).CopyTo(bts, iOffset); iOffset += IP.Length; //Mac地址 bts[iOffset] = 7; iOffset++; bts[iOffset] = Convert.ToByte(Mac.Length + 2); iOffset++; Mac.CopyTo(bts, iOffset); iOffset += Mac.Length; //index bts[iOffset] = 20; iOffset++; bts[iOffset] = Convert.ToByte(4 + 2); iOffset++; byte[] btIndex = new byte[4]; BitConverter.GetBytes((Int32)Index).CopyTo(btIndex, 0); btIndex.CopyTo(bts, iOffset); iOffset += 4; BlockBytes.CopyTo(bts, iOffset); MD5 md5 = new MD5CryptoServiceProvider(); byte[] output = md5.ComputeHash(bts); output.CopyTo(bts, 2); return bts; } 那么呼吸包返回的包就不再仔细说了 breathe.Success = bts[20]; 第20位的字节如果为1的话就是成功了。 好了,讲到这里,相信聪明如你,一定有办法自己写一个认证程序出来了。 本系列终于完!

吵吵微信朋友圈,请付款实名加入:

吵吵 吵吵

4条回应:“安朗认证协议分析与编程思路(三)”

  1. MNTPZ说道:

    以上内容是站长一行一行敲上的么,累坏了吧。

  2. Ltt空城空梦说道:

    你好,我们学校用的是安朗小蝴蝶,可以在电脑认证后路由器克隆Mac来实现路由上网,但是路由器在长时间没有连接终端后会失去认证,我网路由里写了一个一分钟访问百度一次的脚步也没有用,哥知道是什么原因吗,我的QQ1476272537

  3. themebetter说道:

    好长篇,赞一个。

发表评论

电子邮件地址不会被公开。 必填项已用*标注