• 标 题:EditPlus 2.01b 注册机的制作 (22千字)
  • 作 者:夜月
  • 时 间:2001-9-10 18:07:09
  • 链 接:http://bbs.pediy.com

EditPlus 2.01b 注册机的制作

作者:夜月
E-mail:luoyi.ly@yeah.net
写作日期: 30th August,2001
修改时间:30th August,2001
版本:2.01 Beta

软件背景资料

运行平台: Win9X 
文件名称: Ep2setup20b.exe
程序类型: Text Edit
下载地点: http://www.editplus.com
文件大小: 910KB

使用的工具

Trw2000 V1.23--Win9X Debugger
W32Dasm V8.93--Win9X Dissembler
Masm32  V5.00--KeyGen Compiler

难易程度

Easy( )  Medium(X)  Hard( )  Pro( )

                  ------------------=====Begin=====------------------

    关于EditPlus的注册码问题,在《论坛精华2》中,dr0大客曾经说过:“好象从1.21版本就
开始了。它判断注册码不是集中在一个地方判断,程序刚启动的时候判断几位,退出的时候再判断另
外几位,你使用它的preferences菜单的时候再判断一位。
    以上是旧版本的情况。2.01a版本与此有所不同。由于不知道它会在哪里判断注册码,所以不
能清除BPR断点,即要一直保留针对输入的假注册码所设的BPR断点;或者用bpx RegDeleteValueA设断
点,试用一下其各种功能,看它是否会删除注册表中的假注册码,就可以找到判断注册码的地方。另外,
这个软件的注册码的前5位是由后面的计算出来的,而后面有多少位并无明显的限制,所以各人作出来的
注册码长短不一。另,据说注册码似乎有随机性。 ”
    由这段话,我们可以认识到:程序判断注册码的地点不集中,有很多操作可以触发判断注册码
正确与否的例程。但是,由实际分析结果来看,其所说的用bpr断点找到各个判断例程的方法在对付这个
程序时并不适用。原因很简单:该程序判断注册码正确与否的子例程总共有8处之多。而触发这8个判断
代码的条件我们并不清楚。就我跟踪的结果来看,一次是在输入注册码时(这也是最明显的一处,
《论坛精华2》中,xiA Qin的爆破就是在这段子例程中修改代码的);一次是在你关闭程序时;一次是
在你按照程序提示,重新进入程序时;前面这三处还很容易想到,因为大部分的软件都是在这些时候判
断注册码的正确性。而剩下来的其他4次,其触发事件就很独特了。dr0大客说使用preferences菜单时会
判断一位,就我跟踪来看,并不正确。虽然在使用preferences菜单的过程中,会调用判断注册码的例程,
但此时该例程的作用并不是判断注册码正确与否,而仅仅是程序运行的正常需要。倒是在按下工具条上的
“open”快捷纽时,会触发一处注册码判断。至于其他几处,我没有去细找。因为此时,我已经发现了程
序判断注册码的核心子例程了——也就是说,程序每个判断注册码的地方,都要调用该字例程。找到核心
子例程之后,要找判断点就容易多了。该子例程的作用其实很简单——把一个16进制数转成字符串存放在
指定的地点同时作灭零处理(这也就是后面计算注册码步骤中的第7步的原因)。下面就是该程序判断注册
码的核心子例程:

* Referenced by a CALL at Addresses:
|:00419A28  , :004218EA  , :004575A5  , :0045EAFF  , :0045EB2F 
|:0045EB5F  , :0045EB8F  , :0045F6F6  , :004622C1  , :0046264E 
|:004626A4  , :0047B952  , :0049CD24                            ;总共13处调用
|
:0048FBE0 55                      push ebp
:0048FBE1 8BEC                    mov ebp, esp
:0048FBE3 83EC20                  sub esp, 00000020
:0048FBE6 8B4508                  mov eax, dword ptr [ebp+08]
:0048FBE9 56                      push esi
:0048FBEA 8945E8                  mov dword ptr [ebp-18], eax
:0048FBED 8945E0                  mov dword ptr [ebp-20], eax
:0048FBF0 8D4510                  lea eax, dword ptr [ebp+10]
:0048FBF3 C745EC42000000          mov [ebp-14], 00000042
:0048FBFA 50                      push eax
:0048FBFB 8D45E0                  lea eax, dword ptr [ebp-20]
:0048FBFE FF750C                  push [ebp+0C]
:0048FC01 C745E4FFFFFF7F          mov [ebp-1C], 7FFFFFFF
:0048FC08 50                      push eax
:0048FC09 E897470000              call 004943A5
:0048FC0E 83C40C                  add esp, 0000000C
:0048FC11 FF4DE4                  dec [ebp-1C]
:0048FC14 8BF0                    mov esi, eax
:0048FC16 7808                    js 0048FC20
:0048FC18 8B45E0                  mov eax, dword ptr [ebp-20]
:0048FC1B 802000                  and byte ptr [eax], 00
:0048FC1E EB0D                    jmp 0048FC2D

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0048FC16(C)
|
:0048FC20 8D45E0                  lea eax, dword ptr [ebp-20]
:0048FC23 50                      push eax
:0048FC24 6A00                    push 00000000
:0048FC26 E862460000              call 0049428D
:0048FC2B 59                      pop ecx
:0048FC2C 59                      pop ecx

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0048FC1E(U)
|
:0048FC2D 8BC6                    mov eax, esi
:0048FC2F 5E                      pop esi
:0048FC30 C9                      leave
:0048FC31 C3                      ret

    在这里,很清楚地看到,主程序中总共有13处调用了该核心子例程,但并不是每一次调用都是用
来判断注册码的。我们如何区分?很简单,分别到这13处调用处看一下就知道了。下面举一例说明:

:004199F4 83C117                  add ecx, 00000017  <====此处,ecx是根据你名字算出来的一个值
:004199F7 B8ABAAAA2A              mov eax, 2AAAAAAB
:004199FC F7E9                    imul ecx
:004199FE 8BC2                    mov eax, edx
:00419A00 C1E81F                  shr eax, 1F
:00419A03 8D440203                lea eax, dword ptr [edx+eax+03]
:00419A07 8D0CC500000000          lea ecx, dword ptr [8*eax+00000000]
:00419A0E 2BC8                    sub ecx, eax
:00419A10 81E10F000080            and ecx, 8000000F
:00419A16 7905                    jns 00419A1D
:00419A18 49                      dec ecx
:00419A19 83C9F0                  or ecx, FFFFFFF0
:00419A1C 41                      inc ecx

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00419A16(C)
|
:00419A1D 51                      push ecx
:00419A1E 8D542418                lea edx, dword ptr [esp+18]

* Possible StringData Ref from Data Obj ->"%1X"
                                  |
:00419A22 6880534F00              push 004F5380
:00419A27 52                      push edx
:00419A28 E8B3610700              call 0048FBE0    <====调用核心子例程
:00419A2D 8B86F0020000            mov eax, dword ptr [esi+000002F0]
:00419A33 83C40C                  add esp, 0000000C
:00419A36 8B00                    mov eax, dword ptr [eax]  <====eax指向注册码
:00419A38 8378F807                cmp dword ptr [eax-08], 00000007  <====长度大于7?
:00419A3C 7C0B                    jl 00419A49      <====not great,bad guy!
:00419A3E 8A4006                  mov al, byte ptr [eax+06] <====注册码第7位
:00419A41 8A4C2414                mov cl, byte ptr [esp+14] <====由名字计算得出的校验值
:00419A45 3AC1                    cmp al, cl                <====比较
:00419A47 740C                    je 00419A55              <====equal,good guy!
    这就很容易看出,419A28处,一定是一个判断点(但是它的判断触发事件,我们并不知道!)。

    再来看一处不是判断点的代码,区别就很明显了:

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0045EAD5(C)
|
:0045EAE7 D9442420                fld dword ptr [esp+20]
:0045EAEB DC742414                fdiv qword ptr [esp+14]
:0045EAEF 83EC08                  sub esp, 00000008
:0045EAF2 8D442430                lea eax, dword ptr [esp+30]
:0045EAF6 DD1C24                  fstp qword ptr [esp]

* Possible StringData Ref from Data Obj ->"%.2f"
                                  |
:0045EAF9 68C0614F00              push 004F61C0
:0045EAFE 50                      push eax
:0045EAFF E8DC100300              call 0048FBE0
:0045EB04 83C410                  add esp, 00000010
:0045EB07 8D4C2428                lea ecx, dword ptr [esp+28]
:0045EB0B 51                      push ecx
:0045EB0C 8D8E00030000            lea ecx, dword ptr [esi+00000300]
:0045EB12 E88D450400              call 004A30A4
:0045EB17 D9442424                fld dword ptr [esp+24]
:0045EB1B DC742414                fdiv qword ptr [esp+14]
:0045EB1F 83EC08                  sub esp, 00000008
:0045EB22 8D542430                lea edx, dword ptr [esp+30]
:0045EB26 DD1C24                  fstp qword ptr [esp]

    整段程序没有一个比较语句。而且,在跟踪时我们可以通过“d”和“?”发现,该段程序和我们
输入的注册码以及名字没有任何联系,所以我们可以断定:此处不是在比较注册码。

    下面,我把这13处调用及其判断对象整理如下供大家参考:

    (L=注册码长度;Str[]=注册码字符串)
  序号          地址          是否判断点            判断对象     
    01        00419A28            是              L>7 AND Str[6]
    02        004218EA            是              L>A AND Str[9]
    03        004575A5            是              L>8 AND Str[7]
    04        0045EAFF            否
    05        0045EB2F            否
    06        0045EB5F            否
    07        0045EB8F            否
    08        0045F6F6            否
    09        004622C1            是              L>B AND Str[A]
    10        0046264E            是              L>5 AND Str[4]
    11        004626A4            是            Str[2] AND Str[3]
    12        0047B952            是            Str[0] AND Str[1]
    13        0049CD24            是              L>9 AND Str[8]
    由该表我们可以发现,注册码至少应为11位长,程序只判断前11位中的10位(除开第6位,也就是
Str[5])。由于未发现其他判断处,所以,程序的注册码的长度应该只要大于11就可以。其中,第1,2位是
通过后面的号码推算出来的。而其他的(从第3位往后,除开第6位,到第11位为止)号码则由名字推算出来。
    具体算法可以表示如下:

(1)    由名字得到一个Magic Number:

    SUM=1;
    for(i=0;i<strlen(name);i++) SUM+=name[i];

    得到的SUM就是我们所需要的Magic Number。

(2)    由Magic Number算出注册码的第5和7--11位。(具体算法见注册机源码,此处从略)

(3)    由名字算出注册码的第3,4位:
    (num[]是一张256个Word的数据表,详见注册机源码)
    ax=0;dx=0;bx=0;
    for (i=0;i<strlen(name);i++)
    {
     dx=ax;
     dx&=0xff;
     dx^=name[i];
     bx=ax>>8;
     ax=num[dx];
     ax^=bx;
    }
    得出的ax中的ah,就是注册码的第3,4位。

(4)    注册码的第6位任意(在我的注册机里,假设它为0)。

(5)    由注册码的第3--11算出注册码的第1,2位。
    (num[]和第三步的含义相同;newstr[]指向注册码第2位后面的字符串)
    ax=0;dx=0;bx=0;
    for (i=0;i<strlen(newstr);i++)
    {
     dx=ax;
     dx&=0xff;
     dx^=newstr[i];
     bx=ax>>8;
     ax=num[dx];
     ax^=bx;
    }
(6)    得出的ax中的ah,就是注册码的第1,2位

(7)    如果注册码的第1位为0,则需要延长注册码的长度,以使第一位不为0。(在我的注册机里,没有
        使注册码延长的代码。我是通过让用户改变用户名实现的。如果哪位对它有兴趣的话,可以自己
    添加该功能)。
             
                  ------------------===== Last Words=====------------------
    该程序的破解,可以说是动态跟踪+静态分析的典范:为了找全程序的注册监测点,我们需要在多
次动态跟踪,熟悉程序注册流程的基础上对静态反汇编代码仔细分析;分析出来检测点以后,又要由动态
跟踪确定各检测点调用核心子例程的各自参数的含义 。两者结合,才能彻底搞清楚程序的注册算法,从而
写出注册机。
    常看到有人问注册机里面的那张很大的表格是怎么的来的。呵呵……当然不是抄下来的拉。你可以
在Trw2000里“d memeory address l length >filename”把数表所在的内存数据保存下来。再用EditPlus
(也就是在这篇文章里挨揍的那个程序)的“列块选择”功能去掉一些不必要的空格和地址数据以及ASCII
码数据,再用Tc2.0编一个小程序就可以转成你想要的格式的表格了。这个程序编起来不是很复杂,有兴趣
的朋友就当作是学习C语言文件操作的一个入门吧!
    注册机的编译器选择也很重要。在32位程序大行其道的今天,Tc2.0实在是有点力不从心-----虽然
它是最经典的编译器之一。相形之下,Vc++ 6.0则方便得多。而我手头现在没有Vc++,所以只好将就着用
M32asmV5了。我是临时报佛脚地学了一下Win32Asm^_^,所以代码写得比较乱。有条件的朋友建议用Vc++6.0
编注册机,肯定好看多了!:)

                   

------------------=====KeyGen=====------------------
批处理编译文件:makekey.bat
---------------------------------------Cut From Here------------------------------------------
@echo off
set include=d:\masm32\include
set lib=d:\masm32\lib
set path=d:\masm32\bin


if not exist rsrc.rc goto over1
d:\masm32\bin\rc /v rsrc.rc
d:\masm32\bin\cvtres /machine:ix86 rsrc.res
:over1

if exist ep_keygen.obj del ep_keygen.obj
if exist ep_keygen.exe del ep_keygen.exe

d:\masm32\bin\ml /c /coff ep_keygen.asm
if errorlevel 1 goto errasm

if not exist rsrc.obj goto nores

d:\masm32\bin\Link /SUBSYSTEM:WINDOWS ep_keygen.obj rsrc.obj
if errorlevel 1 goto errlink


goto TheEnd

:nores
d:\masm32\bin\Link /SUBSYSTEM:WINDOWS ep_keygen.obj
if errorlevel 1 goto errlink

goto TheEnd

:errlink
echo _
echo Link error
goto TheEnd

:errasm
echo _
echo Assembly Error
goto TheEnd

:TheEnd
-----------------------------------------Cut End---------------------------------------------

资源文件:rsrc.rc
---------------------------------------Cut From Here------------------------------------------
#include          <Resource.h>
#define          IDGEN    10
#define            DLG_MAIN    100
#define            EDIT1        11
#define            EDIT2        12

DLG_MAIN      DIALOGEX    100,150,250,60
STYLE          DS_MODALFRAME|WS_POPUP|WS_VISIBLE|WS_CAPTION|WS_SYSMENU|WS_THICKFRAME
CAPTION          "EditPlus2.01b KenGen By Robotow"
FONT          9,"宋体"

BEGIN
CONTROL        "Name:",-1,"Static",SS_LEFT,10,13,40,17
CONTROL        "SN:"  ,-2,"Static",SS_CENTER,10,40,20,17
CONTROL        ""    ,11,"Edit",ES_LEFT,30,13,150,10
CONTROL        ""    ,12,"Edit",ES_LEFT,30,40,150,10
CONTROL        "GENERATE",IDGEN,"BUTTON",BS_PUSHBUTTON,200,11,40,15
CONTROL        "EXIT",IDCLOSE,"BUTTON",BS_PUSHBUTTON,200,36,41,14
END
-----------------------------------------Cut End----------------------------------------------

程序源代码:ep_keygen.asm
---------------------------------------Cut From Here------------------------------------------
        .386
        .model flat,stdcall
        option casemap:none
include        windows.inc
include        user32.inc
include        kernel32.inc
include        comctl32.inc
include        comdlg32.inc
include        masm32.inc

includelib      masm32.lib
includelib      user32.lib
includelib      kernel32.lib
includelib      comctl32.lib
includelib      comdlg32.lib

DLG_MAIN        equ    100
IDGEN          equ    10
Edit1          equ    11
Edit2          equ    12
       

_ProcDlgMain    PROTO    :DWORD,:DWORD,:DWORD,:DWORD
HtoA            PROTO    :DWORD,:DWORD,:BYTE

        .data?
szSN    db    11 dup(?)

        .data
num dw 00000h,0C0C1h,0C181h,00140h,0C301h,003C0h,00280h,0C241h,0C601h,006C0h,00780h,0C741h
    dw 00500h,0C5C1h,0C481h,00440h,0CC01h,00CC0h,00D80h,0CD41h,00F00h,0CFC1h,0CE81h,00E40h
    dw 00A00h,0CAC1h,0CB81h,00B40h,0C901h,009C0h,00880h,0C841h,0D801h,018C0h,01980h,0D941h
    dw 01B00h,0DBC1h,0DA81h,01A40h,01E00h,0DEC1h,0DF81h,01F40h,0DD01h,01DC0h,01C80h,0DC41h
    dw 01400h,0D4C1h,0D581h,01540h,0D701h,017C0h,01680h,0D641h,0D201h,012C0h,01380h,0D341h
    dw 01100h,0D1C1h,0D081h,01040h,0F001h,030C0h,03180h,0F141h,03300h,0F3C1h,0F281h,03240h
    dw 03600h,0F6C1h,0F781h,03740h,0F501h,035C0h,03480h,0F441h,03C00h,0FCC1h,0FD81h,03D40h
    dw 0FF01h,03FC0h,03E80h,0FE41h,0FA01h,03AC0h,03B80h,0FB41h,03900h,0F9C1h,0F881h,03840h
    dw 02800h,0E8C1h,0E981h,02940h,0EB01h,02BC0h,02A80h,0EA41h,0EE01h,02EC0h,02F80h,0EF41h
    dw 02D00h,0EDC1h,0EC81h,02C40h,0E401h,024C0h,02580h,0E541h,02700h,0E7C1h,0E681h,02640h
    dw 02200h,0E2C1h,0E381h,02340h,0E101h,021C0h,02080h,0E041h,0A001h,060C0h,06180h,0A141h
    dw 06300h,0A3C1h,0A281h,06240h,06600h,0A6C1h,0A781h,06740h,0A501h,065C0h,06480h,0A441h
    dw 06C00h,0ACC1h,0AD81h,06D40h,0AF01h,06FC0h,06E80h,0AE41h,0AA01h,06AC0h,06B80h,0AB41h
    dw 06900h,0A9C1h,0A881h,06840h,07800h,0B8C1h,0B981h,07940h,0BB01h,07BC0h,07A80h,0BA41h
    dw 0BE01h,07EC0h,07F80h,0BF41h,07D00h,0BDC1h,0BC81h,07C40h,0B401h,074C0h,07580h,0B541h
    dw 07700h,0B7C1h,0B681h,07640h,07200h,0B2C1h,0B381h,07340h,0B101h,071C0h,07080h,0B041h
    dw 05000h,090C1h,09181h,05140h,09301h,053C0h,05280h,09241h,09601h,056C0h,05780h,09741h
    dw 05500h,095C1h,09481h,05440h,09C01h,05CC0h,05D80h,09D41h,05F00h,09FC1h,09E81h,05E40h
    dw 05A00h,09AC1h,09B81h,05B40h,09901h,059C0h,05880h,09841h,08801h,048C0h,04980h,08941h
    dw 04B00h,08BC1h,08A81h,04A40h,04E00h,08EC1h,08F81h,04F40h,08D01h,04DC0h,04C80h,08C41h
    dw 04400h,084C1h,08581h,04540h,08701h,047C0h,04680h,08641h,08201h,042C0h,04380h,08341h
    dw 04100h,081C1h,08081h,04040h,0
szMess          db      " 名字的长度必须大于5!",0
szErrName      db      "请修改用户名!",0
szCaption      db      "Error!"
hInstance      dd      0
szName          db        20 dup(0)
lname          dd        0
lSum          dd        0
szTemp          db        20 dup(0)

        .code
        HtoA proc uses ebx edx ecx edi,Hex:DWORD,lpString:DWORD,long:BYTE
        xor ecx,ecx
        mov cl,long
        mov eax,Hex
        mov ebx,eax
        mov edi,lpString
Hloop:
        and eax,0fh
        add eax,30h
        .if eax>'9'
            add eax,7
        .endif
        mov byte ptr[edi+ecx-1],al
        mov eax,ebx
        shr eax,4
        mov ebx,eax
        loop Hloop
        ret
        HtoA endp
           
        _ProcDlgMain proc uses ebx edi esi edx ecx,hWnd:DWORD,wMsg:DWORD,wParam:DWORD,lParam:DWORD
        mov    eax,wMsg
        .if    eax==WM_CLOSE
                invoke  EndDialog,hWnd,NULL
        .elseif eax==WM_COMMAND
                mov  eax,wParam
                and  eax,0ffffh
                .if    eax==IDGEN
                        invoke  GetDlgItemText, hWnd,Edit1,offset szName,20
        invoke  lnstr,offset szName
        .if    eax<5
            invoke  MessageBox,NULL,offset szMess,offset szCaption,MB_OK
            mov    eax,FALSE
            ret
        .endif
        mov    ecx,eax
        mov    esi,eax
        mov    lname,eax
        xor    eax,eax
        
        loop1:
            mov edx, eax                   
                          xor ebx, ebx     
            push esi
            sub  esi,ecx
            mov bl,[szName+esi]         
            pop  esi
            and edx, 0FFh
            xor edx, ebx                   
            xor ebx, ebx                   
            mov bl, ah                     
            mov ax, word ptr [num+2*edx]
            xor ax, bx                     
            loop loop1

        and    eax,0ffffh
        shr    eax,8
        invoke    HtoA,eax,offset szTemp,2
        mov    al,byte ptr[szTemp]
        mov    byte ptr[szSN+2],al              ;注册码第3位
        mov    al,byte ptr [szTemp+1]
        mov    byte ptr [szSN+3],al              ;注册码第4位
        mov    byte ptr [szSN+5],'0'            ;注册码第6位

        mov    ecx,lname
        mov    esi,ecx
        mov    lSum,1
        mov    edi,0ffh

        gen_sum:
            push    esi
            sub    esi,ecx
            mov    dl,byte ptr [szName+esi]
            pop    esi
            and        dx,0ffh
            add    lSum,edx
            loop    gen_sum
        
        mov ecx,lSum

        lea ecx, dword ptr [ecx+8*ecx+0Ah]
        mov eax, 55555556h
        imul ecx
        mov eax, edx
        shr eax, 1Fh
        lea ecx, dword ptr [edx+eax+24h]
        and ecx, 8000000Fh
        jns next5
        dec ecx
        or ecx, 0FFFFFFF0h
        inc ecx
    next5:
        invoke    HtoA,ecx,offset szTemp,1
        mov    dl,byte ptr[szTemp]         
        mov    byte ptr[szSN+4],dl            ;注册码第5位
        
        mov    ecx,lSum

        add ecx, 00000017h                   
        mov eax, 2AAAAAABh                     
        imul ecx                             
        mov eax, edx                         
        shr eax, 1Fh                           
        lea eax, dword ptr [edx+eax+03]       
        lea ecx, dword ptr [8*eax+00000000]   
        sub ecx, eax                         
        and ecx, 8000000Fh                     
        jns next7
        dec ecx                               
        or ecx, 0FFFFFFF0h                     
        inc ecx         
    next7:
        invoke    HtoA,ecx,offset szTemp,1
        mov    dl,byte ptr[szTemp]
        mov    byte ptr [szSN+6],dl          ;注册码第7位
        
        mov    ecx,lSum
        lea ecx, dword ptr [ecx+2*ecx+13h]
        mov eax, 38E38E39h               
        imul ecx                       
        sar edx, 1                     
        mov eax, edx                   
        shr eax, 1Fh                     
        add edx, eax                   
        and edx, 8000000Fh               
        jns next8                   
        dec edx                         
        or edx, 0FFFFFFF0h               
        inc edx     
    next8:
        invoke    HtoA,edx,offset szTemp,1         
        mov    dl,byte ptr[szTemp]
        mov    byte ptr [szSN+7],dl          ;注册码第8位

                    mov ecx,lSum
                lea ecx, dword ptr [ecx+4*ecx+0Bh]
                mov eax, 66666667h               
                imul ecx                       
                sar edx, 1                     
                mov eax, edx                   
                shr eax, 1Fh                     
                add edx, eax                   
                and edx, 8000000Fh               
                jns next9                   
                dec edx                         
                or edx, 0FFFFFFF0h               
                inc edx 
    next9:
        invoke    HtoA,edx,offset szTemp,1
        mov    dl,byte ptr[szTemp]
        mov    byte ptr [szSN+8],dl        ;注册码第9位

        mov    ecx,lSum
        lea eax, dword ptr [ecx+2*ecx+27h]
        cdq                             
        and edx, 00000007               
        add eax, edx                   
        sar eax, 03                     
        and eax, 8000000Fh               
        jns nexta                   
        dec eax                         
        or eax, 0FFFFFFF0h               
        inc eax 
    nexta:
        invoke    HtoA,eax,offset szTemp,1
        mov    dl,byte ptr[szTemp]
        mov    byte ptr [szSN+9],dl        ;注册码第10位

        mov    edi,lSum
        lea ecx, dword ptr [edi+0Ah]
        mov eax, 55555556h         
        imul ecx                 
        mov eax, edx             
        shr eax, 1Fh               
        add edx, eax             
        shl edx, 03               
        and edx, 8000000Fh         
        jns nextb             
        dec edx                   
        or edx, 0FFFFFFF0h         
        inc edx 
    nextb:
        invoke    HtoA,edx,offset szTemp,1         
        mov    dl,byte ptr[szTemp]
        mov    byte ptr [szSN+10],dl        ;注册码第11位
        
        mov    ecx,9
        xor    eax,eax
        mov    esi,11
        loop2:
            mov edx, eax                   
                              xor ebx, ebx                 
            push esi
            sub esi,ecx
            mov bl, byte ptr [szSN+esi]
            pop esi
            movzx edx,dl               
            xor edx, ebx                   
            xor ebx, ebx                   
            mov bl, ah                     
            mov ax, word ptr [num+edx*2]
            xor ax, bx                     
            loop loop2
        
        and    eax,0ffffh
        shr    eax,8
        invoke    HtoA,eax,offset szTemp,2
        mov    al,byte ptr [szTemp]
            .if al==30    ;如果注册码第一位为0,重新输入用户名
                invoke MessageBox,0,offset szErrName,offset szCaption,MB_OK
                mov eax,FALSE
                ret
            .endif
        mov    byte ptr [szSN],al
        mov    al,byte ptr [szTemp+1]
        mov    byte ptr [szSN+1],al
           
        invoke    SetDlgItemText,hWnd,Edit2,offset szSN
        mov    eax,FALSE
                    ret
                .elseif eax==IDCLOSE
                        invoke  EndDialog,hWnd,NULL
                .endif
        .else
                mov    eax,FALSE
                ret
        .endif
        mov    eax,TRUE
        ret

_ProcDlgMain endp


start: 
        invoke  InitCommonControls
        invoke  GetModuleHandle,NULL
        mov    hInstance,eax
        invoke  DialogBoxParam,hInstance,DLG_MAIN,NULL,offset _ProcDlgMain,0
        invoke  ExitProcess,NULL
end    start

end   
-----------------------------------------Cut End--------------------------------------------

                  ------------------=====Ending=====------------------
    aNY qUESTIONS,pLEASE eMAIL tO: luoyi.ly@yeah.net

    Thanks To All!Good Luck!