rdsnow 发表于 2005-10-26 08:17:57

Crackme1 by qxtianlong 的算法分析

【破文作者】   rdsnow

【作者主页】   http://rdsnow.ys168.com

【 E-mail 】   [email protected]

【 作者QQ 】   83757177

【文章题目】   Crackme1 by qxtianlong 的算法分析

【软件名称】   Crackme1 by qxtianlong

【下载地址】   https://www.chinapyg.com/attachment.php?aid=378&checkid=9a1e2&download=1

【破解平台】   Microsoft Windows XP Professional --SP2

----------------------------------------------------------------------------------------------
【文章简介】

最近被Asprotect 2.11壳的Stolen Code搞的头疼,看来偶在脱壳上前途渺茫啊,只能精力集中到算法上了,发现在硬盘上竟然还有个Crackme,打开提示“难度高”,于是就跟进看看。发现这个Crackme爆破难度不大,很容易找到爆破点,看来作者是要找到真注册码,跟出算法。

程序采用了MD5(Usename)=F_En(code)的非明码比较方式。

MD5( )是标准的MD5加密算法没有变形。

F_En( )是作者自己设计的一个加密算法。

F_En( )算法中使用了固定的密钥,给算法注册机制作带来方便,因为密钥的固定,导致在任何机器上密钥初始化过程完全相同。

F_En( )算法中使用的SBox是有44个DWORD元素。

----------------------------------------------------------------------------------------------
【破解过程】

OD插件和W32DASM都没有找到字符串,看来字符加密了。

但是有错误对话框弹出,命令行下断:bp MessageBoxA返回程序领空后向上来到这里:

00401B40   .6A FF         PUSH -1
00401B42   .68 3B5A4200   PUSH Crackme1.00425A3B                   ;SE 句柄安装
00401B47   .64:A1 00000000MOV EAX,DWORD PTR FS:
00401B4D   .50            PUSH EAX
00401B4E   .64:8925 0000000>MOV DWORD PTR FS:,ESP
00401B55   .81EC D4010000   SUB ESP,1D4
00401B5B   .A1 EC204300   MOV EAX,DWORD PTR DS:
00401B60   .898424 D0010000 MOV DWORD PTR SS:,EAX
00401B67   .53            PUSH EBX
00401B68   .B0 5C         MOV AL,5C                              ;以下给密钥Key赋初值,固定密钥
00401B6A   .8BD9            MOV EBX,ECX                              ;Key共128位,分16个byte赋值
00401B6C   .B1 47         MOV CL,47
00401B6E   .884424 07       MOV BYTE PTR SS:,AL               ;byte3=0x5C
00401B72   .884424 11       MOV BYTE PTR SS:,AL            ;byte13=0x5C
00401B76   .68 04010000   PUSH 104
00401B7B   .8D8424 D4000000 LEA EAX,DWORD PTR SS:
00401B82   .884C24 09       MOV BYTE PTR SS:,CL               ;byte1=0x47
00401B86   .884C24 17       MOV BYTE PTR SS:,CL            ;byte15=0x47
00401B8A   .50            PUSH EAX
00401B8B   .8D8B C4000000   LEA ECX,DWORD PTR DS:
00401B91   .C64424 0C 35    MOV BYTE PTR SS:,35               ;byte0=0x35
00401B96   .C64424 0E 82    MOV BYTE PTR SS:,82               ;byte2=0x82
00401B9B   .C64424 10 33    MOV BYTE PTR SS:,33            ;byte4=0x33
00401BA0   .C64424 11 8C    MOV BYTE PTR SS:,8C            ;byte5=0x8C
00401BA5   .C64424 12 85    MOV BYTE PTR SS:,85            ;byte6=0x85
00401BAA   .C64424 13 77    MOV BYTE PTR SS:,77            ;byte7=0x77
00401BAF   .C64424 14 9A    MOV BYTE PTR SS:,9A            ;byte8=0x9A
00401BB4   .C64424 15 67    MOV BYTE PTR SS:,67            ;byte9=0x67
00401BB9   .C64424 16 45    MOV BYTE PTR SS:,45            ;byte10=0x45
00401BBE   .C64424 17 7A    MOV BYTE PTR SS:,7A            ;byte11=0x7A
00401BC3   .C64424 18 6D    MOV BYTE PTR SS:,6D            ;byte12=0x6D
00401BC8   .C64424 1A 16    MOV BYTE PTR SS:,16            ;byte14=0x16
00401BCD   .E8 D9C30100   CALL Crackme1.0041DFAB                   ;读取用户名"rdsnow"
00401BD2   .8D4C24 34       LEA ECX,DWORD PTR SS:
00401BD6   .E8 350D0000   CALL Crackme1.00402910                   ;MD5( )初始化

--> 00402910/$8BD1            MOV EDX,ECX
--> 00402912|.56            PUSH ESI
--> 00402913|.57            PUSH EDI
--> 00402914|.33C0            XOR EAX,EAX
--> 00402916|.8D72 5C         LEA ESI,DWORD PTR DS:
--> 00402919|.8BFE            MOV EDI,ESI
--> 0040291B|.8942 18         MOV DWORD PTR DS:,EAX
--> 0040291E|.8942 14         MOV DWORD PTR DS:,EAX
--> 00402921|.B9 10000000   MOV ECX,10
--> 00402926|.C702 1C7A4200   MOV DWORD PTR DS:,Crackme1.00427A1C
--> 0040292C|.C742 04 0123456>MOV DWORD PTR DS:,67452301    ;这里看到MD5的四个基本常数了
--> 00402933|.C742 08 89ABCDE>MOV DWORD PTR DS:,EFCDAB89
--> 0040293A|.C742 0C FEDCBA9>MOV DWORD PTR DS:,98BADCFE
--> 00402941|.C742 10 7654321>MOV DWORD PTR DS:,10325476
--> 00402948|.F3:AB         REP STOS DWORD PTR ES:
--> 0040294A|.5F            POP EDI
--> 0040294B|.C606 80         MOV BYTE PTR DS:,80
--> 0040294E|.8BC2            MOV EAX,EDX
--> 00402950|.5E            POP ESI
--> 00402951\.C3            RETN

00401BDB   .8D8424 D0000000 LEA EAX,DWORD PTR SS:
00401BE2   .C78424 E0010000>MOV DWORD PTR SS:,0
00401BED   .8D50 01         LEA EDX,DWORD PTR DS:
00401BF0   >8A08            MOV CL,BYTE PTR DS:
00401BF2   .40            INC EAX
00401BF3   .84C9            TEST CL,CL
00401BF5   .^ 75 F9         JNZ SHORT Crackme1.00401BF0
00401BF7   .2BC2            SUB EAX,EDX                              ;相减得到用户名的长度 22
00401BF9   .50            PUSH EAX
00401BFA   .8D8C24 D4000000 LEA ECX,DWORD PTR SS:
00401C01   .51            PUSH ECX
00401C02   .8D4C24 3C       LEA ECX,DWORD PTR SS:
00401C06   .E8 550D0000   CALL Crackme1.00402960                   ;转存用户名
00401C0B   .8D5424 24       LEA EDX,DWORD PTR SS:
00401C0F   .52            PUSH EDX                                 ; /MD5结果保存地址
00401C10   .8D4C24 38       LEA ECX,DWORD PTR SS:            ; |
00401C14   .E8 170E0000   CALL Crackme1.00402A30                   ; \MD5(用户名)
00401C19   .68 04010000   PUSH 104
00401C1E   .8D8424 D4000000 LEA EAX,DWORD PTR SS:
00401C25   .50            PUSH EAX
00401C26   .8D4B 74         LEA ECX,DWORD PTR DS:
00401C29   .E8 7DC30100   CALL Crackme1.0041DFAB                   ;读取假码
00401C2E   .8D8C24 D0000000 LEA ECX,DWORD PTR SS:
00401C35   .51            PUSH ECX
00401C36   .8BCB            MOV ECX,EBX
00401C38   .E8 73F7FFFF   CALL Crackme1.004013B0                   ;检查注册码的长度和格式,跟进
00401C3D   .85C0            TEST EAX,EAX
00401C3F   .74 54         JE SHORT Crackme1.00401C95               ;上面的call检查假码是否由数字和大写A-F组成
00401C41   .56            PUSH ESI
00401C42   .57            PUSH EDI
00401C43   .8D5424 1C       LEA EDX,DWORD PTR SS:
00401C47   .52            PUSH EDX
00401C48   .8D8424 DC000000 LEA EAX,DWORD PTR SS:
00401C4F   .50            PUSH EAX
00401C50   .8BCB            MOV ECX,EBX
00401C52   .E8 F9F7FFFF   CALL Crackme1.00401450                   ;将假码分成四组Sn0,Sn1,Sn2,Sn3
00401C57   .8D4C24 0C       LEA ECX,DWORD PTR SS:
00401C5B   .6A 10         PUSH 10
00401C5D   .51            PUSH ECX
00401C5E   .E8 FDF4FFFF   CALL Crackme1.00401160                   ;F_En( )初始化,跟进
00401C63   .8D5424 24       LEA EDX,DWORD PTR SS:
00401C67   .52            PUSH EDX
00401C68   .8BC2            MOV EAX,EDX
00401C6A   .50            PUSH EAX
00401C6B   .E8 F0F5FFFF   CALL Crackme1.00401260                   ;F_En(假码),跟进
00401C70   .83C4 10         ADD ESP,10
00401C73   .B9 04000000   MOV ECX,4
00401C78   .8D7C24 1C       LEA EDI,DWORD PTR SS:
00401C7C   .8D7424 2C       LEA ESI,DWORD PTR SS:
00401C80   .33D2            XOR EDX,EDX
00401C82   .F3:A7         REPE CMPS DWORD PTR ES:,DWORD PTR D>;MD5(用户名)和F_En(假码)循环比较
00401C84   .5F            POP EDI
00401C85   .5E            POP ESI
00401C86   .75 0D         JNZ SHORT Crackme1.00401C95            ;关键跳(爆点)
00401C88   .52            PUSH EDX
00401C89   .68 147A4200   PUSH Crackme1.00427A14                   ;恭喜
00401C8E   .68 087A4200   PUSH Crackme1.00427A08                   ;注册成功!
00401C93   .EB 0C         JMP SHORT Crackme1.00401CA1            ;F_En( )初始化,跟进
00401C95   >6A 10         PUSH 10
00401C97   .68 007A4200   PUSH Crackme1.00427A00
00401C9C   .68 F0794200   PUSH Crackme1.004279F0                   ;注册码错误!
00401CA1   >8BCB            MOV ECX,EBX
00401CA3   .E8 2C950100   CALL Crackme1.0041B1D4                   ;跳出对话框
00401CA8   .8D4C24 34       LEA ECX,DWORD PTR SS:
00401CAC   .C78424 E0010000>MOV DWORD PTR SS:,-1
00401CB7   .E8 34030000   CALL Crackme1.00401FF0
00401CBC   .8B8C24 D8010000 MOV ECX,DWORD PTR SS:
00401CC3   .64:890D 0000000>MOV DWORD PTR FS:,ECX
00401CCA   .8B8C24 D4010000 MOV ECX,DWORD PTR SS:
00401CD1   .5B            POP EBX
00401CD2   .E8 1AAE0000   CALL Crackme1.0040CAF1
00401CD7   .81C4 E0010000   ADD ESP,1E0
00401CDD   .C3            RETN

小结:

程序采用MD5(Usename)=F_En(Code)的非明码验证方式。

求注册码的过程应该是:code=F_De( MD5( usename ) )

看来要找出F_En( )的解密函数F_De( )了。

----------------------------------------------------------------------------------------------

跟进00401C38 CALL Crackme1.004013B0这一行,看看注册码的构成:

004013B0/$56            PUSH ESI
004013B1|.8B7424 08       MOV ESI,DWORD PTR SS:
004013B5|.8BC6            MOV EAX,ESI
004013B7|.8D50 01         LEA EDX,DWORD PTR DS:
004013BA|.8D9B 00000000   LEA EBX,DWORD PTR DS:
004013C0|>8A08            /MOV CL,BYTE PTR DS:
004013C2|.40            |INC EAX
004013C3|.84C9            |TEST CL,CL
004013C5|.^ 75 F9         \JNZ SHORT Crackme1.004013C0
004013C7|.2BC2            SUB EAX,EDX                              ;相减得到假码的长度
004013C9|.83F8 20         CMP EAX,20                               ;比较假码的长度是否是32
004013CC|.75 74         JNZ SHORT Crackme1.00401442
004013CE|.33C9            XOR ECX,ECX
004013D0|>8A0431          /MOV AL,BYTE PTR DS:
004013D3|.3C 30         |CMP AL,30
004013D5|.7C 04         |JL SHORT Crackme1.004013DB
004013D7|.3C 39         |CMP AL,39
004013D9|.7E 08         |JLE SHORT Crackme1.004013E3             ;是不是数字
004013DB|>3C 41         |CMP AL,41
004013DD|.7C 55         |JL SHORT Crackme1.00401434
004013DF|.3C 46         |CMP AL,46
004013E1|.7F 51         |JG SHORT Crackme1.00401434            ;是不是大写字母A-F
004013E3|>8A4431 01       |MOV AL,BYTE PTR DS:
004013E7|.3C 30         |CMP AL,30
004013E9|.7C 04         |JL SHORT Crackme1.004013EF
004013EB|.3C 39         |CMP AL,39
004013ED|.7E 08         |JLE SHORT Crackme1.004013F7             ;是不是数字
004013EF|>3C 41         |CMP AL,41
004013F1|.7C 36         |JL SHORT Crackme1.00401429
004013F3|.3C 46         |CMP AL,46
004013F5|.7F 32         |JG SHORT Crackme1.00401429            ;是不是大写字母A-F
004013F7|>8A4431 02       |MOV AL,BYTE PTR DS:
004013FB|.3C 30         |CMP AL,30
004013FD|.7C 04         |JL SHORT Crackme1.00401403
004013FF|.3C 39         |CMP AL,39
00401401|.7E 08         |JLE SHORT Crackme1.0040140B             ;是不是数字
00401403|>3C 41         |CMP AL,41
00401405|.7C 25         |JL SHORT Crackme1.0040142C
00401407|.3C 46         |CMP AL,46
00401409|.7F 21         |JG SHORT Crackme1.0040142C            ;是不是大写字母A-F
0040140B|>8A4431 03       |MOV AL,BYTE PTR DS:
0040140F|.3C 30         |CMP AL,30
00401411|.7C 04         |JL SHORT Crackme1.00401417
00401413|.3C 39         |CMP AL,39
00401415|.7E 08         |JLE SHORT Crackme1.0040141F             ;是不是数字
00401417|>3C 41         |CMP AL,41
00401419|.7C 16         |JL SHORT Crackme1.00401431
0040141B|.3C 46         |CMP AL,46
0040141D|.7F 12         |JG SHORT Crackme1.00401431            ;是不是大写字母A-F
0040141F|>83C1 04         |ADD ECX,4
00401422|.83F9 20         |CMP ECX,20                              ;每次校验4个字符
00401425|.^ 7C A9         \JL SHORT Crackme1.004013D0            ;循环检查假码
00401427|.EB 0B         JMP SHORT Crackme1.00401434
00401429|>41            INC ECX
0040142A|.EB 08         JMP SHORT Crackme1.00401434
0040142C|>83C1 02         ADD ECX,2
0040142F|.EB 03         JMP SHORT Crackme1.00401434
00401431|>83C1 03         ADD ECX,3
00401434|>83F9 20         CMP ECX,20
00401437|.75 09         JNZ SHORT Crackme1.00401442
00401439|.B8 01000000   MOV EAX,1                              ;通过检查,返回1
0040143E|.5E            POP ESI
0040143F|.C2 0400         RETN 4
00401442|>33C0            XOR EAX,EAX                              ;不通过检查,返回0
00401444|.5E            POP ESI
00401445\.C2 0400         RETN 4

小结:

注册码是有数字和大写字母A-F组成,一共32个字符

----------------------------------------------------------------------------------------------

跟进00401C5E CALL Crackme1.00401160看看SBox的初始化。

00401160/$83EC 24         SUB ESP,24
00401163|.8B4C24 2C       MOV ECX,DWORD PTR SS:
00401167|.8D41 03         LEA EAX,DWORD PTR DS:
0040116A|.99            CDQ
0040116B|.83E2 03         AND EDX,3
0040116E|.53            PUSH EBX
0040116F|.03C2            ADD EAX,EDX
00401171|.55            PUSH EBP
00401172|.C1F8 02         SAR EAX,2
00401175|.33ED            XOR EBP,EBP
00401177|.49            DEC ECX
00401178|.3BCD            CMP ECX,EBP
0040117A|.56            PUSH ESI
0040117B|.57            PUSH EDI
0040117C|.894424 10       MOV DWORD PTR SS:,EAX
00401180|.896C84 10       MOV DWORD PTR SS:,EBP
00401184|.7C 25         JL SHORT Crackme1.004011AB
00401186|.8B7424 38       MOV ESI,DWORD PTR SS:
0040118A|.8D9B 00000000   LEA EBX,DWORD PTR DS:
00401190|>0FB63C31      /MOVZX EDI,BYTE PTR DS:
00401194|.8BD1            |MOV EDX,ECX
00401196|.C1EA 02         |SHR EDX,2
00401199|.8B5C94 14       |MOV EBX,DWORD PTR SS:
0040119D|.8D5494 14       |LEA EDX,DWORD PTR SS:
004011A1|.C1E3 08         |SHL EBX,8
004011A4|.03FB            |ADD EDI,EBX
004011A6|.49            |DEC ECX
004011A7|.893A            |MOV DWORD PTR DS:,EDI
004011A9|.^ 79 E5         \JNS SHORT Crackme1.00401190             ;128位Key分成Key0、Key1、Key3、Key3
004011AB|>C705 40304300 6>MOV DWORD PTR DS:,B7E15163       ;SBox
004011B5|.B9 44304300   MOV ECX,Crackme1.00433044
004011BA|.8D9B 00000000   LEA EBX,DWORD PTR DS:
004011C0|>8B51 FC         /MOV EDX,DWORD PTR DS:
004011C3|.81EA 4786C861   |SUB EDX,61C88647                        ;SBox[ i]=SBox-0x61C88647
004011C9|.8911            |MOV DWORD PTR DS:,EDX            ;保存SBox[ i]
004011CB|.83C1 04         |ADD ECX,4
004011CE|.81F9 EC304300   |CMP ECX,Crackme1.004330EC               ;从SBox填充到SBox
004011D4|.^ 7E EA         \JLE SHORT Crackme1.004011C0
004011D6|.BA 2C000000   MOV EDX,2C
004011DB|.33FF            XOR EDI,EDI
004011DD|.33C9            XOR ECX,ECX
004011DF|.33F6            XOR ESI,ESI
004011E1|.3BC2            CMP EAX,EDX
004011E3|.7E 02         JLE SHORT Crackme1.004011E7
004011E5|.8BD0            MOV EDX,EAX
004011E7|>8D0452          LEA EAX,DWORD PTR DS:
004011EA|.83F8 01         CMP EAX,1
004011ED|.7C 69         JL SHORT Crackme1.00401258
004011EF|.894424 3C       MOV DWORD PTR SS:,EAX
004011F3|>8B04BD 40304300 /MOV EAX,DWORD PTR DS:   ;取SBox[ i]
004011FA|.03C1            |ADD EAX,ECX                           ;+ 上一轮循环的结果,第一次加0
004011FC|.03F0            |ADD ESI,EAX                           ;+ SBox,第一次加0
004011FE|.8BC6            |MOV EAX,ESI
00401200|.C1E8 1D         |SHR EAX,1D
00401203|.8D14F5 00000000 |LEA EDX,DWORD PTR DS:
0040120A|.0BC2            |OR EAX,EDX                              ;交换相加结果的前3位和后29位
0040120C|.8904BD 40304300 |MOV DWORD PTR DS:,EAX   ;存回SBox[ i]
00401213|.8BF0            |MOV ESI,EAX
00401215|.8B44AC 14       |MOV EAX,DWORD PTR SS:   ;取Key(i%4)
00401219|.03C1            |ADD EAX,ECX                           ;加上上一轮循环的结果
0040121B|.8D1430          |LEA EDX,DWORD PTR DS:          ;Key+SBox[ i],这个结果准备下面的换位
0040121E|.8D1C31          |LEA EBX,DWORD PTR DS:          ;上轮结果+SBox[ i]
00401221|.8BC2            |MOV EAX,EDX
00401223|.83E3 1F         |AND EBX,1F                              ;取相加结果的后五位
00401226|.B9 20000000   |MOV ECX,20                              ;0x20-后五位=cl
0040122B|.2BCB            |SUB ECX,EBX
0040122D|.D3E8            |SHR EAX,CL
0040122F|.8BCB            |MOV ECX,EBX
00401231|.D3E2            |SHL EDX,CL
00401233|.0BC2            |OR EAX,EDX                              ;交换相加结果的前(32-cl)位和后cl位
00401235|.8944AC 14       |MOV DWORD PTR SS:,EAX   ;将结果保存回去
00401239|.8BC8            |MOV ECX,EAX
0040123B|.8D47 01         |LEA EAX,DWORD PTR DS:
0040123E|.99            |CDQ
0040123F|.BF 2C000000   |MOV EDI,2C
00401244|.F7FF            |IDIV EDI
00401246|.8D45 01         |LEA EAX,DWORD PTR SS:
00401249|.8BFA            |MOV EDI,EDX
0040124B|.99            |CDQ
0040124C|.F77C24 10       |IDIV DWORD PTR SS:
00401250|.FF4C24 3C       |DEC DWORD PTR SS:
00401254|.8BEA            |MOV EBP,EDX
00401256|.^ 75 9B         \JNZ SHORT Crackme1.004011F3
00401258|>5F            POP EDI
00401259|.5E            POP ESI
0040125A|.5D            POP EBP
0040125B|.5B            POP EBX
0040125C|.83C4 24         ADD ESP,24
0040125F\.C3            RETN

小结:

因为作者采用的是固定的Key,没有把Key跟电脑的硬件关联,所以SBox的初始化在任何电脑上都一样,所以这个过程就不铺开了。看注释吧。

初始化后内存中的SBox

00433040 -->63D4757A    EE7A3CB4    6D00027A    8831B70F
00433050 -->7728D975    E7BA4156    DC92BFAD    CB17967B
00433060 -->1F8B0D7A    8E0B0D4F    431C9651    22293C7F
00433070 -->0D243845    C23C7BA6    4CAD232E    B35D36FF
00433080 -->551BC015    0BF3CF67    DDAF2D46    A72AA80D
00433090 -->EC1C33D4    896B736B    189D7F20    E46D20BE
004330A0 -->C4DDD5CC    3DBBA1A7    8A692DB8    EEB598C4
004330B0 -->0AA88B0C    F506A11A    ADC68F57    1739E928
004330C0 -->577803DA    958F416A    DF1C7CA7    0BBAD3FE
004330D0 -->AB6DB39C    B8048075    92628ABB    5CEB9507
004330E0 -->64CCA67C    D00B10A6    D4181073    9E147749

----------------------------------------------------------------------------------------------

最后当然跟进00401C6B CALL Crackme1.00401260这一行了。这是整个Crackme最核心的部分。

00401260/$83EC 08         SUB ESP,8
00401263|.8B4424 0C       MOV EAX,DWORD PTR SS:
00401267|.8B50 08         MOV EDX,DWORD PTR DS:             ;Sn2
0040126A|.8B08            MOV ECX,DWORD PTR DS:               ;Sn0
0040126C|.53            PUSH EBX
0040126D|.8B58 04         MOV EBX,DWORD PTR DS:             ;Sn1
00401270|.55            PUSH EBP
00401271|.8B68 0C         MOV EBP,DWORD PTR DS:             ;Sn3
00401274|.A1 44304300   MOV EAX,DWORD PTR DS:            ;SBox
00401279|.895424 14       MOV DWORD PTR SS:,EDX
0040127D|.8B15 40304300   MOV EDX,DWORD PTR DS:            ;SBox
00401283|.56            PUSH ESI
00401284|.03DA            ADD EBX,EDX                              ;Sn1=Sn1+SBox
00401286|.57            PUSH EDI
00401287|.03E8            ADD EBP,EAX                              ;Sn2=Sn3+SBox
00401289|.C74424 10 4C304>MOV DWORD PTR SS:,Crackme1.00433>
00401291|.EB 04         JMP SHORT Crackme1.00401297
00401293|>8B4C24 14       /MOV ECX,DWORD PTR SS:
00401297|>8D441B 01      LEA EAX,DWORD PTR DS:
0040129B|.0FAFC3          |IMUL EAX,EBX                            ;D1=(Sn1*2+1)*Sn1
0040129E|.8BD0            |MOV EDX,EAX
004012A0|.C1E0 05         |SHL EAX,5
004012A3|.C1EA 1B         |SHR EDX,1B
004012A6|.0BD0            |OR EDX,EAX                              ;交换D1的前5位和后27位
004012A8|.8D442D 01       |LEA EAX,DWORD PTR SS:
004012AC|.0FAFC5          |IMUL EAX,EBP                            ;D3=(Sn3*2+1)*Sn3
004012AF|.8BF0            |MOV ESI,EAX
004012B1|.C1E0 05         |SHL EAX,5
004012B4|.C1EE 1B         |SHR ESI,1B
004012B7|.0BF0            |OR ESI,EAX                              ;交换D3的前5位和后27位
004012B9|.8BC6            |MOV EAX,ESI
004012BB|.83E0 1F         |AND EAX,1F                              ;a=D3的后5位
004012BE|.8BFA            |MOV EDI,EDX
004012C0|.33F9            |XOR EDI,ECX                           ;D1=D1^Sn0 (Sn0是上轮循环结果中未被处理的Sn0)
004012C2|.894424 14       |MOV DWORD PTR SS:,EAX
004012C6|.B9 20000000   |MOV ECX,20
004012CB|.2BC8            |SUB ECX,EAX
004012CD|.8BC7            |MOV EAX,EDI
004012CF|.D3E8            |SHR EAX,CL
004012D1|.8B4C24 14       |MOV ECX,DWORD PTR SS:
004012D5|.D3E7            |SHL EDI,CL
004012D7|.895C24 14       |MOV DWORD PTR SS:,EBX         ;保存:Sn0=Sn1
004012DB|.83E2 1F         |AND EDX,1F                              ;b=D1的后5位
004012DE|.0BC7            |OR EAX,EDI                              ;交换D3的前a位和后(32-a)位
004012E0|.8B7C24 10       |MOV EDI,DWORD PTR SS:
004012E4|.0347 FC         |ADD EAX,DWORD PTR DS:            ;保存:Sn3=D1+SBox
004012E7|.337424 1C       |XOR ESI,DWORD PTR SS:         ;D3=D3^Sn2 (Sn2是上轮循环结果中未被处理的Sn2)
004012EB|.B9 20000000   |MOV ECX,20
004012F0|.2BCA            |SUB ECX,EDX
004012F2|.8BDE            |MOV EBX,ESI
004012F4|.D3EB            |SHR EBX,CL
004012F6|.8BCA            |MOV ECX,EDX
004012F8|.D3E6            |SHL ESI,CL
004012FA|.83C7 08         |ADD EDI,8
004012FD|.896C24 1C       |MOV DWORD PTR SS:,EBP         ;保存:Sn2=Sn3(未保存前的Sn3)
00401301|.8BE8            |MOV EBP,EAX
00401303|.0BDE            |OR EBX,ESI                              ;交换D3的前b位和后(32-b)位
00401305|.8B77 F8         |MOV ESI,DWORD PTR DS:
00401308|.03DE            |ADD EBX,ESI                           ;保存:Sn1=D3+SBox
0040130A|.81FF E4304300   |CMP EDI,Crackme1.004330E4
00401310|.897C24 10       |MOV DWORD PTR SS:,EDI         ;循环20轮
00401314|.^ 0F8E 79FFFFFF   \JLE Crackme1.00401293
0040131A|.8B0D EC304300   MOV ECX,DWORD PTR DS:            ;取SBox
00401320|.8B5424 1C       MOV EDX,DWORD PTR SS:            ;取出Sn2
00401324|.8B7424 14       MOV ESI,DWORD PTR SS:            ;取出Sn0
00401328|.03D1            ADD EDX,ECX                              ;Sn2+SBox
0040132A|.8B0D E8304300   MOV ECX,DWORD PTR DS:            ;取出SBox
00401330|.03F1            ADD ESI,ECX                              ;Sn0+SBox
00401332|.8B4C24 20       MOV ECX,DWORD PTR SS:
00401336|.5F            POP EDI
00401337|.8931            MOV DWORD PTR DS:,ESI               ;保存Sn0
00401339|.5E            POP ESI
0040133A|.5D            POP EBP
0040133B|.8959 04         MOV DWORD PTR DS:,EBX             ;保存Sn1
0040133E|.8951 08         MOV DWORD PTR DS:,EDX             ;保存Sn2
00401341|.8941 0C         MOV DWORD PTR DS:,EAX             ;保存Sn3
00401344|.5B            POP EBX
00401345|.83C4 08         ADD ESP,8
00401348\.C3            RETN

小结:

算法中用到一个函数f( )

f(a)就是 (a*2+1)*a,然后将32位结果的前5位和后27位换位。

上面的注释说得不够清楚,用图表示吧:

加密过程:

将待加密的128数据分成四组:Sn0、Sn1、Sn2、Sn3

_____Sn0__________Sn1__________Sn2__________Sn3_____
                   |                         |
                +SBox                  +SBox
                   |                         |
_____Sn0__________Sn1__________Sn2__________Sn3_____ 简单处理后进入循环(循环开始)
      |         /|            |         /|
      |      / f( )         |      / f( )
      |       /    |            |       /    |
      |______/____Xor         |______/____Xor
            /      |                  /      |
         /      换位a            /      换位b
          /      |                /      |
         /      +SBox         /      +SBox
      /          |______________/___       |
       /                         /   |       |
      |            _____________|____|_______|
      |            |            |    |________
      |            |            |            |
_____Sn0__________Sn1__________Sn2__________Sn3_____ 第一轮循环的结果
      |         /|            |         /|
      |      / f( )         |      / f( )
      |       /    |            |       /    |
      |______/____Xor         |______/____Xor
            /      |                  /      |
         /      换位a            /      换位b
          /      |                /      |
         /      +SBox         /      +SBox
      /          |______________/___       |
       /                         /   |       |
      |            _____________|____|_______|
      |            |            |    |________
      |            |            |            |
_____Sn0__________Sn1__________Sn2__________Sn3_____ 第二轮循环的结果

……………………………………………………………………

_____Sn0__________Sn1__________Sn2__________Sn3_____ 第十九轮循环的结果
      |         /|            |         /|
      |      / f( )         |      / f( )
      |       /    |            |       /    |
      |______/____Xor         |______/____Xor
            /      |                  /      |
         /      换位a            /      换位b
          /      |                /      |
         /      +SBox          /      +SBox
      /          |______________/___       |
       /                         /   |       |
      |            _____________|____|_______|
      |            |            |    |________
      |            |            |            |
_____Sn0__________Sn1__________Sn2__________Sn3_____ 第二十轮循环的结果(循环结束)
      |                         |
   +SBox               +SBox
      |                         |
_____Sn0__________Sn1__________Sn2__________Sn3_____ 最终结果

说明:

F_En( )的处理过程分20轮循环完成。

每一轮循环中都有换位,对上表中的换位做一个说明:

换位a:就是将32位结果的前a位和后(32-a)位换位,a取每一轮中f(Sn3)的后五位

换位b:就是将32位结果的前b位和后(32-b)位换位,b取每一轮中f(Sn1)的后五位

----------------------------------------------------------------------------------------------

最后分析一下 F_En( ) 的逆过程:

解密过程:

_____Sn0__________Sn1__________Sn2__________Sn3_____
      |                         |
   -SBox               -SBox
      |                         |
_____Sn0__________Sn1__________Sn2__________Sn3_____ 简单处理后进入循环(循环开始)
      | \          |____________|_\________|
      |\         _____________|__\______|__|
      |   \      |            |   \   |___
      |    \       |            |    \       |
    f( )    \   -SBox   f( )    \   -SBox
      |   |      |            |   |      |
      |   |   换位c         |   |   换位d
      |   |      |            |   |      |
   XOR____|______|         XOR____|______|
      |   |_______            |   |_______
      |            |            |            |
_____Sn0__________Sn1__________Sn2__________Sn3_____ 第一轮循环的结果
      | \          |____________|_\________|
      |\         _____________|__\______|__|
      |   \      |            |   \   |___
      |    \       |            |    \       |
    f( )    \   -SBox   f( )    \   -SBox
      |   |      |            |   |      |
      |   |   换位c         |   |   换位d
      |   |      |            |   |      |
   XOR____|______|         XOR____|______|
      |   |_______            |   |_______
      |            |            |            |
_____Sn0__________Sn1__________Sn2__________Sn3_____ 第二轮循环的结果

……………………………………………………………………

_____Sn0__________Sn1__________Sn2__________Sn3_____ 第十九轮循环的结果
      | \          |____________|_\________|
      |\         _____________|__\______|__|
      |   \      |            |   \   |___
      |    \       |            |    \       |
    f( )    \   -SBox      f( )    \   -SBox
      |   |      |            |   |      |
      |   |   换位c         |   |   换位d
      |   |      |            |   |      |
   XOR____|______|         XOR____|______|
      |   |_______            |   |_______
      |            |            |            |
_____Sn0__________Sn1__________Sn2__________Sn3_____ 第二十轮循环的结果
                   |                         |
                -SBox                  -SBox
                   |                         |
_____Sn0__________Sn1__________Sn2__________Sn3_____ 最终结果

F_De( )也分20轮循环完成:

每一轮循环中都有换位,对上表中的换位做一个说明:

换位c:就是将32位结果的前c位和后(32-c)位换位,a取每一轮中f(Sn2)的后五位

换位d:就是将32位结果的前d位和后(32-d)位换位,b取每一轮中f(Sn0)的后五位

----------------------------------------------------------------------------------------------
【注册机源码】

Microsoft Visual C++ 6.0 编写的MFC,变量的命名跟上面注释相同,不过代码写得太烂了,大家不要笑我。

/////////////////////////////////////////////////////////////////
//
//FileName        :   CrackMe1_qxtianlongDlg.cpp
//Author        :   rdsnow
//Date        :   2005-10-25
//Comment        :   keygen for crackme1 by qxtianlong(关键代码)
//
/////////////////////////////////////////////////////////////////

void CCrackMe1_qxtianlongDlg::OnOK()
{
        // TODO: Add extra validation here
       
        //CDialog::OnOK();

        DWORD Sn0,Sn1,Sn2,Sn3;
        unsigned char inbuff,outbuff;
        int i;
       
        UpdateData(true);
       
        //对UseName进行MD5编码,这里使用不同的MD5类,代码也不相同
        i=m_Edit1.GetLength ();
        memcpy(inbuff,m_Edit1,i+1);
        MD5_CTX context;
        context.MD5Update (inbuff,i);
        context.MD5Final (outbuff);

        //将MD5的结果分成四个DWORD
        Sn0=outbuff+(outbuff<<8)+(outbuff<<16)+(outbuff<<24);
        Sn1=outbuff+(outbuff<<8)+(outbuff<<16)+(outbuff<<24);
        Sn2=outbuff+(outbuff<<8)+(outbuff<<16)+(outbuff<<24);
        Sn3=outbuff+(outbuff<<8)+(outbuff<<16)+(outbuff<<24);

        //用四个分组解密
        m_Edit2=F_De(Sn0,Sn1,Sn2,Sn3);
       
        //输出结果
        UpdateData(false);       
       
}


DWORD CCrackMe1_qxtianlongDlg::Exchange(DWORD Sn, int i)
{       
        //将32位的Sn的前i位和后(32-i)位交换

        DWORD TempData=Sn;
        Sn=Sn << i;
        i=32-i;
        TempData=TempData>>i;
        Sn |=TempData;

        return Sn;
}

DWORD CCrackMe1_qxtianlongDlg::Function1(DWORD Sn)
{
        //算法中用到的一个函数,即f( )

        //Sn=(Sn×2+1)×Sn,然后交换结果的前5位和后27位

        Sn=(Sn*2+1)*Sn;
        Sn=Exchange(Sn,5);

        return Sn;
}

CString CCrackMe1_qxtianlongDlg::F_De(DWORD Sn0, DWORD Sn1, DWORD Sn2, DWORD Sn3)
{

        //解密函数

        CString Code="",TempChar;

        //因为作者使用的是固定的key,所以偶偷懒
        //直接将SBox复制过来了,省掉SBox初始化编程

        DWORD SBox={
                0x63D4757A , 0xEE7A3CB4 , 0x6D00027A , 0x8831B70F,
                0x7728D975 , 0xE7BA4156 , 0xDC92BFAD , 0xCB17967B,
                0x1F8B0D7A , 0x8E0B0D4F , 0x431C9651 , 0x22293C7F,
                0x0D243845 , 0xC23C7BA6 , 0x4CAD232E , 0xB35D36FF,
                0x551BC015 , 0x0BF3CF67 , 0xDDAF2D46 , 0xA72AA80D,
                0xEC1C33D4 , 0x896B736B , 0x189D7F20 , 0xE46D20BE,
                0xC4DDD5CC , 0x3DBBA1A7 , 0x8A692DB8 , 0xEEB598C4,
                0x0AA88B0C , 0xF506A11A , 0xADC68F57 , 0x1739E928,
                0x577803DA , 0x958F416A , 0xDF1C7CA7 , 0x0BBAD3FE,
                0xAB6DB39C , 0xB8048075 , 0x92628ABB , 0x5CEB9507,
                0x64CCA67C , 0xD00B10A6 , 0xD4181073 , 0x9E147749
        };
       
        DWORD TempData1,TempData2;
        int i;

        Sn0-=SBox;
        Sn2-=SBox;

        for (i=0;i<20;i++){

                TempData1=Sn0;
                TempData2=Sn2;

                Sn0=Function1(Sn0);
                Sn2=Function1(Sn2);

                Sn1-=SBox;
                Sn3-=SBox;

                Sn3=Exchange(Sn3,32-(Sn2 & 0x1F));
                Sn1=Exchange(Sn1,32-(Sn0 & 0x1F));

                Sn0 ^= Sn3;
                Sn2 ^= Sn1;

                Sn1=TempData1;
                Sn3=TempData2;

        }
       
        Sn1-=SBox;
        Sn3-=SBox;

        TempChar.Format ("%02X%02X%02X%02X",Sn0&0xFF,Sn0>>8&0xFF,Sn0>>16&0xFF,Sn0>>24&0xFF);
        Code+=TempChar;
        TempChar.Format ("%02X%02X%02X%02X",Sn1&0xFF,Sn1>>8&0xFF,Sn1>>16&0xFF,Sn1>>24&0xFF);
        Code+=TempChar;
        TempChar.Format ("%02X%02X%02X%02X",Sn2&0xFF,Sn2>>8&0xFF,Sn2>>16&0xFF,Sn2>>24&0xFF);
        Code+=TempChar;
        TempChar.Format ("%02X%02X%02X%02X",Sn3&0xFF,Sn3>>8&0xFF,Sn3>>16&0xFF,Sn3>>24&0xFF);
        Code+=TempChar;
       
        return Code;       

}

----------------------------------------------------------------------------------------------
【破解心得】

这是个对称算法,加密和解密使用同一密钥,建议作者密钥跟机器码关联,不要使用固定的密钥。

现在的软件更加倾向于使用强壳阻止程序不被调试,所以要向工作在脱壳第一线的大侠致敬,现在感觉到脱壳真是一件辛苦的事情。

附件:

Crackme1.exe --> crackme1 by qxtianlong

keygen.exe --> 算法注册机

Crackme1 by qxtianlong.txt --> 破文,如果由于流程图显示错位,看txt文档吧!

----------------------------------------------------------------------------------------------
【破解声明】   我是一只小菜鸟,偶得一点心得,愿与大家分享:)

【版权声明】   本文纯属技术交流, 转载请注明作者并保持文章的完整, 谢谢!
----------------------------------------------------------------------------------------------
                                                                     文章写于2005-10-25 22:56:34

[ Last edited by rdsnow on 2005-10-26 at 08:20 AM ]

rdsnow 发表于 2005-10-26 08:21:52

qxtianlong 兄到这里做版主了,gxgx!!!

qxtianlong 发表于 2005-10-26 11:10:15

强,分析的这么透彻,一个Crackme1.0没有要关联机器码,当然在软件中肯定不会这么简单的,要加强壳的!!rdsnow!佩服佩服
我这个斑竹是临时的!!不过谢谢了!!
在职一天就做好自己的事!呵呵~

xingbing 发表于 2005-10-26 19:54:48

分析的真详细,学习。

pentacle 发表于 2005-11-2 13:33:17

牛人啊~~~
看的心里痒痒的~~~
什么时候偶也能到达这个小平啊~~~

underghost 发表于 2005-11-4 18:46:34

详细……无语……

ihhvqu 发表于 2005-11-5 10:47:45

同意5楼的看法.....
页: [1]
查看完整版本: Crackme1 by qxtianlong 的算法分析