• 标 题:破解OICQ的密码算法 (6千字)
  • 作 者:jsjyt
  • 时 间:2001-6-25 17:58:46
  • 链 接:http://bbs.pediy.com

这篇文章和OICQ本身没有什么联系,我只是想讲一下程序破解的原理和方法,我在
这里会尽量用易懂的语言来描述,如果你是一个初学者,那么,此文就是为你而写的。

  我以OICQ99C 0820版为例,其它版本如OICQ2000
的破解方法基本上和此版本没有区
别。
  
首先,在OICQ目录下,每个使用者号码下有一个叫matrix.cnt的文件,此文件的长
度是20字节,它的内容就是此号码的原密码经过加密后对应的密文。要证明这一点非常
容易,只要稍有一点电脑能力的人都可以证明,所以这里我就不费口舌了。在正式开始
前,请用二进制编辑器打开此文件,熟悉一下内容。
  
启动OICQ,出现OICQ用户登录画面,选择你自己的号码,然后输入一个错误的密码
,此时按CTRL+D进入SOFTICE画面,设置断点。我们事先应该都知道OICQ检查密码是否正

确的工作原理,即:先用GetWindowText()来取得密码框中的密码字符串,将此字符串用
其内定的算法进行加密,得出加密后的密文,然后从磁盘中读取matrix.cnt,与用户输
入的密码的加密结果进行比较,如果相同,则用户输入的密码为正确密码。(或者先读
文件,后读取密码框中的值)跟据这个原理,这们应该把断点设在readfile上,即输入
:bpx
readfile。返回程序界面,点击“登录”,出现SOFTICE的画面,指针停在KERNE
L32!READFILE上。按一下F12跳过此函数,将进入OICQ的领空。
  
我们现在要做的就是特别注意转折语句,就是JZ,JNZ,JMP等第一个字符是J的语句
。因为这们己经假设,程序中必定存在一行转折语句,当用户输入的是正解密码时,它
跳转(或不跳转),而当用户输入错误密码时,它不跳转(或跳转)。小心地按F10单步
执行代码,在出现转折语句处设下一断点(只是做一个记号),并且记录它们的跳转情
况(经验丰富者知道哪里有必要下断点而哪里没有必要),直到程序出现密码错误的对
话框为止。
  
这时候你可能己经花去了1分钟的时间。好的,现在输入正确的密码作为比较。同样
单步地执行代码,你将看到上次你在跳转语句上作的记号。比较两次的跳转是否相同。

  你将看到,当程序运行到这里时,前后两次的跳转方向不同:
...
00441C1B  call 0046c256
00441C20  cmp eax, ebx
00441C22  jz 00441C28----->就是这里!
00441C24  mov esi, edi
...

分析一下这几个代码,它首先调用函数0046C256,然后比较EAX与EBX的值,如果EAX与E
BX的值相同,就跳转。我们重新执行程序,发现当输入正确密码时,EAX=1,当输入错误
密码时,EAX=0,而EBX总是0。所以,函数0046C256是一个可疑的函数!
  按F8进入函数0046C256,我们要看看它做了什么工作。
:0046C256  mov eax, 004CD33C
:0046C25B  call 00486B88
:0046C260  sub esp, 00000010
:0046C263  push esi
:0046C264  push edi
:0046C265  mov esi, ecx
:0046C267  mov eax, dword ptr [ebp+08]
:0046C26A  and dword ptr [ebp-04], 00000000
:0046C26E  push [eax-08]
:0046C271  push eax
:0046C272  lea eax, dword ptr [ebp-1C]
:0046C275  push eax
:0046C276  call 00456718
:0046C27B  add esp, 0000000C
:0046C27E  push 00000001
:0046C280  pop edi
:0046C281  cmp dword ptr [esi+04], edi
:0046C284  jbe 0046C29E

:0046C286  lea eax, dword ptr [ebp-1C]
:0046C289  push 00000010
:0046C28B  push eax
:0046C28C  lea eax, dword ptr [ebp-1C]
:0046C28F  push eax
:0046C290  call 00456718
:0046C295  add esp, 0000000C
:0046C298  inc edi
:0046C299  cmp edi, dword ptr [esi+04]
:0046C29C  jb 0046C286
:0046C29E  int 03
:0046C29F  mov byte ptr [eax], 6A
:0046C2A2  adc byte ptr [ebp+5056E445], cl
:0046C2A8  call 00487900
:0046C2AD  mov esi, eax
:0046C2AF  add esp, 0000000C
:0046C2B2  neg esi
:0046C2B4  sbb esi, esi
:0046C2B6  or dword ptr [ebp-04], FFFFFFFF
:0046C2BA  lea ecx, dword ptr [ebp+08]
:0046C2BD  inc esi
:0046C2BE  call 004A0665

:0046C2C3  int 03
:0046C2C4  dec ebp
:0046C2C5  hlt
:0046C2C6  mov eax, esi
:0046C2C8  pop edi
:0046C2C9  pop esi
:0046C2CA  mov dword ptr fs:[00000000], ecx
:0046C2D1  leave
:0046C2D2  ret 0004
...
  按F10单步执行。我们发现在这个函数中有一个非常有意思的地方:
:0046C286  lea eax, dword ptr [ebp-1C]
:0046C289  push 00000010
:0046C28B  push eax
:0046C28C  lea eax, dword ptr [ebp-1C]
:0046C28F  push eax
:0046C290  call 00456718
:0046C295  add esp, 0000000C
:0046C298  inc edi
:0046C299  cmp edi, dword ptr [esi+04]
:0046C29C  jb 0046C286
程序反复地执行这些代码!!

为了能看的更清楚些,我用C++语言来简述这几个语句:
for( long i=0 ; i< *(esi+04) ; i++)
{
eax=*(ebp-1c);
调用函数 00456718;
}
就是说,函数00456718要被调用很多次,这个次数就是ESI+04的值,用D
ESI+04命令来
查看其值,天哪!这不是matrix.cnt的内容吗?(你原先在二进制编辑器中己经打开过
此文件并且看到过它的内容)而ESI+04正是matrix.cnt的前4个字节!我们现在有足够的
理由相信,密码被加密成了128位(即16个字节),且加密函数存在于函数00456718中!

  
抬起眼睛看一下,在函数0046C256中,一开始也调用了一次00456718函数!为了再
进一步地证实我们的设想,让我们再重新运行OICQ,输入一个错误的密码,但这次你输
入的密码长度最好和正确密码的长度相同,在第一次调用00456718前设下一个断点:
:0046C265  mov esi, ecx
:0046C267  mov eax, dword ptr [ebp+08]
:0046C26A  and dword ptr [ebp-04], 00000000----->在这里设一个断点
:0046C26E  push [eax-08]
:0046C271  push eax
:0046C272  lea eax, dword ptr [ebp-1C]
:0046C275  push eax
:0046C276  call 00456718

  
当程序运行至断点处时,查看各个寄存器的值,你在EAX里看到了什么?是的,EAX
里面是你输入的密码,你用这个密码进行登录,程序将出现密码错误的信息,但是,如
果你此时修改一下EAX的值,把它改为正确的密码,你又惊喜地发现:OICQ己经成功地登
录了!注意:只要你稍微留意一下,EAX-8的值正是密码的长度。以上的事实己经证明了
:函数00456718就是OICQ的加密函数。
  
跟据种种迹象,这个函数具体是什么算法,学过密码学的朋友己经不用往下看了。
但我们还必须深入证实。
  按F8,将看到此函数:
:00456718  push ebp
:00456719  mov ebp, esp
:0045671B  sub esp, 0000005C
:0045671E  lea eax, dword ptr [ebp-5C]
:00456721  push eax
:00456722  call 00455A6E
:00456727  push [ebp+10]
:0045672A  lea eax, dword ptr [ebp-5C]
:0045672D  push [ebp+0C]
:00456730  push eax
:00456731  call 00455AA9
:00456736  lea eax, dword ptr [ebp-5C]
:00456739  push eax
:0045673A  push [ebp+08]

:0045673D  call 004565FD
:00456742  add esp, 00000018
:00456745  leave
:00456746  ret
函数看上去很短小,但它又调用了三个函数。查看第一个函数:
:00455A6E  push esi
:00455A6F  mov esi, dword ptr [esp+08]
:00455A73  push 0000005C
:00455A75  push 00000000
:00455A77  push esi
:00455A78  call 00486FD0
:00455A7D  and dword ptr [esi+10], 00000000
:00455A81  and dword ptr [esi+14], 00000000
:00455A85  and dword ptr [esi+58], 00000000
:00455A89  add esp, 0000000C
:00455A8C  mov dword ptr [esi], 67452301
:00455A92  mov [esi+04], EFCDAB89
:00455A99  mov [esi+08], 98BADCFE
:00455AA0  mov [esi+0C], 10325476
:00455AA7  pop esi
:00455AA8  ret
  
看到标有红色的四个语句了吗?加密函数在一开始设置了4个32位的常数!!接下来
,在函数00455D5A中连续出现了64次诸如:

00455D60  mov edi, dword ptr [ebp+08]
00455D63  xor ebx, edx
00455D65  add ebx, dword ptr [eax]
00455D67  lea edi, dword ptr [edi+ebx-28955B88] ;注意,这是个常数!
00455D6E  mov ebx, edi
形式的语句。至此为止,OICQ用的是什么加密算法,己经是不言而明了