MSNP9协议分析
MSN Messenger协议
原作:bhw98
前 言
MSN Messenger 是Microsoft开发的聊天工具,目前在国内拥有很大的用户群。使用MSN
Messenger可以与他人进行文字聊天,语音对话,视频会议等即时交流,还可以通过此软件来查看联系人是否联机等。该软件的最新版本是6.1。
1999年,Microsoft向IETF提交了一份"MSN Messenger Service 1.0
Protocol"草案,这是最初版本的MSN Messenger协议。在以后几年,该公司不再公开有关MSN
Messenger协议的升级、修改细节的官方文档。但无论是开发第三方的聊天客户端软件(如Gaim,
MyIM等),还是做协议分析,必须对其通信协议有深入了解。正因为如此,有一些民间人士对这些协议开展了研究(见本文的"相关资源"一节)。
当我们还在感叹"这世界,变化快"的时候,MSN Messenger的协议已经到了第10版,简称MSNP10,对应于MSN
Messenger 6.1。MSN Messenger 6.0则使用MSNP9。现在Microsoft强迫MSN
Messenger用户升级到6.0或6.1版,因为服务器对MSNP8以下的版本不再支持。本文及后续文章所描述的MSN
Messenger协议主要针对MSNP9/MSNP10。
1. 连 接
MSN
Messenger协议建立在TCP/IP之上。除了文件传输和语音聊天是直接的"点对点"通信之外,其它所有的情形全部通过服务器进行。
在逻辑上,一共有三种类型的服务器,各司其职:
|
派遣服务器(Dispatch Server, DS) -
客户端最初连接的服务器。负责给客户端分配合适的通知服务器。域名是messenger.hotmail.com,标准服务端口是1863。完成派遣任务后,切断TCP连接。
通知服务器(Notification Server, NS) -
客户端需要一直保持连接的服务器。很多任务要在这个会话内完成,包括登录、改变状态、获取用户列表、修改用户信息、发起聊天、接受呼叫、邮件通知、退出等等。服务端口由派遣服务器指定,通常也是1863。
接线服务器(Switchboard Server, SS) -
客户端之间聊天使用的中转服务器。每开一个聊天窗口,客户端和服务器就建立一个TCP会话。当客户端之间需要进行文件传输或语音聊天时,发送系统消息,建立"点对点"会话通道(可能转为使用UDP)。服务端口通常也是1863。"点对点"
通信使用的端口由客户端自动协商决定,如文件传输通常使用6891端口。
2. 命 令
MSN Messenger命令使用纯ASCII码。对非ASCII码字符使用URL编码。命令的语法是
XXX[<SP>TrID<SP>PARAM1<SP>PARAM2…]<CRLF>
其中,<SP>是空白字符,<CRLF>是回车换行,XXX是一个3字符的命令串,TrID是一个流水号,PARAMx是参数,[
]内是可选项。最简单的命令没有流水号和参数。为了方便起见,下面讨论时用"
"代表<SP>,"\r\n"代表<CRLF>,"\x??"代表一个值为0x??字节。红色表示由客户端发出,蓝色表示由服务器发出。一个MSN
Messenger命令的例子如下:
USR 18 TWN I example@hotmail.com\r\n
3. 错 误
无论是由于客户端发出的命令无效,参数无效,还是其他什么原因,服务器可以返回一个错误。格式为
XXX[<SP>TrID]<CRLF>
其中,XXX是一个3位数字的串。如
ADD 21 AL non_existent@passport.com non_existent@passport.com\r\n
205 21\r\n
上例中,non_existent@passport.com是一个不存在的账号。
4. 消 息
MSN Messenger消息符合MIME
1.0标准,由消息头与消息体组成。通常使用UTF-8编码,消息头中也需要URL编码格式,消息体则直接用二进制数据。
一个MSN Messenger消息的例子如下:
MIME-Version: 1.0\r\n
Content-Type: text/plain; charset=UTF-8\r\n
X-MMS-IM-Format: FN=%E5%AE%8B%E4%BD%93; EF=; CO=0; CS=86; PF=0\r\n
\r\n
bhw98\xE4\xBD\xA0\xE5\xA5\xBD\xEF\xBC\x81
经简单分析可知,"%E5%AE%8B%E4%BD%93"是"宋体"的UTF-8加URL编码,而"bhw98\xE4\xBD\xA0\xE5\xA5\xBD\xEF\xBC\x81"是"bhw98你好!"的UTF-8编码。
命令一览
命令
|
来源
|
去向
|
说明
|
备注 |
ACK
|
SS
|
Client
|
确认,做出肯定回答。
|
acknowledgement |
ADD
|
Client
|
NS
|
发出添加新联系人到列表的请求。
|
add user |
NS
|
Client
|
返回添加新联系人请求的应答。 |
ADG
|
Client
|
NS
|
发出添加新联系人组请求。
|
add group |
NS
|
Client
|
返回添加新联系人组请求的应答。 |
ANS
|
Client
|
SS
|
接受聊天连接请求。
|
answer |
BLP
|
Client
|
NS
|
设置对尚未列入明确允许/禁止的联系人列表的保密策略。
|
block list privacy |
NS
|
Client
|
返回设置保密策略请求的应答。 |
BYE
|
SS
|
Client
|
通知客户端结束会话。
|
bye |
CAL
|
Client
|
SS
|
发出建立聊天连接的请求。
|
call |
SS
|
Client
|
返回建立聊天连接请求的应答。 |
CHG
|
Client
|
NS
|
发出改变状态的请求。
|
change state |
NS
|
Client
|
返回改变状态的应答。 |
CHL
|
NS
|
Client
|
服务器发出验证要求。
|
challenge |
SS
|
Client |
CVR
|
Client
|
NS
|
发出客户端的OS、语言、MSN Messenger版本等信息。
|
client version |
Client
|
SS |
NS
|
Client
|
返回推荐的MSN Messenger版本、升级软件需要的下载地址等信息。 |
SS
|
Client |
FLN
|
NS
|
Client
|
通知有联系人列表中的用户下线。
|
off-line |
GTC
|
Client
|
NS
|
设置当有联系人列表中的用户状态改变时给出的提示。
|
greeting to changes? |
NS
|
Client
|
返回设置请求的应答。 |
INF
|
Client
|
NS
|
询问服务器所支持的认证方式。
|
information? |
Client
|
SS |
NS
|
Client
|
返回服务器所支持的认证方式。 |
SS
|
Client |
ILN
|
NS
|
Client
|
当客户端登录或添加联系人到列表时,通知列表中的联系人的状态。
|
initial online state |
IRO
|
SS
|
Client
|
当有新用户加入聊天连接时,通知客户端该连接中的用户名单。
|
initial roster information |
JIO
|
SS
|
Client
|
通知客户端已经同另外的用户建立了聊天连接。
|
jion |
LSG
|
Client
|
NS
|
发出获取联系人组列表的请求。
|
list groups |
NS
|
Client
|
返回获取联系人组列表请求的应答。 |
LST
|
Client
|
NS
|
发出获取联系人列表的请求。
|
list |
NS
|
Client
|
返回获取联系人列表请求的应答。 |
MSG
|
Client
|
SS
|
发送消息到其他用户(聊天对象)。
|
message |
NS
|
Client
|
传递服务器(系统) 的消息到客户端。 |
SS
|
Client
|
传递其他用户(聊天对象)的消息到客户端。 |
NAK
|
SS
|
Client
|
做出否定回答。
|
negative acknowledgement |
NLN
|
NS
|
Client
|
通知客户端联系人上线或改变状态。
|
on-line |
OUT
|
All
|
All
|
结束客户端-服务器的连接。
|
out |
PNG
|
Client
|
NS
|
测试TCP连接状态。
|
ping |
Client
|
SS |
PRP
|
Client
|
NS
|
发出设置个人电话号码的请求。
|
personal phone number |
NS
|
Client
|
返回设置请求的应答 |
PNG
|
Client
|
NS
|
测试TCP连接状态。
|
ping |
Client
|
SS |
QNG
|
NS
|
Client
|
返回测试TCP连接状态的应答。
|
quiz ping? |
SS
|
Client |
QRY
|
Client
|
NS
|
客户端回答服务器的验证要求。
|
quiz reply? |
Client
|
SS |
REA
|
Client
|
NS
|
发出修改用户昵称的请求。
|
rename nickname |
NS
|
Client
|
返回修改用户昵称请求的应答。 |
REG
|
Client
|
NS
|
发出修改联系人组的请求。
|
rename group |
NS
|
Client
|
返回修改联系人组请求的应答。 |
REM
|
Client
|
NS
|
发出从联系人列表中删除用户的请求。
|
rename user |
NS
|
Client
|
返回删除用户请求的应答。 |
RMG
|
Client
|
NS
|
发出删除联系人组的请求。
|
remove group |
NS
|
Client
|
返回删除联系人组请求的应答。 |
RNG
|
NS
|
Client
|
通知客户端有人要建立聊天连接。
|
ring |
SYN
|
Client
|
NS
|
客户端-服务器同步。
|
synchronization |
NS
|
Client |
URL
|
Client
|
NS
|
发出获取MSN服务URL的请求。
|
URL |
NS
|
Client
|
返回获取URL请求的应答。 |
USR
|
All
|
All
|
声明、传递、鉴别用户身份。
|
user |
VER
|
Client
|
DS
|
协商MSN Messenger协议版本。
|
version |
Client
|
NS |
DS
|
Client |
NS
|
Client |
XFR
|
DS
|
Client
|
向客户端分配NS(通知客户端转向连接指定的NS)。
|
transfer |
Client
|
NS
|
发出分配SS的请求。 |
NS
|
Client
|
返回分配SS请求的应答。 |
MSN Messenger协议--错误代码一览
代码
|
含义
|
可能的命令
|
错误示例
|
备注 |
200
|
非法命令
|
|
ABC 18\r\n 200
18\r\n
|
|
201
|
非法参数
|
|
CHG 19 FLN 0\r\n 201 19\r\n ADD 20 AL
aaa@bbb@ccc aaa@bbb@ccc\r\n 201
20\r\n
|
|
205
|
用户不存在
|
ADD
|
ADD 21 AL none@hotmail.com
none@hotmail.com\r\n 205
21\r\n
|
|
206
|
缺少域名
|
|
|
|
207
|
已经登录
|
USR
|
USR 20 TWN I
example@hotmail.com\r\n 207
20\r\n USR 21 TWN S
8d30fc782aa25ec9e1293fdda13cab42\r\n 207 21\r\n
|
|
208
|
非法用户名
|
CAL
|
CAL 2 @@hotmail.com\r\n 208 2\r\n
|
|
209
|
非法用户昵称
|
REA
|
REA 18 one@hotmail.com
kill%20microsoft\r\n 209
18\r\n
|
|
210
|
用户太多
|
ADD
|
ADD 23 FL one@hotmail.com one@hotmail.com
0\r\n 210 23\r\n
|
最大150 |
215
|
用户已在列表中
|
ADD
|
ADD 36 FL one@hotmail.com one@hotmail.com
2\r\n ADD 36 825 FL one@hotmail.com
one@hotmail.com 2\r\n ADD 37 FL
one@hotmail.com one@hotmail.com 2\r\n 215 37\r\n
|
|
CAL
|
CAL 5 two@hotmail.com\r\n CAL 5 RINGING 213697\r\n CAL 6 two@hotmail.com\r\n 215 6\r\n |
216
|
用户不在列表中
|
REM
|
REM 14 FL
three@hotmail.com\r\n 216
14\r\n
|
|
REA
|
REA 48 three@hotmail.com
three\r\n 216 48\r\n |
CAL
|
CAL 2
three@hotmail.com\r\n 216
2\r\n |
217
|
用户不在线
|
CAL
|
CAL 2 two@hotmail.com\r\n 217 2\r\n
|
|
218
|
已在指定模式
|
BLP
|
BLP 17 BL\r\n BLP 17 151 BL\r\n BLP 18
BL\r\n 218 18\r\n
|
|
GTC
|
GTC 19 A\r\n GTC
19 152 A\r\n GTC 20
A\r\n 218 20\r\n |
219
|
用户已经在相反的列表中
|
ADD
|
ADD 15 AL four@hotmail.com
four@hotmail.com\r\n ADD 15 AL 56
four@hotmail.com four\r\n ADD 16 BL
four@hotmail.com four@hotmail.com\r\n 219 16\r\n
|
|
223
|
用户组太多
|
ADG
|
ADG 28 new%20group 0\r\n 223 28\r\n
|
最大30 |
224
|
非法组号
|
ADD
|
RMG 24 3\r\n RMG
24 11506 3\r\n ADD 25 FL
five@hotmail.com 3\r\n 224
25\r\n
|
|
REM
|
RMG 24 3\r\n RMG
24 11506 3\r\n REM 25 FL
five@hotmail.com 3\r\n 224
25\r\n |
REG
|
RMG 24 3\r\n RMG
24 11506 3\r\n REG 25 3 New%20Name
0\r\n 224 25\r\n |
RMG
|
RMG 24 3\r\n RMG
24 11506 3\r\n RMG 25
3\r\n 224 25\r\n |
225
|
用户不在组中
|
REM
|
REM 26 FL six@hotmail.com
3\r\n 225 26\r\n
|
|
229
|
组名太长
|
ADG
|
ADG 27
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
0\r\n 229 27\r\n
|
最大60 |
230
|
不能删除组0
|
RMG
|
RMG 28 0\r\n 230
28\r\n
|
|
300 |
缺少必要的参数 |
|
|
|
302 |
尚未登录 |
|
|
|
500 |
服务器内部错误 |
|
|
|
540 |
验证应答错误 |
|
|
|
600 |
服务器忙 |
|
|
|
707 |
无法建立连接 |
|
|
|
910 |
服务器忙 |
|
|
|
911 |
身份验证失败 |
|
|
|
过
去的MSN
Messenger版本(MSNP8以下),简单地使用MD5等Hash算法对用户身份进行认证。MSNP9/MSNP10使用一种TWN
(Tweener)认证方式,通过SSL/TLS连接到login.passport.com和loginnet.passport.com等服务器,借
助于HTTP协议输入账号和密码,认证通过后,才能取得“入场券”。
真正意义上的身份认证,发生在客户端与通知服务器(NS)之间。客户端登陆NS时,首先交换版本信息。双方均支持MSNP8以上版本时,才能进行认证过程。如果客户端版本较低,不支持TWN,咋办?服务器会将你一脚揣出去,信不信?
好了,废话少说,现在拿一个成功认证的例子看看。在下面的例子中,账号是“example@passport.com”,密码是“password”。
VER 4 MSNP10 MSNP9 CVR0 \r\n
VER 4 MSNP9 CVR0 \r\n
CVR 5
0x0804 winnt 5.0 i386 MSNMSGR 6.1.0203 MSMSGS example@passport.com
\r\n
CVR 5 6.0.0602 6.0.0602 5.0.0527
http://download.microsoft.com/download/d/4/f/d4f560d5-6dc6-4901-b149-a568415561d7/SETUPNT.EXE
http://messenger.msn.com/cn \r\n
USR 6 TWN I
example@passport.com \r\n
USR 6 TWN
Slc=1033,id=507,tw=40,fs=1,ru=http%3A%2F%2Fmessenger%2Emsn%2Ecom,ct=1073355862,kpp=1,kv=5,ver=2.1.0173.1,
tpf=ed1c2f217a21c191c61251eb8b73bb60 \r\n
(此时通过SSL进行身份认证,获得“入场券”)
USR 7 TWN S
t=4m1wWfEupDgUNb53qys5gJdw8OTJEtT82fcuDbS3U672gTymOOs6cgKeafj7WjgZNcufAQggxqHRRXko02DoflZA$$
&p=4QXNnX9rFDDgki9ZqvqPZGDGJa2Mrd5H13Zfl0NNjh4I78qPyfpzmkZPZEe0nxJTkzZSNDYtk!57cVqiYVfO86KgCRYWhi2kudS0M
!7bdi82EDA1FYp3WboHD!sCQ17OZh7lPQI7fozrgsSMZwgSzRi2FNTPxf13oDNIfDCKCG!2guDvZKEpk78A$$
\r\n
USR 7 OK example@passport.com example@passport.com 1 0 \r\n
第一回合(TrID=4),双方协商MSN版本号。客户端说“我能支持MSNP9和MSNP10”,NS说“行,就MSNP9吧”。
第二回合(TrID=5),客户端报告本机信息:OS = Windows 2000 (NT 5.0), 语言 = 简体中文,MSN
Messenger版本 = 6.1.0203, 账号 =
example@passport.com。NS给出了推荐的版本号,能够使用的最老的版本号,新版本下载地址,官方网站地址等信息。
第三回合(TrID=6),客户端要求身份认证(I = Initial),NS则给出所需要的一长串信息(S =
Subsequent)。其中tpf相当于challenge,参与Hash运算,能保证每次认证返回的串是不同的。
第四回合(TrID=7),客户端出示从认证服务器得到的“入场券”,NS放行(OK)。
第三、四回合之间,通过SSL的认证过程如下:
首先在HTTPS端口443向login.passport.com发送一个GET请求,将账号、密码和NS给定的一长串信息送出
GET /login2.srf HTTP/1.1 \r\n
Authorization: Passport1.4
OrgVerb=GET,OrgURL=http%3A%2F%2Fmessenger%2Emsn%2Ecom,sign-in=example%40passport.com,pwd=password,
lc=1033,id=507,tw=40,fs=1,ru=http%3A%2F%2Fmessenger%2Emsn%2Ecom,ct=1073355862,kpp=1,kv=5,ver=2.1.0173.1,
tpf=ed1c2f217a21c191c61251eb8b73bb60 \r\n
Host:
login.passport.com \r\n \r\n
根据情况,会重定向到不同的URL。本例中,重定向到"https://loginnet.passport.com/login2.srf?lc=1033",服务器应答
HTTP/1.1 302 Found \r\n Server: Microsoft-IIS/5.0 \r\n
Date:
Mon, 22 Dec 2003 21:10:05 GMT \r\n
PPServer: H: LAWPPLOG5C006
\r\n
Connection: close \r\n
Content-Type: text/html \r\n
Expires: Mon, 22 Jun 2003 21:09:05 GMT \r\n
Cache-Control:
no-cache \r\n
cachecontrol: no-store \r\n Pragma: no-cache
\r\n
P3P: CP="DSP CUR OTPi IND OTRi ONL FIN"
\r\n
Authentication-Info: Passport1.4 da-status=redir \r\n
Location: https://loginnet.passport.com/login2.srf?lc=1033 \r\n
\r\n ... ...
然后,重新向指定的URL发出请求,得到如下响应
HTTP/1.1 200 OK \r\n
Server: Microsoft-IIS/5.0 \r\n
Date:
Mon, 22 Dec 2003 21:10:07 GMT \r\n
PPServer: H: LAWPPIIS6B061
\r\n
Connection: close \r\n Content-Type: text/html
\r\n
Expires: Mon, 22 Dec 2003 21:09:07 GMT
\r\n
Cache-Control: no-cache \r\n
cachecontrol: no-store \r\n
Pragma: no-cache \r\n
P3P: CP="DSP CUR OTPi IND OTRi ONL
FIN" \r\n
Set-Cookie: ... ... \r\n
Authentication-Info:
Passport1.4
da-status=success,tname=MSPAuth,tname=MSPProf,tname=MSPSec,
from-PP='t=4m1wWfEupDgUNb53qys5gJdw8OTJEtT82fcuDbS3U672gTymOOs6cgKeafj7WjgZNcufAQggxqHRRXko02DoflZA$$
&p=4QXNnX9rFDDgki9ZqvqPZGDGJa2Mrd5H13Zfl0NNjh4I78qPyfpzmkZPZEe0nxJTkzZSNDYtk!57cVqiYVfO86KgCRYWhi2kudS0M
!7bdi82EDA1FYp3WboHD!sCQ17OZh7lPQI7fozrgsSMZwgSzRi2FNTPxf13oDNIfDCKCG!2guDvZKEpk78A$$',
ru=http://messenger.msn.com \r\n
Content-Length: 0 \r\n
\r\n
开始时直接向loginnet.passport.com发出正确的请求,也是可以的。不难看出,在服务器认证成功的返回信息中,Authentication-Info字段的from-PP串值,就是所谓的“入场券”。
如果认证失败,服务器返回401错误
HTTP/1.1 401 Unauthorized \r\n ... ...
这样,就无法拿到“入场券”,自然不能在第四回合中输入合法的串。