首页
社区
课程
招聘
[原创]WINDRAND的一个密码学CrackMe的分析
发表于: 2012-5-30 13:22 9531

[原创]WINDRAND的一个密码学CrackMe的分析

2012-5-30 13:22
9531

附件里包含CrackMe本身,CrackMe的分析文章(WPS个人版创建),IDB(IDA5.0 Free创建)和keygen的源代码+bin。
WPS个人版和IDA 5.0 Free(只限非商业应用)都是免费的,希望大家都支持正版软件


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

上传的附件:
收藏
免费 6
支持
分享
最新回复 (7)
雪    币: 316
活跃值: (128)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
2
站位膜拜CamelLu!!!!!!!
2012-5-30 16:15
0
雪    币: 391
活跃值: (135)
能力值: ( LV2,RANK:140 )
在线值:
发帖
回帖
粉丝
3
这位哥哥太抬举小弟了,受不起啊,小弟分析这个CrackMe比你分析的简单很多。另,你都还没有下载我的文章啊
2012-5-30 16:24
0
雪    币: 1024
活跃值: (240)
能力值: ( LV12,RANK:310 )
在线值:
发帖
回帖
粉丝
4
   123456
2012-5-30 16:32
0
雪    币: 391
活跃值: (135)
能力值: ( LV2,RANK:140 )
在线值:
发帖
回帖
粉丝
5
为了方便大家浏览,把分析文档的内容贴出来
这个CrackMe是看雪论坛网友windrand发布在论坛的多个密码学CrackMe之一,windrand把它命名为TeaCrackMe,但在后面的分析中我们可以看到,这个CrackMe使用的算法其实是XTEA(又名TEAN)。
首先我们把这个CrackMe直接运行起来,然后顺便输入一个用户名和注册码看看有什么反应,结果如图一

(图一)
接下来用OD载入它并运行,对GetDlgItemTextA下断,然后再次随便输入一个用户名和密码,点注册认证,断下来后Alt+F9返回看看
0040A1ED    .  68 00010000   push    100                              ; /Count = 100 (256.)
0040A1F2    .  51            push    ecx                              ; |Buffer
0040A1F3    .  68 E8030000   push    3E8                              ; |ControlID = 3E8 (1000.)
0040A1F8    .  56            push    esi                              ; |hWnd
0040A1F9    .  FFD3          call    ebx                              ; \GetDlgItemTextA
0040A1FB    .  8DBD F0FEFFFF lea     edi, dword ptr [ebp-110]
[COLOR="Red"]0040A201    .  83C9 FF       or      ecx, FFFFFFFF
0040A204    .  33C0          xor     eax, eax
0040A206    .  F2:AE         repne   scas byte ptr es:[edi]
0040A208    .  F7D1          not     ecx
0040A20A    .  49            dec     ecx[/COLOR]
0040A20B    .  83F9 01       cmp     ecx, 1
0040A20E    .  73 26         jnb     short 0040A236
0040A210    .  6A 40         push    40                               ; /Style = MB_OK|MB_ICONASTERISK|MB_APPLMODAL
0040A212    .  68 A4B44200   push    0042B4A4                         ; |Title = ""D7,"",A2,"",B2,"崽崾?
0040A217    .  68 8CB44200   push    0042B48C                         ; |Text = "用",BB,"?,B2,"",BB,"能为空请输入!"
0040A21C    .  56            push    esi                              ; |hOwner
0040A21D    .  FF15 04514200 call    dword ptr [<&USER32.MessageBoxA>>; \MessageBoxA
0040A223    .  33C0          xor     eax, eax
0040A225    .  8B4D F4       mov     ecx, dword ptr [ebp-C]
0040A228    .  64:890D 00000>mov     dword ptr fs:[0], ecx
0040A22F    .  5F            pop     edi
0040A230    .  5E            pop     esi
0040A231    .  5B            pop     ebx
0040A232    .  8BE5          mov     esp, ebp
0040A234    .  5D            pop     ebp
0040A235    .  C3            retn
0040A236    >  8D95 F0FDFFFF lea     edx, dword ptr [ebp-210]
0040A23C    .  68 00010000   push    100
0040A241    .  52            push    edx
0040A242    .  68 07040000   push    407
0040A247    .  56            push    esi
0040A248    .  FFD3          call    ebx
0040A24A    .  8DBD F0FDFFFF lea     edi, dword ptr [ebp-210]
[COLOR="Red"]0040A250    .  83C9 FF       or      ecx, FFFFFFFF
0040A253    .  33C0          xor     eax, eax
0040A255    .  F2:AE         repne   scas byte ptr es:[edi]
0040A257    .  F7D1          not     ecx
0040A259    .  49            dec     ecx[/COLOR]
0040A25A    .  83F9 01       cmp     ecx, 1
0040A25D    .  73 26         jnb     short 0040A285
0040A25F    .  6A 40         push    40                               ; /Style = MB_OK|MB_ICONASTERISK|MB_APPLMODAL
0040A261    .  68 A4B44200   push    0042B4A4                         ; |Title = ""D7,"",A2,"",B2,"崽崾?
0040A266    .  68 74B44200   push    0042B474                         ; |Text = ""D7,"",A2,"",B2,"崧?,B2,"",BB,"能为空请输入!"
0040A26B    .  56            push    esi                              ; |hOwner
0040A26C    .  FF15 04514200 call    dword ptr [<&USER32.MessageBoxA>>; \MessageBoxA
0040A272    .  33C0          xor     eax, eax
0040A274    .  8B4D F4       mov     ecx, dword ptr [ebp-C]
0040A277    .  64:890D 00000>mov     dword ptr fs:[0], ecx
0040A27E    .  5F            pop     edi
0040A27F    .  5E            pop     esi
0040A280    .  5B            pop     ebx
0040A281    .  8BE5          mov     esp, ebp
0040A283    .  5D            pop     ebp
0040A284    .  C3            retn

上面代码中红色的部分是很经典的汇编版strlen函数,执行完毕之后ecx的值为edi所指向的C字符串的长度。我们可以看到,只有当用户名和注册码都不为空的时候,程序才会跳到0x40A285处继续执行,否则会弹出提示框并不再往下执行。
我们到0x40A285处继续往下看
0040A285    > \8D85 F0FCFFFF lea     eax, dword ptr [ebp-310]
0040A28B    .  6A 00         push    0
0040A28D    .  50            push    eax
0040A28E    .  8D8D F0FDFFFF lea     ecx, dword ptr [ebp-210]
0040A294    .  68 68B44200   push    0042B468                         ;  ASCII "WindRand"
0040A299    .  51            push    ecx
0040A29A    .  C745 FC 00000>mov     dword ptr [ebp-4], 0
0040A2A1    .  E8 FAFCFFFF   call    00409FA0
0040A2A6    .  83C4 10       add     esp, 10
0040A2A9    .  8D95 F0FCFFFF lea     edx, dword ptr [ebp-310]
0040A2AF    .  8D85 F0FEFFFF lea     eax, dword ptr [ebp-110]
0040A2B5    .  52            push    edx
0040A2B6    .  50            push    eax
0040A2B7    .  E8 A4FDFFFF   call    0040A060
0040A2BC    .  83C4 08       add     esp, 8
[COLOR="red"]0040A2BF    .  83F8 01       cmp     eax, 1[/COLOR]
0040A2C2    .  6A 40         push    40                               ; /Style = MB_OK|MB_ICONASTERISK|MB_APPLMODAL
0040A2C4    .  68 A4B44200   push    0042B4A4                         ; |Title = ""D7,"",A2,"",B2,"崽崾?
[COLOR="Red"]0040A2C9    .  75 1F         jnz     short 0040A2EA                   ; |[/COLOR]
0040A2CB    .  68 54B44200   push    0042B454                         ; |Text = "恭?,B2,"你?,AC,"",D7,"",A2,"",B2,"崧胝",B7,"!"
0040A2D0    .  56            push    esi                              ; |hOwner
0040A2D1    .  FF15 04514200 call    dword ptr [<&USER32.MessageBoxA>>; \MessageBoxA
0040A2D7    .  33C0          xor     eax, eax
0040A2D9    .  8B4D F4       mov     ecx, dword ptr [ebp-C]
0040A2DC    .  64:890D 00000>mov     dword ptr fs:[0], ecx
0040A2E3    .  5F            pop     edi
0040A2E4    .  5E            pop     esi
0040A2E5    .  5B            pop     ebx
0040A2E6    .  8BE5          mov     esp, ebp
0040A2E8    .  5D            pop     ebp
0040A2E9    .  C3            retn
0040A2EA    > \68 3CB44200   push    0042B43C                         ; |Text = ""D7,"",A2,"",B2,"崧?,B4,"砦螅",AC,"继续加油!"
0040A2EF    .  56            push    esi                              ; |hOwner
0040A2F0    .  FF15 04514200 call    dword ptr [<&USER32.MessageBoxA>>; \MessageBoxA
0040A2F6    .  33C0          xor     eax, eax
0040A2F8    .  8B4D F4       mov     ecx, dword ptr [ebp-C]
0040A2FB    .  64:890D 00000>mov     dword ptr fs:[0], ecx
0040A302    .  5F            pop     edi
0040A303    .  5E            pop     esi
0040A304    .  5B            pop     ebx
0040A305    .  8BE5          mov     esp, ebp
0040A307    .  5D            pop     ebp
0040A308    .  C3            retn

很明显0x40A2C9处的JNZ指令就是关键跳,而在这个跳转上面,程序只调用了两个函数地址分别为0x409FA0和0x40A060,说明这两个函数都很重要,我们一个一个来仔细看。
跟进函数0x409FA0到0x409FE0处的一个call,发现存放注册码的内存地址被作为参数传递进去了,见图二

(图二)
这里一定是在对注册码进行什么操作,跟进去看一下
00409E40   /$  53            push    ebx
00409E41   |.  55            push    ebp
00409E42   |.  8B6C24 14     mov     ebp, dword ptr [esp+14]          ;  取第三个参数,也就是注册码长度的一半
00409E46   |.  56            push    esi
00409E47   |.  33F6          xor     esi, esi
00409E49   |.  57            push    edi
00409E4A   |.  85ED          test    ebp, ebp
00409E4C   |.  7E 29         jle     short 00409E77                   ;  判断第三个参数是否小于等于0,是则跳走
00409E4E   |.  8B7C24 18     mov     edi, dword ptr [esp+18]
00409E52   |.  8B5C24 14     mov     ebx, dword ptr [esp+14]
00409E56   |>  8D4424 1C     /lea     eax, dword ptr [esp+1C]
00409E5A   |.  50            |push    eax
00409E5B   |.  53            |push    ebx
00409E5C   |.  E8 7FFFFFFF   |call    00409DE0                        ;  需要跟进查看
00409E61   |.  83C4 08       |add     esp, 8
00409E64   |.  84C0          |test    al, al
00409E66   |.  74 16         |je      short 00409E7E
00409E68   |.  8A4C24 1C     |mov     cl, byte ptr [esp+1C]
00409E6C   |.  46            |inc     esi
00409E6D   |.  880F          |mov     byte ptr [edi], cl
00409E6F   |.  83C3 02       |add     ebx, 2
00409E72   |.  47            |inc     edi
00409E73   |.  3BF5          |cmp     esi, ebp
00409E75   |.^ 7C DF         \jl      short 00409E56
00409E77   |>  5F            pop     edi
00409E78   |.  5E            pop     esi
00409E79   |.  5D            pop     ebp
00409E7A   |.  B0 01         mov     al, 1
00409E7C   |.  5B            pop     ebx
00409E7D   |.  C3            retn
继续跟进到函数0x409DE0里查看
00409DE0   /$  8B5424 04     mov     edx, dword ptr [esp+4]     ;  取注册码的地址
00409DE4   |.  8A02          mov     al, byte ptr [edx]         ;  取第一位字符
00409DE6   |.  3C 30         cmp     al, 30
00409DE8   |.  7C 08         jl      short 00409DF2
00409DEA   |.  3C 39         cmp     al, 39
00409DEC   |.  7F 04         jg      short 00409DF2
00409DEE   |.  2C 30         sub     al, 30                     ;  如果字符是'0'-'9'之一,则把它们的ascii码减去0x30
00409DF0   |.  EB 0A         jmp     short 00409DFC
00409DF2   |>  3C 41         cmp     al, 41
00409DF4   |.  7C 45         jl      short 00409E3B
00409DF6   |.  3C 46         cmp     al, 46
00409DF8   |.  7F 41         jg      short 00409E3B
00409DFA   |.  2C 37         sub     al, 37                     ;  如果字符是'A'-'F'之一,则把它们的ascii码减去0x37
00409DFC   |>  8B4C24 08     mov     ecx, dword ptr [esp+8]     ;  ecx=第二个参数
00409E00   |.  42            inc     edx                        ;  edx=edx+1
00409E01   |.  8801          mov     byte ptr [ecx], al         ;  将转好好的第一位16进制数存入第二个参数指定的地址中
00409E03   |.  8A02          mov     al, byte ptr [edx]         ;  取第二位字符
00409E05   |.  3C 30         cmp     al, 30
00409E07   |.  7C 17         jl      short 00409E20
00409E09   |.  3C 39         cmp     al, 39
00409E0B   |.  7F 13         jg      short 00409E20
00409E0D   |.  8A01          mov     al, byte ptr [ecx]         ;  如果第二位字符是'0'-'9'之一则来到这里
00409E0F   |.  C0E0 04       shl     al, 4
00409E12   |.  8801          mov     byte ptr [ecx], al         ;  将刚才存放的第一个16进制数左移到高四位
00409E14   |.  8A12          mov     dl, byte ptr [edx]         ;  实际上上面这个mov语句是多余的,下面转换好之后会再存
00409E16   |.  02D0          add     dl, al                     ;  加上第二个字符的ascii码
00409E18   |.  B0 01         mov     al, 1
00409E1A   |.  80EA 30       sub     dl, 30                     ;  因为刚才是直接加了第二个字符的ascii码,所以减去0x30
00409E1D   |.  8811          mov     byte ptr [ecx], dl         ;  将转换好的16进制数存入目标地址
00409E1F   |.  C3            retn
00409E20   |>  3C 41         cmp     al, 41
00409E22   |.  7C 17         jl      short 00409E3B
00409E24   |.  3C 46         cmp     al, 46
00409E26   |.  7F 13         jg      short 00409E3B
00409E28   |.  8A01          mov     al, byte ptr [ecx]         ;  如果第二位字符是'A'-'F'之一则来到这里,否则跳走
00409E2A   |.  C0E0 04       shl     al, 4
00409E2D   |.  8801          mov     byte ptr [ecx], al
00409E2F   |.  8A12          mov     dl, byte ptr [edx]         ;  将刚才存放的第一个16进制数左移到高四位
00409E31   |.  02D0          add     dl, al                     ;  加上第二个字符的ascii码
00409E33   |.  B0 01         mov     al, 1
00409E35   |.  80EA 37       sub     dl, 37                     ;  因为刚才是直接加了第二个字符的ascii码,所以减去0x37
00409E38   |.  8811          mov     byte ptr [ecx], dl         ;  将转换好的16进制数存入目标地址
00409E3A   |.  C3            retn
00409E3B   |>  32C0          xor     al, al                     ;  字符不是16进制字符,返回0
00409E3D   \.  C3            retn

相信我的注释已经写得非常清楚了,函数0x409DE0的功能是,将第一个参数指向的两个16进制字符转换为对应的16进制数(例如"8A"转换成0x8A),并存放到第二个参数指定的内存地址中并且返回1。如果字符非法(不是'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'之一)则返回0,转换不成功,程序就不再继续往下验证了。
这个函数分析清楚之后,函数0x409E40(什么?!你已经想不起来这个函数在哪里出现过?赶紧往回看看,它就是刚刚分析的函数0x409DE0的调用者)的功能也就一目了然了,其作用就是将第一个参数指向的16进制字符串转换为16进制数并存放到第二个参数指定的地址中,而第三个参数正好则是转换后的16进制数所占字节数(字符串"8A"的长度为2,而0x8A只占用一个字节,这就是为什么刚才我们看到第三个参数是注册码长度一半的原因)。
需要注意的一点是,因为注册码的长度被直接除以2(参见图二),如果注册码的长度是奇数的话,多余的那1位就被自动舍弃了,并不会被转换。到目前为止,我们已经搞清楚这个CrackMe的第一点限制,注册码必须是一个16进制字符串!革命尚未成功,继续走
00406F57   .  8B4D 08       mov     ecx, dword ptr [ebp+8]           ;  ecx=8
00406F5A   .  8BC3          mov     eax, ebx                         ;  eax赋值为注册码长度除以2
00406F5C   .  33D2          xor     edx, edx                         ;  将edx清空,因为div指令将edx:eax作为被除数
00406F5E   .  F7F1          div     ecx                              ;  如果edx的值不清空的话,eax可能存不下商,会有溢出
00406F60   .  85D2          test    edx, edx                         ;  除以ecx之后判断余数是否为0
00406F62   .  0F85 7A010000 jnz     004070E2                         ;  余数不为0则跳走

一直F7单步走到0x406F57会发现,这里是对注册码长度的一个校验,假设注册码长度为k,如果(k / 2) % 8 != 0为真的话,就不再继续往下验证。也就是说注册码的长度至少为16个字节且必须为16的倍数(也可以是16的倍数加1,因为前面我们已经说了,如果注册码的长度为奇数,多余的一位并不会参与转换,相当于被舍弃了)。
到这里我们已经知道了注册码的两点限制,现在我们在刚才跟到的地址0x406F57处下一个断点,然后直接F9让CrackMe弹出继续努力的对话框,然后重新输入一个符合要求的注册码,点击注册认证后会断在刚才我们跟到的位置0x406F57,从这里再继续往下走

(图三)
很快我们就来到了这个CrackMe最为关键的地方,相信熟悉密码学常用算法的同学已经看出来这个就是XTEA算法的解密过程
00406A70  /$  83EC 08       sub     esp, 8
00406A73  |.  8D4424 00     lea     eax, dword ptr [esp]
00406A77  |.  53            push    ebx
00406A78  |.  55            push    ebp
00406A79  |.  56            push    esi
00406A7A  |.  8B7424 18     mov     esi, dword ptr [esp+18]
00406A7E  |.  57            push    edi
00406A7F  |.  50            push    eax
00406A80  |.  8BF9          mov     edi, ecx
00406A82  |.  56            push    esi
00406A83  |.  E8 D8BFFFFF   call    00402A60                         ;  将参数1指向的DWORD中的数据反向存入到参数2指向的DWORD中
00406A88  |.  8D4C24 1C     lea     ecx, dword ptr [esp+1C]
00406A8C  |.  83C6 04       add     esi, 4
00406A8F  |.  51            push    ecx
00406A90  |.  56            push    esi
00406A91  |.  E8 CABFFFFF   call    00402A60
00406A96  |.  8B4C24 20     mov     ecx, dword ptr [esp+20]          ;  取第一个DWORD的密文
00406A9A  |.  8B7424 24     mov     esi, dword ptr [esp+24]          ;  取第二个DWORD的密文
00406A9E  |.  83C4 10       add     esp, 10                          ;  在下面我们将第一个DWORD的密文称为v0,第二个DWORD的密文称为v1
00406AA1  |.  B8 2037EFC6   mov     eax, [COLOR="red"]C6EF3720[/COLOR]                    ;  eax=delta<<5
00406AA6  |.  BA 20000000   mov     edx, 20                          ;  rounds=0x20
00406AAB  |>  8BD9          /mov     ebx, ecx                        ;  ebp=v0
00406AAD  |.  8BE9          |mov     ebp, ecx                        ;  ebp=v0
00406AAF  |.  C1EB 05       |shr     ebx, 5                          ;  ebx右移5位
00406AB2  |.  C1E5 04       |shl     ebp, 4                          ;  ebp左移4位
00406AB5  |.  33DD          |xor     ebx, ebp                        ;  上面4句和这一句代码连起来看就是ebx=(v0>>5)^(v0<<4)
00406AB7  |.  8BE8          |mov     ebp, eax                        ;  ebp=eax,也就是ebp=delta<<5,下一次循环的时候就不是这个值了
00406AB9  |.  C1ED 0B       |shr     ebp, 0B                         ;  ebp=ebp>>0x0b
00406ABC  |.  83E5 03       |and     ebp, 3                          ;  ebp=ebp&03
00406ABF  |.  03D9          |add     ebx, ecx                        ;  ebx=ebx+v0
00406AC1  |.  8B6CAF 20     |mov     ebp, dword ptr [edi+ebp*4+20]   ;  取一个DWORD大小的密钥,ebp*4是索引,跟到这里在OD命令行执行db edi+20就可以看到密钥了
00406AC5  |.  03E8          |add     ebp, eax                        ;  ebp=ebp+(delta<<5)
00406AC7  |.  05 4786C861   |add     eax, 61C88647                   ;  eax=eax+0x61c88647,相当于eax=eax-delta
00406ACC  |.  33DD          |xor     ebx, ebp
00406ACE  |.  2BF3          |sub     esi, ebx                        ;  v1 = v1-ebx
00406AD0  |.  8BDE          |mov     ebx, esi
00406AD2  |.  8BEE          |mov     ebp, esi
00406AD4  |.  C1EB 05       |shr     ebx, 5
00406AD7  |.  C1E5 04       |shl     ebp, 4
00406ADA  |.  33DD          |xor     ebx, ebp
00406ADC  |.  8BE8          |mov     ebp, eax
00406ADE  |.  83E5 03       |and     ebp, 3
00406AE1  |.  03DE          |add     ebx, esi
00406AE3  |.  8B6CAF 20     |mov     ebp, dword ptr [edi+ebp*4+20]   ;  再取一个DWORD的密钥
00406AE7  |.  03E8          |add     ebp, eax
00406AE9  |.  33DD          |xor     ebx, ebp
00406AEB  |.  2BCB          |sub     ecx, ebx
00406AED  |.  4A            |dec     edx                             ;  rounds减1
00406AEE  |.^ 75 BB         \jnz     short 00406AAB
00406AF0  |.  8B7C24 20     mov     edi, dword ptr [esp+20]
00406AF4  |.  57            push    edi
00406AF5  |.  51            push    ecx
00406AF6  |.  E8 A5BFFFFF   call    00402AA0                         ;  参数1是解密后的一个DWORD,把它存到到参数二指向的地址中
00406AFB  |.  83C7 04       add     edi, 4
00406AFE  |.  57            push    edi
00406AFF  |.  56            push    esi
00406B00  |.  E8 9BBFFFFF   call    00402AA0                         ;  这个函数被调用了两次,也就是总共存放了两个DWORD
00406B05  |.  83C4 10       add     esp, 10
00406B08  |.  5F            pop     edi
00406B09  |.  5E            pop     esi
00406B0A  |.  5D            pop     ebp
00406B0B  |.  5B            pop     ebx
00406B0C  |.  83C4 08       add     esp, 8
00406B0F  \.  C2 0800       retn    8

上面有几个常数在这里稍微解释下,注释中的delta=0x9E3779B9,它是由计算而来的,而代码中的0xC6EF3720恰好等于delta<<5。语句add eax, 61C88647和语句
sub eax,9E3779B9的运算结果是相同的。0x406A70这个函数的功能就是将我们的注册码转换成的16进制数据解密,而解密所使用的密钥是BYTE keystream[16] = {0x64,0x6E,0x69,0x57,0x64,0x6E,0x61,0x52,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
细心的朋友可能已经发现0x64,0x6E,0x69,0x57,0x64,0x6E,0x61,0x52正好是字符串“dniWdnaR”中字符的ascii码(不包含0结束符),看来windrand有点自恋噢^_^

最后,程序会把注册码对应的16进制数据解密后的数据与用户名进行比较,如果相同就提示“注册码正确”,否则就是叫你“继续加油”。

(图四)
0x40A060就是一个山寨版的strcmp函数,我们不再在这里分析它,它的实现很简单,感兴趣的读者可以自己跟进去详细看看。这个CrackMe的分析过程到这里就算是完了,总结一下它都有哪些限制,1.注册码中的字符只能是16进制字符。2.注册码的长度至少为16个字节并且只能为16的倍数(16的倍数+1也可以,前面已经说过)。3.用户名的长度=注册码的长度/2。其实这些限制都是XTEA算法的要求,因为XTEA算法的分组长度是64位(每次加密或者解密的数据的长度必须是64位=8字节),因此用户名的长度至少为8字节(或者8的倍数),注册码的长度至少为16个字节(因为字符串转换成16进制数据之后,所占的内存大小刚好是16/2也就是8字节)。搞清楚算法之后,很容易我们就可以写出注册机了,也就是把用户名变换一下(详见注册机代码)然后再使用XTEA算法加密(密钥在前面已经说过了),得到的密文就是对应的注册码了,注册机代码如下
#include <stdio.h>
#include <stdlib.h>
void encipher(unsigned int *v,unsigned int *key);
void reverse_dword(unsigned int a);
unsigned char keystream[16] = {0x64,0x6E,0x69,0x57,0x64,0x6E,0x61,0x52,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
char username[9];//为了简单起见,我们设定用户名的长度只能为位
char regcode[17];//存放最后生成的注册码字符串
int main()
{
	char backup[9];//备份用户名
	int i = 0;
	printf("Please enter username:");
	scanf("%08s",&username);
	memset(backup,0,sizeof(backup));
	strcpy(backup,username);
	reverse_dword(&username);//这里之所以要做两次reverse,是因为CrackMe在解密注册码时,也做了两次reverse DWORD的操作
	reverse_dword((unsigned int)(&username) + 4);
	printf("%08x\n",&username);
	printf("%08x\n",(unsigned int)(&username) + 4);
	encipher(&username,&keystream);
	memset(regcode,0,sizeof(regcode));
	sprintf(regcode,"%08X%08X",*(unsigned int*)(&username),*(unsigned int*)(&username[4]));
	printf("Username:%s\n",backup);
	printf("RegCode:%s\n",regcode);
	system("pause");
	return 0;
}
void reverse_dword(unsigned int *n)
{
	unsigned int a;
	unsigned int k;
  a = *n;
	k = a & 0x000000ff;
	a = (a & 0xffffff00) | ((a & 0xff000000) >> 24);
	a = (a & 0x00ffffff) | (k << 24);
	k = a & 0x0000ff00;
	a = (a & 0xffff00ff) | ((a & 0x00ff0000) >> 8);
	a = (a & 0xff00ffff) | (k << 8);
	*n = a;
}
void encipher(unsigned int *v,unsigned int *key) 
{
    int i = 0;
    unsigned int  v0=v[0], v1=v[1], sum=0, delta=0x9E3779B9;
    for (;i < 0x20;i++)
    {
        v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
        sum += delta;
        v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
    }
    v[0]=v0; v[1]=v1;
}

大功告成,上一张胜利的截图^_^
上传的附件:
2012-6-3 23:03
0
雪    币: 316
活跃值: (128)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
6
123456
2012-6-4 09:56
0
雪    币: 230
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
654321
2012-6-8 07:48
0
雪    币: 303
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
778899
2012-6-8 12:01
0
游客
登录 | 注册 方可回帖
返回
//