修改现有程序成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