[这个贴子最后由商朝子在 2003/08/20 12:16pm 第 3 次编辑] For Delphi,让你的注册机变小一些 请抛弃VCL,如果你嫌自己用Delphi编译出来的注册机个头儿过大的话... 事实上这种事情确实在发生,就在今天,就在刚才,在偶还没吃饭的时候就看到一个网友在这样抱怨... 的确如此,KeyGen的个头儿与它的界面一样让人失望,简单的form再来上两个Edit,三个Button。普普通通的界面有着360K这样的大体积,而上过壳后那150K的体积同样不能让人满意... 会造成这样的结果,与VCL是脱不了关系的,事实上你用Delphi编译一个什么也不做的程序也会有351K,否则你以为为何用Delphi写程序是一件相当轻松的事情? 不过这样沉重的代价付到我们可爱的KeyGen上就有点儿太过残忍了,很难想像某天要给一个大小200K左右的软件写注册机时会是怎样一个情景... ![]() 来吧,让我们想点儿办法,目前有一些控件可以很好的完成这个工作,用了它们后你编译出来的可执行文件的个头儿要比平常小很多,不过我并不打算介绍它们 ![]() 这并不是什么太高深的东西,相信也不会很难 ![]() 扔掉了VCL,我们能使用的东西就只有Pascal语言与API函数了,但实际上这就已经足够了:) 这之前先要找个软件做例子,大大大大大大大前天看到一个杂志在海吹敏思硬盘卫士,就Down了一个下来,后来才发现它对我没任何用处。呵呵,好老的一个软件了,一年多没更新了吧,估计注册机漫天飞的都有 ![]() 先用TRW2000来摆平它,启动软件,我的机器码是yjeyj239416,随便输入一个注册码,然后用hmemcpy做断点...... 在004a4165处的时候,我们会发现程序会把正确的注册码装入edx中,比如这我边儿的是3887361024,不过我们对它不感兴趣,在它之上的的004a4160处,才是我们真正感兴趣的地方,正确的注册码就是这个CALL来计算的。 按F8跟进去,来到004a3a08处: 0167:004a3a08 push ebp 0167:004a3a09 mov ebp,esp 0167:004a3a0b push byte +00 0167:004a3a0d push byte +00 0167:004a3a0f push byte +00 0167:004a3a11 push ebx 0167:004a3a12 push esi 0167:004a3a13 mov esi,ecx 0167:004a3a15 mov ebx,edx 0167:004a3a17 mov [ebp-04],eax 0167:004a3a1a mov eax,[ebp-04] 0167:004a3a1d call 00404050 0167:004a3a22 xor eax,eax 0167:004a3a24 push ebp 0167:004a3a25 push dword 004a3a6e 0167:004a3a2a push dword [fs:eax] 0167:004a3a2d mov [fs:eax],esp <--挂SEH,不关咱的事儿 ![]() 0167:004a3a30 lea edx,[ebp-0c] 0167:004a3a33 mov eax,[ebp-04] 0167:004a3a36 call 004085a8 <--将机器码转换为大写字符 0167:004a3a3b mov eax,[ebp-0c] 0167:004a3a3e lea ecx,[ebp-08] 0167:004a3a41 movzx edx,bx <--edx装入Dh,即十进制数13 0167:004a3a44 call 004a3958 <--完成计算正确注册码的第一步 0167:004a3a49 mov edx,esi 0167:004a3a4b mov eax,[ebp-08] 0167:004a3a4e call 004a3bdc <-完成计算正确注册码的第二步 0167:004a3a53 xor eax,eax 0167:004a3a55 pop edx 0167:004a3a56 pop ecx 0167:004a3a57 pop ecx 0167:004a3a58 mov [fs:eax],edx 0167:004a3a5b push dword 004a3a75 0167:004a3a60 lea eax,[ebp-0c] 0167:004a3a63 mov edx,03 0167:004a3a68 call 00403c40 0167:004a3a6d ret 0167:004a3a6e jmp 00403630 0167:004a3a73 jmp short 004a3a60 0167:004a3a75 pop esi 0167:004a3a76 pop ebx 0167:004a3a77 mov esp,ebp 0167:004a3a79 pop ebp 0167:004a3a7a ret 004a3a44处的所谓计算注册码的第一步就是指先对机器码进行一些处理,我们按F8跟进去: 0167:004a3958 push ebp 0167:004a3959 mov ebp,esp 0167:004a395b add esp,byte -14 0167:004a395e push ebx 0167:004a395f push esi 0167:004a3960 push edi 0167:004a3961 xor ebx,ebx 0167:004a3963 mov [ebp-14],ebx 0167:004a3966 mov [ebp-0c],ecx 0167:004a3969 mov [ebp-08],edx 0167:004a396c mov [ebp-04],eax 0167:004a396f mov eax,[ebp-04] 0167:004a3972 call 00404050 0167:004a3977 xor eax,eax 0167:004a3979 push ebp 0167:004a397a push dword 004a39f9 0167:004a397f push dword [fs:eax] 0167:004a3982 mov [fs:eax],esp 0167:004a3985 mov eax,[ebp-0c] 0167:004a3988 call 00403c1c 0167:004a398d mov eax,[ebp-08] 0167:004a3990 add eax,[ebp-08] 0167:004a3993 mov [ebp-10],eax 0167:004a3996 mov eax,[ebp-04] 0167:004a3999 call 00403e9c 0167:004a399e mov edi,eax <--eax中此时装的是机器码的位数 0167:004a39a0 test edi,edi <--测试edi 0167:004a39a2 jng 004a39db <--为0就跳到004a39db处 0167:004a39a4 mov esi,01 <--esi置1,用于后边儿的循环 0167:004a39a9 mov eax,[ebp-04] <--机器码的地址装入eax中,此时的机器码已转换为大写字符 0167:004a39ac xor ebx,ebx <--ebx清0 0167:004a39ae mov bl,[eax+esi-01] <--得到机器码 0167:004a39b2 test esi,01 <--test esi,01可以理解为测试esi中装的值是否为双数 ![]() 0167:004a39b8 jnz 004a39bf <--不为双数就跳到004a39bf处 0167:004a39ba add ebx,[ebp-08] <--ebp-08处装的是Dh,也就是那个13,用其加上ebx中的机器码的ASCII码 0167:004a39bd jmp short 004a39c2 <--无条件跳转到004a39bd处 0167:004a39bf add ebx,[ebp-10] <--ebp-10处装的是1Ah,即十进制数26,用其加上ebx中装的机器码的ASCII码 0167:004a39c2 lea eax,[ebp-14] 0167:004a39c5 mov edx,ebx 0167:004a39c7 call 00403dc4 0167:004a39cc mov edx,[ebp-14] 0167:004a39cf mov eax,[ebp-0c] 0167:004a39d2 call 00408594 <--将得到的ASCII码与26或13相加后的结果保存起来 0167:004a39d7 inc esi <--esi加上1 0167:004a39d8 dec edi <--edi减去1 0167:004a39d9 jnz 004a39a9 <--edi不为零,也就是机器码还没有全计算完,就跳回004a39a9处继续 0167:004a39db xor eax,eax 0167:004a39dd pop edx 0167:004a39de pop ecx 0167:004a39df pop ecx 0167:004a39e0 mov [fs:eax],edx 0167:004a39e3 push dword 004a3a00 0167:004a39e8 lea eax,[ebp-14] 0167:004a39eb call 00403c1c 0167:004a39f0 lea eax,[ebp-04] 0167:004a39f3 call 00403c1c 0167:004a39f8 ret 0167:004a39f9 jmp 00403630 0167:004a39fe jmp short 004a39e8 0167:004a3a00 pop edi 0167:004a3a01 pop esi 0167:004a3a02 pop ebx 0167:004a3a03 mov esp,ebp 0167:004a3a05 pop ebp 0167:004a3a06 ret 这个CALL的作用呢,就是这样的:将机器码中的每个字符的ASCII码,加上26或13(单位,如第1、3、5位加上26,双位,如第2、4、6位加上13),并将之保存起来,以便后面的应用。与我的机器对应的是:sW_fd?MFN>P 好的,我们继续,等到返回后,接着走两步,我们就会到达004a3a4e处,在这个CALL里,会完成计算注册码的最后一步,一起去看看吧: 0167:004a3bdc push ebp 0167:004a3bdd mov ebp,esp 0167:004a3bdf add esp,byte -10 0167:004a3be2 push ebx 0167:004a3be3 mov [ebp-08],edx 0167:004a3be6 mov [ebp-04],eax 0167:004a3be9 mov eax,[ebp-04] 0167:004a3bec call 00404050 0167:004a3bf1 xor eax,eax 0167:004a3bf3 push ebp 0167:004a3bf4 push dword 004a3c62 0167:004a3bf9 push dword [fs:eax] 0167:004a3bfc mov [fs:eax],esp 0167:004a3bff mov eax,[ebp-08] 0167:004a3c02 call 00403c1c 0167:004a3c07 mov ebx,1e61 <--ebx装入1e61h,即十进值数7777 0167:004a3c0c mov eax,[ebp-04] 0167:004a3c0f call 00403e9c 0167:004a3c14 mov edx,eax 0167:004a3c16 test edx,edx 0167:004a3c18 jna 004a3c32 0167:004a3c1a mov eax,01 <--eax中装入1,用于下边儿的循环 0167:004a3c1f mov ecx,[ebp-04] <--ebp-04中装的正是字符串sW_fd?MFN>P的地址 0167:004a3c22 movzx ecx,byte [ecx+eax-01] <--ecx中装入本轮用于计算的字符 0167:004a3c27 add ecx,eax <--ecx加上eax的值 0167:004a3c29 imul ecx,ebx <--乘以ebx,第一次的时候ebx中的值为前边装入的7777 0167:004a3c2c mov ebx,ecx <--结果装入ebx中 0167:004a3c2e inc eax <--eax加上1,用于得到下一个字符 0167:004a3c2f dec edx <--edx减去1 0167:004a3c30 jnz 004a3c1f <--不为零就跳回去继续 0167:004a3c32 mov eax,[ebp-08] 0167:004a3c35 push eax 0167:004a3c36 mov [ebp-10],ebx 0167:004a3c39 mov byte [ebp-0c],00 0167:004a3c3d lea edx,[ebp-10] 0167:004a3c40 xor ecx,ecx 0167:004a3c42 mov eax,004a3c78 0167:004a3c47 call 004098ec 0167:004a3c4c xor eax,eax 0167:004a3c4e pop edx 0167:004a3c4f pop ecx 0167:004a3c50 pop ecx 0167:004a3c51 mov [fs:eax],edx 0167:004a3c54 push dword 004a3c69 0167:004a3c59 lea eax,[ebp-04] 0167:004a3c5c call 00403c1c 0167:004a3c61 ret 0167:004a3c62 jmp 00403630 0167:004a3c67 jmp short 004a3c59 0167:004a3c69 pop ebx 0167:004a3c6a mov esp,ebp 0167:004a3c6c pop ebp 0167:004a3c6d ret 嘿嘿,“平民算法”嘛 ![]() 现在我们手里所掌握的,已足够写出注册机了,通常情况下,我们可以声明这样一个函数用于计算正确的注册码: function GetKey(Name: String): String; var User,Serial:String; i:integer; ASC,jia,Zhi:Cardinal; begin zhi:=7777; User:=UpperCase(Name); if Length(User)=0 then Result:=‘请输入您的机器码...‘ else begin for i:=1 to Length(User) do begin ASC:=Ord(User[i]); if i mod 2 =0 then ASC:=ASC+13 else ASC:=ASC+26; ASC:=ASC+i; jia:=ASC*zhi; zhi:=jia; end; Serial:=inttostr(zhi); Result:=Serial; end; end; 然后我们就可以在Delphi中通过VCL来增大我们KeyGen的体积了,但今天我们不准备这么做 ![]() OK,启动你的Delphi,然后新建一个应用程序,然后就在代码窗口中右键Unit1单元->Close Page,接着选No来把Unit1.Pas给删除掉。 接着选菜单View->Units,双击Project1打开它,我们就靠它吃饭了 ![]() 然后我们可以把它的内容给全部删掉,只留下三行: program KeyGen; begin end. 呵呵,按F9吧,你会什么也感觉不到,但事实上这已经是一个程序了。 如果你看一下所生成的KeyGen.exe的大小,你会发现,尽管我们什么也没有写,可它已经有了8K大。这是因为在任何DELPHI的目标程序中,都会自动包含System单元中的代码,如果你用Delphi5的话,它会是16K。在这小小的8K中,包含了支撑整座DELPHI大厦的基石,VeryVery牢靠。 以后随着我们代码的增加,程序的大小也会从8K开始慢慢跟着增加 ![]() 让我们开始吧,我把注册机与资源文件的代码贴出来(修正了dREAMtHEATER指出的两处错误,同时代码更紧凑了一些^_^): {-<KeyGen.dpr>------------------------------------Code made >> Suunb[CCG]-----.} program KenGen; {$R ‘KenGen.res‘ ‘KenGen.rc‘} (*感谢dREAMtHEATER老哥的指正:)*) uses Windows, Messages; {---------------------------------------------------------<引入相关单元>------;} {----Windows.pas里面包含了常量与API的定义,Messages.pas中则包含了消息的定义----;} const IDI_CCG = 1 ; IDC_EDIT_CODE = 3001 ; IDC_EDIT_SERIAL = 3002 ; IDC_BUTTON_GENERATE = 3003 ; IDC_BUTTON_ABOUT = 3004 ; IDC_BUTTON_EXIT = 3005 ; IDC_ABOUT_BUTTON_GREETING = 3006 ; szDlgName = ‘KeyGenMain‘; szAboutDlg = ‘About‘; {---------------------------------------------------------<以上为常量定义>----;} function GetKey(S: String): String; var Code,Serial:String; i:integer; ASC,Q,P:Cardinal; NN:array [0..512] of Char; begin P:=7777; Code:=S; if Length(Code)=0 then Result:=‘请输入您的机器码...‘ else begin CharUpperBuff(PChar(Code),Length(S)); for i:=1 to Length(Code) do begin ASC:=Ord(Code[i]); if i mod 2 =0 then ASC:=ASC+13 else ASC:=ASC+26; ASC:=ASC+i; Q:=ASC*P; P:=Q; end; if P>2147483647 then begin if (P>2147483647) and (P<3000000000) then begin P:=P-2000000000; if P<100000000 then wvsprintf(NN,‘0%d‘,@P) else wvsprintf(NN,‘%d‘,@P); Serial:=‘2‘+String(NN); end else if (P>3000000000) and (P<4000000000) then begin P:=P-3000000000; if P<100000000 then wvsprintf(NN,‘0%d‘,@P) else wvsprintf(NN,‘%d‘,@P); Serial:=‘3‘+String(NN); end else if (P>4000000000) and (P<5000000000) then begin P:=P-4000000000; if P<100000000 then wvsprintf(NN,‘0%d‘,@P) else wvsprintf(NN,‘%d‘,@P); Serial:=‘4‘+String(NN); end end else begin wvsprintf(NN,‘%d‘,@P); Serial:=String(NN); end; Result:=Serial; end; end; {-------------------------------<上面的这个函数就是用来计算正确的注册码的>----;} function AboutProc (Wnd: hWnd; Msg, wParam: Word; lParam: LongInt) :LongInt; stdcall; begin Case Msg of WM_CLOSE:EndDialog(Wnd,0); WM_COMMAND: begin if (LOWORD(wParam))=IDC_ABOUT_BUTTON_GREETING then EndDialog(Wnd,0) else Result:=1; end; end; Result:=0; end; {-<上面的函数用于处理“关于”对话框接收到的消息,stdcall表示其为一个回调函数>-;} function DlgProc (Wnd: hWnd; Msg, wParam: Word; lParam: LongInt) :LongInt; stdcall; var Code:array[0..512] of Char; Serial:String; begin Case Msg of WM_CLOSE: EndDialog(Wnd,0); WM_INITDIALOG: begin SendMessage(Wnd,WM_SETICON, ICON_SMALL, LoadIcon(hInstance, PChar(IDI_CCG))); end; WM_COMMAND: begin case (LOWORD(wParam)) of IDC_BUTTON_about:DialogBoxParam(hInstance,szAboutDlg,Wnd, @AboutProc, 0); IDC_BUTTON_GENERATE: begin GetDlgItemText(Wnd,IDC_EDIT_CODE,Code,512); Serial:=GetKey(Code); SetDlgItemText(Wnd,IDC_EDIT_SERIAL,PChar(Serial)); end; IDC_BUTTON_EXIT:SendMessage(Wnd,WM_CLOSE, 0, 0); end; end; else Result:=1; end; Result:=0; end; {---------------------------------<主窗口的消息循环,同样为一个回调函数>------;} begin DialogBoxParam(hInstance,szDlgName,0,@DlgProc,0); (*程序开始,调用DialogBoxParam来创建程序的主对话框,hInstance是Delphi提供 给我们的全局变量,不必再去调用GetModuleHandle函数*) {经dREAMtHEATER指正,Delphi会自动调用ExitProcess函数,所以将其免去...} end. //------------------------------------------------------------------------------ //------------------the Shang Dynasty (16th―11th century B.C.)----------------- //--------------------------------------------------------------------Code end-- 下面再把资源文件也贴出来: //-KeyGen.rc-------------------------------------------------by Suunb[CCG]-- #define CCG_ICO 1 #define Su_ICO 2 #define IDC_EDIT_CODE 3001 #define IDC_EDIT_SERIAL 3002 #define IDC_BUTTON_GENERATE 3003 #define IDC_BUTTON_ABOUT 3004 #define IDC_BUTTON_EXIT 3005 #define IDC_ABOUT_BUTTON_GREETING 3006 //-------------------------------------------<以上是一些常量的定义>--------- KeyGenMain DIALOGEX 10, 10, 163, 94 style DS_CENTER | WS_MINIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "CCG KeyGen for 敏思硬盘卫士 v2.2" FONT 9, "Arial" BEGIN GROUPBOX "register",-1,5,3,151,44,BS_FLAT LTEXT "Code:",-1,10,14,23,10 EDITTEXT IDC_EDIT_CODE,35,13,115,12,ES_AUTOHSCROLL,WS_EX_DLGMODALFRAME LTEXT "Serial:",-1,10,29,23,10 EDITTEXT IDC_EDIT_SERIAL,35,29,115,12,ES_AUTOHSCROLL,WS_EX_DLGMODALFRAME PUSHBUTTON "gENERATE",IDC_BUTTON_GENERATE,13,54,39,11,BS_CENTER | BS_FLAT PUSHBUTTON "aBOUT",IDC_BUTTON_ABOUT,61,54,39,11,BS_CENTER | BS_FLAT PUSHBUTTON "eXIT",IDC_BUTTON_EXIT,108,54,39,11,BS_CENTER | BS_FLAT LTEXT "wish it becorning more prosperous every day!",-1,10,77, 143,9 GROUPBOX "Lovely CCG",-1,6,69,151,21,BS_CENTER | BS_FLAT END //--------------------------------------------<以上是程序的的主窗口对话框>--------- About DIALOGEX 10, 10, 143, 141 style DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "The Shang Dynasty (16th - 11th century B.C.)" FONT 9, "Arial" BEGIN ICON 1,-1,13,14,20,20 LTEXT "Code made:",-1,25,3,41,8 LTEXT "by",-1,40,13,8,8 LTEXT "Date:April 26t,2003",-1,48,25,61,9 CONTROL "",-1,"Static",SS_ETCHEDHORZ,4,36,132,1 LTEXT "Personal Individual Greetings go to:",-1,19,41,114,8 LTEXT "Sun Bird - JOJO - KANXUE",-1,11,51,85,9 LTEXT "pll621 - zmworm - yyxzz",-1,46,62,73,9,SS_CENTERIMAGE LTEXT "Personal Group Greetings go to:",-1,22,87,105,8 LTEXT "BCG FCG iPB DFCG DCM",-1,28,101,85,9,WS_BORDER LTEXT "Suunb[CCG]",-1,50,12,45,11,0,WS_EX_DLGMODALFRAME LTEXT "and other CCG guys...",-1,29,74,92,9,SS_CENTERIMAGE ICON 2,-1,112,14,20,20 PUSHBUTTON "gREETING",IDC_ABOUT_BUTTON_GREETING,50,120,41,12,BS_FLAT END //--------------------------------------------<这个是“关于”对话框>-------- 你可以用记事本将这两个文件分别保存为KeyGen.dpr、KeyGen.rc,之后将KeyGen.rc与KeyGen.dpr放置同一目录下用Delphi编译KeyGen.dpr即可... 看一下编译后的KeyGen的大小吧,20.5K,再来个壳就只有10几K了,虽然界面并不怎么漂亮 ![]() OK,就到这儿。 |
|