VATSIM 的新连线客户端 vPilot 出来挺久了,我一直都没去看,Hans 倒是当天就开始研究了。vPilot 有一个问题,就是它是 VATSIM-Only 的设定,除了 VATSIM 的那一票服务器外其他的都不能连。在它的配置文件中,有一项是这样的:pZ9u441bE4a2NCGgqMxKNvwAIy0qEA+AwXB8c3sV90c=
。它看起来是用 Base64 编码的字符串,但是使用 Base64 去解码没有效果。紧随其后的,是名为 CachedServer
的标签,但是它的值也和 NetworkStatusURL
是一样无法解码的。
我今天怀着必胜的信念尝试解决这个问题。因为没有什么顾虑,此处我将披露所有技术细节。在开始前,我可以先说明最初的思路是错误的,但是这不要紧,这些分析方法不一定不适用于所有情况。当然,也算一次有意义的尝试。
刚开始我的方案和 Hans 的方案是基本一致的:想办法去解开这个加密的字符串。之前我做过一个小范围使用的 Bob89 加解密算法(此处不透露细节),于是我在这里编写了几个小程序对其进行了如下处理:①将每个字符的 ASCII 加上 / 减去若干整数;②翻转字符串;③小范围解码测试。不幸的是,以上尝试均以失败告终。
我在想,是不是考虑的方向错了,我不应该从解密这个字符串入手,而是从已知的字符串入手进行加密还原。考虑这样的一个事实:服务器地址是通过网络获取的,否则不存在 cache
的问题。那么首先想到的应该是删掉 CachedServer
信息然后开上抓包程序启动 vPilot 进行信息的抓取。抓包过程还是很顺利的,有不少加密的信息无法获取,但是明文都能抓全。一条条分析,我找出了 vPilot 的 API 根:vpilotapi.metacraft.com
。看上去是一个激动人心的成就,但是事实告诉我这里的 API 并不负责服务器地址的获取。茫忙数据包大海中苦苦搜寻,得来了一个有意义的包:正文部分包含了服务器信息。没错这就是我所需要的,我根据 IP 一层一层回溯上去,找到了本机与对方 IP 的第一次请求:域名是 status.vatsim.net
,资源是 status.txt
。接着往下面找,服务器信息来源于 info.vroute.net/vatsim-servers.txt
。而 status.txt
中是有 vatsim-servers.txt
的,显然 status.txt
是列表的来源。这样的分析是没错的,我想。但是再怎么尝试也不知道如何得到一个像那样加密了的字符串。
我不情愿地打开了 ILSpy。我不想再次反编译 vPilot,它被混淆了,分析它不仅痛苦,而且没什么意义。这次的反编译还是带来了一些好消息:在 Metacraft.Vatsim.vPilot.Core
命名空间中有一个 vPilotConfig
类,它有一个属性,名叫 NetworkStatusURL
。再往下看,有一些被混淆了的方法,但是值得庆幸的是属性名称没有被混淆,这真是令人高兴。有一个方法进入了我的视线:
- // Metacraft.Vatsim.vPilot.Core.vPilotConfig
- private void a()
- {
- string a_ = this.c().ToString();
- string[] array = this.CachedServers.ToArray();
- this.CachedServers.Clear();
- string[] array2 = array;
- for (int i = 0; i < array2.Length; i++)
- {
- string a_2 = array2[i];
- this.CachedServers.Add(vPilotConfig.a(a_2, a_));
- }
- this.NetworkStatusURL = vPilotConfig.a(this.NetworkStatusURL, a_);
- try
- {
- this.NetworkPassword = vPilotConfig.a(this.NetworkPassword, a_);
- }
- catch (CryptographicException)
- {
-
- }
- catch (FormatException)
- {
-
- }
- }
NetworkStatusUrl 出现了!我们再来看看 a 方法的另一个重载:
- // Metacraft.Vatsim.vPilot.Core.vPilotConfig
- private static string a(string A_0, string A_1)
- {
- UTF8Encoding uTF8Encoding = new UTF8Encoding();
- MD5CryptoServiceProvider mD5CryptoServiceProvider = new MD5CryptoServiceProvider();
- byte[] key = mD5CryptoServiceProvider.ComputeHash(uTF8Encoding.GetBytes(A_1));
- TripleDESCryptoServiceProvider tripleDESCryptoServiceProvider = new TripleDESCryptoServiceProvider();
- tripleDESCryptoServiceProvider.Key = key;
- tripleDESCryptoServiceProvider.Mode = CipherMode.ECB;
- tripleDESCryptoServiceProvider.Padding = PaddingMode.PKCS7;
- byte[] array = Convert.FromBase64String(A_0);
- byte[] bytes;
- try
- {
- ICryptoTransform cryptoTransform = tripleDESCryptoServiceProvider.CreateDecryptor();
- bytes = cryptoTransform.TransformFinalBlock(array, 0, array.Length);
- }
- finally
- {
- tripleDESCryptoServiceProvider.Clear();
- mD5CryptoServiceProvider.Clear();
- }
- return uTF8Encoding.GetString(bytes);
- }
其中,a(void)
里面的 a_
变量调用了 c
方法。c
方法只有一条语句:return new Guid(1433775113u, 62174, 18974, 128, 139, 227, 57, 142, 23, 248, 191);
。这样一来就好办了,这些部分完全可以拼在一起用以证实我的猜想。没错,http://status.vatsim.net/
加密出来结果就是配置文件中所看到的 pZ9u441bE4a2NCGgqMxKNvwAIy0qEA+AwXB8c3sV90c=
了。
接下来的任务是什么,已经是十分明朗的:仅需自己做一个 URL 替换掉原来的,便可以自己定义服务器列表了。所以第一个任务是写个程序来加密 URL。简要分析一下这里的加解密方法:基础算法是 Triple DES,密钥是通过特定参数生成的 Guid 的 MD5 散列值。事实上之前的分析并没有出错,这个串的确是 Base64 编码的产物,只不过它的明文不是一个普通的字符串罢了。写个加密程序不费事,不用多久就能完成。
改完了 NetworkStatusURL
,要做的就是把对应的 URL 资源做好了。先在修改的 URL 目录里放一个 status.txt
文件,里面需要有一个行:url1={xxx.txt}
。再建立一个对应的 xxx.txt
,其中按诸如 ASIA:106.186.24.200:Tokyo, JAPAN:ASIA:1:
的格式组织好服务器列表,然后启动 vPilot 即可。
如果你想连接大陆三大平台(A/F/S),请直接修改 NetworkStatusURL
为 6FXSmoBv/MERbMqqCvbTYeIgUd9gK+Aw
,我已经制作好了。这样的服务器列表没有删减 VATSIM 服务器,仅是添加了三平台的服务器。此外,我写了一个小工具,它可以生成 NetworkStatusURL
,其中已打上了必要的提示。你可以通过它来创造自己需要的服务器列表。我没有对其混淆处理,想看看怎么实现的,直接反编译就好了。这里可以下载。