拿到QQ 2005贺岁版后,发现其加密原理并没有新的改变,经过跟踪和分析,编制出暴力破解本地QQ密码的程序.
QQ密码在正确登陆后,会将加密的结果保存在用户目录的ewh.db文件中,加密采用公开的MD5算法,通过N次循环以及异或后求反,最终计算出加密的结果,与用户的ewh.db文件中的密文比较后,发出"输入密码与上次成功登录的密码不一致,$0A是否到服务器验证?"(这条信息在BasicCtrlDll.dll的资源中,$0A在C的格式化中为回车).根据这个提示,完成本地QQ密码的暴力破解.
在QQ系统中,"QD"标志代表QQ Data,例如,我们可以在文件User.db或ewh.db中找到这个以QD开头的数据结构.
一、ewh.db原始数据
51 44 01 01 03 00 04 03 00 BD AF A8 04 00 00 00
00 2E 06 00 07 03 00 B9 AB B4 10 00 00 00 07 22
AA 96 56 19 A3 9E 82 19 B7 2B BD 2D 34 4A 04 03
00 A9 B5 B2 04 00 00 00 3C A8 93 06
其中,红色为AST循环次数,兰色为EWH加密字符串,绿色为UIN QQ号(110340156=0x0693A83C,Intel体系内存中排列顺序为:3CA89306).
二、ewh.db数据结构
HEX
偏移DEC
偏移数据注释变量
标志
0000151 44QD,QQ Data 数据标志Flag
0002301 01保留的数据结构 Reserve
0004503 00总数据段(Data Sections)的个数Sections
0006704第一个数据段(简称1S,下同)的类型,可以从0x01到0x0F,04代表本数据没经过加密处理.Type1S
0007803 001S标志的长度.LenFlag1S
000910BD AF A81S标志(例如AST、UIN、EWH等),是经过简单的异或并求反计算处理的,此处是AST,可能是Algorithm Shift Times 或Axxx Switch Time,管他的呢!Flag1S
000C1304 00 00 001S数据的长度LenData1S
00101700 2E 06 00
= (404992)1S数据,这里是进行MD5转换的次数.
这个数据是同计算机的性能有关的,性能越高的计算机,在QQ注册成功后产生的这个循环控制变量就越大.Data1S
001421072S数据的类型,07代表使用MD5进行加密Type2S
00152203 002S标志的长度LenFlag2S
001724B9 AB B42S标志,此处是EWH,代表本数据段是EWH密码数据,可能是Encrypt With Hash的缩写Flag2S
001A2710 00 00 002S数据的长度LenData2S
001E3107 22 AA 96
56 19 A3 9E
82 19 B7 2B
BD 2D 34 4A2S数据,是经过MD5加密计算后产生的数据,当然还要经过异或并求反的计算处理,参考下面程序中的1000B858 行代码.Data2S
002E47043S数据的类型Type3S
002F4803 003S标志的长度LenFlag3S
003150A9 B5 B23S标志,此处是UIN,代表本数据段是QQ号码,可能是:User Identifier Number的缩写Flag3S
00345304 00 00 003S数据的长度LenData3S
0038573C A8 93 063S数据,3C A8 93 06 = 110340156Data3S
三、加密原理
下面VB伪代码的部分符号引自以上第二点《结构说明》中的变量标志,请注意理解:
Pwd = MD5(Pwd, Len(Pwd))
' Pwd为用户输入的密码,第一轮MD5后,Pwd成为16位字节长度的MD5串.
XorKey As Long = 0'XorKey为用于解密的字节
For k = 1 To Data1S - 1'因为前面已经做过一轮,所以此处要减一
Pwd = MD5(Pwd, 16)
Next k
XorKey = XorKey And &HFFFF
XorKey = (LenData2S And &HFF) Xor (LenData2S \ 256)
XorKey = &HFF - XorKey '求反
For k = 1 To 16
Pwd(k) = Pwd(k) Xor XorKey
Next k
If Pwd <> Data2S Then
MsgBox "输入密码与上次成功登录的密码不一致," & vbcrlf & "是否到服务器验证?"
End If
通过以上的流程,我真的佩服QQ的设计者,如此巨大的循环量,加上循环次数的随机性,如果希望产生一个QQ MD5词典简直不可能.虽然理论上,可以产生一个MD5字典,但是,这个字典将有1.15E+77*16个字节之巨,因此,只好根据ewh.db文件提供的数据暴力破解了,不知是不是有更好的方法呢?
不过我的感觉是,循环次数加多了,应该会产生更多的MD5碰撞,不见得是个好事.
还有一种破解思路,也许更加直接,将在后面的文章中详细探讨.但是我只有在有时间做完实验后才有资格评述,不在本文章的讨论范围内.
四、破解算法
重复进行数十万次MD5加密,会消耗计算机很多时间,如果使用传统的VB或VC,对于一个密码的等待时间也是很可观的(例如使用VB代码,消耗的时间可能是汇编的400倍),因此,我使用汇编语言来编制低层加密解密算法,通过MASM32编译连接,最后用高级语言调用.通过提供算法动态库的方式,方便其他有兴趣的读者自己增加丰富的功能.例如增加多线程等,这也将在以后的探讨中实现.在此不做深入讨论.
附带的例子为VB和VC调用汇编语言动态库的例子,VB代码简单实现了通过密码字典进行单线程破解的功能,读者可以丰富其内容.增加更多的功能.
五、QQ数据结构分析
下面为动态库BasicCtrlDll.dll中反汇编的代码以及代码分析,主要用于分析EWH.DB中数据结构以及QQ数据解调算法的问题.另外,这个算法也可以将User.db中的数据提取出来.深入研究下去,做一个聊天记录查看器之类的软件也非难事.
1000B71DB8 AC160110mov eax,BasicCtr.100116AC
1000B722E8 89460000call BasicCtr.1000FDB0
1000B72783EC 3Csub esp,3C
1000B72A8B45 08mov eax,dword ptr ss:[ebp+8]将数据的开始地址赋给EAX,实际数据为**Data,EAX=*Data
1000B72D53push ebx
1000B72E56push esi
1000B72F57push edi
1000B7308B30mov esi,dword ptr ds:[eax]需要转换的字符串,EAX指示一个结构,第一个成员为实际的数据指针
1000B732894D D8mov dword ptr ss:[ebp-28],ecx局部变量[ebp-28]保存全局的标志结构,ECX为全局参数地址,在调用本函数时跟入
1000B7358B46 F8mov eax,dword ptr ds:[esi-8]为CString结构中长度的成员,表示总共多少个字节
1000B73883F8 06cmp eax,6如果长度小于6,则为无效的数据
1000B73B0F82 81020000jb BasicCtr.1000B9C2如果比6小则跳转退出,说明数据量不够解调的
1000B741803E 51cmp byte ptr ds:[esi],51是否为 QQ Data 的数据,QD为QQ数据标志
1000B7440F85 78020000jnz BasicCtr.1000B9C2
1000B74A807E 01 44cmp byte ptr ds:[esi+1],44
1000B74E0F85 6E020000jnz BasicCtr.1000B9C2
1000B75466:8B7E 04mov di,word ptr ds:[esi+4]对于EWH来说,为第4+1个字节,为0003
1000B75883C6 04add esi,4指向数据段(Sections)的个数
1000B75B46inc esi
1000B75C83C0 FAadd eax,-6EAX去掉6个字节,对于EWH来说,剩下36H个字节
1000B75F46inc esi
1000B7608945 08mov dword ptr ss:[ebp+8],eax指向第一个数据段
1000B763E8 CE050000call BasicCtr.1000BD36在内存(ECX+9C)处开辟一个(100H)字节的空间,空间地址返回到EAX
1000B7688365 E0 00and dword ptr ss:[ebp-20],0 局部变量[ebp-20]清零
1000B76C0FB7C7movzx eax,di转换到EAX,对于EWH,di=3.表示有3段数据
1000B76F85C0test eax,eax
1000B7718945 B8mov dword ptr ss:[ebp-48],eax局部变量[ebp-48]保存数据的段数
1000B7740F8E 21020000jle BasicCtr.1000B99B
1000B77A837D 08 07cmp dword ptr ss:[ebp+8],7如果整个长度小于7,则剩下的应该是QQ号了.第一次进入时=36H
1000B77E0F82 3E020000jb BasicCtr.1000B9C2如果剩余的数据长度小于7则退出
1000B7848A06mov al,byte ptr ds:[esi]第一次进入时,ESI指向第7个数据即数据段的类型,例如 04
1000B78666:8B4E 01mov cx,word ptr ds:[esi+1]CX=后一个数据,及数据段标志的长度,例如 0003
1000B78A46inc esi
1000B78B8B55 08mov edx,dword ptr ss:[ebp+8]剩余的数据长度,如36H
1000B78E836D 08 03sub dword ptr ss:[ebp+8],3去掉3个,例如成为33H=51
1000B792894D C8mov dword ptr ss:[ebp-38],ecx局部变量[ebp-38]保存数据段中标志长度
1000B7950FB7F9movzx edi,cxEDI为标志长度了
1000B79846inc esi
1000B7998845 E4mov byte ptr ss:[ebp-1C],al局部变量[ebp-1C]保存本段的类型
1000B79C8D4F 04lea ecx,dword ptr ds:[edi+4]ECX为取得的标志长度再加上4,例如=7
1000B79F46inc esi第一次时,ESI指向第一个数据段的标志字段了,例如指向BDAFA8,第9个数据开始
1000B7A0394D 08cmp dword ptr ss:[ebp+8],ecx如果剩余的数据比"标志长度+4"还少,则没有数据,因此不进行处理
1000B7A30F82 19020000jb BasicCtr.1000B9C2
1000B7A98B0C37mov ecx,dword ptr ds:[edi+esi]跳过数据段的标志部分,将数据段的长度赋值给ECX.例如[edi+esi]=4
1000B7AC8D1C37lea ebx,dword ptr ds:[edi+esi]EBX保存新的指针,指向数据段中数据部分的长度部分,例如,[EBX]=4
1000B7AF895D C4mov dword ptr ss:[ebp-3C],ebx局部变量[ebp-3C]保存数据段中数据部分的长度指针
1000B7B28D4C0F 07lea ecx,dword ptr ds:[edi+ecx+7]从标志(自身长3,加上4个长度位=7)开始,加上数据长度,为完整数据长度.例如E
1000B7B63BCAcmp ecx,edx如果剩下的数据长度(如36H)不够,则退出
1000B7B8894D CCmov dword ptr ss:[ebp-34],ecx局部变量[ebp-34]保存本数据段的总长度
1000B7BB0F87 01020000ja BasicCtr.1000B9C2如果小于则退出程序
1000B7C18365 F0 00and dword ptr ss:[ebp-10],0局部变量[ebp-10]清零
1000B7C53C 01cmp al,1al保存本段的类型,有效的类型是4或7
1000B7C774 18je short BasicCtr.1000B7E1
1000B7C93C 02cmp al,2
1000B7CB74 14je short BasicCtr.1000B7E1
1000B7CD3C 03cmp al,3
1000B7CF74 10je short BasicCtr.1000B7E1
1000B7D13C 04cmp al,4
1000B7D374 0Cje short BasicCtr.1000B7E1
1000B7D53C 05cmp al,5
1000B7D774 08je short BasicCtr.1000B7E1
1000B7D93C 07cmp al,7
1000B7DB74 04je short BasicCtr.1000B7E1
1000B7DD3C 06cmp al,6
1000B7DF75 19jnz short BasicCtr.1000B7FA
1000B7E151push ecx为本段的总长度,包括(段类型+标志长度+标志+数据长度+数据),例如,对于密码段=1AH
1000B7E2E8 23430000call <MFC42.operator new>
1000B7E7FF75 CCpush dword ptr ss:[ebp-34]n=[ebp-34],局部变量[ebp-34]保存本数据段的总长度
1000B7EA8D4E FDlea ecx,dword ptr ds:[esi-3][esi-3]指向本段的开始(从段类型算起)
1000B7ED8945 F0mov dword ptr ss:[ebp-10],eax局部变量[ebp-10]保存拷贝后的数据
1000B7F051push ecxsrc
1000B7F150push eaxdest
1000B7F2E8 E5450000call <MSVCRT.memcpy>memcpy,将本段的整段数据拷贝到新的地方,数据指针保存在局部变量[ebp-10]中
1000B7F783C4 10add esp,10
1000B7FA8B45 C8mov eax,dword ptr ss:[ebp-38]局部变量[ebp-38]保存数据段中标志长度
1000B7FD33C9xor ecx,ecx
1000B7FF32C4xor al,ah将低位长度与高位异或,例如3 xor 0=3
1000B80185FFtest edi,ediEDI为标志位的长度
1000B80376 12jbe short BasicCtr.1000B817如果本段没有段标志,则跳转
1000B8058A1431mov dl,byte ptr ds:[ecx+esi]开始循环,循环次数为标志的长度.ECX第一次时为0,将第一个数据加载到DL中.
1000B80832D0xor dl,alAL为长度的高位和低位的异或,这里为3
1000B80AF6D2not dlDL=NOT ([数据] xor [数据段标志长度的高位 xor 数据段标志长度的低位])
1000B80C881431mov byte ptr ds:[ecx+esi],dl数据保存在原始的内存中相应的地方
1000B80F41inc ecx
1000B8103BCFcmp ecx,edi
1000B81272 F1jb short BasicCtr.1000B805
1000B8148B5D C4mov ebx,dword ptr ss:[ebp-3C]局部变量[ebp-3C]保存数据段中数据部分的长度指针
1000B81757push ediEDI为标志位的长度
1000B81856push esiESI为指向解密以后的数据,例如AST
1000B8198D4D E8lea ecx,dword ptr ss:[ebp-18]局部变量[ebp-18]存放强制类型转换以后的数据指针的指针,例如AST
1000B81CE8 5B430000call <MFC42.CString::CString>将解密后的数据变成CString类型.返回的类型放在EAX指示的地址中
1000B8218365 FC 00and dword ptr ss:[ebp-4],0局部变量[ebp-4]清零
1000B8256A FCpush -4
1000B82758pop eaxEAX=-4=FFFFFFFC
1000B8288BF3mov esi,ebxebx保存数据段中数据部分的长度指针
1000B82A2BC7sub eax,ediEAX=EAX-EDI=FFFFFFFC-3=FFFFFFF9
1000B82C8B1Emov ebx,dword ptr ds:[esi]ebx为数据段中数据部分的长度了
1000B82E0145 08add dword ptr ss:[ebp+8],eax第一次时,[EBP+8]=33.执行后=2C,相当于33H-7H=2CH
1000B83183C6 04add esi,4ESI指向数据段中的数据部分了
1000B834395D 08cmp dword ptr ss:[ebp+8],ebx[ebp+8]=2C,ebx=4
1000B8370F82 6A010000jb BasicCtr.1000B9A7
1000B83D807D E4 07cmp byte ptr ss:[ebp-1C],7局部变量[ebp-1C]保存本段的类型,4或者7
1000B84174 06je short BasicCtr.1000B849
1000B843807D E4 06cmp byte ptr ss:[ebp-1C],6如果类型不为6,则执行1000B862
1000B84775 19jnz short BasicCtr.1000B862
1000B8498AC3mov al,bl如果数据段的类型为7,则执行此语句.BL包含本段的长度
1000B84B33FFxor edi,edi
1000B84D32C7xor al,bhal=(长度的低位 xor 长度的高位)
1000B84F85DBtest ebx,ebx
1000B85176 0Fjbe short BasicCtr.1000B862如果长度为0,则表示没有密码
1000B8538A0C37mov cl,byte ptr ds:[edi+esi]
1000B85632C8xor cl,al
1000B858F6D1not clDL=NOT ([数据] xor [数据段标志长度的高位xor 数据段标志长度的低位])
1000B85A880C37mov byte ptr ds:[edi+esi],cl
1000B85D47inc edi
1000B85E3BFBcmp edi,ebx
1000B86072 F1jb short BasicCtr.1000B853循环直到全部数据解调完毕
1000B86253push ebx数据串的长度
1000B86356push esi原始的需要变换的数据
1000B8648D4D EClea ecx,dword ptr ss:[ebp-14]局部变量[ebp-14]存放强制CString类型转换以后的数据指针的指针,例如DB2E0600
1000B867E8 10430000call <MFC42.CString::CString>
1000B86C8A45 E4mov al,byte ptr ss:[ebp-1C]局部变量[ebp-1C]保存本段的类型,4或者7
1000B86F295D 08sub dword ptr ss:[ebp+8],ebx去掉已经处理的数据,执行后[ebp+8]=28H,ebx为数据段中数据部分的长度
1000B87203F3add esi,ebxESI指向下一个数据段的开始部分,ebx保存数据段中数据部分的长度指针
1000B87433FFxor edi,edi
1000B87684C0test al,al测试本段的类型是否为0
1000B878C645 FC 01mov byte ptr ss:[ebp-4],1局部布尔型变量[ebp-4]=1
1000B87C0F86 A3010000jbe BasicCtr.1000BA25如果本段的数据类型为0,则执行1000BA25后退出
1000B8823C 07cmp al,7
1000B8840F86 B6000000jbe BasicCtr.1000B940如果小于等于7,则跳转到1000B940执行.对于EWH来说就是这样
1000B88A3C 08cmp al,8
1000B88C0F84 74010000je BasicCtr.1000BA06如果数据类型为8,则直接退出.
1000B8923C 09cmp al,9
1000B89474 5Dje short BasicCtr.1000B8F3
1000B8963C 0Acmp al,0A
1000B8980F85 87010000jnz BasicCtr.1000BA25
1000B89E8B4D D8mov ecx,dword ptr ss:[ebp-28]当数据类型为A时,执行本程序代码
1000B8A18D45 D4lea eax,dword ptr ss:[ebp-2C]
1000B8A450push eax
1000B8A5E8 47FEFFFFcall BasicCtr.1000B6F1
1000B8AA8B45 D4mov eax,dword ptr ss:[ebp-2C]
1000B8ADFF75 ECpush dword ptr ss:[ebp-14]
1000B8B08B08mov ecx,dword ptr ds:[eax]
1000B8B253push ebx
1000B8B350push eax
1000B8B4FF91 BC000000call dword ptr ds:[ecx+BC]
1000B8BA8BD8mov ebx,eax
1000B8BC85DBtest ebx,ebx
1000B8BE0F85 12010000jnz BasicCtr.1000B9D6
1000B8C48B45 D4mov eax,dword ptr ss:[ebp-2C]
1000B8C76A 04push 4
1000B8C98945 DCmov dword ptr ss:[ebp-24],eax
1000B8CC8D45 DClea eax,dword ptr ss:[ebp-24]
1000B8CF50push eax
1000B8D08D4D C0lea ecx,dword ptr ss:[ebp-40]
1000B8D3E8 A4420000call <MFC42.CString::CString>
1000B8D850push eax
1000B8D98D4D EClea ecx,dword ptr ss:[ebp-14]
1000B8DCC645 FC 03mov byte ptr ss:[ebp-4],3
1000B8E0E8 C3400000call <MFC42.CString::operator=>
1000B8E5C645 FC 01mov byte ptr ss:[ebp-4],1
1000B8E98D4D C0lea ecx,dword ptr ss:[ebp-40]
1000B8ECE8 AB400000call <MFC42.CString::~CString>
1000B8F1EB 50jmp short BasicCtr.1000B943
1000B8F38B4D D8mov ecx,dword ptr ss:[ebp-28]当数据类型为9时,执行这个操作
1000B8F68D45 D0lea eax,dword ptr ss:[ebp-30]
1000B8F950push eax
1000B8FAE8 4E180000call BasicCtr.1000D14D
1000B8FF8B45 D0mov eax,dword ptr ss:[ebp-30]
1000B902FF75 ECpush dword ptr ss:[ebp-14]
1000B9058B08mov ecx,dword ptr ds:[eax]
1000B90753push ebx
1000B90850push eax
1000B909FF51 78call dword ptr ds:[ecx+78]
1000B90C8BD8mov ebx,eax
1000B90E85DBtest ebx,ebx
1000B9100F85 D4000000jnz BasicCtr.1000B9EA
1000B9168B45 D0mov eax,dword ptr ss:[ebp-30]
1000B9196A 04push 4
1000B91B8945 DCmov dword ptr ss:[ebp-24],eax
1000B91E8D45 DClea eax,dword ptr ss:[ebp-24]
1000B92150push eax
1000B9228D4D BClea ecx,dword ptr ss:[ebp-44]
1000B925E8 52420000call <MFC42.CString::CString>
1000B92A50push eax
1000B92B8D4D EClea ecx,dword ptr ss:[ebp-14]
1000B92EC645 FC 02mov byte ptr ss:[ebp-4],2
1000B932E8 71400000call <MFC42.CString::operator=>
1000B937C645 FC 01mov byte ptr ss:[ebp-4],1
1000B93B8D4D BClea ecx,dword ptr ss:[ebp-44]
1000B93EEB ACjmp short BasicCtr.1000B8EC
1000B9406A 01push 1当数据段的类型<=7时,直接从此处执行
1000B9425Fpop edi
1000B9438B5D D8mov ebx,dword ptr ss:[ebp-28]局部变量[ebp-28]保存全局的标志结构
1000B9468D45 EClea eax,dword ptr ss:[ebp-14]局部变量[ebp-14]存放强制类型转换以后的数据指针的指针,例如DB2E0600
1000B94950push eaxEAX存放强制类型转换以后的数据指针
1000B94A8D45 E8lea eax,dword ptr ss:[ebp-18]局部变量[ebp-18]存放强制类型转换以后的数据指针的指针,例如AST
1000B94DFF75 E4push dword ptr ss:[ebp-1C]局部变量[ebp-1C]中的第一个字节保存本段的类型,4或者7
1000B9508BCBmov ecx,ebx
1000B95250push eax
1000B953E8 B4FCFFFFcall BasicCtr.1000B60Ccall 1000B60C(CString,Flag,CString)
1000B95885FFtest edi,edi
1000B95A74 18je short BasicCtr.1000B974
1000B95C8B45 E0mov eax,dword ptr ss:[ebp-20]局部变量[ebp-28]第一次为0,为一个计数器
1000B95F8B4B 64mov ecx,dword ptr ds:[ebx+64]存在于标志结构中,为一个全局地址
1000B9628B55 F0mov edx,dword ptr ss:[ebp-10]局部变量[ebp-10]保存拷贝后的数据,即没有经过处理的.例如040300BDAF......
1000B965C1E0 02shl eax,2EAX=EAX*2
1000B968891401mov dword ptr ds:[ecx+eax],edx将未做解调的原始数据放到全局结构中某个指针指示的内存中
1000B96B8B4B 78mov ecx,dword ptr ds:[ebx+78]存在于标志结构中,为一个全局地址
1000B96E8B55 CCmov edx,dword ptr ss:[ebp-34]局部变量[ebp-34]保存本数据段的总长度
1000B971891401mov dword ptr ds:[ecx+eax],edx将数据长度放到全局结构中某个指针指示的内存中
1000B9748065 FC 00and byte ptr ss:[ebp-4],0局部布尔型变量[ebp-4]=0
1000B9788D4D EClea ecx,dword ptr ss:[ebp-14]
1000B97BE8 1C400000call <MFC42.CString::~CString>清除数据段中的数据部分CString
1000B980834D FC FFor dword ptr ss:[ebp-4],FFFFFFFF局部布尔型变量[ebp-4]=-1,为True
1000B9848D4D E8lea ecx,dword ptr ss:[ebp-18]
1000B987E8 10400000call <MFC42.CString::~CString>清除数据段中的标志部分CString,例如AST
1000B98CFF45 E0inc dword ptr ss:[ebp-20]局部变量[ebp-28]计数器加一
1000B98F8B45 E0mov eax,dword ptr ss:[ebp-20]
1000B9923B45 B8cmp eax,dword ptr ss:[ebp-48]局部变量[ebp-48]保存数据的段数
1000B9950F8C DFFDFFFFjl BasicCtr.1000B77A循环解调每个数据段
1000B99B8B45 08mov eax,dword ptr ss:[ebp+8]最后剩余的长度
1000B99EF7D8neg eax
1000B9A01BC0sbb eax,eax
1000B9A283E0 04and eax,4
1000B9A5EB 1Ejmp short BasicCtr.1000B9C5
1000B9A7837D F0 00cmp dword ptr ss:[ebp-10],0
1000B9AB74 09je short BasicCtr.1000B9B6
1000B9ADFF75 F0push dword ptr ss:[ebp-10]
1000B9B0E8 FF3F0000call <MFC42.operator delete>
1000B9B559pop ecx
1000B9B6834D FC FFor dword ptr ss:[ebp-4],FFFFFFFF
1000B9BA8D4D E8lea ecx,dword ptr ss:[ebp-18]
1000B9BDE8 DA3F0000call <MFC42.CString::~CString>
1000B9C26A 04push 4
1000B9C458pop eax
1000B9C58B4D F4mov ecx,dword ptr ss:[ebp-C]
1000B9C85Fpop edi
1000B9C95Epop esi
1000B9CA5Bpop ebx
1000B9CB64:890D 000000mov dword ptr fs:[0],ecx
1000B9D2C9leave
1000B9D3C2 0400retn 4
1000B9D6837D F0 00cmp dword ptr ss:[ebp-10],0
1000B9DA74 09je short BasicCtr.1000B9E5
1000B9DCFF75 F0push dword ptr ss:[ebp-10]
1000B9DFE8 D03F0000call <MFC42.operator delete>
1000B9E459pop ecx
1000B9E58B45 D4mov eax,dword ptr ss:[ebp-2C]
1000B9E8EB 12jmp short BasicCtr.1000B9FC
1000B9EA837D F0 00cmp dword ptr ss:[ebp-10],0
1000B9EE74 09je short BasicCtr.1000B9F9
1000B9F0FF75 F0push dword ptr ss:[ebp-10]
1000B9F3E8 BC3F0000call <MFC42.operator delete>
1000B9F859pop ecx
1000B9F98B45 D0mov eax,dword ptr ss:[ebp-30]
1000B9FC8B08mov ecx,dword ptr ds:[eax]
1000B9FE50push eax
1000B9FFFF51 08call dword ptr ds:[ecx+8]
1000BA028BF3mov esi,ebx
1000BA04EB 03jmp short BasicCtr.1000BA09
1000BA066A 04push 4
1000BA085Epop esi
1000BA098065 FC 00and byte ptr ss:[ebp-4],0
1000BA0D8D4D EClea ecx,dword ptr ss:[ebp-14]
1000BA10E8 873F0000call <MFC42.CString::~CString>
1000BA15834D FC FFor dword ptr ss:[ebp-4],FFFFFFFF
1000BA198D4D E8lea ecx,dword ptr ss:[ebp-18]
1000BA1CE8 7B3F0000call <MFC42.CString::~CString>
1000BA218BC6mov eax,esi
1000BA23EB A0jmp short BasicCtr.1000B9C5
1000BA258065 FC 00and byte ptr ss:[ebp-4],0
1000BA298D4D EClea ecx,dword ptr ss:[ebp-14]
1000BA2CE8 6B3F0000call <MFC42.CString::~CString>
1000BA31EB 83jmp 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值(加密后的密文)
12E62CD38389C3A885D7F6789FEEE8AA5
21F9C9B12E93A0FF2A9B7A714EF4D6CA4
38FE71CEF95D0F0DDAD716651E635D19C
............
999999999A041F9E6DDA44C178F24DABC9B292785
有了这个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号,然后把这个东西放在别人的目录下,就可以看到那个倒霉蛋的聊天记录了.不过,好在腾迅的服务器不买这个帐:看就看吧,只要我这里安全就没事了,多看点多学点,下次聊天就更欢了.
从低层的逻辑也可以看得出来,只要用户的EWH.DB文件符合我们前面的算法,QQ就认为这个用户是合法的.因此,我们完全可以想象,如果我自己做一个EWH.DB文件,让那个40万次的东西变成0,岂不是可以让我的计算机从那个傻呼呼的循环中解脱出来吗?更懒的人可能想到,如果不用密码岂不是更爽?好,我们来实现这个愿望:
运行QQPwdFinality.exe,将登录口令中的"12345"删掉,将"循环次数"改为0,进行单步计算后,是不是得到了一个串?如果你的计算机没骗你的话,大家应该都得到同一个结果:3BF2633660EF5DEB066FE6770317AD91,好了,我们将这个结果替换掉EWH.DB中的那个Hash值07 22 AA 96 56 19 A3 9E 82 19 B7 2B BD 2D 34 4A,最后,EWH.DB文件变成下面这个样子(记得将循环次数也改成0哦):
51 44 01 01 03 00 04 03 00 BD AF A8 04 00 00 00
00 00 00 00 07 03 00 B9 AB B4 10 00 00 00 3B F2
63 36 60 EF 5D EB 06 6F E6 77 03 17 AD 91 04 03
00 A9 B5 B2 04 00 00 00 3C A8 93 06
我知道在座的懒人很多(包括我自己),我特地在QQPwdFinality.exe文件中加入了一个"产生EWH.DB文件"选项,我知道你可能连这都懒得勾,所以默认情况下,我帮你勾上了. 你可以选择任何口令和循环次数来形成EWH.DB文件,文件会生成在当前的目录下.
好了,现在,你再也不用拿着你那个EWH.DB文件到处奔波了,你就用这个吧,毛爷爷不是说过吗?打击敌人的同时还要保护自己,你用自己的口令到处看别人的记录,查出来多不好,况且,一旦忘记删掉了,被受害者拿去,访高手找名人,那你就可能重新申请QQ号了.不过,如果按照这个原理将自己的QQ改造一下,你就比任何人都更快地进入QQ,抢先零点几秒找到PLMM.但是千万别干坏事哦,如果你将别人的AST改成FFFFFF7F的话,估计他要等一天才能和自己心爱的MM聊天了,你忍心下手吗?是不是有人想到发明一个病毒,专门修改这个号,让登录越来越慢啊呵呵.
其实,腾讯稍做努力便可以改掉这个问题,要么在每个QQ Data结构后面加一个Hash验证,要么将用户的QQ号作为密钥,对口令进行加密.最简单的就是用QQ号取代MD5算法中的那个基本字串(即便那几十万个循环中用了一次),这样,自己的EWH.DB就只能自己用了,这才是数字指纹的真正用途嘛.也许你会说,这么做知道了算法还是徒劳,但是,至少可以打破现在的局面——只要会用鼠标的都可以修改EWH.DB,显得这些研究破解的同志多么没有存在的价值啊.
以上谈的这些东西总的说起来都是一些雕虫小技,估计有一半的人看到中途就睡着了,坚持下来的请跟随我进入下一个章节,如果最后还有幸存者,我希望和你交个朋友:)
谈到这里,我想,这篇文章不太适合高手拜读,如果你是高手并且很无聊地走到这里,那么,你可以休息了,走的时候别忘记关灯关门哦.
下面,我们谈谈关于网络的话题.
2、QQ网络登录时存在的问题
MD5的算法固然不错,但并不能取代传统的算法,那种可逆算法在任何时候都是必要的,特别在网络里.我们可以反证一下,如果你在注册QQ或者修改密码的时候,腾讯公司得到的是MD5 Hash,他怎么能知道你原来的密码是多少呢?他绝对不会和我们一样傻,通过暴力破解的方式得到吧.当然,腾讯公司也可以只要MD5,如果是这样,他就等于失去了一次对用户的隐私进行窥探的机会,这对于任何一个公司来说都是不可能的.
退一步说,即使有的公司宣布,为了用户利益,对用户的密码不感兴趣,但是,他还是要知道是谁采用了合法的方式登录,因此,最终都不能免俗,都必须从用户发来的网络包中提取信息来完成身份的校验,以便对上帝们发出邀请或对恶魔们进行拒绝,OK,如果魔鬼扮成上帝呢?
未完待续......
编者:结合作者给的代码http://soft.hackbase.com/54/20050318/6381.html一些弱口令就可以在本地被跑出来了:)
相关推荐
QQ 2005贺岁版登录口令加密算法及其源代码分析.doc
QQ 2005贺岁版的登录口令加密算法是信息安全领域的一个经典案例,它涉及到用户密码的保护,防止未经授权的访问。在网络安全日益重要的今天,理解这种加密算法对于软件开发者和安全研究人员来说至关重要。这里我们将...
QQ 2005贺岁版登录口令加密算法及其源码 SINE256加密可视化以及控制示例 vb 雷达系统 VB破解access密码 VB下实现MD5加密算法源码 财务中将数字转换成中文大写 带输入功能的手写文字识别程序 顶级AES加密类模块 高级...
QQ 2005贺岁版的登录口令加密算法是信息安全领域的一个重要知识点,它涉及到用户账号的安全保护。在2005年的版本中,QQ为了保障用户的隐私和账户安全,采用了一种特定的加密方式来处理用户的登录口令。这种加密方法...
至于压缩包中的文件,"C#版本口令加密BAT源码"很可能是包含了上面提到的C#控制台应用程序的源代码以及一个批处理脚本,该脚本调用编译后的程序来处理密码加密任务。批处理脚本通常以`.bat`或`.cmd`为扩展名,它们是...
前者拥有计算能力,对加密数据、过程数据、通讯接口等可以实施动态密文处理,因此带CPU的加密锁也被演化出了多种多样的加密形式:密文校验、自定义算法、程序移植等等。而不带CPU加密锁相对简单,加密强度较低,成本...
进入时的帐户口令保存在注册表中,通过MD5算法加密,密钥串中包含了“用户的帐户信息+特定字符串”的方式,为破解增加了一定的难度;在语音聊天里(即通过声卡和麦克风的语音聊天),通过动态huffman编码来压缩...
进入时的帐户口令保存在注册表中,通过MD5算法加密,密钥串中包含了“用户的帐户信息+特定字符串”的方式,为破解增加了一定的难度;在语音聊天里(即通过声卡和麦克风的语音聊天),通过动态huffman编码来压缩...
进入时的帐户口令保存在注册表中,通过MD5算法加密,密钥串中包含了“用户的帐户信息+特定字符串”的方式,为破解增加了一定的难度;在语音聊天里(即通过声卡和麦克风的语音聊天),通过动态huffman编码来压缩...
平台独特的安全机制包括VPDN/APN、3DES算法加密、手机号码和IMEI许可验证、动态口令、短信炸弹防护、黑白名单管理以及安全审计等,保障了数据传输和应用使用的安全性。 总的来说,MAStudio是一个全面、强大且易用的...
C#编程经验技巧宝典源代码,目录如下: 第1章 开发环境 1 <br>1.1 Visual Studio开发环境安装与配置 2 <br>0001 安装Visual Studio 2005开发环境须知 2 <br>0002 配置合适的Visual Studio 2005...
INP:Oracle 3.0版或早期版本的表单源代码 INRS:INRS远程通信声频 INS:InstallShield安装脚本;X-Internet签字文件;Ensoniq EPS字簇设备;Cell/ⅡMAC/PC抽样设备 INT:中间代码,当一个源程序经过语法检查后...