【破文标题】Watery Desktop 3D算法分析+系列注册机之汇编实现(中级)
【破文作者】Playboysen
【作者邮箱】playboysen#126.com
【破解工具】OD
【破解平台】Windows7
【软件语言】英文
【原版下载】http://www.push-wallpaper.com/01/
【保护方式】用户名 注册码
【软件简介】Watery Desktop 3D迄今为止我见过的效果最炫的动态壁纸和屏保,惊叹!多处搜索发现此软件只有屏保部分的爆破版本,网上无法找到任何一组全功能注册码(在国外网站找到三个注册码,可以弹出注册成功提示,但其实功能不正常)遂兴趣盎然着手研究……
【破解声明】看本文需要极大耐心。版权所有,转载需注明作者!
【破解内容】

  毫不夸张地说这是我折腾过的所有软件中单靠程序算法验证保护(不靠强壳、VM之类)最严密的一个,从试用、分析到算法还原写注册机,前后耗时一月有余,叹为观止!此程序的注册验证机制精巧,也许有些东西值得我们学习……
  
1、输入假码无提示,获取文本尽量避开常用API如GetDlgItemText GetWindowText等
2、程序对注册码分段、分时、多处验证,验证为假码后无提示功能限制,注册码加密保存(其实大部分时间都耗在暗桩处算法的查找分析上了,汗)
3、验证时临时解密注册表密钥,且解密后参与验证的数据并不是明文注册码(已经过简单处理)
4、程序使用GetUserNameEx参与验证注册码,隐式绑定电脑

上面这些似乎并不高明的伎俩足足耗费了我两三周的时间来分析和应付,几欲放弃……

然而,智者千虑必有一失,程序未加壳、有注册成功提示、配置对话框标题有(unregistered)字样且字符为明文。

有的放矢,搜索字符串轻易找到注册码验证的第一处

代码:
00405264   .  6A 00         push    0                                ; /lParam = 0
00405266   .  6A 00         push    0                                ; |wParam = 0
00405268   .  6A 0E         push    0E                               ; |Message = WM_GETTEXTLENGTH
0040526A   .  52            push    edx                              ; |hWnd
0040526B   .  FFD6          call    esi                              ; \SendMessageW
0040526D   .  83F8 05       cmp     eax, 5
00405270   .  0F85 01040000 jnz     00405677
00405276   .  8D4424 1C     lea     eax, dword ptr [esp+1C]
0040527A   .  50            push    eax                              ; /lParam
0040527B   .  6A 06         push    6                                ; |wParam = 6
0040527D   .  6A 0D         push    0D                               ; |Message = WM_GETTEXT
0040527F   .  57            push    edi                              ; |hWnd
00405280   .  FFD6          call    esi                              ; \SendMessageW
00405282   .  33C0          xor     eax, eax
00405284   >  8A4C44 1C     mov     cl, byte ptr [esp+eax*2+1C]      ;  注册码分四段,每段5个字符
00405288   .  80E9 41       sub     cl, 41                           ;  分别取出每段字符 每一位减去41h后生成一段20位的数据储存
0040528B   .  884C04 28     mov     byte ptr [esp+eax+28], cl
0040528F   .  40            inc     eax
00405290   .  83F8 05       cmp     eax, 5
00405293   .^ 7C EF         jl      short 00405284
……
假设我们输入假码:
U P K P E D I Y P L A Y B O Y S E N H U
对应十六进制为:
55 50 4B 50 45 44 49 59 50 4C 41 59 42 4F 59 53 45 4E 48 55
对应每位减去41h(将这一段数据设为K):
14 0F 0A 0F 04 03 08 18 0F 0B 00 18 01 0E 18 12 04 0D 07 14

第一处验证
代码:
00405300   > /0FB65404 28   movzx   edx, byte ptr [esp+eax+28]       ;  求K的前19位之和放入ecx
00405305   . |40            inc     eax
00405306   . |03CA          add     ecx, edx
00405308   . |83F8 13       cmp     eax, 13
0040530B   .^\7C F3         jl      short 00405300
0040530D   .  0FB64424 3B   movzx   eax, byte ptr [esp+3B]           ;  K20放入eax
00405312   .  F7D1          not     ecx
00405314   .  33C1          xor     eax, ecx
00405316   .  A8 0F         test    al, 0F
00405318   .  0F85 59030000 jnz     00405677
事实证明00405318处的跳转会跳过注册成功提示,说明上面算法就是注册码验证关键了。
不屑~~~ 这么简单,汗。随便写几句代码做个JJ枚举出来几个符合条件的字符串输入,提示注册成功!
本以为万事大吉。重启软件右键任务栏程序图标打开配置对话框,依然显示未注册,注册码输入框可用。

嗯,不是我们太愚钝,实在是敌人太狡猾……

重载程序,接着上面的代码一路小跑企图再找突破点:
代码:
00405300   > /0FB65404 28   movzx   edx, byte ptr [esp+eax+28]
00405305   . |40            inc     eax
00405306   . |03CA          add     ecx, edx
00405308   . |83F8 13       cmp     eax, 13
0040530B   .^\7C F3         jl      short 00405300
0040530D   .  0FB64424 3B   movzx   eax, byte ptr [esp+3B]
00405312   .  F7D1          not     ecx
00405314   .  33C1          xor     eax, ecx
00405316   .  A8 0F         test    al, 0F
00405318   .  0F85 59030000 jnz     00405677
0040531E   .  33C0          xor     eax, eax
00405320   >  8BC8          mov     ecx, eax                         ;  K的第二次变换加密
00405322   .  83E1 03       and     ecx, 3
00405325   .  41            inc     ecx
00405326   .  D26404 28     shl     byte ptr [esp+eax+28], cl
0040532A   .  40            inc     eax
0040532B   .  83F8 14       cmp     eax, 14
0040532E   .^ 7C F0         jl      short 00405320
00405330   .  8D4C24 18     lea     ecx, dword ptr [esp+18]
00405334   .  51            push    ecx
00405335   .  8D9424 480200>lea     edx, dword ptr [esp+248]
0040533C   .  52            push    edx
0040533D   .  6A 02         push    2                                ;  注意这个参数,写注册机时必须用这个参数,否则结果会出错
0040533F   .  E8 52B90400   call    <jmp.&Secur32.GetUserNameExW>
00405344   .  B8 04010000   mov     eax, 104
00405349   .  8D8C24 440200>lea     ecx, dword ptr [esp+244]
00405350   >  66:8339 00    cmp     word ptr [ecx], 0
00405354   .  74 0A         je      short 00405360
00405356   .  83C1 02       add     ecx, 2
00405359   .  83E8 01       sub     eax, 1
0040535C   .^ 75 F2         jnz     short 00405350
0040535E   .  EB 57         jmp     short 004053B7
00405360   >  85C0          test    eax, eax
00405362   .  74 53         je      short 004053B7
00405364   .  BA 04010000   mov     edx, 104
00405369   .  2BD0          sub     edx, eax
0040536B   .  B9 04010000   mov     ecx, 104
00405370   .  2BCA          sub     ecx, edx
00405372   .  8D8454 440200>lea     eax, dword ptr [esp+edx*2+244]
00405379   .  74 34         je      short 004053AF
0040537B   .  BF 28044700   mov     edi, 00470428                    ;  "12345678901234567890"
......
004053C0   >  8A9444 440200>mov     dl, byte ptr [esp+eax*2+244]     ;  K的第三次变换加密
004053C7   .  F6D2          not     dl                               ;  加密因子为GetUserNameEx + '12345678901234567890'
004053C9   .  305404 28     xor     byte ptr [esp+eax+28], dl
004053CD   .  40            inc     eax
004053CE   .  83F8 14       cmp     eax, 14
004053D1   .^ 7C ED         jl      short 004053C0
004053D3   .  8D8424 4C0400>lea     eax, dword ptr [esp+44C]
004053DA   .  50            push    eax
004053DB   .  6A 00         push    0
004053DD   .  FF15 18E44900 call    dword ptr [49E418]
004053E3   .  8D4424 3C     lea     eax, dword ptr [esp+3C]
004053E7   .  BE 04064700   mov     esi, 00470604                    ;  "Thank you for registrating "
......
0040561C   .  6A 14         push    14                               ; /BufSize = 14 (20.)
0040561E   .  8D5424 2C     lea     edx, dword ptr [esp+2C]          ; |
00405622   .  52            push    edx                              ; |Buffer
00405623   .  6A 03         push    3                                ; |ValueType = REG_BINARY
00405625   .  50            push    eax                              ; |Reserved
00405626   .  8B4424 20     mov     eax, dword ptr [esp+20]          ; |
0040562A   .  68 54044700   push    00470454                         ; |ValueName = "REG"
0040562F   .  50            push    eax                              ; |hKey
00405630   .  FF15 10F04600 call    dword ptr [<&ADVAPI32.RegSetValu>; \RegSetValueExW
00405636   .  8B4C24 10     mov     ecx, dword ptr [esp+10]
0040563A   .  51            push    ecx                              ; /hKey
0040563B   .  FF15 00F04600 call    dword ptr [<&ADVAPI32.RegCloseKe>; \RegCloseKey
00405641   >  8B15 D4DB4900 mov     edx, dword ptr [49DBD4]
00405647   .  6A 00         push    0                                ; /lParam = 0
00405649   .  6A 00         push    0                                ; |wParam = 0
0040564B   .  6A 12         push    12                               ; |Message = WM_QUIT
0040564D   .  52            push    edx                              ; |hWnd => NULL
0040564E   .  FF15 64F24600 call    dword ptr [<&USER32.SendMessageW>; \SendMessageW
上面代码有上百行,我们一路F8下来,重点了解上面几处关键点即可
我们知道过了上面第一处验证,程序会对K再进行两次加密然后设置注册表值重启验证

RegQueryValueA RegQueryValueW RegQueryValueExA RegQueryValueExW四个断点齐下
重载,断在RegQueryValueExW,F9几次找到下处
代码:
00404F05  |.  68 54044700   push    00470454                         ;  "REG"
00404F0A  |.  50            push    eax
00404F0B  |.  FFD6          call    esi
00404F0D  |.  8B4C24 0C     mov     ecx, dword ptr [esp+C]
00404F11  |.  51            push    ecx
00404F12  |.  FFD3          call    ebx
00404F14  |>  8D5424 18     lea     edx, dword ptr [esp+18]
00404F18  |.  52            push    edx
00404F19  |.  68 98DC4900   push    0049DC98                         ;  "Senhuan-PC\Senhuan12345678901234567890"
00404F1E  |.  6A 02         push    2
00404F20  |.  C74424 24 040>mov     dword ptr [esp+24], 104
00404F28  |.  E8 69BD0400   call    <jmp.&Secur32.GetUserNameExW>
00404F2D  |.  68 28044700   push    00470428                         ;  "12345678901234567890"
00404F32  |.  68 04010000   push    104
00404F37  |.  68 98DC4900   push    0049DC98                         ;  "Senhuan-PC\Senhuan12345678901234567890"
00404F3C  |.  E8 CFC9FFFF   call    00401910
00404F41  |.  8B8C24 240200>mov     ecx, dword ptr [esp+224]
有点熟悉,嗯一路小跑往下看找找算法,结果翻了上百行代码竟然没发现一点注册码验证的蛛丝马迹

整理下思路。看到上面00404F19处,灵机一动既然程序用GetUserNmaeEx值参与加密保存注册码,那程序启动时又调用这个API,是不是说明程序解密验证时还要在使用这个字符串呢?我们在上面0049DC98处的字符串下硬件访问断点试一试

事实证明可行
代码:
0040C1F0  |> /0FB60C45 98DC>/movzx   ecx, byte ptr [eax*2+49DC98]    ;  硬件访问断点找到的第一处算法
0040C1F8  |. |0FB61445 9ADC>|movzx   edx, byte ptr [eax*2+49DC9A]
0040C200  |. |F6D1          |not     cl
......
0040C250  |. |884C04 0C     |mov     byte ptr [esp+eax+C], cl
0040C254  |. |83C0 05       |add     eax, 5
0040C257  |. |83F8 14       |cmp     eax, 14
0040C25A  |.^\7C 94         \jl      short 0040C1F0
0040C25C  |.  33C0          xor     eax, eax
0040C25E  |.  8BFF          mov     edi, edi
0040C260  |>  8AC8          /mov     cl, al
0040C262  |.  80E1 03       |and     cl, 3
0040C265  |.  FEC1          |inc     cl
0040C267  |.  D26C04 08     |shr     byte ptr [esp+eax+8], cl
0040C26B  |.  40            |inc     eax
0040C26C  |.  83F8 14       |cmp     eax, 14
0040C26F  |.^ 7C EF         \jl      short 0040C260
0040C271  |.  33C9          xor     ecx, ecx
0040C273  |.  33D2          xor     edx, edx
0040C275  |.  33F6          xor     esi, esi
0040C277  |.  33C0          xor     eax, eax
0040C279  |.  57            push    edi
0040C27A  |.  8D9B 00000000 lea     ebx, dword ptr [ebx]
0040C280  |>  0FB67C04 0C   /movzx   edi, byte ptr [esp+eax+C]
0040C285  |.  03CF          |add     ecx, edi
0040C287  |.  0FB67C04 0D   |movzx   edi, byte ptr [esp+eax+D]
0040C28C  |.  83C0 02       |add     eax, 2
0040C28F  |.  03D7          |add     edx, edi
0040C291  |.  83F8 12       |cmp     eax, 12
0040C294  |.^ 7C EA         \jl      short 0040C280
0040C296  |.  83F8 13       cmp     eax, 13
0040C299  |.  5F            pop     edi
0040C29A  |.  7D 05         jge     short 0040C2A1
0040C29C  |.  0FB67404 08   movzx   esi, byte ptr [esp+eax+8]
0040C2A1  |>  0FB64424 1B   movzx   eax, byte ptr [esp+1B]
0040C2A6  |.  03D1          add     edx, ecx
0040C2A8  |.  03D6          add     edx, esi
0040C2AA  |.  F7D2          not     edx
0040C2AC  |.  33D0          xor     edx, eax
0040C2AE  |.  F6C2 0F       test    dl, 0F
0040C2B1  |.  75 07         jnz     short 0040C2BA
0040C2B3  |.  C605 B8DE4900>mov     byte ptr [49DEB8], 1
有没有觉得熟悉?还记得上面我们分析过 程序在弹出注册成功提示时对K进行过两次加密吗?
没错,这一段就是从注册表中取出加密后的字节还原成K并再次验证的过程(没有使用同一段代码来进行重启验证可有效防止菜鸟通过简单修改跳转爆破程序)

算法上面已分析,略过 继续F9

第二处验证
代码:
00401FB0  |>  8A1445 98DC49>/mov     dl, byte ptr [eax*2+49DC98]               ;  硬件访问断点找到的第二处算法
00401FB7  |. |8AC8          |mov     cl, al                                    ;  解密注册表值,最终还原成K
00401FB9  |. |F6D2          |not     dl
00401FBB  |. |3290 A0DE4900 |xor     dl, byte ptr [eax+49DEA0]
00401FC1  |. |80E1 03       |and     cl, 3
00401FC4  |. |FEC1          |inc     cl
00401FC6  |. |D2EA          |shr     dl, cl
00401FC8  |. |40            |inc     eax
00401FC9  |. |83F8 13       |cmp     eax, 13
00401FCC  |. |885404 FF     |mov     byte ptr [esp+eax-1], dl
00401FD0  |.^\7C DE         \jl      short 00401FB0
00401FD2  |.  0FB64C24 05     movzx ecx,byte ptr ss:[esp+5]                    ;  K6
00401FD7  |.  0FB64424 01     movzx eax,byte ptr ss:[esp+1]                    ;  K2
00401FDC  |.  0FB65424 09     movzx edx,byte ptr ss:[esp+9]                    ;  K10
00401FE1  |.  03C1            add eax,ecx
00401FE3  |.  0FB60C24        movzx ecx,byte ptr ss:[esp]                      ;  K1
00401FE7  |.  03C2            add eax,edx
00401FE9  |.  8D5408 05       lea edx,dword ptr ds:[eax+ecx+5]                 ;  edx=K1 + K2 + K6 + K10 +5
00401FED  |.  83E2 0F         and edx,0F
00401FF0  |.  385424 0F       cmp byte ptr ss:[esp+F],dl                       ;  =K16
00401FF4  |.  74 07           je short WateryDe.00401FFD
00401FF6  |.  C605 B8DE4900 0>mov byte ptr ds:[49DEB8],0
继续,第三处验证
代码:
0040C750   > /8A1445 98DC49>mov     dl, byte ptr [eax*2+49DC98]                 ;  硬件访问断点找到的第三处算法
0040C757   . |8AC8          mov     cl, al                                      ;  最终解密成K
0040C759   . |F6D2          not     dl
0040C75B   . |3290 80DB4900 xor     dl, byte ptr [eax+49DB80]
0040C761   . |80E1 03       and     cl, 3
0040C764   . |FEC1          inc     cl
0040C766   . |D2EA          shr     dl, cl
0040C768   . |40            inc     eax
0040C769   . |83F8 13       cmp     eax, 13
0040C76C   . |885404 03     mov     byte ptr [esp+eax+3], dl
0040C770   .^\7C DE         jl      short 0040C750
0040C772   .  0FB64C24 10     movzx ecx,byte ptr ss:[esp+10]                   ;  K13
0040C777   .  0FB64424 07     movzx eax,byte ptr ss:[esp+7]                    ;  K4
0040C77C   .  0FB65424 08     movzx edx,byte ptr ss:[esp+8]                    ;  K5
0040C781   .  33C1            xor eax,ecx
0040C783   .  0FB64C24 0B     movzx ecx,byte ptr ss:[esp+B]                    ;  K8
0040C788   .  33C2            xor eax,edx
0040C78A   .  33C1            xor eax,ecx                                      ;  eax=K4 xor K13 xor K5 xor K8
0040C78C   .  83C0 04         add eax,4
0040C78F   .  83E0 0F         and eax,0F
0040C792   .  384424 11       cmp byte ptr ss:[esp+11],al                      ;  K14
0040C796   .  74 07           je short WateryDe.0040C79F
0040C798   .  C605 74DB4900 0>mov byte ptr ds:[49DB74],0
继续F9,不再中断 终于长舒一口气了,虽然算法调用放在了不同地方,且对注册码进行分段验证,但我们总算找全了算法
重新更改注册机算法,生成可用注册码输入,运行不再有[unregistered]提示,注册码输入框灰色。嗯,不错

但运行了一会,软件提示需要购买?!

淡定、淡定,不可乱了方寸。整理思路……
嗯,由上面可以看出,K共有20位,上面参与运算的却只有十几位,是不是还有几位在隐式验证,我们没发现?

程序每次验证注册码值之前都会取加密后的注册表值进行循环解密,以此为突破点试试,OD搜索所有命令cmp eax,13
竟然奇迹般地找到了所有验证算法处,逐个看看发现漏网之鱼

第四处验证:
代码:
0040CFC2   .  0FB64C24 32   movzx   ecx, byte ptr [esp+32]           ;  K11
0040CFC7   .  0FB64424 33   movzx   eax, byte ptr [esp+33]           ;  k12
0040CFCC   .  0FB65424 30   movzx   edx, byte ptr [esp+30]           ;  k9
0040CFD1   .  03C1          add     eax, ecx
0040CFD3   .  0FB64C24 38   movzx   ecx, byte ptr [esp+38]           ;  k17
0040CFD8   .  03D1          add     edx, ecx
0040CFDA   .  33C2          xor     eax, edx
0040CFDC   .  83E8 07       sub     eax, 7
0040CFDF   .  83E0 0F       and     eax, 0F
0040CFE2   .  384424 2A     cmp     byte ptr [esp+2A], al            ;  k3
0040CFE6   .  74 15         je      short 0040CFFD
再改注册机 测试,终于一切看起来那么顺利。

大约4分钟左右,运行正常的程序突然没了!

崩溃……

bp SetTimer  找到如下代码处,好长一段
跟踪几遍之后发现前面的代码都没什么用处的,关键的在这几句
代码:
0040A639   >  8BC6            mov eax,esi                                      ; esi里面的值参与运算,但却不知哪里算出的
0040A63B   .  C1F8 04         sar eax,4                            ; 下硬件写入断点,断下后回溯找到第六处验证
0040A63E   .  8BCE            mov ecx,esi
0040A640   .  C1F9 0C         sar ecx,0C
0040A643   .  2BC8            sub ecx,eax
0040A645   .  8BD6            mov edx,esi
0040A647   .  C1FA 08         sar edx,8
0040A64A   .  8D4411 FE       lea eax,dword ptr ds:[ecx+edx-2]
0040A64E   .  33C6            xor eax,esi
0040A650   .  A8 0F           test al,0F
0040A652   .  74 22           je short WateryDe.0040A676
0040A654   .  8B0D D4DB4900   mov ecx,dword ptr ds:[49DBD4]
0040A65A   .  57              push edi                                         ; /Timerproc
0040A65B   .  68 78DA0200     push 2DA78                                       ; |Timeout = 187000. ms
0040A660   .  68 93010000     push 193                                         ; |TimerID = 193 (403.)
0040A665   .  51              push ecx                                         ; |hWnd => 001A03CA ('PUSH Wallpaper',class='PushWallpaper',parent=000100E4)
0040A666   .  C705 A8DB4900 0>mov dword ptr ds:[49DBA8],1                      ; |
0040A670   .  FF15 D0F24600   call dword ptr ds:[<&USER32.SetTimer>]           ; \SetTimer

第六处验证

00403C50  |> \8A0D 92E44900   mov cl,byte ptr ds:[49E492]               ;  第十八位 "Senhuan-PC\Senhuan12345678901234567890"
00403C56  |.  0FB605 8CE44900 movzx eax,byte ptr ds:[49E48C]            ;  第十五位 "Senhuan-PC\Senhuan12345678901234567890"
00403C5D  |.  0FB615 A5DB4900 movzx edx,byte ptr ds:[49DBA5]            ;  加密后的注册表密钥第18位
00403C64  |.  F6D1            not cl
00403C66  |.  0FB6F1          movzx esi,cl
00403C69  |.  33F2            xor esi,edx
00403C6B  |.  0FB615 A2DB4900 movzx edx,byte ptr ds:[49DBA2]            ;  加密后的注册表密钥第15位
00403C72  |.  F6D0            not al
00403C74  |.  0FB6C8          movzx ecx,al
00403C77  |.  0FB605 7CE44900 movzx eax,byte ptr ds:[49E47C]            ;  第七位 "Senhuan-PC\Senhuan12345678901234567890"
00403C7E  |.  33CA            xor ecx,edx
00403C80  |.  0FB615 9ADB4900 movzx edx,byte ptr ds:[49DB9A]            ;  加密后的注册表密钥第7位
00403C87  |.  C1EE 02         shr esi,2
00403C8A  |.  C1E9 03         shr ecx,3
00403C8D  |.  C1E6 04         shl esi,4
00403C90  |.  03F1            add esi,ecx
00403C92  |.  F6D0            not al
00403C94  |.  0FB6C8          movzx ecx,al
00403C97  |.  0FB605 94E44900 movzx eax,byte ptr ds:[49E494]            ;  第十九位 "Senhuan-PC\Senhuan12345678901234567890"
00403C9E  |.  33CA            xor ecx,edx
00403CA0  |.  0FB615 A6DB4900 movzx edx,byte ptr ds:[49DBA6]            ;  加密后的注册表密钥第19位
00403CA7  |.  C1E9 03         shr ecx,3
00403CAA  |.  C1E6 04         shl esi,4
00403CAD  |.  03F1            add esi,ecx
00403CAF  |.  F6D0            not al
00403CB1  |.  0FB6C8          movzx ecx,al
00403CB4  |.  33CA            xor ecx,edx
00403CB6  |.  C1E9 03         shr ecx,3
00403CB9  |.  C1E6 04         shl esi,4
00403CBC  |.  03F1            add esi,ecx

一段折腾之后算出esi的值,参与定时器附近的那段验证,总算找全了所有验证算法。

休息一天,静心好好整理一下思路 然后还原算法

首先,生成那个加密因子:
代码:
.data
temp     db   '12345678901234567890',0
Const    db   'ABCDEFGHIJKLMNOPQRSTUVWXYZ',0

.data?
String   db MAX_PATH dup (?)
nSize    dd  ?
      
mov    nSize,sizeof String
invoke GetUserNameEx,2,addr String,addr nSize
invoke lstrcat,addr String,addr temp
其次,还原六处验证算法:
代码:
WateryDesktop3D proc
        LOCAL @Temp[256]:byte
            
;一个大循环,暴力求出符合条件的注册码

      @Fail:
          xor   ecx,ecx
;生成20位的随机字符并稍作处理生成一组K        
        @@:
          invoke   Rand,0,25
        
          mov   esp,offset Const
          movzx   edx,byte ptr[esp+eax]
          sub   dl,41h
          mov  byte ptr[esp+ecx+140],dl
          inc   ecx
              cmp   ecx,20
              jl    @B
;第二段验证算法              
              add   esp,140        ;使esp指向K
              movzx   eax,byte ptr[esp]  
              movzx   ecx,byte ptr[esp+1]
              add   ecx,eax
              movzx   eax,byte ptr[esp+5]
              add  ecx,eax
              movzx   eax,byte ptr[esp+9]
              add   ecx,eax
              add   ecx,5
              and   ecx,0Fh
              mov   byte ptr[esp+15],cl
;第三段验证算法              
              movzx   eax,byte ptr[esp+3]
              movzx   ecx,byte ptr[esp+12]        
              xor   eax,ecx
              movzx   ecx,byte ptr[esp+4]
              xor   eax,ecx
              movzx   ecx,byte ptr[esp+7]
              xor   eax,ecx
              add   eax,4
              and   eax,0Fh
              mov   byte ptr[esp+13],al
;第四段验证算法                                
              movzx   ecx,byte ptr[esp+10]  
              movzx   eax,byte ptr[esp+11]
              add   eax,ecx
              movzx   ecx,byte ptr[esp+8]
              movzx   edx,byte ptr[esp+16]
              add   ecx,edx
              xor   eax,ecx
              sub   eax,7
              and   eax,0Fh
              mov   byte ptr[esp+2],al
                         
              xor   eax,eax
               xor   ecx,ecx
               xor   edx,edx
;求出前19位字符的和
        @@:
            movzx   edx,byte ptr[esp+eax]
            inc   eax
            add   ecx,edx
            cmp   eax,19
          jl   @B
;第一段验证     
            movzx   eax,byte ptr[esp+19]
              not   ecx
              xor   eax,ecx
              test   al,0Fh
              jnz   @Fail
;第六处验证全代码       
          
              lea  edi,@Temp              ;  注意@Temp为局部变量,无法使用offset指令,必须用lea指令
              mov  esi,esp
              mov  ecx,20
              REP  MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
......

;将K还原成明码生成可用注册码                            
              xor   eax,eax
              xor   ecx,ecx
            @@:
              movzx   eax,byte ptr[esp+ecx]
              add   al,41h
              mov   byte ptr[esp+ecx],al
              inc   ecx
              cmp   ecx,19
              jle   @B               
              
              invoke  lstrcpy,addr serial,esp
              
  ret              
WateryDesktop3D endp
总结:
1、其实这个软件算法平平,出彩的是其注册验证的思路和方法;
2、使用计算机名作为注册码的运算因子,绑定电脑。其实我们可以修改自己的计算机名,从而使用别人的注册码;
3、经过多次分析,该公司所有软件加密算法相似(只是变动了相关数值),快速定位几处算法段的方法:
  OD加载后搜索“所有命令”--“cmp eax,13”(因为每次运算几乎都会有循环解密)
  注意有一处暗桩就是即使过了四处验证程序运行时一切正常且提示已注册的情况下,大约3~4分钟无提示自动关闭!!!(下断点SetTimer)
4、给作者的建议:
  1)可以使用这种比较出彩的验证机制和思路,但不要把公司所有的软件使用同一种验证算法(弊端显而易见,就是我研究了一款软件的算法,就直接秒了公司所有软件,系列注册机应运而生!)
  2)让用户名参与注册码的运算,这样算法会更复杂且如果有正版注册码流出,可同时结合用户名和计算机名来定位黑名单
  3)加密一些关键提示字符,尽量无明文(当然全部字符加密不现实也没必要,注册验证相关的几个字串加密即可)
  4)既然输入假码无提示,那么输入真码也无提示就可以了,每次输入注册码后都重启验证(有效浪费Cracker时间),若验证注册码可用就直接解锁所有功能(简单是真,花式越多越容易被人抓小辫儿)
  5)CRC32或其他算法,程序加个自校验吧,校验方法越非主流越好,检验代码也不写在同一处,越隐蔽越好(这样网上就不会有那么多乱飞的爆破版了)
  6)多处暗桩隐式验证注册码(这个估计作者很有心得!四处暗桩前后浪费我近半个月时间)
  7)最好VM一下算法函数,这样很多像我这样的小鸟就只能望洋兴叹
  8)可选择加个强壳(无奈之举,缓兵之计,可挡一大批初级Cracker,但一定记住把希望寄托在别人身上,就是你希望破灭的开始)

注册机运行界面:


参考全部工程文件,请浏览http://bbs.pediy.com/showthread.php?p=768736:
Push Entertinment.rar

  • 标 题:答复
  • 作 者:playboysen
  • 时 间:2010-02-27 10:48:30

BYTE PTR DS:[49DEB8]是个标志位
不过并非全局标志位
赋值共有三处 0040C1E2 00401FF6 0040C7C9 
即使全部修改成1 运行看似一切正常 但等等着 
程序运行五分钟后你就知道会有什么问题了(无提示自动退出了) 

这才是程序的高明之处