• 标 题:EditPlus v2.12en破解全过程
  • 作 者:icytear
  • 时 间:2004-12-22,11:36
  • 链 接:http://bbs.pediy.com

第一篇破文,还望大家多多指教!!!!

【破解作者】icytear
【作者邮箱】icytear@126.com
【使用工具】SoftIce4.3.1,IDA4.5,PEiD0.92
【破解平台】WindowsXP Pro SP1.
【破解日期】2004.12.7 - 2004.12.22
【软件名称】EditPlus v2.12en
【软件大小】913,408 BYTE
【加壳方式】无壳
【下载地址】
【软件简介】

    EditPlus是Internet时代的32位文本编辑程序,HTML编辑及程序员的Windows平台编辑器。它可以充分的替换记事本,它也提供网页作家及程序设计师许多强悍的功能。对于HTML、CSS、PHP、ASP、Perl、C/C++、Java、JavaScript及VBScript的语法突显。当然,它也可以在自定义语法文件后扩充其他的程序语言。嵌合网页浏览器作HTML页的预览,及FTP命令做本地文件上传到FTP服务器。其他功能还包含HTML工具栏、用户工具、列号、标尺、URL突显。自动完成、剪贴文本、行列选择、强大的搜索与替换、多重撤消/重做、拼写检测、自定义键盘快捷键、以及更多。

【破解声明】本文仅供技术交流、学习之用。
【破解内容】

1. 用PEiD打开editplus发现无壳。

2. 打开程序出现注册对话框,点击输入注册码,Username输入"icytear",Regcode输入"280060443",下断点
    addr editplus
    bpx GetWindowTextA
   单击注册按钮,程序被中断,按F12退出GetWindowTextA函数,到如下位置


001B:004BFC91    PUSH    DWORD PTR [ESP+08]
001B:004BFC95    PUSH    DWORD PTR [ESP+08]
001B:004BFC99    PUSH    DWORD PTR [ECX+1C]
001B:004BFC9C    CALL    [USER32!GetWindowTextA] 
001B:004BFCA2    JMP    004BFCB6        ;---->按F12后到这里,goto #exit
...
001B:004BFCB6    RET    0008            ;#exit

按2次F10执行代码,程序返回到#R01

001B:0047C850    MOV    EAX,[ESP+0C]
001B:0047C854    MOV    ECX,[ESP+04]
001B:0047C858    PUSH    ESI
001B:0047C859    MOV    ESI,[ESP+0C]
001B:0047C85D    PUSH    EAX
001B:0047C85E    PUSH    ESI
001B:0047C85F    CALL    004BFC8A
001B:0047C864    MOV    AL,[ESI]        ;#R01---->程序返回到这里
001B:0047C866    CMP    AL,20            ;---->从前向后比较字符是否为空格
001B:0047C868    JZ    0047C86E        ;---->为空格则跳转,继续比较下一个字符
001B:0047C86A    CMP    AL,09            ;---->比较这个字符是否为制表符
001B:0047C86C    JNZ    0047C871        ;---->如果不为空格也不是制表符则跳转到#J01↓
001B:0047C86E    INC    ESI            ;---->如果字符为空格或制表符则删除
001B:0047C86F    JMP    0047C864        ;---->继续比较下一个字符(跳转到#R01↑)
===>:0047C871    PUSH    ESI            ;↑#J01---->用户名入栈
001B:0047C872    CALL    [KERNEL32!lstrlen]    ;---->不用说了吧,得到用户名长度
001B:0047C878    MOV    ECX,[ESP+14]
001B:0047C87C    TEST    EAX,EAX            ;---->检查是否输入了用户名
001B:0047C87E    MOV    [ECX],EAX        ;---->把结果保存起来
001B:0047C880    JLE    0047C899
001B:0047C882    MOV    EAX,[ECX]
001B:0047C884    MOV    DL,[ESI+EAX-01]        ;---->取出最后一个字符
001B:0047C888    CMP    DL,20            ;---->比较是否为空格
001B:0047C88B    JZ    0047C892
001B:0047C88D    CMP    DL,09
001B:0047C890    JNZ    0047C899
001B:0047C892    DEC    EAX            ;---->继续向前比较
001B:0047C893    TEST    EAX,EAX
001B:0047C895    MOV    [ECX],EAX
001B:0047C897    JG    0047C882
001B:0047C899    MOV    EDX,[ECX]
001B:0047C89B    MOV    BYTE PTR [ESI+EDX],00    ;---->删除末尾的空格和制表符
001B:0047C89F    MOV    EAX,[ECX]
001B:0047C8A1    TEST    EAX,EAX            ;
001B:0047C8A3    JNZ    0047C8AD
001B:0047C8A5    PUSH    40
001B:0047C8A7    CALL    [USER32!MessageBeep]
001B:0047C8AD    MOV    EAX,ESI
001B:0047C8AF    POP    ESI
001B:0047C8B0    RET

上面的代码大概的意思就是:检查输入的用户名开始和末尾是否为有效字符(非空格和制表符)。
然后返回到#R02:

...

001B:0047C8E1    CALL    0047C850        ;---->取输入的用户名
001B:0047C8E6    MOV    EBX,EAX            ;#R02---->D EAX;EAX指向的就是输入的用户名icytear
001B:0047C8E8    MOV    EAX,[ESP+20]        ;---->[esp+20]存的是用户名的长度
001B:0047C8EC    ADD    ESP,10            ;---->平衡堆栈
001B:0047C8EF    TEST    EAX,EAX            ;---->检查输入的用用户名是否有效
001B:0047C8F1    JZ    0047C9B1
001B:0047C8F7    LEA    EAX,[ESP+0C]        ;---->SS:[ESP+0C] = 00000034
001B:0047C8FB    LEA    ECX,[ESP+14]        ;---->SS:[ESP+14] = 0
001B:0047C8FF    PUSH    EAX
001B:0047C900    PUSH    31
001B:0047C902    LEA    EDX,[ESI+5C]
001B:0047C905    PUSH    ECX
001B:0047C906    PUSH    EDX
001B:0047C907    CALL    0047C850        ;---->用上面同样的方法得到输入的注册码
001B:0047C90C    MOV    ECX,[ESP+1C]        ;---->[ESP+1C]输入的注册码长度
001B:0047C910    ADD    ESP,10            ;---->平衡堆栈
001B:0047C913    TEST    ECX,ECX            ;---->检查有效注册码长度
001B:0047C915    MOV    EDI,EAX
001B:0047C917    JZ    0047C9B1
001B:0047C91D    XOR    EAX,EAX
001B:0047C91F    TEST    ECX,ECX
001B:0047C921    JLE    0047C93B
001B:0047C923    XOR    ECX,ECX
001B:0047C925    MOV    CL,[EDI+EAX]        ;---->从输入的注册码取一个字符
001B:0047C928    INC    EAX            ;---->移动指针指向下一个字符
001B:0047C929    MOV    DL,[ECX+00522C90]    ;---->查表,把小写转换成大写
001B:0047C92F    MOV    [EDI+EAX-01],DL
001B:0047C933    MOV    ECX,[ESP+0C]
001B:0047C937    CMP    EAX,ECX
001B:0047C939    JL    0047C923
001B:0047C93B    PUSH    EDI            ;---->把输入的注册码入栈
001B:0047C93C    PUSH    EBX            ;---->输入的用户名入栈
001B:0047C93D    CALL    0047CA60        ;#C01---->关键调用
001B:0047C942    ADD    ESP,08
001B:0047C945    TEST    EAX,EAX
001B:0047C947    JNZ    0047C961        ;---->注册成功的关键
001B:0047C949    PUSH    FF
001B:0047C94B    PUSH    10
001B:0047C94D    PUSH    00005F81
001B:0047C952    CALL    004C7E94        ;---->这里就弹出错误对话框了
001B:0047C957    POP    EDI
001B:0047C958    POP    ESI
001B:0047C959    POP    EBX
001B:0047C95A    ADD    ESP,00000230
001B:0047C960    RET
001B:0047C961    PUSH    EDI
001B:0047C962    PUSH    EBX
001B:0047C963    CALL    0047C3B0
...

进入#C01 (CALL    0047CA60)继续跟踪

001B:0047CA60    SUB    ESP,0C
001B:0047CA63    PUSH    EBX            ;---->用户名入栈
001B:0047CA64    MOV    EBX,[KERNEL32!lstrlen]
001B:0047CA6A    PUSH    ESI
001B:0047CA6B    PUSH    EDI
001B:0047CA6C    MOV    EDI,[ESP+1C]        ;---->edi指向了用户名
001B:0047CA70    PUSH    EDI
001B:0047CA71    CALL    EBX            ;---->call [lstrlen],得到用户名长度
001B:0047CA73    MOV    ESI,EAX
001B:0047CA75    TEST    ESI,ESI            ;---->
001B:0047CA77    JNZ    0047CA80        ;---->跳到#C01.C1
001B:0047CA79    POP    EDI
001B:0047CA7A    POP    ESI
001B:0047CA7B    POP    EBX
001B:0047CA7C    ADD    ESP,0C
001B:0047CA7F    RET

001B:0047CA80    CALL    0047C9C0        ;#C01.C1---->跟进去
001B:0047CA85    PUSH    ESI
001B:0047CA86    PUSH    EDI
001B:0047CA87    PUSH    00
001B:0047CA89    CALL    0047CA10        ;#C01.C2---->
001B:0047CA8E    ADD    ESP,0C
001B:0047CA91    AND    EAX,0000FFFF
001B:0047CA96    PUSH    EAX
001B:0047CA97    LEA    EAX,[ESP+10]
001B:0047CA9B    PUSH    0051824C
001B:0047CAA0    PUSH    EAX
001B:0047CAA1    CALL    004AEABC        ;#C01.C3---->sprintf
001B:0047CAA6    MOV    ESI,[ESP+2C]
001B:0047CAAA    MOV    AL,[ESP+18]
...

进入#C01.C1 (CALL 0047C9C0).

001B:0047C9C0    PUSH    ESI
001B:0047C9C1    PUSH    EDI            
001B:0047C9C2    MOV    ECX,00000080        ;---->计数 0x80
001B:0047C9C7    XOR    EAX,EAX            ;---->EAX清零
001B:0047C9C9    MOV    EDI,00523410
001B:0047C9CE    XOR    ESI,ESI
001B:0047C9D0    REPZ    STOSD            ;---->从00523410~00523610清零
001B:0047C9D2    MOV    EDX,00523410
001B:0047C9D7    MOV    EAX,0000C0C1        ;#loop1--->第一层循环
001B:0047C9DC    MOV    ECX,00000001
001B:0047C9E1    TEST    ESI,ECX            ;#loop2--->第二层循环
001B:0047C9E3    JZ    0047C9E8
001B:0047C9E5    XOR    [EDX],AX
001B:0047C9E8    ADD    EAX,EAX
001B:0047C9EA    SHL    ECX,1
001B:0047C9EC    XOR    EAX,00004003
001B:0047C9F1    CMP    ECX,00000100
001B:0047C9F7    JL    0047C9E1        ;---->跳转到#loop2
001B:0047C9F9    ADD    EDX,02
001B:0047C9FC    INC    ESI
001B:0047C9FD    CMP    EDX,00523610
001B:0047CA03    JL    0047C9D7        ;---->跳转到#loop1
001B:0047CA05    POP    EDI
001B:0047CA06    POP    ESI
001B:0047CA07    RET

以上代码用C语言表示如下:

WORD list[0x80*2];
void sub_0047C9C0()
{
    int i,j=0;
    DWORD ieax,iecx,iesi=0;
    WORD temp;
    for(i=0;i<0x80*2;i++)
        list[i] = 0;
    
    do{
        ieax = 0x0C0C1; //ieax = 49345
        iecx = 1;
        do{    
            if ((iesi&iecx)!=0)
            {
                temp = (WORD)(ieax&0x0FFFF);
                list[j] = list[j]^temp;
            }
            ieax*=2;
            iecx*=2;
            ieax = ieax^0x04003;
            
        }while(iecx<0x100);
        j++;
        iesi++;
    }while(j<0x80*2);
}

从#C01.C1 CALL 出来

...
001B:0047CA85    PUSH    ESI            ;---->用户名长度
001B:0047CA86    PUSH    EDI            ;---->用户名指针入栈
001B:0047CA87    PUSH    00
001B:0047CA89    CALL    0047CA10        ;#C01.C2--->跟进去..
001B:0047CA8E    ADD    ESP,0C
...

进入#C01.C2 (CALL 0047CA10).

001B:0047CA10    MOV    ECX,[ESP+08]        ;---->ECX指向用户名'icytear'
001B:0047CA14    MOV    EAX,[ESP+0C]        ;---->EAX存用户名长度,
001B:0047CA18    PUSH    ESI
001B:0047CA19    LEA    ESI,[EAX+ECX]        ;---->ESI指向了用户名的末尾'\0'
001B:0047CA1C    CMP    ECX,ESI
001B:0047CA1E    JAE    0047CA4A        ;---->如果大于等于则跳转#exit.
001B:0047CA20    MOV    EAX,[ESP+08]
001B:0047CA24    PUSH    EBX
001B:0047CA25    MOV    EDX,EAX            ;#loop3
001B:0047CA27    XOR    EBX,EBX
001B:0047CA29    MOV    BL,[ECX]        ;---->开始逐个字符取用户名
001B:0047CA2B    AND    EDX,000000FF
001B:0047CA31    XOR    EDX,EBX
001B:0047CA33    XOR    EBX,EBX
001B:0047CA35    MOV    BL,AH
001B:0047CA37    MOV    AX,[EDX*2+00523410]    ;---->(00523410~00523610) #C01.C1 CALL 生成的东东
001B:0047CA3F    XOR    AX,BX
001B:0047CA42    INC    ECX
001B:0047CA43    CMP    ECX,ESI
001B:0047CA45    JB    0047CA25        ;---->如果小于跳转倒#loop3↑
001B:0047CA47    POP    EBX
001B:0047CA48    POP    ESI
001B:0047CA49    RET
001B:0047CA4A    MOV    AX,[ESP+08]        ;#exit.
001B:0047CA4F    POP    ESI
001B:0047CA50    RET

##C01.C2 (CALL    0047CA10) C语言代码如下:

char username[]="icytear";

int sub_0047CA10(int n,char *pname,int unamelen)
{
    DWORD ieax,iebx,iedx,temp;
    char *iecx;
    iecx = pname;
    ieax = unamelen;
    if(*iecx == '\0')
        return 0;
    
    ieax = n;
    do{
        //MOV EDX,EAX
        iedx = ieax;
        //XOR EBX,EBX
        iebx = 0;
        //MOV BL,[ECX]
        temp = *iecx;
        iebx = iebx & 0xFFFFFF00;
        iebx = iebx ^ temp;
        //AND EDX,000000FF
        iedx = iedx & 0x000000FF;
        //XOR EDX,EBX
        iedx = iedx ^ iebx;
        //XOR EBX,EBX
        iebx = 0;
        //MOV BL,AH
        temp = ieax;
        temp = temp>>8;
        temp = temp & 0x000000FF;
        iebx = iebx & 0xFFFFFF00;
        iebx = iebx ^ temp;
        //MOV AX,[EDX*2+00523410]
        temp = list[iedx]
        ieax = ieax & 0xFFFF0000;
        ieax = ieax ^ temp;
        //XOR AX,BX
        temp = iebx;
        temp = temp & 0x0000FFFF;
        ieax = ieax ^ temp;

        iecx++;
            
    }while(*iecx != '\0');

    return ieax;
}

从#C01.C2 CALL 出来到 #C01CALL

...
001B:0047CA8E    ADD    ESP,0C            ;---->#C01.C2 CALL 出来到这里
001B:0047CA91    AND    EAX,0000FFFF        ;---->AX是用用户名算出的结果
001B:0047CA96    PUSH    EAX
001B:0047CA97    LEA    EAX,[ESP+10]
001B:0047CA9B    PUSH    0051824C        ;---->;"%02X"
001B:0047CAA0    PUSH    EAX            ;
001B:0047CAA1    CALL    004AEABC        ;---->sprintf,把用户名算出的16进制结果以字符形式保存
001B:0047CAA6    MOV    ESI,[ESP+2C]
001B:0047CAAA    MOV    AL,[ESP+18]        ;---->[ESP+18] = "5CF",由"icytear"算出的值
001B:0047CAAE    ADD    ESP,0C
001B:0047CAB1    MOV    CL,[ESI+02]        ;---->[ESI+02] 注册码的第三个字符
001B:0047CAB4    LEA    EDI,[ESI+02]
001B:0047CAB7    CMP    CL,AL            ;---->比较,在这里比较注册码第三个字符是否为'5'
001B:0047CAB9    JZ    0047CAC4
001B:0047CABB    POP    EDI
001B:0047CABC    POP    ESI
001B:0047CABD    XOR    EAX,EAX
001B:0047CABF    POP    EBX
001B:0047CAC0    ADD    ESP,0C
001B:0047CAC3    RET
001B:0047CAC4    MOV    DL,[ESI+03]        ;---->注册码第四个字符
001B:0047CAC7    MOV    AL,[ESP+0D]        ;---->"5CF"的第二个字符
001B:0047CACB    CMP    DL,AL            ;---->比较注册码的第四个字符是否为'C'
001B:0047CACD    JZ    0047CAD8
001B:0047CACF    POP    EDI
001B:0047CAD0    POP    ESI
001B:0047CAD1    XOR    EAX,EAX
001B:0047CAD3    POP    EBX
001B:0047CAD4    ADD    ESP,0C
001B:0047CAD7    RET
001B:0047CAD8    PUSH    ESI            ;---->注册码
001B:0047CAD9    CALL    EBX            ;---->KERNEL!lstrlen
001B:0047CADB    SUB    EAX,02
001B:0047CADE    PUSH    EAX            ;---->EAX == 注册码长度 - 2;
001B:0047CADF    PUSH    EDI            ;---->第三位以后的注册码
001B:0047CAE0    PUSH    00
001B:0047CAE2    CALL    0047CA10        ;---->前面出现过这个函数,用注册码第三为以后的字符计算出一个值
001B:0047CAE7    ADD    ESP,0C
001B:0047CAEA    AND    EAX,0000FFFF
001B:0047CAEF    PUSH    EAX
001B:0047CAF0    LEA    EAX,[ESP+10]
001B:0047CAF4    PUSH    0051824C        ;---->"%02X"
001B:0047CAF9    PUSH    EAX
001B:0047CAFA    CALL    004AEABC        ;---->sprintf
001B:0047CAFF    MOV    CL,[ESI]        ;---->取注册码的第一个字符
001B:0047CB01    MOV    AL,[ESP+18]        ;---->由第三位以后的注册码计算出的值(第一个字符)
001B:0047CB05    ADD    ESP,0C
001B:0047CB08    CMP    CL,AL            ;---->比较注册码的第一位
001B:0047CB0A    JZ    0047CB15
001B:0047CB0C    POP    EDI
001B:0047CB0D    POP    ESI
001B:0047CB0E    XOR    EAX,EAX
001B:0047CB10    POP    EBX
001B:0047CB11    ADD    ESP,0C
001B:0047CB14    RET
001B:0047CB15    MOV    DL,[ESI+01]        ;---->取注册码的第二个字符
001B:0047CB18    MOV    CL,[ESP+0D]        ;---->由第三位以后的注册码计算出的值(第二个字符)
001B:0047CB1C    XOR    EAX,EAX
001B:0047CB1E    CMP    DL,CL            ;---->比较注册码的第二位
001B:0047CB20    POP    EDI
001B:0047CB21    POP    ESI
001B:0047CB22    SETZ    AL
001B:0047CB25    POP    EBX
001B:0047CB26    ADD    ESP,0C
001B:0047CB29    RET                ;---->出#C01 CALL


3.  以上代码,验证了注册码的前四位,以上可知注册码的第3、4位是由用户名用sub_0047CA10(int,char*,int)函数计算出的值16进制码的字符格式的前两位, 在这里为"5C"; 注册码的第1、2位是由注册码第三位以后的字符用sub_0047CA10函数计算出的16进制码的字符格式的前两位,用"5C60443"计算的结果为"EF",所以在这时注册码应为"ef5c60443".
回到注册对话框重新输入注册码"ef5c60443",提示需要重启EditPlus激活注册码,看来第一步验证通过了.

4.  重启EditPlus,提示"Invalid registration code.",看来其它的地方还有注册码的验证过程,好我们回到程序继续跟踪...
这次跟踪注册码用"EF5C60443".

出#C01 CALL 到...

001B:0047C942    ADD    ESP,08            ;---->返回到这里
001B:0047C945    TEST    EAX,EAX            ;---->
001B:0047C947    JNZ    0047C961        ;---->跳转则第一步注册成功
001B:0047C949    PUSH    FF
001B:0047C94B    PUSH    10
001B:0047C94D    PUSH    00005F81
001B:0047C952    CALL    004C7E94        ;---->出错对话框,
001B:0047C957    POP    EDI
001B:0047C958    POP    ESI
001B:0047C959    POP    EBX
001B:0047C95A    ADD    ESP,00000230
001B:0047C960    RET
===>:0047C961    PUSH    EDI            ;---->注册码
001B:0047C962    PUSH    EBX            ;---->用户名
001B:0047C963    CALL    0047C3B0        ;#C02---->关于注册表的动作????,跟进去看看..
001B:0047C968    ADD    ESP,08
001B:0047C96B    TEST    EAX,EAX
001B:0047C96D    JNZ    0047C986        ;--->输入"ef5c60443"时,在这里我们跳转了..
001B:0047C96F    PUSH    FF
001B:0047C971    PUSH    EAX
001B:0047C972    PUSH    00004E7F
001B:0047C977    CALL    004C7E94        ;--->注册失败!!
001B:0047C97C    POP    EDI
001B:0047C97D    POP    ESI
001B:0047C97E    POP    EBX
001B:0047C97F    ADD    ESP,00000230
001B:0047C985    RET
===>:0047C986    MOV    ECX,[ESI+000000D8]
001B:0047C98C    PUSH    EBX            ;---->用户名
001B:0047C98D    CALL    004C0367        ;---->CString::=
001B:0047C992    MOV    ECX,[ESI+000000DC]
001B:0047C998    PUSH    EDI            ;---->注册码"EF5C60443"
001B:0047C999    CALL    004C0367        ;---->CString::=
001B:0047C99E    MOV    EAX,[ESI+000000E4]
001B:0047C9A4    MOV    ECX,ESI
001B:0047C9A6    MOV    DWORD PTR [EAX],00000000
001B:0047C9AC    CALL    004BF482        ;---->CDialog::0nOK(void)
001B:0047C9B1    POP    EDI
001B:0047C9B2    POP    ESI
001B:0047C9B3    POP    EBX
001B:0047C9B4    ADD    ESP,00000230
001B:0047C9BA    RET
001B:0047C9BB    NOP
...


进入#C02 ...

001B:0047C3B0    SUB    ESP,000000E0
001B:0047C3B6    PUSH    EBX            ;---->用户名(保护现场)
001B:0047C3B7    PUSH    EBP
001B:0047C3B8    PUSH    ESI
001B:0047C3B9    MOV    ESI,[ESP+000000F0]    ;---->让ESI指向用户名
001B:0047C3C0    PUSH    EDI            ;---->注册码(保护现场)
001B:0047C3C1    MOV    EDI,[KERNEL32!lstrlen]
001B:0047C3C7    PUSH    ESI            ;---->用户名入栈了
001B:0047C3C8    CALL    EDI            ;---->得到用户名长度
001B:0047C3CA    MOV    EBX,EAX
001B:0047C3CC    MOV    EAX,[ESP+000000F8]
001B:0047C3D3    PUSH    EAX            ;---->EAX指向注册码
001B:0047C3D4    CALL    EDI            ;---->注册码长度
001B:0047C3D6    LEA    EBP,[EBX+EAX+02]    ;---->ebp = name_len+sn_len+2;
001B:0047C3DA    MOV    [ESP+14],EAX
001B:0047C3DE    PUSH    EBP
001B:0047C3DF    CALL    004AD84D        ;---->malloc(size_t)分配内存
001B:0047C3E4    MOV    ECX,EBX
001B:0047C3E6    LEA    EDI,[EAX+01]
001B:0047C3E9    MOV    EDX,ECX    
001B:0047C3EB    MOV    [EAX],BL        ;---->把bl(用户名长度)存到新分配的空间中的第一个字节
001B:0047C3ED    SHR    ECX,02
001B:0047C3F0    REPZ    MOVSD            ;---->把用户名存到分配的空间中
001B:0047C3F2    MOV    ECX,EDX
001B:0047C3F4    MOV    [ESP+1C],EAX
001B:0047C3F8    AND    ECX,03
001B:0047C3FB    REPZ    MOVSB            ;---->把用户名存到分配的空间中
001B:0047C3FD    MOV    ECX,[ESP+18]        ;---->注册码长度
001B:0047C401    MOV    ESI,[ESP+000000FC]    ;---->ESI指向注册码
001B:0047C408    MOV    [EAX+EBX+01],CL        ;---->把cl(注册码长度)存到分配的空间中
001B:0047C40C    LEA    EDI,[EAX+EBX+02]
001B:0047C410    MOV    EAX,ECX
001B:0047C412    SHR    ECX,02
001B:0047C415    REPZ    MOVSD            ;---->存注册码
001B:0047C417    MOV    ECX,EAX
001B:0047C419    MOV    EAX,51EB851F
001B:0047C41E    IMUL    EBP            ;---->EDX:EAX=0x51eb851f * 0x12 = 5C28F5C2E
001B:0047C420    AND    ECX,03
001B:0047C423    REPZ    MOVSB            ;---->存注册码,此时内存中的数据为"\x07icytear\x09EF5C60443"
001B:0047C425    SAR    EDX,05            ;---->移位操作
001B:0047C428    MOV    ECX,EDX
001B:0047C42A    SHR    ECX,1F
001B:0047C42D    ADD    EDX,ECX
001B:0047C42F    LEA    EDX,[EBP+EDX+0C]    ;---->计算要下一个操作要分配多少空间n
001B:0047C433    PUSH    EDX
001B:0047C434    MOV    [ESP+18],EDX        ;---->把n存下来
001B:0047C438    CALL    004AD84D        ;---->malloc(size_t)分配内存
001B:0047C43D    MOV    EDI,[ESP+20]        ;---->EDI指向"\x07icytear\x09EF5C60443"
001B:0047C441    PUSH    EBP
001B:0047C442    LEA    EDX,[ESP+1C]        ;---->EDX指向上次分配空间大小n
001B:0047C446    MOV    ESI,EAX            ;---->让ESI指向最近分配的内存空间
001B:0047C448    PUSH    EDI            ;---->EDI指向"\x07icytear\x09EF5C60443",
001B:0047C449    PUSH    EDX
001B:0047C44A    PUSH    ESI
001B:0047C44B    MOV    [ESP+38],ESI
001B:0047C44F    CALL    0049F820        ;#C1---->关键,计算出一段值(size:1E),要写入注册表License,在此处按F9下一个断点
001B:0047C454    ADD    ESP,18
001B:0047C457    TEST    EAX,EAX
001B:0047C459    JZ    0047C477        ;---->跳转到#J1
001B:0047C45B    PUSH    EDI
001B:0047C45C    CALL    004AD764        ;---->call????
001B:0047C461    PUSH    ESI
001B:0047C462    CALL    004AD764        ;---->call????
001B:0047C467    ADD    ESP,08
001B:0047C46A    XOR    EAX,EAX
001B:0047C46C    POP    EDI
001B:0047C46D    POP    ESI
001B:0047C46E    POP    EBP
001B:0047C46F    POP    EBX
001B:0047C470    ADD    ESP,0E0
001B:0047C476    RET
===>:0047C477    MOV    EAX,[ESP+10]        ;#J1---->
001B:0047C47B    ADD    EAX,08
001B:0047C47E    PUSH    EAX
001B:0047C47F    MOV    [ESP+18],EAX
001B:0047C483    CALL    004AD84D        ;---->malloc(size_t)分配内存
001B:0047C488    MOV    ECX,[0051A58]        ;---->[0051A58]==DWORD 0x182989DB 定值
001B:0047C48E    MOV    EBX,EAX
001B:0047C490    MOV    [EBX],ECX        ;---->write memory...
001B:0047C492    MOV    EDX,[00518A58]
001B:0047C498    PUSH    EDX
001B:0047C499    CALL    004AD987        ;---->void _cdecl srand(unsigned int)
001B:0047C49E    CALL    004AD994        ;---->int rand(void)产生一个伪随机数
001B:0047C4A3    ADD    EAX,EBP            ;---->ebp == name_len+sn_len+2
001B:0047C4A5    MOV    [EBX+04],AX        ;---->write memory... to [ebx+4] 
001B:0047C4A9    MOV    EAX,[00518A58]
001B:0047C4AE    INC    EAX
001B:0047C4AF    PUSH    EAX
001B:0047C4B0    CALL    004A987            ;---->srand
001B:0047C4B5    ADD    ESP,0C
001B:0047C4B8    CALL    004AD994        ;---->rand()  产生一个伪随机数
001B:0047C4BD    MOV    ECX,[ESP+10]
001B:0047C4C1    LEA    EDI,[EBX+08]
001B:0047C4C4    ADD    EAX,ECX
001B:0047C4C6    MOV    [EBX+06],AX        ;---->write memory... to [ebx+6] = 0xb78188+6
001B:0047C4CA    MOV    ECX,[ESP+10]
001B:0047C4CE    MOV    EDX,ECX
001B:0047C4D0    SHR    ECX,02
001B:0047C4D3    REPZ    MOVSD
001B:0047C4D5    MOV    ECX,EDX
001B:0047C4D7    AND    ECX,03
001B:0047C4DA    REPZ    MOVSB
...

把刚才生成的16进制码写注册表...

...

按F5运行程序,又提示重启editplus,退出程序..在这里程序被上面按F9下的那个断点中断(在#C02.#C1,001B:0047C44B),看来程序在关闭时又调用了一次#C02 Call,也就是又写了一次注册表,好我们用D edi命令查看edi指向的内存为"\x07icytear\x090F5C60443",看到区别了吗,前面我们看到的是"\x07icytear\x09EF5C60443",看来是被程序修改了,用这个再一次计算出16进制代码写入注册表,重启editplus时和第一次写得当然就不一样了,看来程序是在这里做了手脚..

我们按F12退出此次调用(CALL 0047C3B0)

...
001B:0042E691    CALL    0047C3B0
001B:0042E696    ADD    ESP,08        ;---->到这里
...
我们向上查看代码,看到
...
001B:0042E661    TEST    EAX,EAX
001B:0042E663    JZ    0042E69A    ;---->这里有个跳转,如果跳就不能执行CALL 0047C3B0了,按F9在这里下个断点(把其它得断点可以先禁掉),如果跳转我们就可以注册成功..

重新输入注册码后退出editplus程序被中断,单步执行看看
...
001B:0042E653    MOV    EAX,[ESI+B44]
001B:0042E659    MOV    DWORD PTR [ESP+10],08
001B:0042E661    TEST    EAX,EAX
001B:0042E663    JZ    0042E69A
001B:0042E665    MOV    EAX,[ESI+0B30]    ;---->D EAX,发现eax指向的还是"EF5C60443",由此判断可能是由于下面的代码改变了此字符串,我们保持此内存的窗口,继续往下执行
001B:0042E66B    PUSH    EDI
001B:0042E66C    LEA    EDI,[ESI+0B30]
001B:0042E672    MOV    AL,[EAX]
001B:0042E674    CMP    AL,30
001B:0042E676    JNZ    0042E67C
001B:0042E678    PUSH    31
001B:0042E67A    JMP    0042E67E
001B:0042E67C    PUSH    30
001B:0042E67E    PUSH    00
001B:0042E680    MOV    ECX,EDI
001B:0042E682    CALL    004C069E    ;#C03---->执行此调用后,"EF5C60443"被改为"0F5C60443",看来我们的判断是真确的.
001B:0042E687    MOV    EDI,[EDI]
001B:0042E689    MOV    EAX,[ESI+B2C]
001B:0042E68F    PUSH    EDI
001B:0042E690    PUSH    EAX
001B:0042E691    CALL    0047C3B0
...
我们再看看TEST EAX,EAX 中的EAX从那来的,向上看到MOV  EAX,[ESI+B44],用D ESI+B44查看内存,显示如下
...
0023:00B7590C 01 00 00 00 .....
0023:00B7591C .......
...
那我们下一个内存断点试试,BPMD 0B7590C,再次输入注册码点击注册,OK,程序被中断在..
...
001B:0047C9A6    MOV    DWORD PTR [EAX],0    ;---->这里改变内存,赋值0
001B:0047C9AC    CALL    004BF482        ;---->中断在此
...

MOV DWORD PTR [EAX],0;把内存B7590C赋值0,我们要看看在那里把其改为1的,按F5执行程序,再次中断

...
001B:0047C718    MOV    DWORD PTR [ECX],01    ;---->改变内存,赋值1
001B:0047C71E    LEA    ECX,[EDI+98]        ;---->中断在此
...

我们向上看程序
001B:0047C707    MOV    AL,[EAX,04]        ;---->把第5位注册码存入AL
001B:0047C70A    MOV    CL,[ESP+10]        ;---->那[esp+10]存的是什么呢?我们继续向上看程序


001B:0047C670    PUSH    FF
001B:0047C672    PUSH    004E6183
001B:0047C677    MOV    EAX,FS:[00000000]
001B:0047C67D    PUSH    EAX
001B:0047C67E    MOV    FS:[0],ESP
001B:0047C685    SUB    ESP,10
001B:0047C688    PUSH    EBP
001B:0047C689    PUSH    ESI
001B:0047C68A    PUSH    EDI
001B:0047C68B    MOV    EDI,ECX
001B:0047C691    MOV    DWORD PTR [EDI],004F41E0
001B:0047C697    MOV    EAX,[EDI+D8]
001B:0047C69D    MOV    DWORD PTR [ESP+24],01
001B:0047C6A5    MOV    EBP,[EAX]        ;---->让EBP指向用户名"icytear"
001B:0047C6A7    MOV    ESI,[EBP-08]        ;---->ESI存用户名长度
001B:0047C6AA    TEST    ESI,ESI
001B:0047C6AC    JLE    0047C71E        ;---->跳过赋值1的那个指令
001B:0047C6AE    XOR    EAX,EAX
001B:0047C6B0    MOV    ECX,01            ;---->ECX赋值1
001B:0047C6B5    TEST    ESI,ESI
001B:0047C6B7    JLE    0047C6C5
===>:0047C6B9    XOR    EDX,EDX            ;#LOOP
001B:0047C6BB    MOV    DL,[EBP+EAX]
001B:0047C6BE    ADD    ECX,EDX
001B:0047C6C0    INC    EAX
001B:0047C6C1    CMP    EAX,ESI
001B:0047C6C3    JL    0047C6B9        ;---->跳到#LOOP,用户名每一位的值相加再加1;
001B:0047C6C5    LEA    ECX,[ECX*8+ECX+0A]
001B:0047C6C9    MOV    EAX,55555556
001B:0047C6CE    IMUL    ECX
001B:0047C6D0    MOV    EAX,EDX
001B:0047C6D2    SHR    EAX,1F
001B:0047C6D5    LEA    ECX,[EAX+EDX+24]    
001B:0047C6D9    AND    ECX,8000000F
001B:0047C6DF    JNS    0047C6E6
001B:0047C6E1    DEC    ECX
001B:0047C6E2    OR    ECX,-10
001B:0047C6E5    INC    ECX
===>:0047C6E6    PUSH    ECX
001B:0047C6E7    LEA    EDX,[ESP+14]
001B:0047C6EB    PUSH    0051700C        ;---->"%1X"
001B:0047C6F0    PUSH    EDX
001B:0047C6F1    CALL    004AEABC        ;---->sprintf
001B:0047C6F6    MOV    EAX,[EDI+DC]
001B:0047C6FC    ADD    ESP,0C
001B:0047C6FF    MOV    EAX,[EAX]        ;---->EAX指向注册码
001B:0047C701    CMP    DWORD PTR[EAX-8],05    ;---->[EAX-8]存着注册码长度,也就是说注册码长度应该大于5
001B:0047C605    JL    0047C712
001B:0047C607    MOV    AL,[EAX,04]        ;---->注册码第5位
001B:0047C60A    MOV    CL,[ESP+10]        ;---->很显然是我们用用户名算出的那个值
001B:0047C60E    CMP    AL,CL            ;---->验证第5位注册码,在这里我们计算出的是'D'
001B:0047C610    JZ    0047C71E        ;---->第二次验证通过则注册成功..^_^,胜利!!
001B:0047C612    MOV    ECX,[EDI+E4]
001B:0047C618    MOV    DWORD PTR[ECX],01
...

5.  用我的用户名'icytear'计算出的值是'D'所以我的第5位注册码应为'D',前2位应用'5CD0443'由sub_0047CA10重新计算为'E4',重新输入注册码'E45CD0443',哈哈,注册成功!!

6.  好累,注册机大家写吧,别忘了给我发一份!!


####################
username: icytear
Regcode: E45CD0443
####################