Quantcast
Channel: 看雪安全论坛
Viewing all articles
Browse latest Browse all 9556

【原创】再来一枚KeygenMe

$
0
0
今天找到了这样一个程序,拿到OD里初看觉得太简单了,关键的汇编代码就那么几十行,就当是练手吧。后来分析了一阵子发现里面另有玄机,看似短短的几十行汇编代码,里面却有几个地方,稍不留神,就无法写出完整的注册机。
闲话就不多说了,直接进入主题吧。
这是一个控制台的程序,所用的编程语言我也没见到过,既然无法从API上下断,也无法从入口点下手,那就查找字符串吧。。经过一小步,就找到了这个关键代码处,由于代码比较少,我就详细的解释下,有不对的地方,恳请各位大大多多指教。:o:
代码:

00401478  |> /837D E0 09    /CMP DWORD PTR SS:[EBP-20],9            ;  判断是否大于9
0040147C  |. |77 26        |JA SHORT Keygen_#.004014A4              ;  大于则跳转
0040147E  |. |89E8          |MOV EAX,EBP
00401480  |. |0345 E0      |ADD EAX,DWORD PTR SS:[EBP-20]          ;  基址加上偏移
00401483  |. |83E8 08      |SUB EAX,8                              ;  减8定位到字符串
00401486  |. |0FB600        |MOVZX EAX,BYTE PTR DS:[EAX]            ;  依次取字符串的各个字符
00401489  |. |8845 EF      |MOV BYTE PTR SS:[EBP-11],AL            ;  将取到的字符存入变量,记为变量a
0040148C  |. |0FBE55 EF    |MOVSX EDX,BYTE PTR SS:[EBP-11]          ;  将变量a的值传给edx
00401490  |. |8D45 F0      |LEA EAX,DWORD PTR SS:[EBP-10]          ;  eax=ebp-0x10,即让eax指向这个地址
00401493  |. |0110          |ADD DWORD PTR DS:[EAX],EDX              ;  eax所指向的变量b,加上edx的值
00401495  |. |8B55 F0      |MOV EDX,DWORD PTR SS:[EBP-10]          ;  将b的值传给edx
00401498  |. |8D45 E8      |LEA EAX,DWORD PTR SS:[EBP-18]          ;  eax=ebp-0x18,即让eax指向这个地址
0040149B  |. |0110          |ADD DWORD PTR DS:[EAX],EDX              ;  将eax指向的变量c,加上edx的值
0040149D  |. |8D45 E0      |LEA EAX,DWORD PTR SS:[EBP-20]          ;  eax=ebp-0x20,即让eax指向变量i
004014A0  |. |FF00          |INC DWORD PTR DS:[EAX]                  ;  变量i加1,为下一轮循环做准备
004014A2  |.^\EB D4        \JMP SHORT Keygen_#.00401478

这段代码看似简单,就是一个while循环的特征。但是仔细分析,发现里面涉及到4个变量的使用,不认真分析还是比较模糊的。

结合上面的分析,整理一下思路:
1、 用一个变量做为循环,记为变量i,循环次数为10次
2、 依次取各字符的值,放入变量a
3、 依次累加各字符的值,即name[0]+name[1]+name[2]…记为变量b.
4、 用一个常量0x8099,依次加上变量b的值,结果存入变量c
容易忽略的地方:
1、 当字符串长度不足10个字节时,还是得循环10 次,那剩下的次数都在做什么呢。
2、 当字符串长度超过10个字符时,多余的部分又怎么算呢。
分析发现:
1、 当字符串不足8位时,用0补足8位,再在后面加上两个字节,值分别是0xb0,0xff
2、 当字符串长度为8位时,第9位补0,第10位为0xff.
3、 当字符串长度为9位时,第10位补0 。
4、 当字符串长度为10位时,则按字符串的值进行累加,不添加其它值。
代码:

004014A4  |> \8B45 F0      MOV EAX,DWORD PTR SS:[EBP-10]            ;  取变量b的值
004014A7  |.  8B55 E8      MOV EDX,DWORD PTR SS:[EBP-18]            ;  取变量c的值
004014AA  |.  01C2          ADD EDX,EAX                              ;  c=c+b
004014AC  |.  8B45 E4      MOV EAX,DWORD PTR SS:[EBP-1C]            ;  取变量d,d的初始值为7
004014AF  |.  0FAFC2        IMUL EAX,EDX                            ;  d=d*c
004014B2  |.  8945 E4      MOV DWORD PTR SS:[EBP-1C],EAX
004014B5  |.  8B55 F0      MOV EDX,DWORD PTR SS:[EBP-10]            ;  取变量b的值
004014B8  |.  8B45 E4      MOV EAX,DWORD PTR SS:[EBP-1C]            ;  取变量 d的值
004014BB  |.  89C1          MOV ECX,EAX
004014BD  |.  29D1          SUB ECX,EDX                              ;  d=d-b
004014BF  |.  8B55 E8      MOV EDX,DWORD PTR SS:[EBP-18]            ;  取变量c 的值
004014C2  |.  89D0          MOV EAX,EDX
004014C4  |.  C1F8 1F      SAR EAX,1F                              ;  算术右移,最高位不变
004014C7  |.  C1E8 1F      SHR EAX,1F                              ;  逻辑右移,用0补空位
004014CA  |.  8D0402        LEA EAX,DWORD PTR DS:[EDX+EAX]          ;  eax=edx+eax
004014CD  |.  89C2          MOV EDX,EAX
004014CF  |.  D1FA          SAR EDX,1                                ;  算术右移一位,相当于除2
004014D1  |.  89D0          MOV EAX,EDX
004014D3  |.  01C0          ADD EAX,EAX                              ;  相当于eax*2
004014D5  |.  01D0          ADD EAX,EDX                              ;  eax=eax+edx
004014D7  |.  C1E0 02      SHL EAX,2                                ;  eax=eax*4
004014DA  |.  01D0          ADD EAX,EDX                              ;  eax=eax+edx
004014DC  |.  8D1401        LEA EDX,DWORD PTR DS:[ECX+EAX]          ;  edx=ecx+eax
004014DF  |.  8B45 E4      MOV EAX,DWORD PTR SS:[EBP-1C]            ;  取变量d的值
004014E2  |.  0FAFC2        IMUL EAX,EDX                            ;  做乘法
004014E5  |.  8945 E4      MOV DWORD PTR SS:[EBP-1C],EAX
004014E8  |.  837D E4 00    CMP DWORD PTR SS:[EBP-1C],0              ;  若算出来的数是负数,则进行处理
004014EC  |.  79 0F        JNS SHORT Keygen_#.004014FD
004014EE  |.  8B45 E4      MOV EAX,DWORD PTR SS:[EBP-1C]
004014F1  |.  BA 00000000  MOV EDX,0
004014F6  |.  29C2          SUB EDX,EAX                              ;  用0来减去这个负数,等价于把负号去掉.
004014F8  |.  89D0          MOV EAX,EDX
004014FA  |.  8945 E4      MOV DWORD PTR SS:[EBP-1C],EAX            ;  得到key
004014FD  |>  8B45 F4      MOV EAX,DWORD PTR SS:[EBP-C]
00401500  |.  3B45 E4      CMP EAX,DWORD PTR SS:[EBP-1C]
00401503  |.  75 16        JNZ SHORT Keygen_#.0040151B
00401505  |.  C74424 04 1C0>MOV DWORD PTR SS:[ESP+4],Keygen_#.004401>;  Congrats, now write me keygen!\n\n
0040150D  |.  C70424 C03344>MOV DWORD PTR SS:[ESP],Keygen_#.004433C0
00401514  |.  E8 9FAC0300  CALL Keygen_#.0043C1B8
00401519  |.  EB 14        JMP SHORT Keygen_#.0040152F
0040151B  |>  C74424 04 3D0>MOV DWORD PTR SS:[ESP+4],Keygen_#.004401>;  Sorry, try again!\n\n
00401523  |.  C70424 C03344>MOV DWORD PTR SS:[ESP],Keygen_#.004433C0
0040152A  |.  E8 89AC0300  CALL Keygen_#.0043C1B8
0040152F  |>  C70424 510144>MOV DWORD PTR SS:[ESP],Keygen_#.00440151 ; |pause
00401536  |.  E8 95F20000  CALL <JMP.&msvcrt.system>                ; \system

经过上面的分析,注册机也就能完整的写出来了。
注册机代码如下:
代码:

#include <stdio.h>
#include <string.h>
main()
{
        char name[20];
        int i,a,b=0,c=0x80899,d=7,len,z,c1,d1,t;
        printf("Please input a name:\t");
        scanf("%s",name);
        len=strlen(name);
        if(len>=10)//当字符串长度大于10时,则取前面10个字符,依次累加。
        {
                for(i=0;i<=9;i++)
                {
                        b=name[i]+b;
                        c=c+b;
                }
        }
        if(len<8)//当字符串不足8位时,用0补足8位,再在后面加上两个字节,值分别是0xb0,0xff
        {
                z=8-len;
                for(i=0;i<len;i++)
                {
                        b=name[i]+b;
                        c=c+b;
                }
                while(z!=0)
                {
                        c=c+b;
                        z--;
                }
                b=b-80;
                c=c+b;
                b=b-1;
                c=c+b;
        }
        if(len==8)//当字符串长度为8位时,第9位补0,第10位为0xff.
        {
                for(i=0;i<len;i++)
                {
                        b=name[i]+b;
                        c=c+b;
                }
                c=c+b;
                b=b-1;
                c=c+b;
        }
        if(len==9)//当字符串长度为9位时,第10位补0 。
        {
                for(i=0;i<len;i++)
                {
                        b=name[i]+b;
                        c=c+b;
                }
                c=c+b;
        }
        c1=c+b;
        d=d*c1;
        d1=d-b;
        t=d1;
        c1=c;
        c1=c1>>1;
        d1=c1;
        c1=c1+c1;
        c1=c1+d1;
        c1=c1<<2;
        c1=c1+d1;
        c1=c1+t;
        c1=c1*d;
        if(c1&0x800000000)//若算出来是一个负数,则用0来减去它。
        {
                c1=0-c1;
        }
        printf("The key is :\t%d\n",c1);
}

总结:
1、 这个程序关键代码不长,但是要完全分析还是要点时间,因为里面有几个细节点。
2、 最后算出的结果是负数的时候,之前我认为这样的name值是没有key的,经过认真调试才发现后面还有一段代码是将负数变成正数,加上这段代码,注册机就更完整了。
3、 如果注册name超过10个字符,虽然用同样的key可以注册成功,但是会出现一个缓冲区溢出的错误。
4、 通过这个程序的分析,发现有时候简单的事,做起来不一定简单。

可用的注册名如:name: thankyou
The key is : 801777276

name: helloworld
The key is : 1900304355

上传的附件
文件类型: zip Keygenme_#2_by_Nicohogtag.zip (184.7 KB)

Viewing all articles
Browse latest Browse all 9556

Latest Images

Trending Articles

<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>