p-14] 局部变量[ebp-14]存放强制CString类型转换以后的数据指针的指针,例如DB2E0600 1000B867 E8 10430000 call <MFC42.CString::CString> 1000B86C 8A45 E4 mov al,byte ptr ss:[ebp-1C] 局部变量[ebp-1C]保存本段的类型,4或者7 1000B86F 295D 08 sub dword ptr ss:[ebp+8],ebx 去掉已经处理的数据,执行后 [ebp+8]=28H,ebx为数据段中数据部分的长度 1000B872 03F3 add esi,ebx ESI指向下一个数据段的开始部分,ebx保存数据段中数据部分的长度指针 1000B874 33FF xor edi,edi 1000B876 84C0 test al,al 测试本段的类型是否为0 1000B878 C645 FC 01 mov byte ptr ss:[ebp-4],1 局部布尔型变量[ebp-4]=1 1000B87C 0F86 A3010000 jbe BasicCtr.1000BA25 如果本段的数据类型为0,则执行 1000BA25后退出 1000B882 3C 07 cmp al,7 1000B884 0F86 B6000000 jbe BasicCtr.1000B940 如果小于等于7,则跳转到1000B940执行。对于EWH来说就是这样 1000B88A 3C 08 cmp al,8 1000B88C 0F84 74010000 je BasicCtr.1000BA06 如果数据类型为8,则直接退出。 1000B892 3C 09 cmp al,9 1000B894 74 5D je short BasicCtr.1000B8F3 1000B896 3C 0A cmp al,0A 1000B898 0F85 87010000 jnz BasicCtr.1000BA25 1000B89E 8B4D D8 mov ecx,dword ptr ss:[ebp-28] 当数据类型为A时,执行本程序代码 1000B8A1 8D45 D4 lea eax,dword ptr ss:[ebp-2C] 1000B8A4 50 push eax 1000B8A5 E8 47FEFFFF call BasicCtr.1000B6F1 1000B8AA 8B45 D4 mov eax,dword ptr ss:[ebp-2C] 1000B8AD FF75 EC push dword ptr ss:[ebp-14] 1000B8B0 8B08 mov ecx,dword ptr ds:[eax] 1000B8B2 53 push ebx 1000B8B3 50 push eax 1000B8B4 FF91 BC000000 call dword ptr ds:[ecx+BC] 1000B8BA 8BD8 mov ebx,eax 1000B8BC 85DB test ebx,ebx 1000B8BE 0F85 12010000 jnz BasicCtr.1000B9D6 1000B8C4 8B45 D4 mov eax,dword ptr ss:[ebp-2C] 1000B8C7 6A 04 push 4 1000B8C9 8945 DC mov dword ptr ss:[ebp-24],eax 1000B8CC 8D45 DC lea eax,dword ptr ss:[ebp-24] 1000B8CF 50 push eax 1000B8D0 8D4D C0 lea ecx,dword ptr ss:[ebp-40] 1000B8D3 E8 A4420000 call <MFC42.CString::CString> 1000B8D8 50 push eax 1000B8D9 8D4D EC lea ecx,dword ptr ss:[ebp-14] 1000B8DC C645 FC 03 mov byte ptr ss:[ebp-4],3 1000B8E0 E8 C3400000 call <MFC42.CString::operator=> 1000B8E5 C645 FC 01 mov byte ptr ss:[ebp-4],1 1000B8E9 8D4D C0 lea ecx,dword ptr ss:[ebp-40] 1000B8EC E8 AB400000 call <MFC42.CString::~CString> 1000B8F1 EB 50 jmp short BasicCtr.1000B943 1000B8F3 8B4D D8 mov ecx,dword ptr ss:[ebp-28] 当数据类型为9时,执行这个操作 1000B8F6 8D45 D0 lea eax,dword ptr ss:[ebp-30] 1000B8F9 50 push eax 1000B8FA E8 4E180000 call BasicCtr.1000D14D 1000B8FF 8B45 D0 mov eax,dword ptr ss:[ebp-30] 1000B902 FF75 EC push dword ptr ss:[ebp-14] 1000B905 8B08 mov ecx,dword ptr ds:[eax] 1000B907 53 push ebx 1000B908 50 push eax 1000B909 FF51 78 call dword ptr ds:[ecx+78] 1000B90C 8BD8 mov ebx,eax 1000B90E 85DB test ebx,ebx 1000B910 0F85 D4000000 jnz BasicCtr.1000B9EA 1000B916 8B45 D0 mov eax,dword ptr ss:[ebp-30] 1000B919 6A 04 push 4 1000B91B 8945 DC mov dword ptr ss:[ebp-24],eax 1000B91E 8D45 DC lea eax,dword ptr ss:[ebp-24] 1000B921 50 push eax 1000B922 8D4D BC lea ecx,dword ptr ss:[ebp-44] 1000B925 E8 52420000 call <MFC42.CString::CString> 1000B92A 50 push eax 1000B92B 8D4D EC lea ecx,dword ptr ss:[ebp-14] 1000B92E C645 FC 02 mov byte ptr ss:[ebp-4],2 1000B932 E8 71400000 call <MFC42.CString::operator=> 1000B937 C645 FC 01 mov byte ptr ss:[ebp-4],1 1000B93B 8D4D BC lea ecx,dword ptr ss:[ebp-44] 1000B93E EB AC jmp short BasicCtr.1000B8EC 1000B940 6A 01 push 1 当数据段的类型<=7时,直接从此处执行 1000B942 5F pop edi 1000B943 8B5D D8 mov ebx,dword ptr ss:[ebp-28] 局部变量[ebp-28]保存全局的标志结构 1000B946 8D45 EC lea eax,dword ptr ss:[ebp-14] 局部变量[ebp-14]存放强制类型转换以后的数据指针的指针,例如DB2E0600 1000B949 50 push eax EAX存放强制类型转换以后的数据指针 1000B94A 8D45 E8 lea eax,dword ptr ss:[ebp-18] 局部变量[ebp-18]存放强制类型转换以后的数据指针的指针,例如AST 1000B94D FF75 E4 push dword ptr ss:[ebp-1C] 局部变量[ebp-1C]中的第一个字节保存本段的类型,4或者7 1000B950 8BCB mov ecx,ebx 1000B952 50 push eax 1000B953 E8 B4FCFFFF call BasicCtr.1000B60C call 1000B60C(CString,Flag,CString) 1000B958 85FF test edi,edi 1000B95A 74 18 je short BasicCtr.1000B974 1000B95C 8B45 E0 mov eax,dword ptr ss:[ebp-20] 局部变量[ebp-28]第一次为0,为一个计数器 1000B95F 8B4B 64 mov ecx,dword ptr ds:[ebx+64] 存在于标志结构中,为一个全局地址 1000B962 8B55 F0 mov edx,dword ptr ss:[ebp-10] 局部变量[ebp-10]保存拷贝后的数据,即没有经过处理的。例如040300BDAF…… 1000B965 C1E0 02 shl eax,2 EAX=EAX*2 1000B968 891401 mov dword ptr ds:[ecx+eax],edx 将未做解调的原始数据放到全局结构中某个指针指示的内存中 1000B96B 8B4B 78 mov ecx,dword ptr ds:[ebx+78] 存在于标志结构中,为一个全局地址 1000B96E 8B55 CC mov edx,dword ptr ss:[ebp-34] 局部变量[ebp-34]保存本数据段的总长度 1000B971 891401 mov dword ptr ds:[ecx+eax],edx 将数据长度放到全局结构中某个指针指示的内存中 1000B974 8065 FC 00 and byte ptr ss:[ebp-4],0 局部布尔型变量[ebp-4]=0 1000B978 8D4D EC lea ecx,dword ptr ss:[ebp-14] 1000B97B E8 1C400000 call <MFC42.CString::~CString> 清除数据段中的数据部分CString 1000B980 834D FC FF or dword ptr ss:[ebp-4],FFFFFFFF 局部布尔型变量[ebp-4]=-1,为True 1000B984 8D4D E8 lea ecx,dword ptr ss:[ebp-18] 1000B987 E8 10400000 call <MFC42.CString::~CString> 清除数据段中的标志部分CString,例如AST 1000B98C FF45 E0 inc dword ptr ss:[ebp-20] 局部变量[ebp-28]计数器加一 1000B98F 8B45 E0 mov eax,dword ptr ss:[ebp-20] 1000B992 3B45 B8 cmp eax,dword ptr ss:[ebp-48] 局部变量[ebp-48]保存数据的段数 1000B995 0F8C DFFDFFFF jl BasicCtr.1000B77A 循环解调每个数据段 1000B99B 8B45 08 mov eax,dword ptr ss:[ebp+8] 最后剩余的长度 1000B99E F7D8 neg eax 1000B9A0 1BC0 sbb eax,eax 1000B9A2 83E0 04 and eax,4 1000B9A5 EB 1E jmp short BasicCtr.1000B9C5 1000B9A7 837D F0 00 cmp dword ptr ss:[ebp-10],0 1000B9AB 74 09 je short BasicCtr.1000B9B6 1000B9AD FF75 F0 push dword ptr ss:[ebp-10] 1000B9B0 E8 FF3F0000 call <MFC42.operator delete> 1000B9B5 59 pop ecx 1000B9B6 834D FC FF or dword ptr ss:[ebp-4],FFFFFFFF 1000B9BA 8D4D E8 lea ecx,dword ptr ss:[ebp-18] 1000B9BD E8 DA3F0000 call <MFC42.CString::~CString> 1000B9C2 6A 04 push 4 1000B9C4 58 pop eax 1000B9C5 8B4D F4 mov ecx,dword ptr ss:[ebp-C] 1000B9C8 5F pop edi 1000B9C9 5E pop esi 1000B9CA 5B pop ebx 1000B9CB 64:890D 000000 mov dword ptr fs:[0],ecx 1000B9D2 C9 leave 1000B9D3 C2 0400 retn 4 1000B9D6 837D F0 00 cmp dword ptr ss:[ebp-10],0 1000B9DA 74 09 je short BasicCtr.1000B9E5 1000B9DC FF75 F0 push dword ptr ss:[ebp-10] 1000B9DF E8 D03F0000 call <MFC42.operator delete> 1000B9E4 59 pop ecx 1000B9E5 8B45 D4 mov eax,dword ptr ss:[ebp-2C] 1000B9E8 EB 12 jmp short BasicCtr.1000B9FC 1000B9EA 837D F0 00 cmp dword ptr ss:[ebp-10],0 1000B9EE 74 09 je short BasicCtr.1000B9F9 1000B9F0 FF75 F0 push dword ptr ss:[ebp-10] 1000B9F3 E8 BC3F0000 call <MFC42.operator delete> 1000B9F8 59 pop ecx 1000B9F9 8B45 D0 mov eax,dword ptr ss:[ebp-30] 1000B9FC 8B08 mov ecx,dword ptr ds:[eax] 1000B9FE 50 push eax 1000B9FF FF51 08 call dword ptr ds:[ecx+8] 1000BA02 8BF3 mov esi,ebx 1000BA04 EB 03 jmp short BasicCtr.1000BA09 1000BA06 6A 04 push 4 1000BA08 5E pop esi 1000BA09 8065 FC 00 and byte ptr ss:[ebp-4],0 1000BA0D 8D4D EC lea ecx,dword ptr ss:[ebp-14] 1000BA10 E8 873F0000 call <MFC42.CString::~CString> 1000BA15 834D FC FF or dword ptr ss:[ebp-4],FFFFFFFF 1000BA19 8D4D E8 lea ecx,dword ptr ss:[ebp-18] 1000BA1C E8 7B3F0000 call <MFC42.CString::~CString> 1000BA21 8BC6 mov eax,esi 1000BA23 EB A0 jmp short BasicCtr.1000B9C5 1000BA25 8065 FC 00 and byte ptr ss:[ebp-4],0 1000BA29 8D4D EC lea ecx,dword ptr ss:[ebp-14] 1000BA2C E8 6B3F0000 call <MFC42.CString::~CString> 1000BA31 EB 83 jmp short BasicCtr.1000B9B6
以上代码看不懂没关系,在压缩包中的VB代码 LoAnalysisQD (fbyt() As Byte) 函数概括了上述的思想,不过,对于4和7以外的数据类型,由于与本文内容无关,因此,不深入分析。
六、 QQ的另类破解
1、QQ密码算法和身份验证中存在的问题
搞个几十万次加密其实并不能阻止密码的破解,我对现在收集的EWH.db进行统计,那个循环的AST都在40万以上(可以买房子了,不是么?),因此,只要做一个40万的QQ Hash词典,在此基础上进行破解,破解速度不就提高了几万倍么?
这个词典如何形成?问得好!事实上,国际上有合作破解的先例。例如,可以给我分配000000000~999999999的段进行计算,给你分配999999999~1999999999的段进行计算……,最后,将所有结果合成为一个词典(或者根本不用合成,把Hash值发给大家就可以了,总有人能中大奖:)。
这个词典称为Hash词典,大致上是这样的(密码经过400000次加密后得到): 索引(密码) Hash值(加密后的密文) 1 2E62CD38389C3A885D7F6789FEEE8AA5 2 1F9C9B12E93A0FF2A9B7A714EF4D6CA4 3 8FE71CEF95D0F0DDAD716651E635D19C …… …… 999999999 A041F9E6DDA44C178F24DABC9B292785
有了这个Hash词典,我们完全没有必要重新忍受漫长的计算过程,直接查表就可以了,不是吗?用黑客的行话来说,就是跑字典了。
利用压缩包中提供的函数QQMD5(unsigned char *, long, long, QQSum *),就完全可以形成这个词典了。
别笑,这好象是大炮打蚊子,对于一个小小的QQ来说,何必劳驾全世界呢?既然这是我们闲得无聊下来干的事,我们就该自己来做。
最简单的Hash词典就是利用黑客词典(网上简直多如牛毛)再通过QQMD5函数来生成,如果你想到用数据库来管理这些Hash值,你一定是个非常敬业的黑客,不过数据库也是TB级的了。
Hash散列算法非常容易产生碰撞,如果你偶尔发现输入12345和输入78952一样可以登录QQ,你千万不要怪腾讯公司没水平,这是因为12345和78952产生的Hash相同(别试,我随便说的两个数),这说明,一个Hash输出可能对应多个输入,这就是碰撞。听说山东大学的王教授就很擅长产生这种碰撞,强烈要求他来做我们的斑竹。
因为碰撞,我们的词典就会小一点,不过,这些理想和共产主义一样,离我们很远,即使王教授的快速碰撞理论,对于QQ这样的重复加密来说,只是加快计算时间,并不能对整个加密体系构成威胁,所以,即使这个世界有那么多的恐怖份子,你我夏天还是穿个短裤就可以上街。
况且,现在很多的及时通讯系统都采用本地和服务器联合验证的方式,这非常有效,即使本地的密码被攻破了,但是,不能保证服务器的也被攻破。因为,通过暴力方式破解的登录口令也只是众多产生碰撞的口令之一,如果使用不同的加密方式,即使这个口令能进入本地系统,但是不能保证同一个口令在服务器端得到验证。所以,QQ从这个意义上说,依然是相对安全的(不过众多口令中,可能就只有一个特别象口令)。
如果我是腾讯的老总,说实话,在我垄断市场的情况下,我并不希望自己的口令体系多么坚不可摧,用户有了危机感,我才能从用户手中收取保护费吧:)所以,诸如此类的文章或者工具即使漫天飞也只是帮他做市场而已。
刚才提到密码算法的问题,谈到的所有问题都是本地的密码机制,对于网络之间的报文,采用的又是TEA的加密算法。这个是我后面的文章要分析的。
在QQ的身份验证中,还存在另外一个漏洞,这个漏洞很早就被发现了,一直保留到现在,也许将来也不会改变。
我不说大家也都知道,将EWH.DB中的QQ号(第一点中的那个3C A8 93 06)替换成别人的QQ号,然后把这个东西放在别人的目录下,就可以看到那个倒霉蛋的聊天记录了。不过,好在腾迅的服务器不买这个帐:看就看吧,只要我这里安全就没事了,多看点多学点,下次聊天就更欢了。 从低层的逻辑也可以看得出来,只要用户 上一页 [1] [2] [3] 下一页
|