作 者: zyr零零发
时 间: 2010-02-27


【破文标题】Editplus v3.12(bilud 531)最新版算法分析并注册
【破文作者】zyr零零发
【破解工具】OD
【破解平台】Win7(xpxp3亦可)
【程序下载】http://search.newhua.com/search_list...%90+%E4%BA%A4+   最新版。
【保护方式】用户名 注册码
【软件简介】一套功能强大,可取代记事本的文字编辑器,拥有无限制的 Undo/Redo、英文拼字检查、自动换行、列数标记、搜寻取代、同时编辑多文件。。。。。

【破解声明】我以前一直用它,我觉得还不错的。本论坛有一篇以前版本分析算法的文章,现在是最新版的,

算法验证不同了,感兴趣请自行搜索。破解完后,虽然注册了,也避开了重启验证,但是重启验证的代码没找

到,有感兴趣的可以找找看,然后告诉我,不胜感激啊。。。注册脱离OD不行。纯粹是

学习讨论,没别的目的。第一次发破文,失误之处请多指教。此文版权所有,转载需注明作者!
【破解内容】
首先用OD打开程序,F9运行,输入用户名(zyr零零发)和假码(34567890),假码要求八位以上吧,我用8位假码,没仔细研

究。BPX GetWindowTextW(此程序用到的API大部分是UNIONCODE)下断点,按Register按钮,在此处断下:


004DED15  |.  FF15 A0B55000 call    dword ptr [<&USER32.GetWindowTex>; \GetWindowTextW


接下来是验证用户名和假码中有违规字符,看下面代码(也可略过不看):


00495DAF  |.  E8 4F8F0400   call    004DED03
00495DB4  |>  66:8B06       /mov     ax, word ptr [esi]
00495DB7  |.  66:3D 2000    |cmp     ax, 20                          ;  第一个字符是否空格
00495DBB  |.  74 06         |je      short 00495DC3
00495DBD  |.  66:3D 0900    |cmp     ax, 9                           ;  第一个字符是否句号
00495DC1  |.  75 05         |jnz     short 00495DC8
00495DC3  |>  83C6 02       |add     esi, 2
00495DC6  |.^ EB EC         \jmp     short 00495DB4
00495DC8  |>  56            push    esi
00495DC9  |.  E8 23550300   call    004CB2F1                         ;  验证用户名中有否0
00495DCE >|.  8B4C24 18     mov     ecx, dword ptr [esp+18]
00495DD2  |.  83C4 04       add     esp, 4
00495DD5  |.  85C0          test    eax, eax                         ;  验证用户名长度是否为0
00495DD7  |.  8901          mov     dword ptr [ecx], eax
00495DD9  |.  7E 1A         jle     short 00495DF5
00495DDB  |>  8B01          /mov     eax, dword ptr [ecx]
00495DDD  |.  66:8B5446 FE  |mov     dx, word ptr [esi+eax*2-2]


一路F8到了关键点(从这里必看):

00495ED7   .  E8 84000000   call    00495F60   ;  依次验证假码第三,四位,一位,二位,真确的话

EAX返回1

要F7跟进去:


00495F60  /$  81EC 34020000 sub     esp, 234
00495F66  |.  53            push    ebx
00495F67  |.  8B9C24 3C0200>mov     ebx, dword ptr [esp+23C]         ;  取用户名到EDX
00495F6E  |.  56            push    esi
00495F6F  |.  57            push    edi
00495F70  |.  53            push    ebx
00495F71 >|.  E8 7B530300   call    004CB2F1                         ;  验证用户名字符中是否有

0,EAX返回用户名长度
00495F76  |.  8BF0          mov     esi, eax
00495F78  |.  83C4 04       add     esp, 4
00495F7B  |.  85F6          test    esi, esi                         ;  又一次验证用户名是否输

入为空
00495F7D  |.  75 0A         jnz     short 00495F89
00495F7F  |.  5F            pop     edi
00495F80  |.  5E            pop     esi
00495F81  |.  5B            pop     ebx
00495F82  |.  81C4 34020000 add     esp, 234
00495F88  |.  C3            retn
00495F89  |>  8BBC24 480200>mov     edi, dword ptr [esp+248]         ;  取假码到EDI
00495F90  |.  55            push    ebp
00495F91  |.  57            push    edi
00495F92  |.  E8 5A530300   call    004CB2F1                         ;  验证假码字符中是否有空

格,EAX返回假码长度
00495F97  |.  83C4 04       add     esp, 4
00495F9A  |.  8BE8          mov     ebp, eax
00495F9C  |.  8D4424 50     lea     eax, dword ptr [esp+50]
00495FA0  |.  6A 00         push    0                                ; /pDefaultCharUsed = 

NULL
00495FA2  |.  6A 00         push    0                                ; |pDefaultChar = NULL
00495FA4  |.  68 F3010000   push    1F3                              ; |MultiByteCount = 1F3 

(499.)
00495FA9  |.  50            push    eax                              ; |MultiByteStr
00495FAA  |.  56            push    esi                              ; |WideCharCount
00495FAB  |.  53            push    ebx                              ; |WideCharStr
00495FAC  |.  8B1D 60B45000 mov     ebx, dword ptr [<&KERNEL32.WideC>; |

kernel32.WideCharToMultiByte
00495FB2  |.  6A 00         push    0                                ; |Options = 0
00495FB4  |.  6A 00         push    0                                ; |CodePage = CP_ACP
00495FB6  |.  FFD3          call    ebx                              ; \WideCharToMultiByte
00495FB8  |.  6A 00         push    0                                ; /pDefaultCharUsed = 

NULL
00495FBA  |.  6A 00         push    0                                ; |pDefaultChar = NULL
00495FBC  |.  8D4C24 24     lea     ecx, dword ptr [esp+24]          ; |
00495FC0  |.  6A 31         push    31                               ; |MultiByteCount = 31 

(49.)
00495FC2  |.  51            push    ecx                              ; |MultiByteStr
00495FC3  |.  55            push    ebp                              ; |WideCharCount
00495FC4  |.  8BF0          mov     esi, eax                         ; |
00495FC6  |.  57            push    edi                              ; |WideCharStr
00495FC7  |.  6A 00         push    0                                ; |Options = 0
00495FC9  |.  6A 00         push    0                                ; |CodePage = CP_ACP
00495FCB  |.  C64434 70 00  mov     byte ptr [esp+esi+70], 0         ; |
00495FD0  |.  FFD3          call    ebx                              ; \WideCharToMultiByte
00495FD2  |.  8BF8          mov     edi, eax                         ;  假码长度送EDI
00495FD4  |.  C6443C 1C 00  mov     byte ptr [esp+edi+1C], 0         ;  将0送12ED9C指向的一个

字节
00495FD9  |.  E8 92F6FFFF   call    00495670                         ;  动态生成以数据表,大小

128B,和用户名假码无关。
00495FDE  |.  8D5424 50     lea     edx, dword ptr [esp+50]          ;  取用户名到EDX
00495FE2  |.  56            push    esi                              ;  用户名长度入栈
00495FE3  |.  52            push    edx                              ;  用户名入栈
00495FE4  |.  6A 00         push    0                                ;  
00495FE6  |.  E8 D5F6FFFF   call    004956C0                         ;  把用户名每位字母和上表

运算得出数据6F61
00495FEB  |.  83C4 0C       add     esp, 0C
00495FEE  |.  25 FFFF0000   and     eax, 0FFFF
00495FF3  |.  50            push    eax                              ;  把运算的数据入栈保存
00495FF4  |.  8D4424 14     lea     eax, dword ptr [esp+14]
00495FF8  |.  68 80D45300   push    0053D480                         ;  ASCII "%02X"
00495FFD  |.  50            push    eax                              ;  EAX是个空白指针12ED88

内容是0000000D
00495FFE  |.  E8 91640300   call    <wsprintf>                       ;  就是wsprintf函数,把

6F61转换为字符
00496003  |.  8A4C24 2A     mov     cl, byte ptr [esp+2A]            ;  取假码第三位
00496007  |.  8A4424 1C     mov     al, byte ptr [esp+1C]            ;  取真码6F61的第一位
0049600B  |.  83C4 0C       add     esp, 0C
0049600E  |.  3AC8          cmp     cl, al
00496010  |.  5D            pop     ebp
00496011  |.  74 0C         je      short 0049601F
00496013  |.  5F            pop     edi
00496014  |.  5E            pop     esi
00496015  |.  33C0          xor     eax, eax
00496017  |.  5B            pop     ebx
00496018  |.  81C4 34020000 add     esp, 234
0049601E  |.  C3            retn
0049601F  |>  8A5424 1B     mov     dl, byte ptr [esp+1B]            ;  取假码的第四字符,那么

假码改为346F7890
00496023  |.  8A4424 0D     mov     al, byte ptr [esp+D]             ;  取6F61第二个字符F
00496027  |.  3AD0          cmp     dl, al
00496029  |.  74 0C         je      short 00496037
0049602B  |.  5F            pop     edi
0049602C  |.  5E            pop     esi
0049602D  |.  33C0          xor     eax, eax
0049602F  |.  5B            pop     ebx
00496030  |.  81C4 34020000 add     esp, 234
00496036  |.  C3            retn
00496037  |>  83C7 FE       add     edi, -2
0049603A  |.  8D4424 1A     lea     eax, dword ptr [esp+1A]
0049603E  |.  57            push    edi
0049603F  |.  50            push    eax
00496040  |.  6A 00         push    0
00496042  |.  E8 79F6FFFF   call    004956C0                         ;  取假码后六位计算第二组

真码返回EAX值5AC0
00496047  |.  83C4 0C       add     esp, 0C
0049604A  |.  25 FFFF0000   and     eax, 0FFFF
0049604F  |.  8D4C24 0C     lea     ecx, dword ptr [esp+C]
00496053  |.  50            push    eax
00496054  |.  68 80D45300   push    0053D480                         ;  ASCII "%02X"
00496059  |.  51            push    ecx
0049605A  |.  E8 35640300   call    <wsprintf>
0049605F  |.  8A5424 24     mov     dl, byte ptr [esp+24]            ;  取假码第一位
00496063  |.  8A4424 18     mov     al, byte ptr [esp+18]            ;  取真码第一位
00496067  |.  83C4 0C       add     esp, 0C
0049606A  |.  3AD0          cmp     dl, al
0049606C  |.  74 0C         je      short 0049607A
0049606E  |.  5F            pop     edi
0049606F  |.  5E            pop     esi
00496070  |.  33C0          xor     eax, eax
00496072  |.  5B            pop     ebx
00496073  |.  81C4 34020000 add     esp, 234
00496079  |.  C3            retn
0049607A  |>  8A4C24 19     mov     cl, byte ptr [esp+19]
0049607E  |.  8A5424 0D     mov     dl, byte ptr [esp+D]
00496082  |.  33C0          xor     eax, eax
00496084  |.  3ACA          cmp     cl, dl
00496086  |.  5F            pop     edi
00496087  |.  5E            pop     esi
00496088      0F94C0        sete    al
0049608B  |.  5B            pop     ebx
0049608C  |.  81C4 34020000 add     esp, 234
00496092  \.  C3            retn


以上这段代码看起来比较乱,大致的思路是这样的:
从这儿说起吧,00495FD9  |.  E8 92F6FFFF   call    00495670(上面的代码看注释就不写了)。
这个CALL生成一个128b大小的数据块,就叫他是一张表吧,下面的算法要用到其中的数据。在这就不贴这张表的代码了



往下到这儿:

00495FE6  |.  E8 D5F6FFFF   call    004956C0


这个CALL就是计算真码的,第一次调用(调用了三次)是根据用户名和上面的表生成一组真码,我这里是‘6F61’,返回在AX

中。

接下来到这里:

0049605A  |.  E8 35640300   call    <wsprintf>

我这里看到的是<wsprintf>,在你的OD里可能是地址004CC494,我把它加了标签了。功能是把‘6F61’转化为

字符。


紧接着就是第一次验证:

00496003  |.  8A4C24 2A     mov     cl, byte ptr [esp+2A]          ;  取假码第三位
00496007  |.  8A4424 1C     mov     al, byte ptr [esp+1C]          ;  取真码6F61的第一位
0049600B  |.  83C4 0C       add     esp, 0C
0049600E  |.  3AC8          cmp     cl, al
00496010  |.  5D            pop     ebp
00496011  |.  74 0C         je      short 0049601F



就是假码第三个字符5和6比较,6从哪里来?6从上面6F61的第一个字符来的。若相同往下跳,继续第二次验证

,到这里:

0049601F  |> \8A5424 1B     mov     dl, byte ptr [esp+1B]          ;  取假码的第四字符,那么假

码改为346F7890
00496023  |.  8A4424 0D     mov     al, byte ptr [esp+D]           ;  取6F61第二个字符F
00496027  |.  3AD0          cmp     dl, al
00496029  |.  74 0C         je      short 00496037

假码第四个字符6和F比较,F从从上面6F61的第二个字符来的。

根据以上程序验证,我把我的假码34567890改为346F6190,注意是把假码替换了四位,把78也换了。让以上验

证成功。

若相同往下跳,到这里:


00496037  |> \83C7 FE       add     edi, -2
0049603A  |.  8D4424 1A     lea     eax, dword ptr [esp+1A]
0049603E  |.  57            push    edi
0049603F  |.  50            push    eax
00496040  |.  6A 00         push    0
00496042  |.  E8 79F6FFFF   call    004956C0                       ;  取假码后六位计算第二组真

码返回AX值5AC0



第二次 call    004956C0,(第一次在那里?还有第三次调用,是根据机器硬盘序列号和生成的表生成第三

组真码,不过在这里用不上,感兴趣请自行分析),取假码346F6190中的后6位6F6190,和上面生成的表计算出

第二组真码,同样返回在AX中,我这里是5AC0。接下来还是用0049605A  |.  E8 35640300   call    

<wsprintf>把5AC0转化为字符。接下来当然是验证了:



0049605F  |.  8A5424 24     mov     dl, byte ptr [esp+24]          ;  取假码第一位
00496063  |.  8A4424 18     mov     al, byte ptr [esp+18]          ;  取真码5AC0第一位
00496067  |.  83C4 0C       add     esp, 0C
0049606A  |.  3AD0          cmp     dl, al
0049606C  |.  74 0C         je      short 0049607A
0049606E  |.  5F            pop     edi
0049606F  |.  5E            pop     esi
00496070  |.  33C0          xor     eax, eax
00496072  |.  5B            pop     ebx
00496073  |.  81C4 34020000 add     esp, 234
00496079  |.  C3            retn
0049607A  |>  8A4C24 19     mov     cl, byte ptr [esp+19];取假码第二位
0049607E  |.  8A5424 0D     mov     dl, byte ptr [esp+D];取真码5AC0第二位
00496082  |.  33C0          xor     eax, eax
00496084  |.  3ACA          cmp     cl, dl

为了验证成功,把假码改为5A6F6190,接下来就简单了,一路F8,直到程序出现重启验证的对话框,这时关闭

OD,重启程序,在菜单help里看一下:

注册成功!



总结:一,这是侥幸注册成功,用户名和注册码只是用了本文提到的,别的没试过。
     二,这是程序验证漏洞?
     三,本人水平有限,本文分析的很粗浅,在此抛砖引玉,希望有更好的分析文章出现。
     四,希望大家拿它练练手,哈,感觉还是蛮难的。