missviola 发表于 2009-10-13 17:06

jmzhwf一个好玩的CM 算法分析+注册机

本帖最后由 missviola 于 2009-10-20 15:59 编辑

【破文标题】jmzhwf一个好玩的CM 算法分析+注册机
【破文作者】missviola
【破解工具】PEID OD
【破解平台】Windows XP
【原版下载】http://www.52pojie.cn/thread-33387-1-1.html
【破解声明】只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
------------------------------------------------------------------------
【破解过程】今天在52pojie下了这个crackme,跟了一下感觉挺不错的,写点心得和大家分享一下吧。首先我们用PEID查壳,发现为

MASM32 / TASM32,由汇编语言编写。用OD载入,在第二个GetDlgItemTextA后面F2下断点,输入name:viola,password:123456,

点击Register,发现没有断下。。。 -_-||| 这个断点可是小菜我在破解那些汇编写的程序时屡试不爽的一个方法啊。看来此路不通

,我们只能想其他方法。我们找到第二个GetDlgItemTextA写入内存的地址,下内存写入断点试试看吧。

004012C6   . FF35 40304000PUSH DWORD PTR DS:               ; /Count = 14 (20.)
004012CC   . 68 23304000    PUSH 3.00403023                        ; |Buffer = 3.00403023
004012D1   . 68 ED030000    PUSH 3ED                                 ; |ControlID = 3ED (1005.)
004012D6   . FF75 08      PUSH DWORD PTR SS:                ; |hWnd
004012D9   . E8 BE020000    CALL <JMP.&user32.GetDlgItemTextA>       ; \GetDlgItemTextA

我们在00403023这里下内存写入断点,输入刚才的注册信息。点击Register,终于断下来了,我们来到这里:

77D32192   8B4D 08          MOV ECX,DWORD PTR SS:
77D32195   E8 4663FEFF      CALL user32.77D184E0
77D3219A   8BF0             MOV ESI,EAX
77D3219C   8975 E4          MOV DWORD PTR SS:,ESI
77D3219F   3BF3             CMP ESI,EBX
77D321A1   0F84 A3F20000    JE user32.77D4144A
77D321A7   56               PUSH ESI
77D321A8   E8 6A6CFFFF      CALL user32.77D28E17
77D321AD   6A 01            PUSH 1
77D321AF   57               PUSH EDI
77D321B0   FF75 10          PUSH DWORD PTR SS:
77D321B3   6A 0D            PUSH 0D
77D321B5   56               PUSH ESI
77D321B6   85C0             TEST EAX,EAX
77D321B8   0F85 ED8E0000    JNZ user32.77D3B0AB
77D321BE   E8 177BFFFF      CALL user32.77D29CDA
77D321C3   834D FC FF       OR DWORD PTR SS:,FFFFFFFF
77D321C7   E8 3464FEFF      CALL user32.77D18600
77D321CC   C2 0C00          RETN 0C

我们一路F8单步走回去,来到这里:

004012DE   . E8 4F000000    CALL 3.00401332

我们F7跟进去看看:

00401332   $ 33C0         XOR EAX,EAX
00401334   . B9 00000000    MOV ECX,0
00401339   . BE 23304000    MOV ESI,3.00403023                     ;ASCII "123456"
0040133E   . 8A06         MOV AL,BYTE PTR DS:
00401340   . EB 10          JMP SHORT 3.00401352
00401342   > 0FB6C0         MOVZX EAX,AL                           依次取password各位ASCII码
00401345   . 80B8 50314000 >CMP BYTE PTR DS:,2         这里有个小机关进行比较
0040134C   . 75 0A          JNZ SHORT 3.00401358
0040134E   . 41             INC ECX                                 
0040134F   . 8A0431         MOV AL,BYTE PTR DS:
00401352   > 3C 00          CMP AL,0                                 有没有比较完
00401354   .^77 EC          JA SHORT 3.00401342                      没有跳回去继续比较

这里401345这里是比较password各位的ASCII码值加上固定地址403150以后,该内存地址的值是不是为2,我们d 403150过去看看,发

现如下:

0040315000 09 09 09 09 09 09 09 09 06 05 09 09 05 09 09.............
0040316009 09 09 09 09 09 09 09 09 09 09 09 09 09 09 09................
0040317006 05 05 05 09 05 05 05 05 05 04 04 05 04 05 04.
0040318001 01 01 01 01 01 01 01 01 01 05 05 05 05 05 05
0040319009 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02.
004031A002 02 02 02 02 02 02 02 02 02 02 05 05 05 05 05

从403091到4031AA这里都是2,也就说password各位的ASCII码值范围应该在0x41-0x49之间,也就说password必须为大写字母。

00401356   . EB 07          JMP SHORT 3.0040135F
00401358   > C605 44304000 >MOV BYTE PTR DS:,40
0040135F   > BE 00304000    MOV ESI,3.00403000                     ;ASCII "viola"
00401364   . 33C9         XOR ECX,ECX
00401366   . B8 01000000    MOV EAX,1
0040136B   . 33D2         XOR EDX,EDX
0040136D   . C705 45304000 >MOV DWORD PTR DS:,0            =0
00401377   > B9 00000000    MOV ECX,0
0040137C   . 8A0C32         MOV CL,BYTE PTR DS:             依次取name各位ASCII码值
0040137F   . 80F9 00      CMP CL,0                                 有没有取完
00401382   . 74 09          JE SHORT 3.0040138D
00401384   . 42             INC EDX                                  循环次数+1
00401385   . 000D 45304000ADD BYTE PTR DS:,CL            进行累加,初始值为0
0040138B   .^EB EA          JMP SHORT 3.00401377

上面的这个小循环是分别取name各位的ASCII码值进行累加,取十六进制结果的最后结果进行下一步运算,我们记为zonghe1,接着跟

下去。

0040138D   > A1 45304000    MOV EAX,DWORD PTR DS:    zonghe1送eax
00401392   . B9 18000000    MOV ECX,18                     0x18送ecx
00401397   . 99             CDQ                              edx清零
00401398   . F7F9         IDIV ECX                         zonghe1 idiv 0x18
0040139A   . 8815 4F304000MOV BYTE PTR DS:,DL      余数送
004013A0   . 8A0D 44304000MOV CL,BYTE PTR DS:
004013A6   . 80F9 40      CMP CL,40                        如果刚才password比较的时候出错的话,这里CL就会为0x40,

也就是会跳向失败
004013A9   . 75 05          JNZ SHORT 3.004013B0
004013AB   . E9 45010000    JMP 3.004014F5
004013B0   > E9 CB000000    JMP 3.00401480                   跳向下一步运算

上面的第二个循环分别取name的各位的ASCII码值,相加以后除以0x18,取余数送,记为yushu,我们继续看~~

00401480   > E8 8B000000    CALL 3.00401510   F7跟进

00401510   $ A0 24304000    MOV AL,BYTE PTR DS:             取password第二位的ASCII码值
00401515   . 3C 45          CMP AL,45                               比较是否为0x45(0x45 = E)
00401517    ^75 DC          JNZ SHORT 3.004014F5                  不相等就跳向失败
00401519   $ BF 96124000    MOV EDI,3.00401296                     ;Entry address
0040151E   . B9 00010000    MOV ECX,100
00401523   . B0 99          MOV AL,99
00401525   . 34 55          XOR AL,55
00401527   . F2:AE          REPNE SCAS BYTE PTR ES:
00401529   . 85C9         TEST ECX,ECX
0040152B   . 74 06          JE SHORT 3.00401533
0040152D   . 5E             POP ESI
0040152E   . 33F6         XOR ESI,ESI
00401530   . 57             PUSH EDI
00401531   .^EB C2          JMP SHORT 3.004014F5
00401533   > C3             RETN

从上面这个call我们可以知道password第二位必须为E。


00401485   . 33DB         XOR EBX,EBX
00401487   . BF 80144000    MOV EDI,3.00401480
0040148C   . 83EF 60      SUB EDI,60
0040148F   . B8 DE000000    MOV EAX,0DE
00401494   . 83F0 12      XOR EAX,12
00401497   . B9 59000000    MOV ECX,59
0040149C   . F2:AE          REPNE SCAS BYTE PTR ES:
0040149E   . 85C9         TEST ECX,ECX
004014A0   . 74 06          JE SHORT 3.004014A8
004014A2   . 5E             POP ESI
004014A3   . 33F6         XOR ESI,ESI
004014A5   . 57             PUSH EDI
004014A6   . EB 4D          JMP SHORT 3.004014F5
004014A8   > C3             RETN

走过上面的retn我们来到这里。

004012E3   . 68 53304000    PUSH 3.00403053                        ;zwatrqlcghpsxyenvbjdfkmu
004012E8   . E8 C9000000    CALL 3.004013B6                        F7跟进,注意上面的字符串

004013B6   $ 55             PUSH EBP
004013B7   . 8BEC         MOV EBP,ESP
004013B9   . 68 23304000    PUSH 3.00403023                        ;
004013BE   . E8 7D010000    CALL 3.00401540                         这个call获取password长度
004013C3   . 83F8 0A      CMP EAX,0A                              同0xA比较
004013C6   0F85 29010000JNZ 3.004014F5                        不相等就跳向失败

到这里我们知道password必须要为10位。

004013CC   . BE 23304000    MOV ESI,3.00403023                     ;
004013D1   . B8 00000000    MOV EAX,0
004013D6   . BB 00000000    MOV EBX,0
004013DB   . 33C9         XOR ECX,ECX
004013DD   . EB 06          JMP SHORT 3.004013E5
004013DF   > 8A0C30         MOV CL,BYTE PTR DS:          依次取password各位的ASCII码值
004013E2   . 03D9         ADD EBX,ECX                           相加,ebx初始值为0
004013E4   . 40             INC EAX                               循环次数+1
004013E5   > 83F8 09      CMP EAX,9                           同9比较
004013E8   .^72 F5          JB SHORT 3.004013DF                   没算完接着跳回去算
004013EA   . 8BC3         MOV EAX,EBX                           和送eax
004013EC   . B9 09000000    MOV ECX,9                           ecx=0x9
004013F1   . 99             CDQ
004013F2   . F7F9         IDIV ECX                              刚才运算的和 idiv 9
004013F4   . A3 4A304000    MOV DWORD PTR DS:,EAX         除下来的商送
004013F9   . 8B7D 08      MOV EDI,DWORD PTR SS:          字符串 ZWATRQLCGHPSXYENVBJDFKMU 送edi
004013FC   . 8A15 4F304000MOV DL,BYTE PTR DS:         yushu1送dl
00401402   . 8AC2         MOV AL,DL
00401404   . 3C 18          CMP AL,18                           yushu1同0x18比较
00401406   . 76 02          JBE SHORT 3.0040140A                  小于等于就跳走
00401408   . 2C 18          SUB AL,18                           大于就减去0x18
0040140A   > A2 4E304000    MOV BYTE PTR DS:,AL
0040140F   . 33C0         XOR EAX,EAX
00401411   . A0 4E304000    MOV AL,BYTE PTR DS:
00401416   . 8A2438         MOV AH,BYTE PTR DS:          根据yushu1的大小取字符串对应位置的字符
00401419   . 8A36         MOV DH,BYTE PTR DS:            取password第一位
0040141B   . 38F4         CMP AH,DH                           比较
0040141D   0F85 D2000000JNZ 3.004014F5                        不相等就跳向失败

这里用yushu1作为下标来到字符串中获取对应的字符。比如我这里的yushu1=3,那么就是取字符串中下标为3的那个字符,也就是“T

”(字符串的下标从0开始)。

00401423   . 80EE 41      SUB DH,41                           注册码第一位ASCII码值减去0x41
00401426   . 8AF2         MOV DH,DL
00401428   . B4 00          MOV AH,0
0040142A   . A2 4E304000    MOV BYTE PTR DS:,AL         
0040142F   . 33C0         XOR EAX,EAX
00401431   . A0 4E304000    MOV AL,BYTE PTR DS:         
00401436   . 02C2         ADD AL,DL                           yushu1 = yushu1*2
00401438   . 3C 18          CMP AL,18
0040143A   . 76 02          JBE SHORT 3.0040143E                  同0x18比较
0040143C   . 2C 18          SUB AL,18
0040143E   > B9 02000000    MOV ECX,2
00401443   . 8A2438         MOV AH,BYTE PTR DS:          根据新的yushu1来取字符串中的字符
00401446   . 8A3431         MOV DH,BYTE PTR DS:          取password第三位
00401449   . 38F4         CMP AH,DH                           比较
0040144B   0F85 A4000000JNZ 3.004014F5                        不相等就跳向失败
00401451   . EB 24          JMP SHORT 3.00401477

这里将第一次的yushu1乘以2以后,有了新的下标,去取字符串中的对应字符,作为password的第三位。(之前我们已经知道了

password第二位为E)


00401453   > A2 4E304000    MOV BYTE PTR DS:,AL          新的yushu1送
00401458   . 33C0         XOR EAX,EAX
0040145A   . A0 4E304000    MOV AL,BYTE PTR DS:
0040145F   . 80EE 41      SUB DH,41                            当前位置的password字符的ASCII码值减去0x41,记为A1
00401462   . 8AD6         MOV DL,DH                           
00401464   . 41             INC ECX                              循环次数+1
00401465   . 02C2         ADD AL,DL                            yushu1 = yushu1 + A1
00401467   . 3C 18          CMP AL,18                            同18比较
00401469   . 76 02          JBE SHORT 3.0040146D
0040146B   . 2C 18          SUB AL,18
0040146D   > 8A2438         MOV AH,BYTE PTR DS:         根据新的下标取字符
00401470   . 8A3431         MOV DH,BYTE PTR DS:         依次取password第四到第九位
00401473   . 38F4         CMP AH,DH                            比较
00401475   75 7E          JNZ SHORT 3.004014F5               不相等就跳向失败
00401477   > 83F9 08      CMP ECX,8                           
0040147A   .^72 D7          JB SHORT 3.00401453                  没算完就跳回去继续算
0040147C   . C9             LEAVE
0040147D   . C2 0400      RETN 4

上面的这个循环就是将当前位置password的ASCII码值减去0x41,加上yushu1,作为下一位的下标到字符串中去取字符。这个循环算

出了password的第四到第九位。步过retn的后,我们来到这里:

004012ED   . E8 DC010000    CALL 3.004014CE                      继续跟进

004014CE/$ BE 23304000    MOV ESI,3.00403023                     
004014D3|. A1 4A304000    MOV EAX,DWORD PTR DS:            取password各位运算以后的商
004014D8|. 8A5E 09      MOV BL,BYTE PTR DS:               取password第十位
004014DB|. 38D8         CMP AL,BL                              比较
004014DD   75 16          JNZ SHORT 3.004014F5                     不相等就跳向失败
004014DF|. B8 6C304000    MOV EAX,3.0040306C                     ;great job!
004014E4|. 8BD8         MOV EBX,EAX
004014E6|. 83C3 0B      ADD EBX,0B
004014E9|. 6A 00          PUSH 0                                 ; /Style = MB_OK|MB_APPLMODAL
004014EB|. 50             PUSH EAX                                 ; |Title => "Great Job!"
004014EC|. 53             PUSH EBX                                 ; |Text => "You have completed Key Gen Me #1."
004014ED|. 6A 00          PUSH 0                                 ; |hOwner = NULL
004014EF|. E8 B4000000    CALL <JMP.&user32.MessageBoxA>         ; \MessageBoxA
004014F4|. C3             RETN

------------------------------------------------------------------------
【破解总结】这个crackme分析到这里,算法应该很清晰了,我们最后来总结一下:
1.password必须为大写字符,且长度要为10,第二位是E。
2.将name各位的ASCII码值进行累加,取和的十六进制形式的最后两位,除以0x1B以后,取余数,记为yushu1。
3.将yushu1作为下标,到字符串ZWATRQLCGHPSXYENVBJDFKMU中取对应位置的字符,作为password的第一位。
4.将yushu1*2,作为第三位的下标,去字符串中取字符。
5.取当前位置的password字符的ASCII码值,减去0x41以后,再加上yushu1,作为新的下标,依次到字符串中取字符。依次类推,分

别算出password的第四到第九位。期间如果算出新的下标大于0x18的话,减去0x18,再作为新的下标。
6.password最后一位字符为前9位字符的ASCII码值累加,除以9以后的商对应的ASCII码值的字符。
最后送上我用python编写的注册机源代码(在python 2.5+windows xp下编译通过):
import sys

zonghe1 = 0
zifuchuan = 'ZWATRQLCGHPSXYENVBJDFKMU'
zonghe2 = 0
sn = ''

regname = raw_input('Please input youre regname:')
for i in range(0, len(regname)):
    zonghe1 = zonghe1 + ord(regname)

zonghe1 = zonghe1 & 0xFF
yushu1 = zonghe1 % 24
try:
    sn = zifuchuan + 'E' + zifuchuan
    yushu1 = yushu1*2
except:
    print 'your regname is invalid!'
    print 'press any key to exit'
    raw_input()
    sys.exit()
   
for j in range(0, 6):
    yushu1 = (ord(zifuchuan)-65) + yushu1
    if yushu1 >= 24:
      yushu1 = yushu1-24
    else:
      yushu1 = yushu1
    sn = sn + zifuchuan

for k in range(0, len(sn)):
    zonghe2 = zonghe2 + ord(sn)

zonghe2 = zonghe2 / 9
print "Your register string is:%s" % (sn + chr(zonghe2))
print 'press any key to exit'
raw_input()


------------------------------------------------------------------------
【版权声明】本文原创于52pojie技术论坛, 转载请注明作者并保持文章的完整, 谢谢!

asdfslw 发表于 2009-10-13 20:39

LZ分析得不错。
我来补充一下:之所以下INT3断点程序会马上弹出失败的消息窗口,是由于程序中有很多检验----多次搜索代码段里的0xCC,如果发现代码段里某个字节为0xCC,就马上弹出失败的消息窗口。这一点仔细跟踪就会发现了。为了能够把程序断下来,并且不被检测出来,可以改下硬件断点。
顺便给出我写的注册机代码:(VC 6 下测试通过)

#include <stdio.h>
#include <string.h>

int main()
{
        char table[] = {"ZWATRQLCGHPSXYENVBJDFKMU"},
                UserName,
                Key;
        unsigned index, sum = 0;

        printf("UserName:");
        scanf("%s", UserName);
        for (unsigned i=0; i<strlen(UserName); i++)
        {
                sum += UserName;
        }
        sum &= 0xff;
        index = sum % 0x18;

        Key = table;
        Key = 'E';
        index *= 2;
        if (index > 0x18)
        {
                index -= 0x18;
        }
        Key = table;
        for (i=3; i<9; i++)
        {
                index += Key - 'A';
                if (index > 0x18)
                {
                        index -= 0x18;
                }
                Key = table;
        }

        sum = 0;
        for (i=0; i<9; i++)
        {
                sum += Key;
        }
        Key = (char)(sum/9);
        Key = '\0';
        printf("Key   :%s\n", Key);

        return 0;
}

healthy 发表于 2009-10-16 13:53

都是高手啊,华山论剑啊

Yewn 发表于 2009-10-17 14:48

IFY嘿嘿~~

热火朝天 发表于 2009-10-17 22:38

都是高人,我也来学习一下

jmzhwf 发表于 2009-10-17 23:10

本帖最后由 jmzhwf 于 2009-10-17 23:15 编辑

分析的非常好~~~~~

其实这个CM最精华的就是这一句

REPNE SCAS BYTE PTR ES:

如果在代码中发现了CC既INT 3断点

就会在ECX记录它出现的位置

如果不为0就跳向死亡
页: [1]
查看完整版本: jmzhwf一个好玩的CM 算法分析+注册机