首页
社区
课程
招聘
菜鸟练习,一个md5+密的keygenme的破解
发表于: 2006-7-9 01:26 6563

菜鸟练习,一个md5+密的keygenme的破解

2006-7-9 01:26
6563

【文章标题】: 一个MD5算法的Keygenme
【文章作者】: push_eax
【下载地址】: 自己搜索下载
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------菜鸟
【详细过程】
     闲来无事,抓出个不知何时下的Keygenme,练练手也好。分析后可知,这个Keygenme有2张数据表,由注册名经MD5运算后进行查表,得出2个DWORD数据,然后再由注册码经过一个可逆循环得出2个DWORD数据,最后对比是否正确的。

      开始分析,查壳,无壳。Od载入,随便输入Name和Sierial很容易得知Sierial应为16位。很简单的来到下面:
  00401D19       6A 28   push 28
  00401D1B       68 E430>push KeyGenMe.004030E4
  00401D20       FF35 04>push dword ptr ds:[403004]
  00401D26       E8 CF01>call <jmp.&user32.GetWindowTextA>  //这个call读入Name
  00401D2B       68 D430>push KeyGenMe.004030D4   
  00401D30       50      push eax    //压入的参数是Name长度
  00401D31       68 E430>push KeyGenMe.004030E4   //压入的参数是Name
  00401D36       E8 C5F2>call KeyGenMe.00401000
  这个call是对Name的初步处理,我们跟入:
  00401000       55      push ebp
  00401001       8BEC    mov ebp,esp
  00401003       83C4 F0 add esp,-10
  …………
  0040104B       8B55 0C mov edx,dword ptr ss:[ebp+C]
  0040104E       8B7D 08 mov edi,dword ptr ss:[ebp+8]
  00401051       8B75 10 mov esi,dword ptr ss:[ebp+10]
  00401054       C706 01>mov dword ptr ds:[esi],67452301
  0040105A       C746 04>mov dword ptr ds:[esi+4],EFCDAB89
  00401061       C746 08>mov dword ptr ds:[esi+8],98BADCFE
  00401068       C746 0C>mov dword ptr ds:[esi+C],10325476
  这里4个常数,01234567,89ABCDEF,FEDCBA98,7654321(内存中是低位存放低字节,高位存放高字节,所以显示的是67452301,EFCDAB89,98BADCFE,10325476)基本上可以肯定是MD5算法。出这个CALL,看看压入CALL的参数:
  00401D2B       68 D430>push KeyGenMe.004030D4
  可以看到出CALL后,这个004030D4返回了一个字符串,应该是MD5码吧,找个MD5计算的程序对比下,OK肯定了是MD5加密的CALL,继续向下走。
  00401D43        8D35 D4>lea esi,dword ptr ds:[4030D4]   //取得MD5码的地址
  00401D49        B9 0400>mov ecx,4
  00401D4E        0BD0    or edx,eax
  00401D50        33C0    xor eax,eax
  00401D52        AC     lods byte ptr ds:[esi]
  00401D53        C1E2 08 shl edx,8
  00401D56        E2 F6   loopd short KeyGenMe.00401D4E
  00401D58        0BD0    or edx,eax
  00401D5A       8915 9D>mov dword ptr ds:[40309D],edx
  小循环根据MD5字符取得一个DWORD数据,记为m2="N[4]N[3]N[2]N[1]",其中N[m]表示MD5字符串的第m个字符
  00401D60        B9 0400>mov ecx,4
  00401D65        33C0    xor eax,eax
  00401D67        33D2    xor edx,edx
  00401D69        0BD0    or edx,eax
  00401D6B        AC      lods byte ptr ds:[esi]
  00401D6C        C1E2 08 shl edx,8
  00401D6F        E2 F8   loopd short KeyGenMe.00401D69
  00401D71        0BD0    or edx,eax
  00401D73        8915 95>mov dword ptr ds:[403095],edx  //取得m1="N[8]N[7]N[6]N[5]"
  00401D79        6A 0C   push 0C  
  00401D7B       68 C730>push KeyGenMe.004030C7 //字符串"ChinaCracker",呵呵,原来是国产的
  00401D80        E8 53FC>call KeyGenMe.004019D8 //关键call 1,计算出一个从[4032D6]开始的DWORD数据表,记为table2
  00401D85        68 9530>push KeyGenMe.00403095 //压入m1
  00401D8A       68 9D30>push KeyGenMe.0040309D //压入m2
  00401D8F        E8 E8FB>call KeyGenMe.0040197C //关键call 2
  跟入关键CALL 1,来到:
  004019E3       BE D632>mov esi,KeyGenMe.004032D6 //[4032D6]开始是个DWORD数据表,记做Table2
  004019E8       B8 AE43>mov eax,KeyGenMe.004043AE
  004019ED       8D4E 48 lea ecx,dword ptr ds:[esi+48]
  004019F0       BA 0001>/mov edx,100   //          将从[4043AE]开始的1000H
  004019F5       8B38    |/mov edi,dword ptr ds:[eax] // 字节内容复制到[40331E]
  004019F7       83C0 04 ||add eax,4      //           至[40431E]的区间内。
  004019FA       8939    ||mov dword ptr ds:[ecx],edi   生成一个1000H大小的数据表
  004019FC       83C1 04 ||add ecx,4                  记为table1
  004019FF       4A      ||dec edx
  00401A00       75 F3   |\jnz short KeyGenMe.004019F5
  00401A02       3D AE53>|cmp eax,KeyGenMe.004053AE
  00401A07       7C E7   \jl short KeyGenMe.004019F0
  上面这个小循环生成1000H的数据表table 1,[40331E]是开始地址,继续:
  00401A09       8B55 08 mov edx,dword ptr ss:[ebp+8]  //"ChinaCracker"
  00401A0C       BF 6643>mov edi,KeyGenMe.00404366
  00401A11       33C0    xor eax,eax
  00401A13       2BFE    sub edi,esi
  00401A15       C745 FC>mov dword ptr ss:[ebp-4],12  //循环12H次,记N为循环计数器
  00401A1C       33C9    /xor ecx,ecx
  00401A1E       C745 F8>|mov dword ptr ss:[ebp-8],4 //小循环:
  00401A25       33DB    |/xor ebx,ebx  
  00401A27       8A1C02  ||mov bl,byte ptr ds:[edx+eax]
  00401A2A       C1E1 08 shl ecx,8
  00401A2D       0BCB   or ecx,ebx
  00401A2F       40      inc eax    //eax表示从"ChinaCracker"取的字符个数
  00401A30       3B45 0C cmp eax,dword ptr ss:[ebp+C] // "ChinaCracker"的长度12
  00401A33       7C 02  jl short KeyGenMe.00401A37//每次取4个字符,比较是否取满12
  00401A35       33C0   xor eax,eax    //取满清零EAX,重新开始取
  00401A37       8B5D F8  mov ebx,dword ptr ss:[ebp-8] //小循环计数器
  00401A3A       4B      dec ebx
  00401A3B       895D F8 mov dword ptr ss:[ebp-8],ebx
  00401A3E       75 E5 jnz short KeyGenMe.00401A25 //小循环从"ChinaCracker"中取4字节组成1个DWORD数据赋给ECX, ECX="ChinaCracker"的第((12-N )mod 4)+1个4字节
  00401A40       8B1C3E  mov ebx,dword ptr ds:[esi+edi] //从[404366]开始每次取1个DWORD数据
  00401A43       83C6 04  add esi,4
  00401A46       33D9    xor ebx,ecx  //异或
  00401A48       8B4D FC |mov ecx,dword ptr ss:[ebp-4]
  00401A4B       895E FC |mov dword ptr ds:[esi-4],ebx //运算后的数据覆盖table2的相应DWORD数据
  00401A4E       49   dec ecx  //N-1
  00401A4F       894D FC |mov dword ptr ss:[ebp-4],ecx
  00401A52       75 C8   \jnz short KeyGenMe.00401A1C
  上面这个小循环:从[404366]取12H个DWORD数据,处理后赋给table2,table2长度为12H
  00401A54       BB D632>mov ebx,KeyGenMe.004032D6
  00401A59       33C0    xor eax,eax
  00401A5B       A3 5E43>mov dword ptr ds:[40435E],eax  //[40435E]和[404362]2个全局变量清零,记[40435E]为key1,[4043620]为key2
  00401A60       A3 6243>mov dword ptr ds:[404362],eax         
  00401A65       8BF3    mov esi,ebx
  循环开始,对table2进运算:
  00401A67       BF 0900>mov edi,9 //循环9次
  00401A6C       8D05 5E>/lea eax,dword ptr ds:[40435E]
  00401A72       8D0D 62>|lea ecx,dword ptr ds:[404362]
  00401A78       50     push eax   //key1地址
  00401A79       51     push ecx   //key2地址
  00401A7A       E8 FDFE>|call KeyGenMe.0040197C //记为函数f1
  00401A7F       A1 6243>|mov eax,dword ptr ds:[404362]  //EAX=key2
  00401A84       8B0D 5E>|mov ecx,dword ptr ds:[40435E]  //ECX=key1
  00401A8A       8906  mov dword ptr ds:[esi],eax //每次覆盖table2的2个DWORD数据
  00401A8C       894E 04 |mov dword ptr ds:[esi+4],ecx
  00401A8F       83C6 08 |add esi,8 //table2指针+8
  00401A92       4F      |dec edi
  00401A93       75 D7   \jnz short KeyGenMe.00401A6C
  又一循环,对table1进行运算:
  00401A95       8D73 4C lea esi,dword ptr ds:[ebx+4C]
  00401A98       C745 F4>mov dword ptr ss:[ebp-C],4   //循环4次
  00401A9F       BF 8000>/mov edi,80     //小循环,循环80H次
  00401AA4       8D0D 5E>|/lea ecx,dword ptr ds:[40435E]
  00401AAA       8D15 62>||lea edx,dword ptr ds:[404362]
  00401AB0       51   |push ecx       key1地址
  00401AB1       52   push edx       key2地址
  00401AB2       E8 C5FE>||call KeyGenMe.0040197C     //f1
  00401AB7       8B0D 62>||mov ecx,dword ptr ds:[404362]  //ecx=key2
  00401ABD       8B15 5E>||mov edx,dword ptr ds:[40435E]   //ecx=key1
  00401AC3       894E FC ||mov dword ptr ds:[esi-4],ecx //每次覆盖table1的2个数据
  00401AC6       8916    ||mov dword ptr ds:[esi],edx
  00401AC8       83C6 08 ||add esi,8
  00401ACB       4F      ||dec edi
  00401ACC       75 D6   |\jnz short KeyGenMe.00401AA4 //小循环结束
  00401ACE       FF4D F4 |dec dword ptr ss:[ebp-C]   
  00401AD1       75 CC   \jnz short KeyGenMe.00401A9F//循环结束
  00401AD3       59      pop ecx
  00401AD4       5A      pop edx
  00401AD5       5E      pop esi
  00401AD6       5F      pop edi
  00401AD7       5B      pop ebx
  00401AD8       C9      leave
  00401AD9       C2 0800 retn 8
  上面两个大循环分别对table2和table1作处理,起作用的是都是call 0040197C,跟入:
  00401987       8B45 08 mov eax,dword ptr ss:[ebp+8]
  0040198A       8B4D 0C mov ecx,dword ptr ss:[ebp+C]
  0040198D       8B00    mov eax,dword ptr ds:[eax]  //EAX=key2
  0040198F       8B31    mov esi,dword ptr ds:[ecx]   //ESI=key1
  00401991       BF D632>mov edi,KeyGenMe.004032D6   //table2首地址
  00401996       C745 FC>mov dword ptr ss:[ebp-4],10   //循环10H次
  0040199D       8BDF    mov ebx,edi
  小循环:
  0040199F       3303    /xor eax,dword ptr ds:[ebx]
  004019A1       8BD0    |mov edx,eax
  004019A3       50      |push eax
  004019A4       E8 67FF>|call KeyGenMe.00401910  //记为函数f2()
  004019A9       8B4D FC |mov ecx,dword ptr ss:[ebp-4]   //循环计数器
  004019AC       33C6    |xor eax,esi  
  004019AE       83C3 04 |add ebx,4                               ;  
  004019B1       49   |dec ecx   
  004019B2       8BF2    |mov esi,edx      
  004019B4       894D FC |mov dword ptr ss:[ebp-4],ecx   
  004019B7       75 E6   \jnz short KeyGenMe.0040199F

  小循环用C语言描述,即:
  for(n=0;n<16;n++)
  {EAX=table2[n] xor ESI;
   EDX=EAX;  EAX=f2(EAX);
    EAX=EAX xor ESI;  ESI=EDX;}

  004019B9       8B4F 40 mov ecx,dword ptr ds:[edi+40] //table2的第17个数据,table2[16]
  004019BC       8B57 44 mov edx,dword ptr ds:[edi+44] //table2最后1个数据,table2[17]
  004019BF       33C8    xor ecx,eax
  004019C1       33D6    xor edx,esi
  004019C3     8915 62>mov dword ptr ds:[404362],edx  //key2=ESI xor table2[17]
  004019C9       890D 5E>mov dword ptr ds:[40435E],ecx    //key1=EAX xor table2[16]
  接着就出这个CALL了,看样子还要跟入f2,即00401910,那就跟入吧,来到这里:
  00401918       8B4D 08 mov ecx,dword ptr ss:[ebp+8] //[ebp+8]是压入CALL的参数变量
  0040191B       8AC1    mov al,cl
  0040191D       25 FF00>and eax,0FF
  00401922       C1E9 08 shr ecx,8
  00401925       8BD0    mov edx,eax   //edx= 0FF  AND 参数变量
  00401927       8AC1    mov al,cl
  00401929       BF D632>mov edi,KeyGenMe.004032D6
  0040192E       25 FF00>and eax,0FF
  00401933       C1E9 08 shr ecx,8
  00401936       8BF0    mov esi,eax    //esi=(0FF00H  AND  参数变量)>>8
  00401938       8BC1    mov eax,ecx
  0040193A       C1E8 08 shr eax,8
  0040193D       25 FF00>and eax,0FF   // eax=(FF000000H  AND  参数变量)>>24
  00401942       81E1 FF>and ecx,0FF  //ecx=(0FF0000H   AND   参数变量)>>16
  00401948       81E6 FF>and esi,0FFFF
  0040194E       81E2 FF>and edx,0FFFF
  00401954       8B4487 >mov eax,dword ptr ds:[edi+eax*4+48] //查表table1,[edi+48]是table1的开始地址
  00401958       8B9C8F >mov ebx,dword ptr ds:[edi+ecx*4+448]
  0040195F       8B8CB7 >mov ecx,dword ptr ds:[edi+esi*4+848]
  00401966       03C3    add eax,ebx
  00401968       33C1    xor eax,ecx
  0040196A       8B8C97 >mov ecx,dword ptr ds:[edi+edx*4+C48]
  00401971       03C1    add eax,ecx
  这个call用C语言描述为:
  DWORD f2(para)
  {DWORD eax=0,ecx=0,edx=0,esi=0,ebx=0;
  edx=para&0ffH;esi=(para0ff00H)>>8;eax=(para&0ff000000H)>>24;ecx=(para&0ff0000H)>>16;
  eax=table1[eax];ebx=table1[ecx+0x100];ecx=table1[esi+0x200];
  eax+=ebx;eax^=ecx;ecx=table1[edx+0x300];eax+=ecx;}
  注意DWORD占4字节,上面位移,要除以4([edi+48]是table1的开始地址
  ,则[edi+eax*4+48]就是table[eax])。
  搞定,返回到最外面CALL:
  00401D85       68 9530>push KeyGenMe.00403095    //压入的是前面m1的地址
  00401D8A      68 9D30>push KeyGenMe.0040309D    //m2的地址
  00401D8F       E8 E8FB>call KeyGenMe.0040197C    //关键call 2
  00401D94       90      nop
  这CALL调用前面函数f1,继续对key1,key2作变换,只是压入参数变为m1,m2了。我们继续向下看:
  00401E1C       B9 0800>mov ecx,8  //传送2个DWORD,key1,key2地址相临,一起传送
  00401E21       8D35 5E>lea esi,dword ptr ds:[40435E]    //key1
  00401E27       8D3D C0>lea edi,dword ptr ds:[4031C0]  //保存最终key1,key2
  00401E2D       F3:A4   rep movs byte ptr es:[edi],byte ptr ds>
  00401E2F       68 B630>push KeyGenMe.004030B6    //ASCII "[BCG][FCG][DFCG]"
  00401E34       68 AC31>push KeyGenMe.004031AC  //Sierial地址
  00401E39        E8 46FD>call KeyGenMe.00401B   //key call 3对Sierial作变换

  前面可以看出,对Name的变化是MD5加密了的,我们不能根据这个算出的key1,key2作逆变换,得出用户名,那么写Keygen关键就在这个对Sierial处理的CALL:
  00401B84       55      push ebp
  00401B85       8BEC    mov ebp,esp
  00401B87       60      pushad
  00401B88       8B75 0C mov esi,dword ptr ss:[ebp+C]// 由"[BCG][FCG][DFCG]"
  00401B8B       8B06    mov eax,dword ptr ds:[esi]  //得到4个常数
  00401B8D       8B5E 04 mov ebx,dword ptr ds:[esi+4]
  00401B90       8B4E 08 mov ecx,dword ptr ds:[esi+8]
  00401B93       8B56 0C mov edx,dword ptr ds:[esi+C]
  00401B96       A3 C053>mov dword ptr ds:[4053C0],eax //这里是4个常数
  00401B9B       891D C4>mov dword ptr ds:[4053C4],ebx
  00401BA1       890D C8>mov dword ptr ds:[4053C8],ecx
  00401BA7       8915 CC>mov dword ptr ds:[4053CC],edx
  00401BAD      55      push ebp
  00401BAE       8B5D 08 mov ebx,dword ptr ss:[ebp+8]
  00401BB1       BA 2037>mov edx,C6EF3720 ,edx初始化为0x C6EF3720
  00401BB6       8B33    mov esi,dword ptr ds:[ebx]
  00401BB8       8B7B 04 mov edi,dword ptr ds:[ebx+4]
  这里esi="Sierial前8个字符",edi="Sierial后8个字符",具体的说,若Sierial=1234567887654321,则esi=78563412H,edi=21436587H。
  循环记数器,下面循环开始:
  00401BBB       BD 2000>mov ebp,20
  00401BC0       8BC6    /mov eax,esi
  00401BC2       8BDE    |mov ebx,esi
  00401BC4       8BCE    |mov ecx,esi
  00401BC6       C1E0 04 |shl eax,4  
  00401BC9       0305 C8>|add eax,dword ptr ds:[4053C8] //eax=(esi>>5)+5d474346H,记为sa;
  00401BCF       C1EB 05 |shr ebx,5
  00401BD2       031D CC>|addebx,dword ptr ds:[4053CC]//ebx=(esi<<4)+445b5d47H,记为sb
  00401BD8       03CA    |add ecx,edx //ecx=esi+edx,记为sc
  00401BDA       33C8    |xor ecx,eax
  00401BDC       33CB   |xor ecx,ebx  //(sc^sa)^sb,记为fs(esi)
  00401BDE       2BF9    |sub edi,ecx  //edi=edi-ecx
  00401BE0       8BC7    |mov eax,edi  
  00401BE2       8BD8    |mov ebx,eax
  00401BE4       8BC8    |mov ecx,eax
  00401BE6       C1E0 04 |shl eax,4  
  00401BE9       0305 C0>|add eax,dword ptr ds:[4053C0]//eax=(edi>>5)+43465b5DH,记为da
  00401BEF       C1EB 05 |shr ebx,5
  00401BF2       031D C4>|add ebx,dword ptr ds:[4053C4]//ebx=(edi<<)+4743425bH,记为db
  00401BF8       03CA    |add ecx,edx  //ecx= edi+edx,记为dc
  00401BFA       33C8    |xor ecx,eax
  00401BFC       33CB    |xor ecx,ebx   //(da^dc)^db,记为fd(edi)
  00401BFE       2BF1    |sub esi,ecx
  00401C00       81EA B9>|sub edx,9E3779B9 //edx-=9E3779B9H
  00401C06       4D      |dec ebp
  00401C07       75 B7   \jnz short KeyGenMe.00401BC0
  00401C09       892D C0>mov dword ptr ds:[4053C0],ebp
  00401C0F       892D C4>mov dword ptr ds:[4053C4],ebp
  00401C15       892D C8>mov dword ptr ds:[4053C8],ebp
  00401C1B       892D CC>mov dword ptr ds:[4053CC],ebp
  00401C21       5D      pop ebp
  00401C22       8B5D 08 mov ebx,dword ptr ss:[ebp+8] //Sierial地址,保存Sierial计算出的2个值,记为s1,s2
  00401C25       8933    mov dword ptr ds:[ebx],esi
  00401C27       897B 04 mov dword ptr ds:[ebx+4],edi
  00401C2A       61      popad
  00401C2B       C9      leave
  00401C2C       C2 0800 retn 8

  上面这个循环用C语言描述的话:
  edx=c6ef3720H;
  for(i=0;i<20H;i++)
  {edi=edi-fs(esi);
  esi=esi-fd(edi);
  edx-=9e3779b9;}
   把edi,esi当作数列的话,则有:edi(N)=edi(N-1)-fs(esi(N-1));esi(N)=esi(N-1)-fd(edi(N))移项一下得到:esi(N-1)= esi(N)+ fd(edi(N));edi(N-1)= edi(N)+ fs(esi(N-1))。可见这个是可逆的,从edi(N),esi(N)我们能推算出最初始化的esi,edi,也就是说,写Keygen时由Name计算出的key1,key2得到正确的Sierial(正确的化,esi,edi被初始化为key1,key2)。

   整个过程基本OK。写Keygen时:table1,table2两表由程序中固定数值换算得到。我把原始数据导出为data1.dat,data2.dat。写的时候按上面分析的得出table1,table2,再进行计算得到key1,key2,最后由key1,key2得到正确的Sierial即可。代码较长,这里就不赘述了,源代码和data1.dat,data2.dat见附件。
  
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!

                                                       2006年07月09日 上午 01:10:28


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 7
支持
分享
最新回复 (2)
雪    币: 225
活跃值: (1211)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
首先表示支持
其次这个crackme还使用了blowfish,tea算法.还有就是注册机有一点点问题
你可以参考一下R斑竹的文章:
http://bbs.pediy.com/showthread.php?s=&threadid=25551&highlight=lnn1123
2006-7-9 12:25
0
雪    币: 225
活跃值: (40)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
3
晕,早觉得第2个加密过程也是个什么算法来着,原来是BlowFish加密的啊....看来要好好补补课了...
2006-7-9 17:26
0
游客
登录 | 注册 方可回帖
返回
//