Active Desktop Calendar 破解过程

软件用VC++编写,用OD载入,下断点GetWindowTextA,(要在用户名和注册码都输好之后再下断)
不然会一直中断。

点"Register",程序中断在:
004ADDB7  call    [<&USER32.GetWindowTextA>]  ; \GetWindowTextA
返回到:0044EF2C,继续返回。来到:

004A5D1F  mov     dword ptr [ebp+8], 1    ; 返回到这里
004A5D26  jmp     short <loc_4A5D4F>
...
再返回(看过偶写的破VC++的文章都是要返回大约3次吧),来到:

0044EFD3  call    <sub_4A5CCA>
0044EFD8  lea     esi, [edi+154]    ; 返回到这里
0044EFDE  mov     ecx, esi
0044EFE0  call    <sub_49DD96>
0044EFE5  mov     ecx, esi    ; esi指向注册码
0044EFE7  call    <sub_49DC9F>
0044EFEC  push    ecx
0044EFED  mov     ecx, esp
0044EFEF  mov     [esp+14], esp
0044EFF3  push    esi
0044EFF4  call    <sub_4A2B2A>
0044EFF9  call    <sub_457C80>    ; 计算注册码,进入
0044EFFE  add     esp, 4
0044F001  test    eax, eax
0044F003  je      <loc_44F167>    ; 不跳则注册成功

现在说一下,看倒数第二行代码,可见注册成功返回的是非0值。我们进入函数
call    <sub_457C80>。看函数的最后,有两个retn,一个返回的是0,另一个返回的是1。
看一下跳到返回0的跳转有:

跳转来自 00457E49, 00457E63, 00457E7D, 00457F1E, 00457F60
把上面的跳转全部nop掉就可以爆破了。
但00457E49, 00457E63, 00457E7D:这三处是不会跳转的。不知道用来干什么。
00457F1E, 00457F60:这两处是用来判断注册码中是否有非法字符的,这个待会会说。

现在分析一下算法,还是刚才那个函数
call    <sub_457C80>:
...
00457D29  mov     eax, [esp+10]              ;  注册码
00457D2D  xor     esi, esi
00457D2F  mov     ecx, [eax-8]               ;  注册码长度
00457D32  test    ecx, ecx
00457D34  jle     short <loc_457D54>
00457D36  /cmp     byte ptr [esi+eax], 2D
00457D3A  |jnz     short <loc_457D4C>
00457D3C  |push    2C
00457D3E   |push    esi
00457D3F   |lea     ecx, [esp+18]
00457D43  |call    <CString::SetAt(int,char)>
00457D48  |mov     eax, [esp+10]
00457D4C  |mov     ecx, [eax-8]
00457D4F   |inc     esi
00457D50    |cmp     esi, ecx
00457D52  \jl      short <loc_457D36>
00457D54  push    2C
00457D56  lea     ecx, [esp+14]
00457D5A  call    <sub_4A327C>    ; 判断注册码是否满足格式,如:DC8B7-02FC1-27AAE
00457D5F  mov     esi, eax
00457D61  or      ebp, FFFFFFFF
00457D64  cmp     esi, ebp
00457D66  jle     short <loc_457DBF>
...
下面的一段有点长的代码,大概就是分割注册码吧,就不贴出来了。
...
00457E30  call    <sub_4A2EEE>
00457E35  mov     eax, [esp+20]    ; 第一段注册码
00457E39  push    0050A850    ; /Arg2 = 0050A850
00457E3E  push    eax      ; |Arg1
00457E3F  call    <__mbscmp>    ; \ADC.004877A5
00457E44  add     esp, 8
00457E47  test    eax, eax
00457E49  je      <loc_458018>    ; 跳到注册失败,但根本不会跳.下面还有比
            ; 较第二、第三段的注册码,跟这个一样。
...
00457EED  mov     esi, [esp+14]
00457EF1  xor     ebx, ebx
00457EF3  mov     edx, 1
00457EF8  mov     eax, [esi-8]
00457EFB  lea     ecx, [eax-1]
00457EFE  test    ecx, ecx
00457F00  jl      short <loc_457F2F>
00457F02  /mov     al, [ecx+esi]    ; 第二段注册码第n位(倒数)
00457F05  |cmp     al, 30
00457F07  |jl      short <loc_457F15>  ; < 30,跳
00457F09  |cmp     al, 39
00457F0B  |jg      short <loc_457F15>  ; > 39,跳
00457F0D  |movsx   eax, al
00457F10  |sub     eax, 30
00457F13  |jmp     short <loc_457F24>
00457F15  |movsx   eax, al
00457F18  |sub     eax, 37
00457F1B  |cmp     eax, 0F
00457F1E  |jg      <loc_458018>    ; 不能跳,也就是注册码只能是A-F,0-9,就是16进制啦
00457F24  |imul    eax, edx
00457F27  |add     ebx, eax    ; *****
00457F29  |shl     edx, 4
00457F2C  |dec     ecx
00457F2D  \jns     short <loc_457F02>
            ; 上面的代码主要是判断注册码是否是16进制数。
            ; 其实还有一个功能。运行到00457F2F,看一下
            ; 寄存器ebx的值,另一个功能就是把字符串转
            ; 成数字,下面的也一样
00457F2F  mov     edi, [esp+24]    ; 第一段注册码和第三段注册码后3位
00457F33  xor     esi, esi
00457F35  mov     edx, 1
00457F3A  mov     eax, [edi-8]
00457F3D  lea     ecx, [eax-1]
00457F40   test    ecx, ecx
00457F42  jl      short <loc_457F71>
00457F44  /mov     al, [ecx+edi]    ; 从最后一位开始
00457F47  |cmp     al, 30
00457F49  |jl      short <loc_457F57>
00457F4B  |cmp     al, 39
00457F4D  |jg      short <loc_457F57>
00457F4F  |movsx   eax, al
00457F52  |sub     eax, 30
00457F55  |jmp     short <loc_457F66>
00457F57  |movsx   eax, al
00457F5A  |sub     eax, 37
00457F5D  |cmp     eax, 0F
00457F60  |jg      <loc_458018>    ; 不能跳
00457F66  |imul    eax, edx
00457F69  |add     esi, eax    ; *****
00457F6B  |shl     edx, 4
00457F6E  |dec     ecx
00457F6F  \jns     short <loc_457F44>  ; 下面这几行才是算法。

00457F71  lea     ecx, [esi+237505C5]  ; esi=第一段注册码和第三段注册码后3位转成的数字
00457F77  mov     eax, 487EDE05
00457F7C  imul    ecx      ; "溢出"(不知道是不是这样说)部份放入edx
00457F7E  sar     edx, 5
00457F81  mov     eax, edx
00457F83  mov     byte ptr [esp+34], 5
00457F88  shr     eax, 1F
00457F8B  add     edx, eax    ; 注意edx的值
00457F8D  mov     eax, 30C30C31    ; 这条指令应该和下一条换一下位置,这样更好看
00457F92  mov     ecx, edx    ; ecx=edx,值1

00457F94  imul    ebx      ; eax=eax*ebx,eax的值在00457F8D
00457F96  sar     edx, 3
00457F99  mov     eax, edx
00457F9B  shr     eax, 1F
00457F9E  add     edx, eax    ; 值2
00457FA0  cmp     ecx, edx    ; 不相等则注册失败
00457FA2  lea     ecx, [esp+24]
00457FA6  jnz     short <loc_458021>  ; 跳到注册失败
======================================================
现在分析一下算法:

程序通过注册码算出两个值,这两个值不相等则注册失败。

以下全为16进制
值1:

X equ esi=第一段注册码和第三段注册码后3位转成的数字

(X+237505c5)*487EDE05,高位(正确的说法好像是这样吧)放入edx
sar edx,5
;下面的运算实际上是不需要的,因为值2也是这样算,约掉了。
shr eax,1f  ;eax=edx
add eax,ebx
-------------------------------
值2:

第二段注册码*30C30C31
sar edx,3
;下面的运算实际上是不需要的,因为值1也是这样算,约掉了。
shr eax,1f  ;eax=edx
add eax,ebx

就是这样了,暂时还没想到怎么写注册机,先给个注册码吧!
DC8B7-02FC1-27AAE

偶是用最后的值去逆推的。以123为例

2460 <- 123 sal 5
918  <- 123 sal 3

下面这两步推了好久,偶用的是"类穷举法"
Z*487EDE05,高位为2460,==>Z=8073~8076,X=DC8B7AAE~DC8B7AB1
S*30C30C31,高位为918,==>S=02FC1~2FC3,所以注册码就是DC8B7-02FC1-27AAE
=============================================================================================
注册码保存在:HKEY_CURRENT_USER\Software\XemiComputers\Active Desktop Calendar\4.6\regdata
============================================================================================
 

X

点击这里给我发消息
微信号:crackgou