| 我灌!
Internet網(wǎng)絡(luò )的迅速發(fā)展,為軟件高效傳播開(kāi)辟更加廣闊的天地 。如國內著(zhù)名的金蜘蛛軟件下載中心,就是一個(gè)典型的發(fā)布軟件集散 地。發(fā)布共享軟件主要包括兩種形式:日期限制形式和電子注冊形式 。日期限制形式允許下載軟件的用戶(hù)使用軟件一段時(shí)間,如一個(gè)月等 ,如果用戶(hù)認可該軟件,可購買(mǎi)該軟件的注冊序列號繼續使用;電子 注冊形式就是根據用戶(hù)所用機器的硬件信息產(chǎn)生注冊碼,并在軟件中 對某些先進(jìn)或常用功能進(jìn)行限制,如果用戶(hù)要使用其全部功能,必須 將軟件采集的有關(guān)硬件信息反饋給開(kāi)發(fā)者,并交一定的注冊費可獲得 該軟件在自己機器中的注冊碼,才能正常使用。 前一種形式很容易給盜版者造成可乘之機,如果制作盜版者購買(mǎi) 了一個(gè)注冊序列號并公布于天下,則所有用戶(hù)使用這個(gè)注冊號都可進(jìn) 行正常使用;后者對用戶(hù)來(lái)說(shuō)注冊手段稍顯復雜些,對開(kāi)發(fā)者來(lái)說(shuō)也 需要一定的編程真功夫,但其具有“八點(diǎn)鎖緊”功能,防盜性卻是不 容置疑。本文根據自己的實(shí)踐,將后者的實(shí)現過(guò)程介紹給想要制作發(fā) 布共享軟件的讀者。 一、注冊源 在WIN98/95的保護模式下,要根據硬件信息形成注冊碼可不是一 件容易的事,在實(shí)模式下可通過(guò)硬盤(pán)端口1F6H和1F7H直接讀取硬盤(pán)的 序列號等信息作為注冊的數據源,但這一方法在保護模式下卻被亮出 了紅牌。利用BIOS中的主板序列號、BIOS版本序列號或主機出廠(chǎng)日期 和標志等,完全可以作為注冊碼的注冊源。如ROMBIOS中F000H-FFFFH 區域中就存在與硬件配置有關(guān)的信息,還可以采集其它一處或幾處主 板等的信息作為注冊碼的生產(chǎn)基地。例如可根據F000H:FFF5H-F000H: FFFFH 中存放的主機出廠(chǎng)日期和主機標志值,產(chǎn)生應用程序的注冊碼 。由于計算機產(chǎn)品的更新?lián)Q代比較快,而且所有用戶(hù)使用的計算機不 可能配置都完全相同,所以注冊碼產(chǎn)生的源也不會(huì )完全相同。而且這 些硬件信息內容在任何操作系統下均完全相同,兼容性非常好,更不 會(huì )因為操作系統的更新而造成注冊功能失效。 注冊源確定之后,關(guān)鍵的問(wèn)題就是共享軟件安裝程序如何采集注 冊源信息,并讓用戶(hù)將其返回給開(kāi)發(fā)者。最簡(jiǎn)單的方法就是將采集到 的注冊源信息經(jīng)過(guò)位操作加密后存放到一個(gè)文本中,形成注冊碼的數 據源資料。這個(gè)注冊源數據串可稍長(cháng)一些,但不宜過(guò)長(cháng),使用戶(hù)能夠 通過(guò)電子郵箱、電話(huà)或信件順利轉給開(kāi)發(fā)者為宜。如筆者安裝程序是 用C語(yǔ)言編制的,如果將上述內存地址作為注冊源,數據串文本文件 名為KEYID.DOC,長(cháng)度為20個(gè)字符。其示例代碼如下: FILE *fp2; unsigned int keyrom[9]; unsigned char buff[0x410]; unsigned char pathstmp[80]; unsigned char path[80]={ "C:\\WBCOOL "}; unsigned int far *pt=(unsigned int far*)0xf000fff6L; ...... outportb(0x21,0x2); strcpy(pathstmp,path); strcat(pathstmp, "\\ "); strcat(pathstmp, "KEYID.DOC "); for(i=0;i <5;i++) keyrom[i]=(*(pt+i)+0x1818)^0x5858;//第一級加密算法 sprintf(buff, "KEYID:%04x%04x%04x%04x%04x ", keyrom[0],keyrom[1],keyrom[2],keyrom[3],keyrom[4]); buff[0x1a]=0; if((fp2=fopen(pathstmp, "wb "))==NULL){ printf( "FILE %s CREATE ERROR! ",pathtmp); } else { fseek(fp2,0L,SEEK_SET); fprintf(fp2, "%s\xd\xa ",buff); fclose(fp2); } outportb(0x21,0x0); 二、注冊機 開(kāi)發(fā)者得到用戶(hù)提供的注冊源數據之后,就需要利用注冊機生成 注冊碼并返回給用戶(hù)。注冊機利用既定的位操作和不可逆算法,形成 用戶(hù)比較容易操作的字符串注冊碼,注冊碼的長(cháng)度一般為8-16位為宜 ,用戶(hù)只需注冊一次就可以長(cháng)期使用,所以注冊碼的長(cháng)度不會(huì )影響用 戶(hù)的注冊操作。當然注冊機的算法應與共享軟件中的算法部分基本相 同。對于遠程用戶(hù),注冊機應該具有從鍵盤(pán)和內存兩種取得注冊源數 據的功能,所以注冊機的加密算法實(shí)際為兩個(gè)分支:第一個(gè)分支是從 鍵盤(pán)獲取注冊源數據后直接根據注冊算法形成注冊碼的過(guò)程,是直接 給遠程用戶(hù)反饋注冊碼的過(guò)程;第二個(gè)分支是直接從ROM BIOS中根據 注冊源算法取得注冊源數據,再根據注冊算法形成注冊碼的過(guò)程,是 直接讀取本地機注冊碼的。 用戶(hù)得到注冊碼后,根據共享發(fā)布軟件的注冊方法進(jìn)行一次注冊 ,應用程序會(huì )自動(dòng)將這個(gè)注冊碼存放到軟件的特定位置處,當應用程 序被他人拷貝到其它機器中去后,由于注冊碼因不同機器而異,所以 應用程序的功能或使用次數仍然受限,要在其它機器中使用該應用程 序,還必須進(jìn)行重新注冊,達到共享軟件發(fā)布目的。同時(shí)由于注冊源 數據的算法和注冊碼算法均可因人而異,因此這種方法非??煽?。本 人實(shí)現的注冊機帶參數時(shí)接受鍵盤(pán)輸入注冊源;不帶任何參數時(shí)從本 地機器內直接采集注冊源數據。我的注冊機示例程序如下: #include <conio.h> #include <dos.h> #include <io.h> #include <dir.h> #include <alloc.h> #include <string.h> #include <stdio.h> #include <process.h> #include <fcntl.h> #include <ctype.h> #include <stdlib.h> unsigned char Buff[18]; unsigned char Buff1[18]; unsigned int keyrom[9]; unsigned int sum,sum1,sumi,sumj; unsigned int far *pt=(unsigned int far *)0xf000fff6L; unsigned int i=0,j=0,m,imecom; unsigned char p; unsigned int nn,nn1,nn2; unsigned char rbuff[100],cc,cc1,cc2; int fp; void main(int argc,char *argv[]) { if(argc> =2){ printf( "KEYID: "); scanf( "%s ",rbuff);//接受鍵盤(pán)輸入遠程注冊源 j=strlen(rbuff); if(j!=20) exit(1); for(i=0;i <20;i++){//讀入20位注冊源數據 if((rbuff[i]> = 'a ')&&(rbuff[i] <= 'f ')) rbuff[i]&=0xdf; if((rbuff[i]> = 'A ')&&(rbuff[i] <= 'F ')) rbuff[i]-=0x37; else if((rbuff[i]> = '0 ')&&(rbuff[i] <= '9 ')) rbuff[i]-=0x30; else exit(1); } for(i=0;i <5;i++){//形成字符串 cc1=rbuff[i*4]&0xf; cc2=rbuff[i*4+1]&0xf; cc=(cc1 < <4)|cc2; nn1=(unsigned int)cc; cc1=rbuff[i*4+2]&0xf; cc2=rbuff[i*4+3]&0xf; cc=(cc1 < <4)|cc2; nn2=(unsigned int)cc; nn=(nn1 < <8)|nn2; keyrom[i]=nn; } sum=0x1234; sum1=0x7456; for(sumj=0;sumj <4;sumj++){//形成16位注冊碼 for(sumi=0;sumi <5;sumi++){ sum+=keyrom[sumi]; //形成前4位碼 sum1+=keyrom[sumi]; } sum^=0x1234 < <sumj; //進(jìn)行移位異或處理 sum1^=0x7456 < <sumj; sprintf(Buff+4*sumj, "%04x ",sum); sprintf(Buff1+4*sumj, "%04x ",sum1); } //形成16位注冊碼 printf( "\nWIN-KEY: "); printf(Buff); printf( "\nDOS-KEY: "); printf(Buff1); exit(1); } else { sum=0x1234; sum1=0x7456; for(sumj=0;sumj <4;sumj++){//形成16位注冊碼 for(sumi=0;sumi <5;sumi++){ sum+=(*(pt+sumi)+0x1818)^0x5858; sum1+=(*(pt+sumi)+0x1818)^0x5858; } sum^=0x1234 < <sumj; sum1^=0x7456 < <sumj;//進(jìn)行移位異或處理 sprintf(Buff+4*sumj, "%04x ",sum); sprintf(Buff1+4*sumj, "%04x ",sum1); } printf( "\nWIN-KEY: "); printf(Buff); printf( "\nDOS-KEY: "); printf(Buff1); } } 三、注冊碼 當用戶(hù)注冊成功后,注冊碼就被寫(xiě)到共享軟件的相應位置。這時(shí) 共享軟件必須對用戶(hù)注冊碼進(jìn)行實(shí)時(shí)檢測與判斷,才能實(shí)現注冊限制 功能。這時(shí)要求共享軟件必須內部取得注冊源數據,并利用注冊機中 相同的算法產(chǎn)生內部注冊碼。這就要求共享軟件直接讀取ROM BIOS的 注冊源信息,并在共享軟件中需要限制的功能處增加注冊碼檢測判斷 功能,這需要根據共享軟件的實(shí)際需要、軟件大小和實(shí)現的難易程度 來(lái)確定限制的數量,使盜版者很難進(jìn)行解密。這樣既使計算機中多個(gè) 共享軟件使用相同的注冊源,也不會(huì )發(fā)生注冊沖突問(wèn)題;既使是使用 了相同的注冊源數據,由于注冊算法的不同注冊碼也不會(huì )相同;即使 解密者知道注冊算法的注冊源地址,由于無(wú)法知道注冊算法而且注冊 點(diǎn)遍布整個(gè)共享軟件,也很難進(jìn)行盜版。因此,這一注冊方法使共享 軟件有效地跨越各種系統平臺。 要在共享軟件內部產(chǎn)生注冊碼,必須在共享軟件中讀取ROM BIOS 數據源內存數據。WINDOWS保護模式下必須利用段選擇符方法和API編 程接口提供的函數才能實(shí)現: 1.AllocSelector(Selector)分配一個(gè)與參數相同的空選擇器 2.FreeSelector(Selector) 釋放分配的選擇器 3.SetSelectorBase() 設置選擇器描述符物理起始地址 4.GetSelectorBase() 獲取選擇器描述符物理起始地址 5.SetSelectorLimit() 設置選擇器描述符訪(fǎng)問(wèn)界限 6.GetSelectorLimit() 獲取選擇器描述符訪(fǎng)問(wèn)界限 其中函數AllocSelector(Selector) 是保護模式下物理內存訪(fǎng)問(wèn) 的關(guān)鍵,Selector是分配空選擇器的段寄存器模板,可以利用Global Alloc()函數分配內存,再利用GlobalHandleToSel()函數將內存句柄 轉換為相應選擇器,內存單元訪(fǎng)問(wèn)結束后再利用GlobalFree()釋放分 配的內存。最簡(jiǎn)單的方法就是將系統的數據段寄存器__DS直接作為模 板參數,這個(gè)參數在一般應用程序中完全可以正常使用。然后利用Se tSelectorBase()和SetSelectorLimit( )函數分別設置內存的物理起 始地址和訪(fǎng)問(wèn)界限值,利用正常的指針操作*pt=Value和Value=*pt訪(fǎng) 問(wèn)物理內存單元,訪(fǎng)問(wèn)結束后必須使用FreeSelector()函數釋放分配 的選擇器,因為WINDOWS 并不自動(dòng)釋放無(wú)用的選擇器,而且系統的選 擇器共享資源是非常有限,只有8192個(gè)供使用。根據以上原理及注冊 機中的注冊源和注冊碼算法,就不難實(shí)現共享軟件內部注冊碼函數: UINT ImeCmpkey(void) { //共享軟件內部注冊碼產(chǎn)生函數 static unsigned int sum; static BOOL flag; static unsigned int far *pt; static UINT Sel1,Sel2; static WORD Seg,Off,Start; static DWORD Bas,Lim; flag=TRUE; sum=0x1234; __asm mov Sel1,ds;//將DS作為模板 Sel2=AllocSelector(Sel1);//分配一個(gè)新選擇符 if(Sel2==NULL){ flag=FALSE; pt=(unsigned int far*)0xf000fff0L; } else { Seg=0xffff; //絕對地址段址 Off=0x10; //絕對地址偏移 Start=0x0; Bas=((unsigned long)Seg) < <4|Start; Lim=(unsigned long)Off-1; SetSelectorBase(Sel2,Bas); SetSelectorLimit(Sel2,Lim); pt=(unsigned int far*)((((unsigned long)Sel2) < <16)|Start); } for(j=0;j <4;j++){//形成16位注冊碼 for(i=0;i <5;i++) sum+=(*(pt+3+i)+0x1818)^0x5858;//形成前4位 sum^=0x1234 < <j;//進(jìn)行移位異或處理 wsprintf((LPSTR)sImeG.ImeKey+4*j,(LPSTR) "%04x ",sum); } if(flag==TRUE) FreeSelector(Sel2); sImeG.ImeKey[16]=0;//對注冊碼本身加密 for(i=16;i> 0;i--) sImeG.ImeKey[16-i]^=(unsigned char)i; for(i=0;i <16;i++){ //判斷注冊碼 if(sImeG.ImeKey[i]!=lpImeL-> ZcMyOk[i]) break; } if(i==16){ sImeG.ZcFlag=FALSE; sImeG.ZcCount=0x0; lpImeL-> UseNum=0x0; for(i=0;i <16;i++) sImeG.ImeKey[i]=0x0; return(0); } else { sImeG.ZcFlag=TRUE; sImeG.ZcCount=0x0; sImeG.iSel = 1; sImeG.FScrCz = TRUE; lstrcpy(sImeG.szSel[0],(LPSTR) "注冊:_________________ "); sImeG.szSel[0][6]=0x11; sImeG.szSel[0][23]=0x0; UpdateInList(); return(~0); } } 四、注冊點(diǎn) 共享軟件內部注冊碼產(chǎn)生后,需要對抗盜版的注冊點(diǎn)的多少取決 于共享軟件的自身價(jià)值、開(kāi)發(fā)者的加密深度和軟件實(shí)現的復雜程度等 諸多因素,同時(shí)這也決定了注冊提示信息的顯示頻度,來(lái)套磁用戶(hù)進(jìn) 行合法使用。但就筆者自身而言,至少應該將共享軟件中實(shí)現難度較 大、深受用戶(hù)歡迎以及普遍使用的功能加上注冊點(diǎn)。建議不同平臺之 間的注冊碼要分別設計注冊算法和注冊碼。其代碼示例如下: 注冊點(diǎn)一: if(lpImeL-> UseNum> =0x3f80){ if(ImeCmpKey()==~0){ sImeG.ZcFlag=TRUE; } else { sImeG.ZcFlag=FALSE; lpImeL-> UseNum=0x0; } } else lpImeL-> UseNum+=sImeG.iWord; 注冊點(diǎn)二: if(ImeCmpKey()==~0){ sImeG.ZcFlag=TRUE; return; } else { sImeG.ZcFlag=FALSE; lpImeL-> UseNum=0x0; } 五、注冊口 對于共享軟件,不管其實(shí)現何種功能,最好采取再線(xiàn)注冊方式, 這樣可以減少用戶(hù)很多重復操作。同時(shí)應該采取多個(gè)注冊入口,如本 人軟件可以在增加或刪除詞組等時(shí)進(jìn)行注冊,只要一處注冊成功整個(gè) 軟件就算注冊成功,并注意對注冊口輸入的注冊碼進(jìn)行再加密處理。 筆者共享軟件中注冊口代碼示例代碼如下: if(sImeG.ZcFlag==TRUE){ if((cCharCode==0x8)||(cCharCode==0x4b)){ if(sImeG.ZcCount> 0){ //刪除鍵處理 if(sImeG.ZcCount <17) sImeG.szSel[0][sImeG.ZcCount+6]=0x5f; else sImeG.szSel[0][sImeG.ZcCount+6]=0x0; sImeG.ZcCount--; sImeG.szSel[0][sImeG.ZcCount+6]=0x11; lpImeL-> ZcMyOk[sImeG.ZcCount]=0x0; sImeG.iSel = 0x1; sImeG.FScrCz = TRUE; UpdateInList(); } else MessageBeep(-1); } else if (cCharCode==0xd){//回車(chē)鍵處理 if(sImeG.ZcCount==0x10){ sImeG.ZcFlag=FALSE; sImeG.ZcCount=0x0; sImeG.iSel = 0x0; ScrnCode(sImeG.iStart); sImeG.FScrCz = TRUE; UpdateInList(); for(i=16;i> 0;i--) lpImeL-> ZcMyOk[16-i]^=(unsigned char)i; lpImeL-> ZcMyOk[16]=0; for(i=0;i <16;i++){ if(sImeG.ImeKey[i]!=lpImeL-> ZcMyOk[i]) break; } if(i==16){//寫(xiě)入注冊碼 for(i=0;i <16;i++) sImeG.ImeKey[i]=0x0; j=GetSystemDirectory(FileName,80); if((j==0)||(j> 64)){ wsprintf((LPSTR)sImeG.ImeBuff,(LPSTR) "系統路徑非法! "); ErrMessageBox((LPSTR)sImeG.ImeBuff); for(i=0;i <16;i++) lpImeL-> ZcMyOk[i]=0x0; ShowMessTs(8); } else { lstrcat(FileName,(LPSTR) "\\ "); lstrcat(FileName,(LPSTR) "WBCOOL.IME "); if((hTmp=_lopen(FileName,READ_WRITE))==-1){ wsprintf((LPSTR)sImeG.ImeBuff,(LPSTR) "程序打開(kāi)出錯! "); ErrMessageBox((LPSTR)sImeG.ImeBuff); for(i=0;i <16;i++) lpImeL-> ZcMyOk[i]=0x0; ShowMessTs(8); } else { _llseek(hTmp,0x12345L,SEEK_SET);//12345為注冊碼地址 _lwrite(hTmp,lpImeL-> ZcMyOk,16); _lclose(hTmp); ShowMessTs(7); } } } else { for(i=0;i <16;i++){ sImeG.ImeKey[i]=0x0; lpImeL-> ZcMyOk[i]=0x0; } ShowMessTs(8); } } else MessageBeep(-1); } else if ((cCharCode> =0x30)&&(cCharCode <= '~ ')){ if(sImeG.ZcCount <16){ if((cCharCode> = 'A ')&&(cCharCode <= 'Z ')) cCharCode^=0x20; lpImeL-> ZcMyOk[sImeG.ZcCount]=cCharCode; sImeG.szSel[0][sImeG.ZcCount+6]=cCharCode; sImeG.ZcCount++; sImeG.szSel[0][sImeG.ZcCount+6]=0x11; sImeG.iSel = 0x1; sImeG.FScrCz = TRUE; UpdateInList(); } else MessageBeep(-1); } else MessageBeep(-1); return(iRet); } 總之,共享發(fā)布軟件的制作應做到:注冊源要選準、注冊算法要 多變、注冊碼要再加密、注冊機要管好、注冊點(diǎn)要多方位、注冊方式 要在線(xiàn)、注冊入口要多點(diǎn)。這樣才能確保軟件的安全可靠。
我在灌?。?! 在微軟的操作系統中,硬盤(pán)的磁盤(pán)序列號是在硬盤(pán)分區后系統產(chǎn)生的8字節隨機數字,理論上有重復的可能,但實(shí)際上找到兩個(gè)相同的分區序列號很難,因此,在實(shí)際應用中可以把硬盤(pán)序列號作為唯一識別碼用于我們的加密系統,實(shí)際應用中可以對序列號做幾次邏輯運算,讓別人覺(jué)得不是磁盤(pán)序列號。注意,軟盤(pán)也有磁盤(pán)序列號,是在格式化后產(chǎn)生的,當進(jìn)行磁盤(pán)復制時(shí),兩個(gè)磁盤(pán)的序列號也被復制為一樣的。因此,不能利用軟盤(pán)的序列號進(jìn)行加密。當然,硬盤(pán)也可以用GHOST等軟件克隆,但是為了使用某個(gè)軟件而去克隆整個(gè)硬盤(pán)的情況是極少發(fā)生的。
---- 下面代碼是一個(gè)C動(dòng)態(tài)連接庫,在Windows98/95/NT4.0+VC5.0環(huán)境下編譯通過(guò),用于產(chǎn)生密碼鎖。
// MyDll.cpp
//產(chǎn)生密碼鎖 /
#include < windows.h > #include < winbase.h > #include < stdio.h >
//#include "mydll.h "
BOOL APIENTRY DllMain(HANDLE hInst, DWORD ul_reason_being_called, LPVOID lpReserved) { return 1; UNREFERENCED_PARAMETER(hInst); UNREFERENCED_PARAMETER(ul_reason_being_called); UNREFERENCED_PARAMETER(lpReserved); }
/////////////////////////////////////////////////// DWORD APIENTRY GetDiskSerialNo(void) { LPCTSTR lpRootPathName= "c:\\ "; //取C盤(pán)的序列號 LPTSTR lpVolumeNameBuffer=new char[12];//磁盤(pán)卷標 DWORD nVolumeNameSize=12;
DWORD VolumeSerialNumber;//磁盤(pán)序列號 DWORD MaximumComponentLength; LPTSTR lpFileSystemNameBuffer=new char[10]; DWORD nFileSystemNameSize=10; DWORD FileSystemFlags; GetVolumeInformation(lpRootPathName, lpVolumeNameBuffer, nVolumeNameSize, &VolumeSerialNumber, &MaximumComponentLength, &FileSystemFlags, lpFileSystemNameBuffer, nFileSystemNameSize); VolumeSerialNumber^=0x90909090;//做一轉換, 別讓人一眼就看出是磁盤(pán)序列號 return VolumeSerialNumber; }
DWORD APIENTRY CreateLock(void) { DWORD dwNo; dwNo=GetDiskSerialNo(); dwNo^=0x90909090; return dwNo; }
DWORD APIENTRY TestKey(char far *sSN) {
DWORD dwSN; if(strlen(sSN)==0){return 0;} sscanf(sSN, "%8lX ",&dwSN); dwSN=~dwSN;///////// 加密運算 dwSN^=0x88888888;//////////加密運算 sprintf(sSN, "%8lX ",dwSN); if (dwSN==CreateLock())return 1; else return 0; }
---- 下面是解密機的源代碼。因為功能簡(jiǎn)單,不用編譯成Windows應用程序,做成控制臺(DOS)下應用程序就可以完成任務(wù)。 //GetKey.C //解密機,獲得密匙 #include < stdio.h > void main(void) { unsigned long dwSN; printf( "\n請輸入密碼鎖號: "); scanf( "%8lX ",&dwSN); dwSN=~dwSN;//////解密運算 dwSN^=0x88888888;/////////解密運算 printf( "該用戶(hù)的密匙是 %8lX\n ",dwSN);
}
---- 使用說(shuō)明:上面的DLL隨應用程序一同發(fā)行。解密機軟件作者保留,千萬(wàn)別散發(fā). ---- 注冊時(shí),調用CreateLock()得到機器的識別號(密碼鎖),在應用程序中顯示給用戶(hù), 用戶(hù)得到該序列號后通知軟件作者(作者一定得留下聯(lián)系方法),作者根據密碼鎖利用解密機(見(jiàn)程序)得到密匙,通過(guò)網(wǎng)絡(luò )或電話(huà)送給用戶(hù),用戶(hù)根據密匙注冊,注冊后應用程序把密匙寫(xiě)進(jìn)Windows注冊表,以備TestKey(x)函數檢測密匙用。
---- 以后應用程序每次運行都要調用TestKey(x)函數(其中x=密匙),用于檢測密匙是否正確,正確返回非零值(合法用戶(hù)),程序繼續運行,否則(非法用戶(hù))退出程序。
---- 下圖用VC5.0編制的一個(gè)基于對話(huà)框的簡(jiǎn)單應用程序的界面(),當輸入正確的密匙,將顯示”合法用戶(hù)”,否則顯示”非法用戶(hù)”,并退出程序.因篇幅所限,源程序不再這里給出,請見(jiàn)諒.
|