November 4th, 2015

YY 2015 逆向分析(一) 底层通讯机制

前言

  之前研究过YY 6.2版本的协议,今天有没事又看了一次,发现现在的版本8.3.0.0版本较之前又有了翻天覆地的变化, 密钥交互时不再使用ZLIB压缩解压缩,并且所有的命令字是100%的全部更新了,但是通讯算法还是没变,仍然是RSA + RC4

底层通讯过程

  1. YY的封包结构是4字节长度,4字节命令字,剩下的就是数据了,也就是说,根据首4字节就可以将包分割开。但是也有一些其他情况, 比如嵌套包,一个命令字包中又包含了一个或者多个子命令包,这些子命令包有时是独立处理,有时需要用到父包的数据。

  2. 首先,客户端先给几个(我这里是4个)服务器发送UDP包,请求IP地址,服务器会根据收到的版本号下发对应的支持此版本号的服务器和列表。
    61 00 00 00 1E 73 00 00 C8 00 FF FF FF FF FF FF   a....s..?.......
    FF FF 03 01 00 00 00 00 00 00 20 00 35 34 64 37     ........ .54d7
    63 62 39 31 35 38 31 38 32 30 39 38 65 38 36 32   cb9158182098e862
    66 66 38 39 30 64 62 66 61 33 63 30 00 00 30 80   ff890dbfa3c0..0.
    07 00 38 2E 33 2E 30 2E 30 02 00 79 79 00 00 00   ..8.3.0.0..yy...
    00 00 00 00 00 00 00 04 08 00 00 00 00 00 00 00   ................
    00††††††††††††††††††††††    .
    

    然后其中一个会返回0x741E,返回请求到的IP列表(其他服务器可能也返回了,但是这时客户端收到一个就不再继续接收了)。 注意这里每个包的+4处是命令字(下同),比如这里是0x731E。

  3. 客户端从收到的IP列表中随机选几个(我这里是6个)发送登陆请求,其中5个是UDP,1个TCP。包的数据都是一样的。
    34 00 00 00 01 14 00 00 C8 00 00 00 00 00 30 80   4.......?.....0.
    FF FF FF FF 08 00 62 75 67 73 74 65 73 74 00 00   ..bugstest..
    04 08 00 00 01 00 00 00 00 00 00 00 00 00 00 00   ................
    00 00 00 00††††††††††††††††††   ....
    
  4. 响应最快的那个服务器返回0x1501命令,+0C处是反馈结果,当为0xC8时表示一切正常,后面的数据就是真正要登陆的服务器 的IPPort了;如果为其他值(比如97 01 00 00),表示版本过低,需要升级。
    54 00 00 00 01 15 00 00 C8 00 00 00 C8 00 00 00   T.......?...?...
    02 00 00 00 70 75 DC EB DA 85 3B 14 00 00 03 00   ....pu????;.....
    00 00 91 1F 39 04 51 00 6F B2 91 8A 4D E6 3B 14   ..?9.Q.o???M?;.
    00 00 03 00 00 00 94 1F 39 04 54 00 00 00 00 00   ......?9.T.....
    01 00 11 00 AB 5B 54 76 00 00 00 00 00 00 00 00   ....?[Tv........
    F4 01 00 00††††††††††††††††††   ?...
    
  5. 此时才是真正的登陆过程,一切从这里开始。客户端根据收到的登陆用的IP列表,随机选择一个建立连接,连接成功后,客户端 主动给服务器发送一条密钥交互包,命令字是0x1104或者0x3204(因为登陆时会连接多个服务器,比如验证服务器\频道服务器\好友服务器, 命令字根据类型的不同是不一样的)
    53 00 00 00 04 11 00 00 C8 00 40 00 E9 03 40 F2   S.....?@.?@?
    4D ED C4 86 32 9B 25 F1 7A D8 94 70 D5 E2 20 6C   M砟??駔財p这 l
    F0 B7 AF FD 5A DD A4 A6 F3 CA F5 8C CE 7B 9B CB   鸱Z荬术屛{浰
    2D AB 49 45 04 61 93 9C 4F 7D 40 DE E1 E4 8B 19   -獻Ea摐O}@掎鋴
    BC 89 C2 E0 76 29 58 2E 2F BB A6 51 01 00 03 00   級锣v)X./沪Q..
    00 00 00                                          ...
    

    或者

    66 00 00 00 04 32 00 00 C8 00 40 00 E9 03 40 F2  f...2..?@.?@?
    4D ED C4 86 32 9B 25 F1 7A D8 94 70 D5 E2 20 6C  M砟??駔財p这 l
    F0 B7 AF FD 5A DD A4 A6 F3 CA F5 8C CE 7B 9B CB  鸱Z荬术屛{浰
    2D AB 49 45 04 61 93 9C 4F 7D 40 DE E1 E4 8B 19  -獻Ea摐O}@掎鋴
    BC 89 C2 E0 76 29 58 2E 2F BB A6 51 01 00 03 13  級锣v)X./沪Q.
    00 00 00 13 00 00 00 04 E8 0B 00 C8 00 00 00 05  ......?.?..
    00 6C 6F 67 69 6E                                .login
    

    +4处的0x40字节是客户端生成的公钥,即RSAN;+0x4E处一字节是RSAE。这个包的作用是通知服务端,下发的RC4密钥使用这个RSA密钥进行加密,

  6. 服务器收到客户端的密钥后,返回0x3304\0x1504,以前这个包的key是经过zlib压缩的,现在取消掉了,估计服务器吃不消给优化掉了。
    50 00 00 00 04 15 00 00 C8 00 40 00 7B 89 12 1B  P.........@.{...
    FE 8F AC 7A BF 02 D4 34 42 D9 52 29 21 EA 0D 13  ...z...4B.R)!...
    27 EC C0 11 BB 59 A8 7F 9C 95 AE B7 9C 09 5A 3C  '....Y........Z<
    43 A1 55 8E DA A0 2D F6 CA E9 EB F4 DC D3 DC C5  C.U...-.........
    64 8E 75 91 AE 32 A4 28 04 88 CA 79 00 00 00 00  d.u..2.(...y....
    

    或者

    18 09 00 00 04 33 00 00  C8 00 40 00 22 79 01 D6 .....3....@."y..
    58 E0 86 9C A2 57 37 21  89 4B BF 7F C2 F2 BE 64 X....W7!.K.....d
    48 00 8B E6 14 82 C3 0E  D0 FF 80 3C 2C 60 F2 DD H..........<,`..
    7F BA A4 67 17 C1 DA CA  F4 D6 C0 4E B2 EE A7 90 ...g.......N....
    23 3E 46 1D D4 A4 B4 E5  91 14 FE F5 C8 08 00 00 #>F.............
    C8 08 00 00 04 E9 0B 00  C8 00 00 00 BA 08 B6 08 ................
    00 00 06 00 04 00 00 00  FF FF 00 00 17 27 54 08 .............'T.
    00 00 78 DA 8D 57 4D 6C  1B C7 15 7E 43 AD 4D 49 ..x..WMl...~C.MI
    D6 8F AD C6 86 64 BB 8E  9A BA 46 0E 75 10 5B 76 .....d....F.u.[v
    E5 9C 32 4B 8A 22 F5 47  D2 22 25 45 69 22 7B 45 ..2K.".G."%Ei"{E
    ...后面省略
    

顺便一提,3304这个包除了密钥外还下发了一个字节码模块,交给客户端初始化动态环境,这样的数据包后面还有很多,都是服务端下发的动态检测代码。

这两个包的0C处开始的0x40字节便是下发的经过RSA加密过的RC4密钥了,经过RSA解密后的RC4密钥复制成两份,一份是专门用来加密,一份专门用来解密。 到这一步,YY密钥协商过程完成,后面发送的数据都是加密数据了。

至于算法的实现在dwUtility.dll中的导出函数,这里挑出几个用得到的翻译一下功能:

导出函数名称 功能
DwUtility::dwBaseFunc::dwUtilityrgk 初始化RSA结构
DwUtility::dwBaseFunc::dwUtilityrpd RSA解密算法
DwUtility::dwBaseFunc::dwUtilitybb 将大数转换为二进制
DwUtility::dwBaseFunc::dwUtilityrsk 初始化RC4结构
DwUtility::dwBaseFunc::dwUtilityr RC4加\解密算法

以上就是几个关键的算法了,这个DLL中还有很多其他基础的功能,有兴趣的可以自己去研究,这里就不一一列举了。

由于收发数据有的加密有的不加密,并且连接的SOCKET太多,只用OD分析实在不方便,为此我写了一个现实加密前和解密后的封包分析工具, 加上了每个原始包对应的SOCKET加以区分。show一下:)

结束

YY在登陆时是一次性给N个服务器发送请求,谁快用谁,这对登陆服务器集群的性能要求特别高,因为这样 每个服务器处理所需要的资源成倍提高。通过建立密钥这个过程可以看出YY把负载均衡真的是做到了极致。

这一章就到此结束,下次有空写下一章YY登陆的封包实际内容分析。

PS:研究仅为学习,没有任何不纯目的:)

如有任何疑问请联系:bughoho[at]gmail.com,欢迎指出错误。

本作品由bughoho创作,采用知识共享署名 4.0 国际许可协议进行许可。转载请注明出处。