• 标 题:修改现有程序成KEYGEN
  • 作 者:doskey
  • 时 间:2004-05-01,12:23
  • 链 接:http://bbs.pediy.com

修改现有程序成KEYGEN
dOsKey[Nuke Group]

    做过KEYGEN的CRACKER可以都遇到过这种情况:生成注册码的算法十分复杂。虽然可以逆向分析出整个算法,但是需要很长时间。而程序内部已经包含了算注册码的函数的时候,我们可以使用程序内部的算码函数来实现我们自己的KEYGEN,以达到四两拨千斤的效果。
    目标程序是某个国产手机写码软件。第一次运行的时候程序要求输入根据机器码得来的用户名和注册码。用FI扫描后得知目标软件无壳,用VC6写的,目前很少国产软件不加壳了,看来我们很幸运,省略了脱壳的步骤。接下来我们用OllyDbg载入目标软件,F9运行。在目标程序中输入用户名“DOSKEY”和注册码U20-111111111111,点OK,弹出有“注册失败!”的提示窗口。不管它,切换回OllyDbg按F12暂停在主线程。然后按CTRL+F12直到OllyDbg状态为“Till return”,切换回目标程序点消息框的确定按钮。目标程序的进程再次被OllyDbg暂停住,一直按CTRL+F12返回到主线程我们就来到这里:

代码:
0042675F  /$  55              PUSH    EBP 00426760  |.  8BEC            MOV     EBPESP 00426762  |.  E8 91140000     CALL    2210.00427BF8 00426767  |.  8B40 04         MOV     EAXDWORD PTR DS:[EAX+4] 0042676A  |.  85C0            TEST    EAXEAX 0042676C  |.  74 15           JE      SHORT 2210.00426783 0042676E  |.  FF75 10         PUSH    DWORD PTR SS:[EBP+10] 00426771  |.  8B10            MOV     EDXDWORD PTR DS:[EAX] 00426773  |.  8BC8            MOV     ECXEAX 00426775  |.  FF75 0C         PUSH    DWORD PTR SS:[EBP+C] 00426778  |.  FF75 08         PUSH    DWORD PTR SS:[EBP+8] 0042677B  |.  FF92 8C000000   CALL    DWORD PTR DS:[EDX+8C] 00426781  |.  EB 10           JMP     SHORT 2210.00426793          <= 返回到这里,上面就是调用MessageBox显示消息框的CALL 00426783  |>  FF75 10         PUSH    DWORD PTR SS:[EBP+10]                                    ; /Arg3 00426786  |.  33C9            XOR     ECXECX                                                 ; | 00426788  |.  FF75 0C         PUSH    DWORD PTR SS:[EBP+C]                                     ; |Arg2 0042678B  |.  FF75 08         PUSH    DWORD PTR SS:[EBP+8]                                     ; |Arg1 0042678E  |.  E8 E5FEFFFF     CALL    2210.00426678                                            ; \2210.00426678 00426793  |>  5D              POP     EBP 00426794  \.  C2 0C00         RETN    0C

很显然这只是一个显示间接消息框的子函数,CTRL+F12返回到这个地方:
代码:
00403815   >  6A 00           PUSH    0                                                        ; /Arg3 = 00000000 00403817   .  6A 00           PUSH    0                                                        ; |Arg2 = 00000000 00403819   .  68 14844300     PUSH    2210.00438414                                            ; |Arg1 = 00438414 0040381E   .  E8 3C2F0200     CALL    2210.0042675F                                            ; \2210.0042675F 00403823   .  8BCD            MOV     ECXEBP 00403825   .  E8 36000000     CALL    2210.00403860 0040382A   >  8BCD            MOV     ECXEBP             <= 返回到这里 0040382C   .  E8 9AC40100     CALL    2210.0041FCCB 00403831   .  8D4C24 10       LEA     ECXDWORD PTR SS:[ESP+10]

看403819h处压入的438414h就是“注册失败!”,看来我们已经离比较注册码的地方不远了。看403815h处前面的“>”说明有语句跳转到这里。选择403815h这个语句在这个语句前面上,OllyDbg的Information栏目显示“Jump from 004037E2”,我们来到4037E2h处:
代码:
004037DB   .  E8 5AFB0100     CALL    2210.0042333A 004037E0   .  84DB            TEST    BLBL 004037E2   .  74 31           JE      SHORT 2210.00403815    <= 把这里NOP掉就爆破了

这个CALL就是比较注册码的地方。我们把4037E2h的语句NOP掉就爆破了。可是我们要的不是这个结果。是该祭出CRACKER的杀手工具IDA的时候了。
    用IDA打开目标程序,待IDA分析完毕后按G跳转到4037DBh处。向上翻页到这个函数的首部4036A0h处,这里就是我们要分析的地方:
代码:
.text:004036A0     sub_4036A0      proc near               ; DATA XREF: .rdata:0042DA8Co .text:004036A0      .text:004036A0     var_1C          = dword ptr -1Ch .text:004036A0     var_18          = dword ptr -18h .text:004036A0     var_14          = dword ptr -14h .text:004036A0     var_10          = dword ptr -10h .text:004036A0     var_C           = dword ptr -0Ch .text:004036A0     var_4           = dword ptr -4 .text:004036A0      .text:004036A0 000                 mov     eax, large fs:0 .text:004036A6 000                 push    0FFFFFFFFh .text:004036A8 004                 push    offset loc_42ADE8 .text:004036AD 008                 push    eax .text:004036AE 00C                 mov     large fs:0, esp .text:004036B5 00C                 sub     esp, 10h .text:004036B8 01C                 push    ebx .text:004036B9 020                 push    ebp .text:004036BA 024                 push    esi .text:004036BB 028                 push    edi .text:004036BC 02C                 mov     ebpecx .text:004036BE 02C                 push    1 .text:004036C0 030                 call    FUN_UpdateData()      ; 刷新数据,CDialog::UpdateData() .text:004036C5 02C                 mov     eax, [ebp+60h] .text:004036C8 02C                 lea     edi, [ebp+60h] .text:004036CB 02C                 cmp     dword ptr [eax-8], 10h  ;比较注册码长度是否为16个字符,是就跳,否则提示并结束 .text:004036CF 02C                 jz      short loc_4036F2 .text:004036D1 02C                 push    0 .text:004036D3 030                 push    0 .text:004036D5 034                 push    offset aVSIDA   ; "注册码长度错误!" .text:004036DA 038                 call    FUN_MessageBox     ;提示注册码长度错误 .text:004036DF 02C                 mov     ecx, [esp+2Ch+var_C] .text:004036E3 02C                 mov     large fs:0, ecx .text:004036EA 02C                 pop     edi .text:004036EB 028                 pop     esi .text:004036EC 024                 pop     ebp .text:004036ED 020                 pop     ebx .text:004036EE 01C                 add     esp, 1Ch .text:004036F1 000                 retn .text:004036F2     ; ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ .text:004036F2      .text:004036F2     loc_4036F2:                             ; CODE XREF: sub_4036A0+2Fj .text:004036F2 02C                 mov     ecx, [ebp+64h] .text:004036F5 02C                 push    0 .text:004036F7 030                 cmp     dword ptr [ecx-8], 5  ; 比较用户名长度是否大于5个字符, 大于跳, 否则显示提示并结束 .text:004036FB 030                 jg      short loc_40371C .text:004036FD 030                 push    0 .text:004036FF 034                 push    offset aVSZDJ5  ; "注册用户名长度应大于5位!" .text:00403704 038                 call    FUN_MessageBox .text:00403709 02C                 mov     ecx, [esp+2Ch+var_C] .text:0040370D 02C                 mov     large fs:0, ecx .text:00403714 02C                 pop     edi .text:00403715 028                 pop     esi .text:00403716 024                 pop     ebp .text:00403717 020                 pop     ebx .text:00403718 01C                 add     esp, 1Ch .text:0040371B 000                 retn                    ; time_t * ======================================== 看了上面的代码可能你以为没什么,但是不知道你发现没有,[EBP+60]里面就是注册码的地址,然后注册码的地址减8就是注册码的长度。 用户名是[EBP+64]是用户名的字符串地址,用户名的地址减8就是用户名的长度。同理可以取得机器码的地址[EBP+5C]。这几个是非常关键的数据。 ======================================== .text:0040371C     ; ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ .text:0040371C      .text:0040371C     loc_40371C:                             ; CODE XREF: sub_4036A0+5Bj .text:0040371C 030                 call    _time           ;取得系统时间 .text:00403721 030                 push    eax             ; unsigned int .text:00403722 034                 call    _srand          ;将系统时间做SEED .text:00403727 034                 add     esp, 8 .text:0040372A 02C                 call    _rand           ;生成随机数 .text:0040372F 02C                 mov     blal          ;AL里面是随机数 .text:00403731 02C                 push    edi .text:00403732 030                 lea     ecx, [esp+30h+var_1C] .text:00403736 030                 mov     byte ptr [esp+30h+var_14], bl ; 保存随机数到临时变量中 .text:0040373A 030                 call    FUN_GetBuf      ; 取缓冲 .text:0040373F 02C                 mov     eax, [edi]      ; EDI=注册码地址 .text:00403741 02C                 mov     esi, 4          ; 初始化for循环控制变量,不运算“U20-” .text:00403746 02C                 mov     [esp+2Ch+var_4], 0  .text:0040374E 02C                 cmp     [eax-8], esi    ; 进入循环的第一次比较,[eax-8]中是注册码长度 .text:00403751 02C                 jle     short loc_403789 .text:00403753      .text:00403753     loc_403753:                             ; CODE XREF: sub_4036A0+E7j .text:00403753 02C                 mov     al, [eax+esi]   ; 取注册码中位置为esi的字符 .text:00403756 02C                 mov     dlbl          ; bl为随机数,dl为运算用变量 .text:00403758 02C                 and     dl, 7Fh         ; 保留低7位 .text:0040375B 02C                 mov     ecxedi  .text:0040375D 02C                 xor     dlal          ; 注册码中的字符和随机数进行异或运算,结果在dl中 .text:0040375F 02C                 add     dl, 20h         ; 运算结果加20h .text:00403762 02C                 mov     byte ptr [esp+2Ch+var_18], dl ; 保存运算结果到临时变量中 .text:00403766 02C                 mov     eax, [esp+2Ch+var_18]    ; 取回来准备压栈 .text:0040376A 02C                 push    eax  ; 压入运算后的结果 .text:0040376B 030                 push    esi  ; 压入位置 .text:0040376C 034                 call    ?SetAt@CString@@QAEXHD@Z ; CString::SetAt(int,char)  ; 调用CString::SetAt()来替换掉原先注册码中的字符 .text:00403771 02C                 inc     bl               ; 随机数加1 .text:00403773 02C                 test    bl, 1            ; 如果随机数等于一跳转到40377F处 .text:00403776 02C                 jz      short loc_40377F .text:00403778 02C                 shr     bl, 1            ; 右移 .text:0040377A 02C                 or      bl, 80h          ; 逻辑加80h .text:0040377D 02C                 jmp     short loc_403781 .text:0040377F     ; ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ .text:0040377F      .text:0040377F     loc_40377F:                             ; CODE XREF: sub_4036A0+D6j .text:0040377F 02C                 shr     bl, 1            ; 右移 .text:00403781      .text:00403781     loc_403781:                             ; CODE XREF: sub_4036A0+DDj .text:00403781 02C                 mov     eax, [edi] .text:00403783 02C                 inc     esi             ; for循环的第三部分,控制变量加一 .text:00403784 02C                 cmp     esi, [eax-8]    ; for循环的第二部分,控制变量是否小于注册码长度,小就继续循环,否则离开循环 .text:00403787 02C                 jl      short loc_403753 .text:00403789      .text:00403789     loc_403789:                             ; CODE XREF: sub_4036A0+B1j ======================================== 这里的for循环是将输入的注册码编码。为了后面的比较不是直接的明码,但是这样做并不高明。看后面离就知道了 ======================================== .text:00403789 02C                 mov     ecx, [esp+2Ch+var_14] ; 取回刚才保护的随机数 .text:0040378D 02C                 lea     edx, [ebp+5Ch]  ; 取机器码 .text:00403790 02C                 push    ecx .text:00403791 030                 push    ecx ; 压入随机数 .text:00403792 034                 mov     ecxesp ; .text:00403794 034                 mov     [esp+34h+var_14], esp .text:00403798 034                 push    edx .text:00403799 038                 call    FUN_GetBuf      ; 取机器码缓冲 .text:0040379E 034                 push    ecx ; 压入机器码 .text:0040379F 038                 lea     esi, [ebp+64h] .text:004037A2 038                 mov     ecxesp .text:004037A4 038                 mov     [esp+38h+var_10], esp .text:004037A8 038                 push    esi  ; 压入用户名 .text:004037A9 03C                 mov     byte ptr [esp+3Ch+var_4], 1 .text:004037AE 03C                 call    FUN_GetBuf .text:004037B3 038                 lea     eax, [esp+38h+var_18] .text:004037B7 038                 mov     ecxebp .text:004037B9 038                 push    eax ; 压入保存正确的经过编码的注册码的临时空间 .text:004037BA 03C                 mov     byte ptr [esp+3Ch+var_4], 0 .text:004037BF 03C                 call    FUN_Main ======================================== 这个CALL是计算正确的经过编码的注册码的。这里是本问所要利用到的算码函数!我们看看进入CALL之前堆栈中的内容: 0012F904   0012F928  |Arg1 = 0012F928  <= 临时空间 0012F908   00936F38  |Arg2 = 00936F38 ASCII "DOSKEY" <= 用户名 0012F90C   00936EE8  |Arg3 = 00936EE8 ASCII "0000E87C1C92" <= 机器码 0012F910   000000CE  \Arg4 = 000000CE <= 随机数,编码用的 ======================================== .text:004037C4 02C                 mov     eax, [eax] ; eax中保存的是正确的经过编码的注册码的地址 .text:004037C6 02C                 mov     edi, [edi] ; 我们输入的注册码,经过编码的 .text:004037C8 02C                 push    eax ; 压入正确的 .text:004037C9 030                 push    edi ; 压入我们输入的 .text:004037CA 034                 call    __mbscmp ; 比较 .text:004037CF 034                 add     esp, 8 .text:004037D2 02C                 lea     ecx, [esp+2Ch+var_18] .text:004037D6 02C                 test    eaxeax .text:004037D8 02C                 setz    bl .text:004037DB 02C                 call    sub_42333A .text:004037E0 02C                 test    blbl ; 如果不相同就跳,显示“注册失败” .text:004037E2 02C                 jz      short loc_403815 .text:004037E4 02C                 push    ecx .text:004037E5 030                 mov     ecxesp .text:004037E7 030                 mov     [esp+30h+var_10], esp .text:004037EB 030                 push    esi .text:004037EC 034                 call    FUN_GetBuf .text:004037F1 030                 push    ecx .text:004037F2 034                 lea     edx, [esp+34h+var_1C] .text:004037F6 034                 mov     ecxesp .text:004037F8 034                 mov     [esp+34h+var_14], esp .text:004037FC 034                 push    edx .text:004037FD 038                 mov     byte ptr [esp+38h+var_4], 2 .text:00403802 038                 call    FUN_GetBuf .text:00403807 034                 mov     ecxebp .text:00403809 034                 mov     byte ptr [esp+34h+var_4], 0 .text:0040380E 034                 call    sub_402950 ; 将注册码保存到注册表中,下次启动的时候检查 .text:00403813 02C                 jmp     short loc_40382A .text:00403815     ; ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ .text:00403815      .text:00403815     loc_403815:                             ; CODE XREF: sub_4036A0+142j .text:00403815 02C                 push    0 .text:00403817 030                 push    0 .text:00403819 034                 push    offset aVSZ     ; "注册失败!" .text:0040381E 038                 call    FUN_MessageBox .text:00403823 02C                 mov     ecxebp .text:00403825 02C                 call    sub_403860 .text:0040382A      .text:0040382A     loc_40382A:                             ; CODE XREF: sub_4036A0+173j .text:0040382A 02C                 mov     ecxebp .text:0040382C 02C                 call    ?OnOK@CDialog@@MAEXXZ ; CDialog::OnOK(void) .text:00403831 02C                 lea     ecx, [esp+2Ch+var_1C] .text:00403835 02C                 mov     [esp+2Ch+var_4], 0FFFFFFFFh .text:0040383D 02C                 call    sub_42333A .text:00403842 02C                 mov     ecx, [esp+2Ch+var_C] .text:00403846 02C                 pop     edi .text:00403847 028                 pop     esi .text:00403848 024                 pop     ebp .text:00403849 020                 mov     large fs:0, ecx .text:00403850 020                 pop     ebx .text:00403851 01C                 add     esp, 1Ch .text:00403854 000                 retn .text:00403854     sub_4036A0      endp .text:00403854      .text:00403854     ; ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

    好了,我们已经分析完比较的过程。大概是这样:
1、生成随机数
2、将输入的注册码利用随机数进行编码
3、根据输入的注册码、用户名、机器码和随机数生成正确的经过编码的注册码。
4、比较编码后的注册码是否相同。如果不同就提示错误。否则就注册成功。

    我们现在来看看4037BFh处的生成注册码的函数。我们跳转到402A30h处。汗。变量就一百多个,代码有1.8K字节之多。我们只能找捷径了。向下翻页到这里:
代码:
.text:004030B8     loc_4030B8:                             ; CODE XREF: FUN_Main+6ACj .text:004030B8 18C                 mov     clbyte ptr [esp+esi+18Ch+var_158] .text:004030BC 18C                 mov     dlal .text:004030BE 18C                 and     dl, 7Fh .text:004030C1 18C                 xor     dlcl .text:004030C3 18C                 add     dl, 20h .text:004030C6 18C                 inc     al .text:004030C8 18C                 test    al, 1 .text:004030CA 18C                 mov     byte ptr [esp+esi+18Ch+var_158], dl .text:004030CE 18C                 jz      short loc_4030D6 .text:004030D0 18C                 shr     al, 1 .text:004030D2 18C                 or      al, 80h .text:004030D4 18C                 jmp     short loc_4030D8 .text:004030D6     ; ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ .text:004030D6      .text:004030D6     loc_4030D6:                             ; CODE XREF: FUN_Main+69Ej .text:004030D6 18C                 shr     al, 1 .text:004030D8      .text:004030D8     loc_4030D8:                             ; CODE XREF: FUN_Main+6A4j .text:004030D8 18C                 inc     esi .text:004030D9 18C                 cmp     esi, 0Ch .text:004030DC 18C                 jl      short loc_4030B8

    发现没有?和我们前面分析的那个for循环如出一辙,只是这段代码是直接操作的内存。我们用OllyDbg跟踪到4030B8h处看看。esp+esi+18Ch+var_158中就是没有“U20-”的正确注册码。然后:
代码:
.text:004030F9 18C                 push    ecx .text:004030FA 190                 push    offset aU20     ; "U20-" .text:004030FF 194                 push    edx .text:00403100 198                 call    sub_4235CF

这个函数将“U20-”和编码后的注册码连接成正确的经过编码的注册码。最后函数直接返回了这个字符串的地址。用OllyDbg跟踪到4030B8h处,把4030B8h到4030DCh的代码全部填充成NOP。我们再继续运行,这个函数就返回的正确的未编码的注册码了。

    好了。到这里我们的准备工作已经都做好了。我们要开始DIY这个程序了首先,备份原始文件,以防万一。然后我们要修改对话框可以接受输入机器码。用Resource Hacker打开目标程序,找到对话框资源1160的2052代码页,点显示对话框,选择机器码的EDIT控件,在编辑器里面去掉ES_READONLY属性,保存退出。修改算注册码的函数然它可以自己生成未编码的注册码。用HIEW打开目标程序,将4030B8h到4030DCh的数据填充成90h,保存退出。用LordPE打开目标文件,在目录中点击引入表后面的“..”,点右键添加引入表,添加两个我们要使用的API函数,一个是USER32的FindWindowA和USER32的SetDlgItemTextA。最后我们开始做主要的修改,然目标程序可以自己显示注册码。
    用OllyDbg打开刚才修改过后的目标程序。CTRL+G跳转到4036C5h(4036C5h是刷新数据的地方),F2下断点,然后F9运行目标程序。随便输入用户名和注册码,点OK后被OllyDbg中断。点击右键,在上下文菜单中选择Search for\Name(label) in current module,查找FindWindowA、SetDlgItemTextA、VirtualAlloc、VirtualFree的地址。下面我要调用这些API的时候要这个写:call [API 地址],这些地址是在引入表中的地址,如果不这样调用,程序就无法跨平台。问为什么?看看PE格式的详细解释吧。
    选择4036C5h到40371Bh的代码,点右键,在上下问菜单中选择Binary\Fill With NOPs,将这个函数后面的代码填充为NOP。然后我们逐行输入下面的代码:
代码:
004036A0   .  64:A1 00000000  MOV     EAXDWORD PTR FS:[0] 004036A6   .  6A FF           PUSH    -1 004036A8   .  68 E8AD4200     PUSH    复件_221.0042ADE8 004036AD   .  50              PUSH    EAX 004036AE   .  64:8925 0000000>MOV     DWORD PTR FS:[0], ESP 004036B5   .  83EC 10         SUB     ESP, 10 004036B8   .  53              PUSH    EBX 004036B9   .  55              PUSH    EBP 004036BA   .  56              PUSH    ESI 004036BB   .  57              PUSH    EDI 004036BC   .  8BE9            MOV     EBPECX 004036BE   .  6A 01           PUSH    1 004036C0   .  E8 B4E80100     CALL    复件_221.00421F79 ================================上面是原有的代码========================================== 004036C5   .  8B45 64         MOV     EAXDWORD PTR [EBP+64]  ;取得用户名地址 004036C8   .  8B58 F8         MOV     EBXDWORD PTR [EAX-8]  ;取得用户名长度 004036CB   .  83FB 05         CMP     EBX, 5      ;比较长度是否大于5,否则就利用原先代码中的42675Fh函数显示提示字符串,并终止本函数 004036CE   .  7F 21           JG      SHORT 复件_221.004036F1 004036D0   .  6A 00           PUSH    0                                                        ; /Arg3 = 00000000 004036D2   .  6A 00           PUSH    0                                                        ; |Arg2 = 00000000 004036D4   .  68 20844300     PUSH    复件_221.00438420                                          ; |Arg1 = 00438420 004036D9   .  E8 81300200     CALL    复件_221.0042675F                                          ; \复件_221.0042675F 004036DE   .  8B4C24 20       MOV     ECXDWORD PTR [ESP+20] 004036E2   .  64:890D 0000000>MOV     DWORD PTR FS:[0], ECX 004036E9   .  5F              POP     EDI 004036EA   .  5E              POP     ESI 004036EB   .  5D              POP     EBP 004036EC   .  5B              POP     EBX 004036ED   .  83C4 1C         ADD     ESP, 1C 004036F0   .  C3              RETN ==============================用户名长度OK就跳转到下面来================================= 在这里为我们算出的序列号分配临时内存空间。按道理来说可以直接写入会原先保存注册码的内存,然后使用UpdateData(FALSE)写会到界面,但是我测试过好多次都没有通过,所以使用比较低级一点的方法:P 004036F1   >  6A 04           PUSH    4                                                        ; /Protect = PAGE_READWRITE      可读写 004036F3   .  68 00100000     PUSH    1000                                                     ; |AllocationType = MEM_COMMIT  提交 004036F8   .  6A 11           PUSH    11                                                       ; |Size = 11 (17.)    长度17,不要忘记字符串后面的0 004036FA   .  6A 00           PUSH    0                                                        ; |Address = NULL    地址NULL,不指定地址 004036FC   .  FF15 B4D24200   CALL    DWORD PTR [<&KERNEL32.VirtualAlloc>]                     ; \VirtualAlloc      分配内存,地址在EAX中 00403702   .  8BD0            MOV     EDXEAX 00403704   .  52              PUSH    EDX  ;把分配后的地址保存压栈保护起来,为了后面的释放 00403705   .  6A 00           PUSH    0                                                        ; /Arg4 = 00000000 ;压入参数4,随机数,由于我们已经在上面去掉随机数的处理,所以我们可以随便压入一个值 00403707   .  8B45 5C         MOV     EAXDWORD PTR [EBP+5C]                                  ; | 0040370A   .  50              PUSH    EAX                                                      ; |Arg3 ;压入参数3,机器码 0040370B   .  8B45 64         MOV     EAXDWORD PTR [EBP+64]                                  ; | 0040370E   .  50              PUSH    EAX                                                      ; |Arg2 ;压入参数2,用户名 0040370F   .  52              PUSH    EDX                                                      ; |Arg1 ;压入参数1,我们分配的内存 00403710   .  E8 1BF3FFFF     CALL    复件_221.00402A30                                          ; \复件_221.00402A30  ;算码的关键函数,注册码的地址在EAX中 00403715   .  8B00            MOV     EAXDWORD PTR [EAX]  ;返回了指向注册码的字符串地址,我们取回来 ===================================下面是我们为了将注册码显示回编辑框==================================== 00403717   .  50              PUSH    EAX                                                      ; /Text ;刚才取回来的序列号 00403718   .  68 32040000     PUSH    432                                                      ; |ControlID = 432 (1074.) ;注册码编辑框的ID,这个ID可以用OllyDbg或者是ResHacker取得 0040371D   .  68 53374000     PUSH    复件_221.00403753                                        ; |/Title = "SW Register" ;查找主窗口的标题,这个字符串是我们自己修改的,放在这个函数最后面 00403722   .  6A 00           PUSH    0                                                        ; ||Class = 0 ;类不用,压0 00403724   .  FF15 2B904400   CALL    DWORD PTR [<&user32.FindWindowA>]                        ; |\FindWindowA ;找主窗口句柄保存在EAX中 0040372A   .  50              PUSH    EAX                                                      ; |hWnd ;压入主窗口句柄 0040372B   .  FF15 27904400   CALL    DWORD PTR [<&user32.SetDlgItemTextA>]                    ; \SetDlgItemTextA ;调用这个API设置对话框项目的文本 00403731   .  58              POP     EAX  ;弹出我们刚才入栈的,我们自己分配的内存空间的地址,准备释放 00403732   .  68 00400000     PUSH    4000                                                     ; /FreeType = MEM_DECOMMIT ;一个对一个 00403737   .  6A 11           PUSH    11                                                       ; |Size = 11 (17.) ;长度 00403739   .  50              PUSH    EAX                                                      ; |Address ;地址 0040373A   .  FF15 B4D14200   CALL    DWORD PTR [<&KERNEL32.VirtualFree>]                      ; \VirtualFree ;释放 ==================================下面是照抄403842h到403854h的代码======================================= 00403740   .  8B4C24 20       MOV     ECXDWORD PTR [ESP+20] 00403744   .  64:890D 0000000>MOV     DWORD PTR FS:[0], ECX 0040374B   .  5F              POP     EDI 0040374C   .  5E              POP     ESI 0040374D   .  5D              POP     EBP 0040374E   .  5B              POP     EBX 0040374F   .  83C4 1C         ADD     ESP, 1C 00403752   .  C3              RETN 00403753   .  53 57 20 52 65 >ASCII   "SW Register",0 ;窗口标题字符串

    到这里我们已经初步将程序修改好了。然后我们将其保存。点右键,在上下文菜单中选择Copy to Executable\All Modifications,然后在弹出从窗口中选择Save File保存。测试一下,感觉还不错。做一下修饰,改下标题名,放个宣传文本就成下图这样了。

    很久没有写文章了。如果有错误或者不足之处,请拼命的提。