我注意到UC的时候它的版本是v2.40,那时UC的聊天数据就已经加密了,,一直到现在的UC2005(内部版本号好象是什么4.10.625)。另外软件本身也加了壳,在这一点上,UC比QQ做的好那么一点。然而,它的聊天文本加密是一个典型的完全不顾及密码学基本常识的蹩脚作品。我是说,它居然使用固定密钥!!!实际上再好的公开算法,也经不住固定密钥的糟蹋。真是可惜呀。
关于Blowfish:Blowfish算法是密码学牛人Bruce Schneier设计的算法,是一个64位分组,变长密钥的分组密码算法。据作者本人讲,设计这个算法要达到四个目标:Fast,Compact, Simple和Variably Secure,想来应该是做到了。我不是十分清楚对该算法的分析现在达到了什么境界,不过本文目的不是去分析算法,而是针对特定的错误应用找出其中的漏洞。因此我们只要能辨认出这个算法就可以了。
这个算法明显的汇编特征是,需要维护一个不小的表,加密的轮函数需要不断查表以进行运算。具体一点说是4个100h元的数组,每个元是一个32位整数。总计大小4*400h*4=1000h字节。另外附近还有一个18个元素的表,每个元素也是4字节。这些表的数值由加密密钥初始化而得到,但是在以后的加密过程中这个表是只读的,就是说其中的数据是不变的。表的使用方法是,把输入的整数拆分成四个字节,分别作四次查表,得到的结果作加法和异或运算后得到最后的该轮运算的结果。如果你看到某加密算法的汇编代码中有这样特征的话,十有八九就是Blowfish了。
分析UC的过程,这里以UC2.40为例。首先查壳,ASPack 2.11 -> Alexey Solodovnikov(后来变成了ASPack 2.12),脱壳过程就不赘述了,跟踪也不难,稍微细心一点,我们应该很快就能找到对数据进行加密处理的地方。一层层钻进call里面去后,我们可以看到下面这样一段代码:
.text:005F92D4 sub_5F92D4 proc near ; CODE XREF: sub_5F9330+23 p
.text:005F92D4 ; sub_5F9394+24 p
.text:005F92D4
.text:005F92D4 var_2 = word ptr -2
.text:005F92D4 arg_0 = dword ptr 8
.text:005F92D4 arg_4 = dword ptr 0Ch
.text:005F92D4
.text:005F92D4 push ebp
.text:005F92D5 mov ebp, esp
.text:005F92D7 push ecx
.text:005F92D8 push ebx
.text:005F92D9 mov eax, [ebp+arg_4]
.text:005F92DC mov edx, [ebp+arg_0]
.text:005F92DF mov ecx, eax
.text:005F92E1 and cx, 0FFh
.text:005F92E6 mov [ebp+var_2], cx
.text:005F92EA shr eax, 8
.text:005F92ED mov ebx, eax
.text:005F92EF and bx, 0FFh
.text:005F92F4 shr eax, 8
.text:005F92F7 mov ecx, eax
.text:005F92F9 and cx, 0FFh
.text:005F92FE movzx ecx, cx
.text:005F9301 shr eax, 8
.text:005F9304 and ax, 0FFh ; 到这里之前把输入拆成了4个单字节
.text:005F9308 movzx eax, ax
.text:005F930B mov eax, [edx+eax*4+48h] ; 这里
.text:005F930F add eax, [edx+ecx*4+448h] ; 这里
.text:005F9316 movzx ecx, bx
.text:005F9319 xor eax, [edx+ecx*4+848h] ; 这里
.text:005F9320 movzx ecx, [ebp+var_2]
.text:005F9324 add eax, [edx+ecx*4+0C48h] ; 这里
.text:005F932B pop ebx
.text:005F932C pop ecx
.text:005F932D pop ebp
.text:005F932E retn
.text:005F932E sub_5F92D4 endp
看到了吗?EDX这个地方是一个18元的整数表,其实也就是Blowfish代码中维护的P盒,18*4=72=48h,那么EDX+48h,EDX+448h,EDX+848h和EDX+C48h就是S盒了,每个大小400h。标注“这里”的这四句是四次查表运算,结果放在EAX里返回。完全和Blowfish的轮函数F相同,所以看到这里,基本可以确定算法就是Blowfish了。多次观察加密过程,可以发现这个表里的数据总是不变的,说明UC的运行没有任何密钥协商过程,用的是固定密钥。
Bruce原来的代码(
http://www.schneier.com/code/bfsh-sch.zip)中用P,S来命名,我觉得不大好,所以我改写的时候稍微把名字加长了些。
由于固定密钥,为了代码的简便,我选择了直接从内存里(7B0034)把这段生成好的1048h字节数据扒下来,写在了我的解密代码中,这也就是我的代码中P_BOX和S_BOX的来历。这样做一个好处是省去了调用InitializeBlowfish函数的时间,另外也真正做到专码专用了。只要UC一天不改,这代码就一天有效,决不含糊。
现在的UC还是一直就使用这样的算法,至少两年都没改过了,而且还把新浪Web聊天室也如此包装了一下。解密既然不难,接下来的事情其实就都没有难度了,我随便分析了一下,写了个ucsniffer。
关于UC的语音聊天没有仔细研究,如果没改动过的话,应该还是明码,直接在线收听或者录下来慢慢听都是可以实现的。