修改现有程序成KEYGEN

修改现有程序成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     EBP, ESP
00426762  |.  E8 91140000     CALL    2210.00427BF8
00426767  |.  8B40 04         MOV     EAX, DWORD PTR DS:[EAX+4]
0042676A  |.  85C0            TEST    EAX, EAX
0042676C  |.  74 15           JE      SHORT 2210.00426783
0042676E  |.  FF75 10         PUSH    DWORD PTR SS:[EBP+10]
00426771  |.  8B10            MOV     EDX, DWORD PTR DS:[EAX]
00426773  |.  8BC8            MOV     ECX, EAX
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     ECX, ECX                                                 ; |
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     ECX, EBP
00403825   .  E8 36000000     CALL    2210.00403860
0040382A   >  8BCD            MOV     ECX, EBP             <= 返回到这里
0040382C   .  E8 9AC40100     CALL    2210.0041FCCB
00403831   .  8D4C24 10       LEA     ECX, DWORD PTR SS:[ESP+10]
看403819h处压入的438414h就是“注册失败!”,看来我们已经离比较注册码的地方不远了。看403815h处前面的“>”说明有语句跳转到这里。选择403815h这个语句在这个语句前面上,OllyDbg的Information栏目显示“Jump from 004037E2”,我们来到4037E2h处:
004037DB   .  E8 5AFB0100     CALL    2210.0042333A
004037E0   .  84DB            TEST    BL, BL
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     ebp, ecx
.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     bl, al          ;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     dl, bl          ; bl为随机数,dl为运算用变量
.text:00403758 02C                 and     dl, 7Fh         ; 保留低7位
.text:0040375B 02C                 mov     ecx, edi
.text:0040375D 02C                 xor     dl, al          ; 注册码中的字符和随机数进行异或运算,结果在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     ecx, esp ;
.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     ecx, esp
.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     ecx, ebp
.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    eax, eax
.text:004037D8 02C                 setz    bl
.text:004037DB 02C                 call    sub_42333A
.text:004037E0 02C                 test    bl, bl ; 如果不相同就跳,显示“注册失败”
.text:004037E2 02C                 jz      short loc_403815
.text:004037E4 02C                 push    ecx
.text:004037E5 030                 mov     ecx, esp
.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     ecx, esp
.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     ecx, ebp
.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     ecx, ebp
.text:00403825 02C                 call    sub_403860
.text:0040382A    
.text:0040382A     loc_40382A:                             ; CODE XREF: sub_4036A0+173j
.text:0040382A 02C                 mov     ecx, ebp
.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     cl, byte ptr [esp+esi+18Ch+var_158]
.text:004030BC 18C                 mov     dl, al
.text:004030BE 18C                 and     dl, 7Fh
.text:004030C1 18C                 xor     dl, cl
.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     EAX, DWORD 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     EBP, ECX
004036BE   .  6A 01           PUSH    1
004036C0   .  E8 B4E80100     CALL    复件_221.00421F79
================================上面是原有的代码==========================================
004036C5   .  8B45 64         MOV     EAX, DWORD PTR [EBP+64] ;取得用户名地址
004036C8   .  8B58 F8         MOV     EBX, DWORD 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     ECX, DWORD 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     EDX, EAX
00403704   .  52              PUSH    EDX ;把分配后的地址保存压栈保护起来,为了后面的释放
00403705   .  6A 00           PUSH    0                                                        ; /Arg4 = 00000000 ;压入参数4,随机数,由于我们已经在上面去掉随机数的处理,所以我们可以随便压入一个值
00403707   .  8B45 5C         MOV     EAX, DWORD PTR [EBP+5C]                                  ; |
0040370A   .  50              PUSH    EAX                                                      ; |Arg3 ;压入参数3,机器码
0040370B   .  8B45 64         MOV     EAX, DWORD 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     EAX, DWORD 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     ECX, DWORD 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保存。测试一下,感觉还不错。做一下修饰,改下标题名,放个宣传文本就成下图这样了。

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

顺便做个广告:)
承接各类软件开发、加密解密、逆向分析项目
http://www.erawtfos.com
http://www.erawtfos.net

你可能感兴趣的:(原创,c,fun,byte,api,user,算法)