MSN协议分析以及Java实现MSN登陆
一、MSN协议分析部分
1.1 基本介绍
MSN是微软推出的IM工具,他的通信协议是微软自己提出的MSNP(即MSN Protocol)。当前MSN协议最高版本为MSNP18,但可获取的资料很少。这里仅仅以MSNP12做说明。
命令是服务器与客户交互的一种方式。MSNP12的命令使用纯ASCII码,对非ASCII码使用URL编码。命令的一般格式是:
XXX TrID PARAM1 PARAM2…
其中,XXX是一个3字符的命令串,TrID是一个流水号,PARAMx是参数。最简单的命令没有流水号和参数。一个典型的MSNP命令形如
VER 1 MSNP18 MSNP17 MSNP16 CVR0
每个发送的MSN命令以回车换行作为一条命令的结束。具体命令的含义和使用参数可以参见参考资料[1]所示的MSN协议非官方wiki。
1.2 利用VER命令测试当前服务器支持版本
在服务器的messenger.hotmail.com的1863端口发送命令VER来测试。在本文中用”>”符号表示发送的命令,用”<”符号表示收到的命令。
>VER 1 MSNP18 CVR0
<VER 1 MSNP18
由以上可以看出,服务器当前支持MSNP18。类似的,可以发送多个参数的VER命令到服务器
>VER 1 MSNP18 MSNP12 CVR0
<VER 1 MSNP18 MSNP12
1.3 MSN登陆过程
MSN登录可以分为2个步骤:
(1) 向服务器messenger.hotmail.com的1863端口请求实际登录地址
(2) 在获得的新的服务器上进行登录
因此,在MSN登录过程可以看到2次产生登录请求,MSN服务器在第二次登陆时才真正去做用户认证。2次登陆过程是都是向服务器发送VER, CVR和USR这3个命令,然后处理服务器反馈的命令来进行下一步处理。
发送信息和返回信息的过程可以是异步的,也可以是同步的。这意味着用户可以发送一个命令后,进行阻塞等待回复。客户端收到回复后,再发出第二个命令。客户端也可以连续发送3个命令至服务器,而后再进行阻塞等待,获取服务器的回复后再进行后续处理。
两次登陆的不同点在于两次发送USR命令后得到的回复命令。在第一个服务器(messenger.hotmail.com)上发送USR命令后,服务器反馈时返回XFR命令。XFR命令表示连接中止。XFR的参数中包含了客户端第二次需要登陆的MSN服务器地址。而后客户端可以进行在新服务器上进行登录。在第二个服务器上发送USR命令后,服务器不再返回XFR命令,而是返回USR命令。返回的USR命令要求客户端提供用户名和密码,需要在认证服务器上进行登录,而后返回认证相关信息。
第一次登陆过程的过程的收发命令类似如下
>VER 1 MSNP12 CVR0
<VER 1 MSNP12
>CVR 2 0x0804 winnt 5.1 i386 MSNMSGR 8.1.0178 MSMSGS csdnjay@hotmail.com
<CVR 2 8.1.0178 8.1.0178 8.1.0178 http://msgruser.dlservice.microsoft.com/download/5/6/4/5646481F-33EF-4B08-AF00-4904F7677B89/ZH-CHS/Install_WLMessenger.exe http://get.live.com/cn
>USR 3 TWN I csdnjay@hotmail.com
<XFR 3 NS 207.46.108.93:1863 0 207.46.28.94:1863
这里得知第二次欲登陆的服务器为207.46.108.93,端口为1863
第二次登陆过程收发命令类似如下
>VER 1 MSNP12 CVR0
<VER 1 MSNP12
>CVR 2 0x0804 winnt 5.1 i386 MSNMSGR 8.1.0178 MSMSGS csdnjay@hotmail.com
<CVR 2 8.1.0178 8.1.0178 8.1.0178 http://msgruser.dlservice.microsoft.com/download/5/6/4/5646481F-33EF-4B08-AF00-4904F7677B89/ZH-CHS/Install_WLMessenger.exe http://get.live.com/cn
>USR 3 TWN I csdnjay@hotmail.com
<USR 3 TWN S ct=1225934765,rver=5.5.4177.0,wp=FS_40SEC_0_COMPACT,lc=1033,id=507,ru=http:%2F%2Fmessenger.msn.com,tw=0,kpp=1,kv=4,ver=2.1.6000.1,rn=1lgjBfIL,tpf=b0735e3a873dfb5e75054465196398e0
>USR 4 TWN S t=9zmW8WUaNGes6RPIvobJZxog26Re6uEiF8NfCot1WADagYV*xBD!yobWolu4iQma712fndE2L0cI1t0zCErX*yCKKFA9SnStf5SgUDBm3c7wxTEphaA*Wy*bZdj1nfiuu!&p=9m2c8ZG8!FTK82I9Gd15dopRFWPlLk3mpIJ2*PdD3!IP8CFu8I5wWGkLslOu1QC3EEaZQnIqwBZTtzpWWzu18FYFFY75fcvzb85e649MYCwu8Mi3AyIv8R9PsyYdTrE6tbUo3fzlu5RZmE2X0RXv!yO8yemZ!Xwm8s2nYsh3E3HqmNNRBEBPKZ7g$$
<USR 4 OK csdnjay@hotmail.com 1 0
<SBS 0 null
<MSG Hotmail Hotmail 537
[ size = 537, num = 537]
MIME-Version: 1.0
Content-Type: text/x-msmsgsprofile; charset=UTF-8
LoginTime: 1225934767
EmailEnabled: 1
MemberIdHigh: 442365
MemberIdLow: -1451244322
lang_preference: 2052
preferredEmail:
country: CN
PostalCode:
Gender:
Kid: 0
Age:
BDayPre:
Birthday:
Wallet:
Flags: 1073742915
sid: 507
MSPAuth: 9zmW8WUaNGes6RPIvobJZxog26Re6uEiF8NfCot1WADagYV*xBD!yobWolu4iQma712fndE2L0cI1t0zCErX*yCKKFA9SnStf5SgUDBm3c7wxTEphaA*Wy*bZdj1nfiuu!&p
ClientIP: 222.191.237.170
ClientPort: 22030
ABCHMigrated: 1
MPOPEnabled: 0
由以上的收发消息列表可以看出,第二次登陆在发送USR命令后,服务器不在返回XFR消息,而是返回USR命令。注意在返回的USR命令中包含类似的字符串
ct=1225934765,rver=5.5.4177.0,wp=FS_40SEC_0_COMPACT,lc=1033,id=507,ru=http:%2F%2Fmessenger.msn.com,tw=0,kpp=1,kv=4,ver=2.1.6000.1,rn=1lgjBfIL,tpf=b0735e3a873dfb5e75054465196398e0
客户端可以利用这个字符串做MSN认证具体获取ticket的方法,参见第二部分Java实现。若认证成功,客户端收到类似的USR 4 OK csdnjay@hotmail.com 1 0的命令。这时服务器将会再次返回SBS 0 null命令和MSG Hotmail Hotmail 537命令。MSG命令的第2个参数537表示,服务器将发送537个字节来进一步该账户相关信息。
此时MSN登陆过程的全过程已经结束。
1.4 获取好友列表和分组信息
为了获取MSN帐户好友以及改帐户分组的相关信息,还要发送SYN命令进行数据同步,这样MSN服务器才能发送好友列表信息。
>SYN 5 0 0
<SYN 5 2008-10-29T03:24:39.063-07:00 2008-10-27T23:57:40.58-07:00 2 3
<GTC A
<BLP BL
<PRP MFN ?
<PRP MBE N
<PRP WWE 0
<LSG 朋友 c274104c-8db9-47fc-9d0d-07d6fbea0ff1
<LSG 家人 24a9c4c5-221a-43b2-bc80-9935e35cf11c
<LSG 同事 4c60904d-8a23-42d8-a340-b1033e6877ef
<LST N=csdnss@hotmail.com F=CSDNss@hotmail.com C=83c611d9-1f58-4577-b70c-3cb154e3a8a0 11 1 c274104c-8db9-47fc-9d0d-07d6fbea0ff1
<BPR HSB 1
<LST N=chx1477@hotmail.com F=chx1477@hotmail.com C=1c0bf657-7ded-4837-a69d-cc6803543929 11 1 4c60904d-8a23-42d8-a340-b1033e6877ef
以上便是发送SYN命令后和发送后的服务器反馈信息。由此可以分析出该帐号的好友列表和分组信息。LSG表示该MSN用户分组信息,后面的字符串(例如,第一个LST中csdnss@hotmail.com 最后的c274104c-8db9-47fc-9d0d-07d6fbea0ff1)表示分组ID。LST表示联系人信息,其中N表示好友的邮件,F表示可能的昵称,整条消息最后的字符串(例如c274104c-8db9-47fc-9d0d-07d6fbea0ff1)表示该联系人所在的分组。在上例中,该帐号有三个分组,分别为朋友、家人和同事。字符串c274104c-8db9-47fc-9d0d-07d6fbea0ff1表示朋友分组,联系人csdnss@hotmail.com在朋友分组中。4c60904d-8a23-42d8-a340-b1033e6877ef表示同事分组,联系人chx1477@hotmail.com在同事分组中。
1.5 获取好友具体信息
当然仅仅依靠SYN返回联系人的信息是不够全面的。SYN命令仅返回所有的组,联系人列表以及部分联系人的昵称。若要想进一步获取更多联系人的相关信息,可以继续发送CHG 命令,以获取联系人昵称和签名信息。
>CHG 6 NLN 0x5004802C
<BPR HSB 1
<CHG 6 NLN 0
<MSG Hotmail Hotmail 289
[MIME-Version: 1.0
Content-Type: text/x-msmsgsinitialmdatanotification; charset=UTF-8
Mail-Data: <MD><E><I>1</I><IU>0</IU><O>0</O><OU>0</OU></E><Q><QTM>409600</QTM><QNM>204800</QNM></Q></MD>
Inbox-URL: /cgi-bin/HoTMaiL
Folders-URL: /cgi-bin/folders
Post-URL: http://www.hotmail.com
]
<ILN 6 NLN csdn_jay@163.com Jin%20Jian 2254295084 %3Cmsnobj%20Creator%3D%22csdn_jay%40163.com%22%20Type%3D%223%22%20SHA1D%3D%22dlhdylsYA70%2Fzci5JUbQo5OYd1o%3D%22%20Size%3D%227064%22%20Location%3D%220%22%20Friendly%3D%22agBhAHkAAAA%3D%22%2F%3E
<UBX csdn_jay@163.com 123
[<Data><PSM>AGAIN</PSM><CurrentMedia></CurrentMedia><MachineGuid>{6B9562F0-1957-432A-8ACD-A04CD7961841}</MachineGuid></Data>]
<ILN 6 NLN chx1477@hotmail.com (R)流浪狗 1985859628 %3Cmsnobj%20Creator%3D%22chx1477%40hotmail.com%22%20Type%3D%223%22%20SHA1D%3D%22cmJCJjUVJ%2Bx1g5BUj5TlTOAoFIo%3D%22%20Size%3D%2218476%22%20Location%3D%220%22%20Friendly%3D%220W5%2FZwAA%22%2F%3E
<UBX chx1477@hotmail.com 118
[<Data><PSM></PSM><CurrentMedia></CurrentMedia><MachineGuid>{7840D262-7F06-46B8-9573-114AEE3530F5}</MachineGuid></Data>]
以上便是在发送CHG和发送后的服务器反馈信息。ILN命令中第2个参数为联系人email,第3个参数为联系人的昵称。UBX命令后面跟着的XML数据表示联系人的具体信息,其中在PSM数据段中表示就是联系人签名。在本例中chx1477@hotmail.com联系人的昵称为(R)流浪狗,这个联系人没有签名。csdn_jay@163.com联系人的昵称为Jin Jian,而签名为AGAIN。
这里需要说明的是UBX命令后面的数字是指XML数据的字节数。有的联系人的签名中可能存在非英文字符(例如中文字符,法文字符或日文字符等),这样在程序读取时候需要做特殊处理。
二、Java核心实现
2.1 Socket连接部分
Socket socket = new Socket(address, port);
InputStream socketInputStream = new DataInputStream(socket.getInputStream());
OutputStream socketOutputStream = socket.getOutputStream();
OutputStream buffered = new BufferedOutputStream(socketOutputStream);
OutputStreamWriter writer = new OutputStreamWriter(buffered, "ASCII");
2.2 发送消息部分
void writeCommandToSocket(String command)
throws IOException, UnsupportedEncodingException {
writer.write(command + "/r/n");
writer.flush();
}
2.3 获取消息部分
String readLine() throws IOException {
byte[] ba = new byte[1024];
int n = 0;
while(n < 1024) {
int b = getSocketInputStream().read();
if((b == '/r') || (b == '/n')) {
if(b == '/r') {
getSocketInputStream().skip(1);
}
break;
} else {
ba[n] = (byte)b;
n ++;
}
}
return new String(ba, 0, n, "UTF-8");
}
2.4 获取ticket部分
客户端通过https连接MSN认证服务器login.live.com,之后便可以获取登陆凭证ticket。
在连接时需要设置Authorization属性为
Passport1.4 OrgVerb=GET,OrgURL=https%3A%2F%2Flogin.live.com%2Flogin2.srf,sign-in=csdnjay%40hotmail.com,pwd=abcdefg,ct=1226474476,rver=5.5.4177.0,wp=FS_40SEC_0_COMPACT,lc=1033,id=507,ru=http:%2F%2Fmessenger.msn.com,tw=0,kpp=1,kv=4,ver=2.1.6000.1,rn=1lgjBfIL,tpf=b0735e3a873dfb5e75054465196398e0
上述字符串由如下部分组成
Passport1.4 OrgVerb=GET,OrgURL=" + encode(visitUrl) + ",sign-in=" + encode(passport) + ",pwd=" + encode(password) + "," + challengeStr
其中chanllengeStr即在收到在第二次登陆服务器发送USR命令后返回的USR命令的中的字符串(具体内容参见1.3节所示),visitUrl为https://login.live.com/login2.srf。若这一过程认证失败,则ticket获取失败。
具体的代码如下
private String getLoginTicket(String visitUrl, String passport,
String password, String challengeStr)
throws IOException {
Pattern ticketPattern = Pattern.compile(".*from-PP='([^']*)'.*");
HttpsURLConnection conn = null;
try {
conn = (HttpsURLConnection) new URL(visitUrl).openConnection();
conn.setUseCaches(false);
conn.setRequestProperty("Authorization",
"Passport1.4 OrgVerb=GET,OrgURL="
+ encode(visitUrl)
+ ",sign-in="
+ encode(passport)
+ ",pwd="
+ encode(password) + ","
+ challengeStr);
switch (conn.getResponseCode()) {
case 200: //success
Matcher matcher = ticketPattern.matcher(conn
.getHeaderField("Authentication-Info"));
if (matcher.matches()) {
return matcher.group(1);
}
return null;
case 302: //redirect
visitUrl = conn.getHeaderField("Location");
return getLoginTicket(visitUrl, passport, password, challengeStr);
case 401: //failed
return null;
default:
}
} finally {
if (conn != null) {
conn.disconnect();
}
}
return null;
}
三、注意事项
【注意事项一】关于DataInputStream
在socket收到消息时,需要注意返回的内容可能有非英文字符,例如1.5节所提及到的UBX命令。在收到UBX消息时候,该消息已经指定了下面将要收到的字节数目。由于中文字符的是双字节或三字节,若直接利用socket. getInputStream方法进行读取,获取的中文部分出错或者造成读取阻塞。因此在2.1节所示的连接部分需要把InputStream利用DataInputStream进行封装。
InputStream socketInputStream = new DataInputStream(socket.getInputStream());
【注意事项二】关于发送命令
做MSN协议实现的时候,需要在命令结束发送/r/n以表示命令结束,如2.2节所示的实现,在命令后追加/r/n。
【注意事项三】关于接收命令
由于MSN服务器有时发送来的消息的数目是不确定的,例如1.4节所示的发送SYN命令后反馈得到的LSG和LST命令和1.5节所示的发送CHG命令后反馈得到的ILN命令。因此,读取服务器发送来的消息时,又不能以/r/n作为消息的结束标志。这样如果一直等待,就会在读取过程造成阻塞。
解决这个问题有2种方式。第一是利用多线程建立事件响应来处理收到的反馈。第二是先判断数据流中是否有可用信息。若无可用信息,则等待一段时间,再进行下一次判断。若连续多次都无可用信息,则退出循环。
下面给出第二种方式的具体实现
int n = 0;
while(true) {
if(getSocketInputStream().available() > 0) {
n = 0;
String command = readLineFromSocket();
//收到命令做处理
} else {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
n ++;
System.out.println("等待" + n +"秒");
if(n >= waitingSeconds) break;
}
}
上面这个程序表示在连续等待waitingSeconds秒后,此时仍然没有可以读取的数据,程序退出且不在读取。
【注意事项四】关于socket接收和发送
在做socket发送命令给服务器的时候,发送端要有唯一的入口。这就意味着在实现时只能从一个统一控制的地方获取inputStream和writer,而不能在程序中随时通过socket.getInputStream或经过包装过的reader中获取相应的数据。最后需要指出的是reader是面向字符的,而InputStream是面向字节的。
四、参考资料以及相关文档
[1] 介绍MSN协议非官方wiki http://msnpiki.msnfanatic.com/index.php/Main_Page
[2] 介绍DataInputStream的使用 http://www.javanb.com/j2se/1/5298.html
[3] 介绍MSN协议简介http://blog.csdn.net/bripengandre/archive/2008/03/20/2199924.aspx
[4] 介绍 MSNP12 的一种 Java 实现 http://java-jml.sourceforge.net/
MSN协议分析以及Java实现MSN登陆相关推荐
- MSN Media协议分析
整理记录 版本 时间 内容 整理人 V1.0 2008-04-09 MSN Media协议分析初稿 彭令鹏 MSN Media协议分析 第1章. 文档说明 本文档建立在另一篇文章--<M ...
- 百度登陆协议分析!!!用libcurl来模拟百度登陆
有空就分析了下百度的登陆协议. 大家看代码: size_t CURLWriteDataCallbak(char *data, size_t size, size_t nmemb, string *wr ...
- Skype 协议分析(2006版)
Skype 协议分析(2006版) 整理翻译:袁建明 Angel_YY@126.com 概要: Skype是创建Kazaa的组织在2003年开发的一个基于Peer-to-Peer(对等网络)的VoIP ...
- webqq2协议分析和qq聊天机器人简单实现(转)
webqq2协议分析和qq聊天机器人简单实现 转之http://hfutxf.javaeye.com/blog/800866 通过webqq接口,可以实现发送qq消息接收qq消息等,这样,想实现一个q ...
- Android逆向分析案例——某点评APP登陆请求数据解密
今天,七夕,单身23载的程序汪,默默地写着博客~ 上一次的逆向分析案例中讲了如何去分析某酒店的APP登陆请求,为了进一步学习如何逆向分析以及学习其他公司的网络传输加解密,本次案例将继续就登陆请求的数据 ...
- 微信安卓协议分析笔记
一.查资料 网上没找到SDK可以分析,关于微信安卓协议的文章也比较少,比较有用的是<微信交互协议和加密模式研究>,这篇论文里介绍了微信使用RSA2048与AES-CBC-128结合的加密算 ...
- 中国移动飞信协议分析
登录 POST /nav/getsystemconfig.aspx HTTP/1.1 User-Agent: IIC2.0/PC 2.2.0230 Content-Type: application/ ...
- 飞信的协议分析(转)
飞信的协议分析 转自:http://hi.baidu.com/nathan2007/blog/category/%B7%C9%D0%C5%D0%AD%D2%E9%B7%D6%CE%F6 作者:na ...
- oracle监听协议是什么意思,【ORACLE|ORACLE-TNS协议分析】TNS|协议|分析-傻大方
『傻大方知识库摘要_ORACLE|ORACLE-TNS协议分析』TNS协议传输可以使用TCP/IP协议.使用SSL的TCP/IP协议.命名管道和IPC协议传输,其中TCP/IP协议传输是使用明文传送. ...
最新文章
- html标签ref,HTML: param 标签
- python 复制、移动文件到指定目录并修改名字
- C语言中指针的地址和内容
- discuz 版块导航function_forumlist.php,Discuz! X2“扩建”左侧版块导航 让社区层次一目了然...
- 从ncbi下载数据_如何从NCBI下载所有细菌组件
- leetcode104. 二叉树的最大深度(dfs)
- 使用一个程序同时启动多个程序(c#)
- 【codevs1074】食物链
- 【kafka】Kafka JMX监控报错 Failed to get broker metrics for BrokerIdentity
- linux 编程 调度,Linux的进程线程及调度
- E站账号cookie分享_58云账号跨域实践总结
- windows如何设定定时关机和取消定时
- powerbuilder mysql_powerbuilder+mysql5.0的连接过程 | 学步园
- c语言入门这一篇就够了-学习笔记(一万字)
- python3.6 exe_详解Python3.6的py文件打包生成exe
- 大数据平台运维之Hbase
- 防火墙NAT综合实验——nat控制,豁免,远程,DMZ区域(带命令)
- C# 小数位修约(保留小数位有效数位)
- 海尔微型计算机硬盘如何拆卸,海尔xqb507288拆解图
- 基于Matlab/Simulink的简单三相交流系统扫频仿真