飞信的协议分析

时间:2022-06-07 01:53:47
转自:http://hi.baidu.com/nathan2007/blog/category/%B7%C9%D0%C5%D0%AD%D2%E9%B7%D6%CE%F6

作者: nathan

以下分析均基于飞信的这一版本:Fetion 2006 beta 版本 2.1.0.0。

*开始用飞信(Fetion),痛苦啊,这玩意儿开发了几年(飞信博客上一家伙说参加飞信项目两年了),而且用的是.NET(工作量要比 C++小了去了),居然这么烂,也算是个奇迹了。。。。自己找点乐子,分析飞信的通信协议好了。。。这不是什么破解,俺也不是什么什么客,纯属无聊。
抓包看了一下,飞信是用了混合协议的:

1、基于HTTP(XML Web Services吧?)进行获取系统配置、更新程序、注册用户

2、基于HTTPS进行登录时密码验证

3、应用层协议是SIP协议,但不是标准的,估计是自创的?所有交互过程如发消息、短信通过SIP协议进行。


关于SIP,有巨多的RFC描述,飞信的SIP协议栈实现的是TCP、HTTP承载

1.TCP承载方式:连接服务器(目前是221.130.45.203)的8080端口,这时在客户端的“网络设置”中显示的是“TCP直接连接”,SIP信令直接就放在TCP的包中。
2.HTTP承载方式:连接服务器(目前是221.130.45.203)的80端口,采用POST方式,将信令包在POST请示中,这时在客户端的“网络设置”中显示的是“HTTP直接连接”

因为是TCP和HTTP承载,所以其包格式是非常清楚的,那么注意力就可以直接放到SIP协议或SIP信令上,详细的内容稍后再写。

总的来说,飞信协议是比较简单的,不对,准确地说法是比较规范和清晰,但协议本身是复杂的,另外:
1. 飞信的协议是明文,这一点如同其兄弟MSN,是不如QQ和RTX的,因此,通过飞信的交谈过程是可轻易截获的,通过很简单的工具,就可以截到同一网段上所 有人的交谈,估计会有人写Fetion Chat Sniffer的,就跟MSN一样,假如有一天Fetion有那么流行......要不我写一个?:)

2.协议效率比较低,我加了近200人,一次登录过程要传递的数据量居然超过了230K,我靠。

3.状态有问题,presence处理得不太好好象,我在线别人却看到我离线,真是奇怪,而且一会儿发一个presence一会儿发一个,讨厌啊。



以下分析均基于飞信的这一版本:Fetion 2006 beta 版本 2.1.0.0。

作协议分析时,一抓包,就发现飞信工作时连的是221.130.45.203这个服务器。那这个IP地址从哪来的呢?会变吗?飞信的客户端程序中并 没有配置服务器地址这一说。固定一个IP?不会吧,一面向全国的系统,不可能用一个IP地址。用一个固定域名解析出来的多IP地址中的一个吗?抓出它访问 DNS的包一看,它就只在开始时解析过一次域名:nav.fetion.com.cn,这个域名的IP是221.130.45.201——听说开发飞信的 人就是微软开发MSN的人,所以啥都跟MSN一样,你看那飞信的主界面元素,你能找一个位置和功能跟MSN不一样的吗?连解析域名这点都跟MSN一样,没 意思啊,印象中MSN也是一开始就解析一个地址,好象是Messenger.msn.com?如果想在局域网内*MSN,就把这个域名给指向 127.0.0.1,MSN就傻了。

既然只解析过nav.fetion.com.cn,那么221.130.45.203这个工作服务器(SIP的Proxy Server)地址,就应该是nav.fetion.com.cn返回来的了。确实是,但只是第一次登录时返回,并保存在了本地。后面再登录时,如果版本 不更新,是不会再返回这些系统配置信息的。所以,除第一次外,再抓包是看不到这些配置信息的。


本地配置文件并没放在Fetion的程序目录中,而是放到了%USERPROFILE%/Application/Fetion目录下。这个目录下有 configuration.dat和飞信的用户目录,每个飞信用户目录下还有configuration.dat、contacts.dat、 userinfo.dat这三个配置文件,看名字就知道是与用户相关的系统配置文件、好友列表文件、用户的个人信息文件。

这些文件全是XML格式的,所以可以用Notepad打开,不过,你打开后就会发现,这些文件的内容全被加密了,变态啊,这些文件有什么好加密的呢。

我们如何获得这里头的信息呢?

方法有两个:

一、我们让Fetion不要加密这些文件的内容,方法是:修改FetionFx.EXE文件。用ildasm,将FeionFX.EXE反汇编出 来,将其中的Imps.Client.Pc.PersistentManager.EncodeMode1和 Imps.Client.Pc.PersistentManager.DecodeMode1这两个函数改掉,将这两个函数体改成以下内容:
.maxstack 1
IL_0000: ldarg.0
IL_0001: ret

即,立即将参数返回。然后再用ilasm工具重新汇编生成FetionFX.EXE文件,覆盖掉以前那个,然后,再运行飞信,所有配置文件就不会再加密了。

二、构造一个请求,给nav.fetion.com.cn,让它返回。请求的内容很简单,抓一下包就会知道,取系统配置的请求过程是:
xxx.xxx.xxx.xxx:xxxx >>>>>>>> 221.130.45.201:80
POST /nav/getsystemconfig.aspx HTTP/1.1
User-Agent: IIC2.0/PC 2.1.0.0
Host: nav.fetion.com.cn
Content-Length: 233
Connection: Keep-Alive


--------------------------------------
xxx.xxx.xxx.xxx:xxxx <<<<<<<< 221.130.45.201:80
HTTP/1.1 100 Continue


--------------------------------------
xxx.xxx.xxx.xxx:xxxx >>>>>>>> 221.130.45.201:80
<config><user mobile-no="139xxxxxxxx" /><client type="PC" version="2.1.0.0" platform="W5.1" /><servers version="12" /><service-no version="1" /><parameters version="4" /><hints version="4" /><http-applications version="5" /></config>

将以上内容中的从servers version开始的version全置为"0",服务器就会返回配置信息。注意,配置信息全是UTF-8编码的。用nc就可构造一个http请求发过去,服务器立马就返回了。


推荐用方法一,因为这样可以看和修改所有配置信息,而方法二仅有系统配置信息。


与我们关注的服务器地址相关的信息在飞信用户目录下的configuration.dat中,一看就明白是啥:
....
<sipc-proxy>221.130.45.203:8080</sipc-proxy>     这就是我们关心的TCP直接连接时的服务器地址
<http-tunnel>HTTP://221.130.45.203/ht/sd.aspx</http-tunnel> HTTP直接连接时的入口地址
<get-pic-code>HTTP://221.130.45.201/nav/GetPicCode.aspx</get-pic-code> 注册时,取验证代码图片的URL
<get-system-status>HTTP://221.130.45.201/nav/GetSystemStatus.aspx</get-system-status> 取系统状态啦
....


这个配置信息放到飞信用户目录下是有原因的,就象SIP协议支持的,登录的服务器是可以分用户群的,不同的用户可以登录不同的Proxy Server,每个飞信用户(手机)可以分别登录到本省的Proxy Server,就如同现在的手机和电话网络一样。


其实这些配置内容没什么啊,为什么要加密呢?而且随便构造一个http请求,就可以获得这些内容的。 最变态的是通过飞信的交谈内容不加密不变换,却把无关紧要配置文件加密了。

另外,用户的密码就保存在Application Data/Fetion目录下的Configuration.dat中,当然这个密码进行了变换,遗憾的是,在程序中是还原出了用户密码的,因此,用户密 码别人是可以轻易获得的。幸好丢了可以手机找回。但这仍然是个非常不安全的因素。



作者: nathan

飞信老是断线,这点互联网上很多用户都有提及。我待的地方就老断,一说是跟网络有关系,一说是飞信本身有问题。近一两天跟踪看了看,发现断得可真是恐怖,一天断20-30次的都有。可有一次在家通过ADSL上呢,好象又还好。难道是时间的关系?


但白天碰到的多次断线我感觉是服务器的问题,理由是通过Sniffer,我明确地看到了服务器端发过来的TCP RST的包,也就是说服务器对这个TCP连接做了CLOSE的操作。

让我们看看sniffer记录了什么,来看一次断线的记录(这是OmniPeek的PacketVisualizer Data):
.....
9451 12:46:48.661 > IP L=   40 TCP .A.... S=      4522    L=    0        10994=A W=64367        TCP Invalid Checksum
9452 12:46:51.181 << IP L= 119 TCP .AP...         4522=A L=   79 S=     10994            W=65402
9453 12:46:51.182 >> IP L= 115 TCP .AP... S=      4522    L=   75        11073=A W=64288        TCP Invalid Checksum
9454 12:46:51.343 < IP L=   40 TCP .A....         4597=A L=    0 S=     11073            W=65327
9455 12:53:38.501 > IP L=   98 TCP .AP... S=      4597    L=   58        11073=A W=64288        TCP Invalid Checksum
9456 12:53:38.532 < IP L=   40 TCP ...R..                 L=    0 S=     11073            W=    0

从记录中我们看到,12:53:38,客户机发了9455号包到服务器,而服务器回了一个RST包(9456号包),从TCP的序列号看,通信过程都是正常的,否则RST包的Sequence就不会是11073了。9455的包的内容是:
R fetion.com.cn SIP-C/2.0
F: 565248767
I: 1
Q: 4 R

这是一个向SIP Proxy Server注册的SIP请求。我猜测是客户端有几分钟没收到服务器的任何消息(通常服务器在不停地给客户端发presence消息),客户端就向服务器 发起一个注册请求,这时服务器应该将所有用户列表向飞信的客户端返回,然而,服务器却回了一个字:滚!;) 然后,飞信就痛苦地开始了重新登录过程,然后你的好友就看到你又缓缓地从他屏幕的右下角慢慢地爬了上来,冤啊。


一个半天可以记录到10多次这样的断线,每次均是以如此方式结束:客户端发一个请求到服务器,服务器回以RST。是网络问题吗?我想应该不 是,TCP的交互过程是正常的,MSN也是同时用TCP连着的,它也不断。我认为是飞信服务器的用户状态机处理有问题,莫名其妙地把活动的用户给干掉 了。:)

飞信自己应该知道这种情况的啊,Fetion本身的日志文件中是这么记录的:
....
<Summary>通信层异常</Summary>
<Detail>System.Net.Sockets.SocketException: 远程主机强迫关闭了一个现有的连接。
....


可怜的飞信。


等着在淘宝上淘来的升级部件邮寄的时侯还是回到飞信吧,以下分析仍基于Fetion 2006 beta 2.1.0.0。

飞信所使用的协议版本标记是"SIP-C/2.0",协议栈中标记的版权信息是"Copyright (c) 2004-2006 China Mobile Limited. All rights reserved.",(再次说明飞信开发了很久了嘛;))。抓协议包初看的印象是,它基于IETF(Internet Engineering Task Force)所制定的标准SIP协议作了一丁点调整。关于标准的SIP协议,请参见IETF或RFC3261以及其它一些对它进行扩展的RFC,内容实在 是太多了,不过如果有点基础的话,估计看个半小时的RFC就明白了个大概。另外,飞信照着微软做的,所以看微软的实时通信协议的说明也一样: Look

一个SIP的请求消息的格式是:请求行+消息头+空行+消息体,请求行的格式是:SIP方法+空格+接受方uri+空格+SIP协议版本,如:
    INVITE sip:bob@biloxi.com SIP/2.0
接下来是消息头部,消息头的格式跟http头格式一样,也是Field Name:Field Value的形式,如:
     Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds
      Max-Forwards: 70
      To: Bob <sip:bob@biloxi.com>
      From: Alice <sip:alice@atlanta.com>;tag=1928301774
      Call-ID: a84b4c76e66710@pc33.atlanta.com
      CSeq: 314159 INVITE
      Contact: <sip:alice@pc33.atlanta.com>
      Content-Type: application/sdp
      Content-Length: 142

IETF SIP中对这些Header Field均有专门的定义,见RFC3261以及IETF的SIP草案文档。Fetion的SIP-C/2.0协议中用到的Header Field有:
    Authorization    缩写为"A"
    CallID        "I"
    Contact        "M"
    ContentEncoding    "E"
    ContentLength    "L"
    ContentType    "C"
    CSeq        "Q"
    Date        "D"
    EndPoints    "EP"
    Event        "N"
    Expires        "X"
    From        "F"
    MessageID    "XI"
    ReferredBy    "RB"
    ReferTo        "RT"
    Require        "RQ"
    RosterManager    "RM"
    Source        "SO"
    Supported    "K"
    To        "T"
    Unsupported    "UK"
    WWWAuthenticate    "W"


以上这些头域很多在RFC并没有定义,只在IETF的协议草案中有定义,如Event。SIP消息头后,由一个空行分隔,接下来就是SIP的消息体。消息 体通过是用SDP协议描述的,见RFC2327,当然也有直接文本或XML表示的信息,消息体的格式按RFC中的定义,应由头部的Content- Type来决定,但飞信好象不是,具体是怎么定义,还没深入去研究,留待下次吧。


飞信所支持的SIP方法(SIP Method)有以下几种:
1. Ack方法。在Fetion的SIP-C/2.0中,缩写为"A",以下是一个Ack消息,会话(Session)发起方用以向SIP Proxy确认会话的开始:
A fetion.com.cn SIP-C/2.0
I: 16
Q: 1 A
T: sip:987654321@fetion.com.cn;p=1234
F: 123456789

其中:123456789是发起方的SIP地址,这个显然没按SIP标准来表达,只是用了一个飞信号码。sip:987654321@fetion.com.cn,这个是按标准表达的对方地址。

2.BENotify方法(Fetion缩写为"BN"),这不是个标准的SIP方法,既没在RFC中定义,也没出现在IETF的协议草案中,这是微软在其LCS中定义的: BENOTIFY ("best effort" notify)enhances server performance by eliminating the response requirement. Otherwise, a BENOTIFY request has the same behavior as NOTIFY. Applications that require NOTIFY support need to implement similar processing for BENOTIFY."(见 MSDN)。就是个不需要回复的NOTIFY,微软扩展出这个方法是为了支持大量用户。以下是飞信的一个BENotify消息,表示用户987654321的在线状态的变化:
BN 123456789 SIP-C/2.0
Q: 13 BN
N: presence
X: xxxx
I: 9
L: xxx

<events><event type="PresenceChanged"><presence uri="sip:987654321@fetion.com.cn;p=xxxx"><basic value="100" device-id="PCCL025722" device-type="PC" device-caps="simple-im,im-session,temp-group" /></presence></event></events>



3.Bye方法(Fetion缩写为"B"),以下是一个BYE的请求消息,用以结束会话:
B fetion.com.cn SIP-C/2.0
F: 123456789
I: 11
Q: 2 B
T: sip:987654321@fetion.com.cn;p=1234


4.Cancel方法(C),用以取消正在进行中的INVITE请求。

5.Info方法(IN),用以在SIP协议中支持应用相关的控制信息,飞信传文件时,用到了这个方法。这是RFC中的一个扩展。以下是一个INFO方法的消息:
I N fetion.com.cn SIP-C/2.0
F: 123456789
I: 22
Q: 3 IN
T: sip:987654321@fetion.com.cn;p=1234
L: 266

<action type="share-content" method="request"><transmit type="relay" session-id="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" /><file name="xxxxxx.xx" size="xxx" url="HTTP://221.130.45.206/hds/RamRelayDownloadShareContent.aspx?FileUri=xxxxxxxxxxxxx" /></action>


6.Invite方法(I),这是用建立会话过程的SIP方法。以下是飞信的INVITE方法的一个协议消息,表示用户123456789要求和987654321建立会话,准备进行文本消息的通信:
I fetion.com.cn SIP-C/2.0
F: 123456789
I: 16
Q: 1 I
T: sip:987654321@fetion.com.cn;p=1234
K: text/html-fragment
K: multiparty
L: 137

v=0
o=-0 0 IN xxx.xxx.xxx.xxx:xxxx
s=session
c=IN IP4 xxx.xxx.xxx.xxx:xxxx
t=0 0
m=message 1769 sip sip:123456789@fetion.com.cn;p=xxxx



7.Message方法(M),这也是一个标准的SIP扩展,用以支持即时消息。以下是飞信的一个Message消息,用户123456789向用户987654321发消息说“Hello!你好” :
M fetion.com.cn SIP-C/2.0
F: 123456789
I: 16
Q: 2 M
T: sip:987654321@fetion.com.cn;p=1972
C: text/html-fragment
K: SaveHistory
L: 121

<Font Face='Arial' Color='-16777216' Size='9'>hello! </Font><Font Face='SimSun' Color='-16777216' Size='12'>你好</Font>


8.Negotiate方法(NEG),这是一个IETF定义的SIP的方法,好象没见纳入RFC? 用以在会话建立(INVITE)前的协商会话相关的参数,但没见飞信用啊....以后再说。

9.Notify方法(N),这是IETF定义的一个SIP扩展方法,并被RFC接纳,见RFC3265。消息见前面的BENotify。这个消息是需要进行回复的。

10.Options方法(O),标准的SIP方法,用来查询对端或服务器的能力。比如了解对方支持什么编码类型。在飞信传文件时使用了以下消息:
O fetion.com.cn SIP-C/2.0
F: 123456789
I: 22
Q: 2 O
K: ShareContent
T: sip:987654321@fetion.com.cn;p=xxxx

11.Refer方法(REF),这是已纳入RFC的一个SIP扩展方法,其功能是要求接受方通过使用在请求中提供的联系地址信息联系第三方。

12.Register方法(R),这是SIP的标准方法,用来向服务器登记。如以下飞信在注册时发出的消息:
R fetion.com.cn SIP-C/2.0
F: 123456789
I: 1
Q: 2 R
A: Digest response="xxxxxxxxxxxxxxxxxxxxxxxxxx",cnonce="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
L: 147

<args><device type="PC" version="131" /><caps value="simple-im;im-session;temp-group" /><events value="contact;permission;system-message" /></args>


13.Service方法(S),这是IETF定义的一个SIP扩展方法,好象未纳入RFC?用来向SIP服务器请求额外的服务。如一下飞信发出的消息:
S fetion.com.cn SIP-C/2.0
F: 123456789
I: 2
Q: 1 S
N: GetPersonalInfo
L: 172

<args><personal version="11" attributes="all" /><services version="11" attributes="all" /><config version="109" attributes="all" /><mobile-device attributes="all" /></args>

以上是飞信客户端要求服务器返回用户信息。又如:
S fetion.com.cn SIP-C/2.0
F: 123456789
I: 12
Q: 1 S
N: StartVoiceChat
L: 103

<args><voice-chat begin-date="2007-01-01 00:00:00.1234" /><users><user sid="987654321" /></users></args>

这是通过服务器开始飞信语聊。


14.Subscribe方法(SUB),这是IETF定义的一个SIP扩展方法,并被RFC接纳,见 RFC3265。这个方法被用来向服务器订阅事件异步通知。服务器就会用NOTIFY或BENOTIFY(微软扩展的)方法,将事件通知给飞信客户端。如 飞信订阅用户的presence事件,比如上线啊,下线啊什么的:
SUB fetion.com.cn SIP-C/2.0
F: 123456789
I: 1
Q: 1 SUB
N: presence
L: xxx

<args><subscription><contacts><contact uri="sip:xxxxxxxx@fetion.com.cn;p=xxxx" /><presence><basic attributes="all" /><personal attributes="all" /><extended types="sms;location;listening;ring-back-tone" /></presence></subscription></args>




以上只是粗粗列出了飞信按SIP协议的实现的一些SIP方法,具体的协议通信过程还需要具体分析。后面有空再做吧。

另外,飞信的协议为什么叫SIP-C呢?放狗一查,没找到这个,只查到一个SIPC的,那是Columbia大学的一个SIP协议的IP电话软件。是 SIP-CHINA? SIP-CHINAMOBILE? SIP-COMPACT? 我估计是Compact,因为SIP方法在SIP规范中是不用缩写的,只有Header中才用缩写,以便在UDP报文上承载,缩写一下,然后丢掉SIP要 求的URI的规范表达,简化Header的支持,就Compact了,就自主创新了,嘻嘻 ;)

以下分析基于Fetion 2006 beta 2.1.0.0,其SIP-C协议的版本是2.0。

前面列出了飞信的SIP-C协议支持的所有SIP Method以及Header Field,下面就两个用户间的聊天,即互相发送文本消息来看看飞信通过SIP-C协议的工作过程。


先看看RFC规定的SIP的建立会话的标准过程,在RFC3261中通过SIP建立会话的过程是这样子的:

                    atlanta.com . . . biloxi.com
                 .      proxy              proxy     .
               .                                       .
       Alice's . . . . . . . . . . . . . . . . . . . . Bob's
      softphone                                        SIP Phone
         |                |                |                |
         |    INVITE F1   |                |                |
         |--------------->|    INVITE F2   |                |
         | 100 Trying F3 |--------------->|    INVITE F4   |
         |<---------------| 100 Trying F5 |--------------->|
         |                |<-------------- | 180 Ringing F6 |
         |                | 180 Ringing F7 |<---------------|
         | 180 Ringing F8 |<---------------|     200 OK F9 |
         |<---------------|    200 OK F10 |<---------------|
         |    200 OK F11 |<---------------|                |
         |<---------------|                |                |
         |                       ACK F12                    |
         |------------------------------------------------->|
         |                   Media Session                  |
         |<================================================>|
         |                       BYE F13                    |
         |<-------------------------------------------------|
         |                     200 OK F14                   |
         |------------------------------------------------->|
         |                                                  |

上图中F1-F14分别是步骤的编号,而Medai Session过程在飞信中,就是文本聊天消息的传递过程了。在RFC3428中对标准SIP的扩展了一个Message方法,通过Message方法承 载这种即时消息,这样,上图中的Media Session按RFC3428的规定,就是这样的:
           | F1 MESSAGE         |                         |
           |--------------------> | F2 MESSAGE             |
           |                      | ----------------------->|
           |                      |                         |
           |                      | F3 200 OK              |
           |                      | <-----------------------|
           | F4 200 OK           |                         |
           |<-------------------- |                         |
           |                      |                         |
           |                      |                         |
           |                      |                         |
        User 1                  Proxy                    User 2

飞信是完全依照以上RFC的要求来实现这一过程来的。


下面来看飞信具体实现的聊天过程是怎么样的。假定有这么一个聊天过程是:飞信用户A(飞信号为123456789,飞信用TCP方式工作在 111.111.111.111:1111)双击好友列表中的飞信用户B(飞信号为987654321,飞信用TCP方式工作在 222.222.222.222:2222),这时聊天窗口弹出,用户A对B发出:"Hello!测试“,然后用户A关闭了聊天窗口,结束了这次即时消息 对话过程。

我们看用户A这一端来看整个聊天过程中,用户A与服务器(SIP的Proxy Server)的交互过程是 (请对照上面RFC的SIP会话建立过程,另外,下面的红色字是表示数据的发送方向,蓝色字是全部的SIP消息,即TCP包的包体部分。):
第一步: 111.111.111.111:1111 >>>>>>>> 221.130.45.203:8080
I fetion.com.cn SIP-C/2.0
F: 123456789
I: 16
Q: 1 I
T: sip:987654321@fetion.com.cn;p=1972
K: text/html-fragment
K: multiparty
L: 137

v=0
o=-0 0 IN 111.111.111.111:1111
s=session
c=IN IP4 111.111.111.111:1111
t=0 0
m=message 1111 sip sip:123456789@fetion.com.cn;p=xxxx

上面是发到服务器的第一条消息,是一个SIP协议的INVITE请求,发到飞信的服务器,服务器地址是221.130.45.203,端口8080,协议是TCP,上面的SIP消息就直接放在TCP的包中,UTF-8编码。

消息的第1是请求行,I=INVITE(这缩写的标准在哪里?我还没找到来依据),fetion.com.cn是请求对象(又没用标准的uri),"sIP-C/2.0"是当前的SIP-C协议版本。
第2行开始是消息头:"F"即From,标识从用户A(123456789)发出的请求(依然没用标准uri,应该是 sip:123456789@xx.fetion.com.cn才对吧....);"I"即CallID,按标准的SIP的规定,这应该是一个随机产生的 一个全局唯一的标识符,它应该在客户端和服务器整个交互的过程中保持不变,但在飞信的SIP-C协议中,这是一个序列号,第一次与8080端口建立连接发 出第一个请求时,这个值为1,以后每次发出一个请求则加1;"Q"即CSeq,也就是Command Sequence,它由一个整数的序列号和一个SIP方法组成,SIP方法与这个消息的SIP方法相同,这个序列号在一个会话过程中每次加1,这样来标识 SIP消息的顺序;"T"就是To啦,这里倒是用了标准uri的表示,"K"即Supported,表示客户端所支持扩展;"L"即Content- Length,就是消息体的长度。

从v=0开始的是消息体,用SDP(Session Description Protocol)描述的,根据RFC:
v= (protocol version),在目前飞信的INVITE中,固定的v=0
o= (owner/creator and session identifier). 在目前飞信的INVITE中,前面是固定的,后面是发起人(用户A)的IP地址和端口
s= (session name). 在目前飞信的INVITE中,固定的为s=session
c=* (connection information - not required if included in all media).在目前飞信的INVITE中,除后面的发起人(用户A)的IP地址和端口外,前面是固定的。
t= (time the session is active) 。在目前飞信的INVITE中,固定的为"t=0 0"
m= (media name and transport address)。在目前飞信的INVITE中,"1111"是发起人的TCP端口,后面的sip:xxxxxxxx,是发起人的uri。


第二步: 111.111.111.111:1111 <<<<<<<< 221.130.45.203:8080
SIP-C/2.0 100 Trying
Q: 1 I
T: sip:987654321@fetion.com.cn;p=xxxx
I: 16


这是服务器回过来的对第一步的INVITE消息的一个RESPONSE,所以,I=16,Q=1 I。表示正在查找用户B。

第三步: 111.111.111.111:1111 <<<<<<<< 221.130.45.203:8080
SIP-C/2.0 200 OK
Q: 1 I
K: text/html-fragment
K: multiparty
T: sip:123456789@fetion.com.cn;p=xxx
I: 16
L: 135

v=0
o=-0 0 IN 222.222.222.222:2222
s=session
c=IN IP4 222.222.222.222:2222
t=0 0
m=message 2222 sip sip:987654321@fetion.com.cn;p=xxx

这是服务器转过来的用户B对用户A的INVITE的回复,是:OK,其中222.222.222.222:2222是用户B的IP和端口。

第四步: 111.111.111.111:1111 >>>>>>>> 221.130.45.203:8080
A fetion.com.cn SIP-C/2.0
I: 16
Q: 1 A
T: sip:987654321@fetion.com.cn;p=xxx
F: 123456789

接着,用户A发出ACK消息。

第五步: 111.111.111.111:1111 >>>>>>>> 221.130.45.203:8080
M fetion.com.cn SIP-C/2.0
F: 123456789
I: 16
Q: 2 M
T: sip:987654321@fetion.com.cn;p=xxxx
C: text/html-fragment
K: SaveHistory
L: 121

<Font Face='Arial' Color='-16777216' Size='9'>hello! </Font><Font Face='SimSun' Color='-16777216' Size='12'>测试</Font>

这就是用户A向对方发的即时消息,内容是“hello!测试"。这里用的是SIP Message方法,消息体是XML表示的即时消息,消息头跟INVITE相似。

第六步: 111.111.111.111:1111 <<<<<<<< 221.130.45.203:8080
SIP-C/2.0 200 OK
Q: 2 M
T: sip:123456798@fetion.com.cn;p=xxxx
I: 16
D: Mon, 01 Mar 2007 00:00:00 GMT
XI: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

这是用户B收到第五步发出的即时消息后回过来的RESPONSE,结果是OK,其中XI这个消息头域的MessageID。

第七步: 111.111.111.111:1111 >>>>>>>> 221.130.45.203:8080
B fetion.com.cn SIP-C/2.0
F: 123456789
I: 16
Q: 3 B
T: sip:987654321@fetion.com.cn;p=xxxxx

这是用户A开始关闭聊天窗口准备结束聊天了,向用户B发出了一个BYE的SIP请求消息。

第八步: 111.111.111.111:1111 <<<<<<<< 221.130.45.203:8080
SIP-C/2.0 200 OK
Q: 3 B
T: sip:123456789@fetion.com.cn;p=xxxx
I: 16

这是最后用户B对对用户A发出的BYE的回复。


在用户B那边,整个交互过程跟用户A这边对应,用户A在发出INVITE,要求和B会话,而用户B这边则是回应邀请。

那发短信是怎么样的呢?也基本差不多,如发一个内容为test的短信到用户B时的SIP Message请求消息是这样的:
111.111.111.111:1111 >>>>>>>> 221.130.45.203:8080
M fetion.com.cn SIP-C/2.0
F: 12345678
I: 16
Q: 1 M
T: sip:987654321@fetion.com.cn;p=xxxx
N: SendSMS
L: 4

test



以上分析都是飞信通过TCP直连方式工作时的情况,飞信通过HTTP直连呢?工作过程跟上面完全一样,不同的是,连接的是 221.130.45.203:80,协议是HTTP,采用的POST请求是:
POST /ht/sd.aspx?t=s&i=2 HTTP/1.1
POST的数据分就是上面一样的SIP消息了。

飞信发送通过即时消息聊天时,整个过程就是这样子。 通过这个分析,我们也可以看到,飞信聊天时整个内容是既未加密又未变换的(MSN还作了点点变换),因此聊天过程是可以被截取的,因此通过飞信聊天是会被 别人偷听的,就如同去年MSN曝出的监听事件一样。那这个问题是可以解决的吗?当然是可以的,其实扩展SIP的Message方法时,RFC中就提到这一 点,我想飞信目前只是没去做,一旦这个问题暴露出来,对聊天内容进行加密,甚至是公开密钥的端到端强加密,都是可能和容易的,只不过不知道会不会不符合监 管要求,呵呵:)。


补记:发现了吗?上面分析中提到的URI(在Header Field "To"中)中都带着小尾巴:p=xxxx,这里的xxxx是个数字,怎么来的呢?文中我没解释,因为我还没完全搞清楚,下回再说。

以下分析基于客户端版本Fetion 2006 beta 2.1.0.0,协议版本是SIP-C 2.0。

一位朋友留言提到飞信的手机语聊,我这里也把两个飞信用户间手机语聊的通信过程贴出来,大家一起探讨。

以下文字中:红色的表示数据的传递方向,其中221.130.45.203:8080是Fetion用TCP直连方式连接的服务器地址和端口,也就是SIP Proxy Server的IP和Port,蓝色部分是完整的SIP-C消息,就是TCP包的包体部分。

以下的过程是用户A(飞信号:123456789,IP:Port=111.111.111.111:1111)向用户B(飞信 号:987654321,IP:Port=222.222.222.222:2222)发起手机语聊,我们可以看到,整个过程仍基本是一个标准的SIP会 话建立过程(就是没有Ringing)。

用户A这边:

STEP 1: 通过Proxy转发,发出INVITE到用户B
111.111.111.111:1111 >>>>>>>> 221.130.45.203:8080
I fetion.com.cn SIP-C/2.0
F: 1234567890
I: 11
Q: 1 I
T: sip:987654321@fetion.com.cn;p=xxxx
L: 180

v=0
o=-0 0 IN P4111.111.111.111:1111
s=VoiceChat
c=IN IP4 111.111.111.111:1111
t=0 0
a=user:sip:987654321@fetion.com.cn;p=xxxx
m=ivr 1111 sip sip:123456789@fetion.com.cn;p=yyyy


STEP 2:收到Proxy Server返回的“正在联络B..."
111.111.111.111:1111 <<<<<<<< 221.130.45.203:8080
SIP-C/2.0 100 Trying
Q: 1 I
T: sip:987654321@fetion.com.cn;p=xxxx
I: 11



STEP 3:收到Proxy Server转来的用户B的同意消息
111.111.111.111:1111 <<<<<<<< 221.130.45.203:8080
SIP-C/2.0 200 OK
Q: 1 I
T: sip:987654321@fetion.com.cn;p=xxxx
I: 11


STEP 4:回复用户B已收到其同意的消息 (ACK)
111.111.111.111:1111 >>>>>>>> 221.130.45.203:8080
A fetion.com.cn SIP-C/2.0
I: 11
Q: 1 A
T: sip:987654321@fetion.com.cn;p=xxxx
F: 123456789



STEP 5:要求Proxy Server开始接通过手机进行语聊,所以这里发出的是S,S=Service方法,用来请求服务器提供额外的服务,这个服务就是"StartVoiceChat"。
111.111.111.111:1111 >>>>>>>> 221.130.45.203:8080
S fetion.com.cn SIP-C/2.0
F: 123456789
I: 12
Q: 1 S
N: StartVoiceChat
L: 103

<args><voice-chat begin-date="2007-00-00 00:00:00.0000" /><users><user sid="987654321" /></users></args>


到这里就跟飞信客户端无关了,我猜测下面就是飞信服务器那边在通过7号信令接通两部手机,通过IVR给两部手机放音,然后捆绑话路,然后两部手机通过飞信的话路控制平台进行话音通话。

用户B那边呢?和用户A这边是一一对应的:

STEP 1: 用户B收到INVITE
222.222.222.222:2222 <<<<<<<< 221.130.45.203:8080
I 123456789 SIP-C/2.0
Q: 1 I
F: sip:987654321@fetion.com.cn;p=xxxx
I: -1
L: 178

v=0
o=-0 0 IN P4222.222.222.222:2222
s=VoiceChat
c=IN IP4 222.222.222.222:2222
t=0 0
a=user:sip:123456789@fetion.com.cn;p=yyyy
m=ivr 3418 sip sip:987654321@fetion.com.cn;p=xxxx

收到这个后,用户B的飞信会弹出窗口,问用户B是否接受用户A发起的手机语聊。这个消息的消息体中的a=,根据SDP,这里是<attribute>:<value>的形式。


STEP 2: 用户B发出同意消息:
222.222.222.222:2222 >>>>>>>> 221.130.45.203:8080
SIP-C/2.0 200 OK
I: -1
Q: 1 I
F: sip:987654321@fetion.com.cn;p=xxxx
L: 180

v=0
o=-0 0 IN P4222.222.222.222:2222
s=VoiceChat
c=IN IP4 222.222.222.222:2222
t=0 0
a=user:sip:987654321@fetion.com.cn;p=xxxx
m=ivr 1082 sip sip:123456789@fetion.com.cn;p=yyyy


STEP 3:收到用户A的ACK确认:
222.222.222.222:2222 <<<<<<<< 221.130.45.203:8080
A 123456789 SIP-C/2.0
Q: 1 A
F: sip:987654321@fetion.com.cn;p=xxxx
I: -1


到这里用户B手机也就响了。

其实,上面这整个过程体现的是真正的SIP过程,跟即时消息不同,SIP的出发点就是建立会话,至于会话的具体过程,如语音还是数据,用什么承载,是人对人还是人对机器,这都不是SIP协议所关心的事情,它最主要的任务只是建立会话。

今天又写了个小工具:FetionCfgEditor, 下载地址。

这个工具可以任意修改飞信的配置文件。飞信的配置文件保存在%userprofile%/application data/fetion这个目录下,这些文件全变换过了,用文本编辑器打开是不能读的,它是被编码变换过的。所以我写了FetionCfgEditor这 个小工具,这样我们可以任意地看和修改这些配置文件。

这个小工具的一个直接的应用,就是修改飞信的群发短信的人数限制。目前,飞信群发短信时只能发给8个人,我们可以用这个工具打开飞信的用户配置文件,这个 配置文件在%userprofile%/application data/fetion目录下的以飞信号码命名的子目录下,名字为configuration.dat,用FetionCfgEditor打开这个文件, 解码,找到其中的这一部分:
...
<batch-sms-max-receivers>8</batch-sms-max-receivers>
...
这是群发短信人数的限制,修改这个值,然后重新编码,保存即可。这个值到底能设多大,我没试过,但我想SIP消息是有超时控制的,所以这个值一定是有上限的。

注意修改时飞信不能运行,否则飞信退出时会以它事先读出的配置重新覆盖所有配置文件。

这个用户配置文件中,还有其它一些参数的设置,有兴趣可以慢慢去试,希望有结果时跟我分享一下:)。对了,据说修改300个好友数的限制是不起作用的。


花了好几天时间终于把飞信加密的密码用非.NET的方式给还原了,并用C++完全实现了一遍,回头把这个C++版的ShowPasswd贴上来。

看到有朋友问到飞信的加密方法,这里就把飞信用的加密和变换的方法一起简要介绍一下。

1.飞信的配置文件的加密方法
飞信的配置文件其实不是加密,只是进行了一下变换。这种变换的编码方法是:将原来的XML文本(UTF-8编码的)进行压缩,然后将压缩后的结果进行 base64编码,编码后的结果作为XML的DocumentElement的值,同时在XML的DocumentElement的Attributes 中,加上 StoreMode=1。

压缩采用的是标准GZIP算法,飞信用的是.NET的类库System.IO.Compression.GZipStream。

2.飞信的用户密码的加密方法
我们把飞信的配置文件还原,就会看到保存的密码。这个可是真正的加密了,采用的是TripleDES算法,依然是标准的DES算法,.NET的类库是 System.Security.Cryptography。配置文件中是加密以后的结果的BASE64编码。

加密和解密重要的是KEY和IV。IV (initialization vector)在飞信中是固定的值,KEY的算法是:取当前用户的SID(security identifiers),表示成字符形式(S-1-5-xx-xxxxxxxxx-xxxxxxxxx.....),求这个字串的MD5,KEY即为这 个字符串的求MD5 Digest的值。TripleDES是对称加密算法,用同样的KEY和IV就可以将其解密了。

感谢 Fetion8朋友敏锐地发现uri中的p跟手机号段有关系。

飞信的用户URI为:sip:123456789@fetion.com.cn;p=1234,这里头123456789是飞信号,而p这个tag是怎么来的我一直不清楚,也忘了研究了,呵。感谢这位朋友一下发现了这个跟手机号段有关系。

在这位朋友的提醒下,我下午看了下Contacts.dat文件中的好友列表(用本博客中的DecodeCfg或FetionCfgEditor工具可以解码这个文件),研究发现确实是跟手机号段有关系的。就我看到的号码来看,其生成的规律就是:

p=手机号码前6位 - 134099


请大家帮着验证一下哈。

为什么是134099?我估计134100是中移动的第一个号段。

再次感谢这位朋友的重要发现。


好久没搞这个了,现在根据一些朋友提出的问题继续分析好了。:)

飞信的整个登录过程包括取系统配置、验证身分、取联系人、取离线消息等等一堆过程,这里就不细述了,下面主要说明到是一些朋友比较关心的身份验证是怎么做的。

飞信有两处验证身份,第一次是通过SSL向SSIPortal登录时,第二次是用SIP协议向Proxy Server注册时。

一、向SSIPortal登录

SSI是啥,我一直没弄明白,是Single Sign-In? 习惯上不是SSO(Single Sign-On)吗?反正飞信一开始,必须向nav.fetion.com.cn登录,这是飞信的Portal。登录过程其实很简单,就是通过SSL做一 个的GET,请求是:
    https://nav.fetion.com.cn/ssiportal/SSIAppSignIn.aspx?mobileno=13XXXXXXXXX&pwd=XXXX
pwd就是这个用户的密码。服务器验证通过后,就会返回200 OK,Cookie中就有关键的SSIC了。SSIC应该是服务器给客户端的Credential,以后再跟服务器以Http的方式打交道,就必须带上这 个Credential了,如下一步的可选操作Geturi:
    POST /hds/geturi.aspx HTTP/1.1
    User-Agent: IIC2.0/PC 2.1.0.0
    Host: 221.130.45.205
    Cookie: ssic=xxxxxxxxxxxxxxxx
    Content-Length: 0
    Connection: Keep-Alive

如果采用http方式而非tcp直连方式(连proxy server的8080端口),那么与服务器间的sip协议是用http承载的,每次都必须带上这个ssic。

这次登录除获得Credential外,还一个重要的作用就是获得用户的URI,即"sip:123456789@fetion.com.cn;p=xxxx".

二、向Proxy Server注册
完成上一步,就开始SIPC的交互过程了,首先用Register方法向Proxy Server注册,Proxy Server的地址(221.130.45.203)从哪来的,以前文章专门说过。

注册的过程是:
-----------------------------------------------------
xxx.xxx.xxx.xxx:xxx >>>>>>>> 221.130.45.203:8080

R fetion.com.cn SIP-C/2.0
F: 123456789
I: 1
Q: 1 R
L: 249

<args>.......</args>
-----------------------------------------------------
xxx.xxx.xxx.xxx:xxxx <<<<<<<< 221.130.45.203:8080

SIP-C/2.0 401 Unauthorized
I: 1
Q: 1 R
W: Digest algorithm="MD5-sess",nonce="xxxxxxxxxxxxxxxx"

-----------------------------------------------------
xxx.xxx.xxx.xxx:xxxx >>>>>>>> 221.130.45.203:8080

R fetion.com.cn SIP-C/2.0
F: 123456789
I: 1
Q: 2 R
A: Digest response="xxxxxxxxxxxxxxx",cnonce="xxxxxxxxxxxxxxxx"
L: 249

<args>......</args>
-----------------------------------------------------
xxx.xxx.xxx.xxx:xxxx <<<<<<<< 221.130.45.203:8080

SIP-C/2.0 200 OK
I: 1
Q: 2 R
X: 600
L: 877

<results>.....</results>


这是个标准的Digest Authentication验证过程,具体去看SIP或HTTP的文档好了:) 我们来关心"W"即WWWAuthenticate这个SIP Header Field,nonce是服务器给出的,那response和cnonce是怎么计算出来的呢?

计算response和cnonce需要用到sid(飞信号)、password、domain(fetion.com.cn或vodafone等用户所 属域)、nonce。飞信计算cnonce是用了4个随机数组成一个16进制的字符串然后求这个字符串的MD5,responce的计算方法是:
1.求"sid:domain:passwd"这个utf8字符串的MD5值,得出key
2.求"key:nonce:cnonce"这个utf8字符串的MD5字符串,得出H1
3.求“REGISTER:sid"这个utf8字符串的MD5字符串,得出H2
4.求"H1:nonce:H2"这个utf8字符串的MD5字符串,即为response.


以上就是飞信的身分验证过程,我还没来得及写代码测试,应该就是这样子。另外,如果知道用户的URI,是否可以不用注册到SSIPortal?这个需要测试才知道,我看飞信中好象还对注册到SSIPortal的Session做了Keep Logon。

好啦,分析到现在我们可以做一个飞信的客户端了,哈,谁有兴趣? ;)