Internet網絡的迅速發展,爲軟件高效傳播開闢更加廣闊的天地。如國內著名的金蜘蛛軟件下載中心,就是一個典型的發佈軟件集散地。發佈共享軟件主要包括兩種形式:日期限制形式和電子註冊形式。日期限制形式容許下載軟件的用戶使用軟件一段時間,如一個月等,若是用戶承認該軟件,可購買該軟件的註冊序列號繼續使用;電子註冊形式就是根據用戶所用機器的硬件信息產生註冊碼,並在軟件中對某些先進或經常使用功能進行限制,若是用戶要使用其所有功能,必須將軟件採集的有關硬件信息反饋給開發者,並交必定的註冊費可得到該軟件在本身機器中的註冊碼,才能正常使用。
前一種形式很容易給盜版者形成可乘之機,若是製做盜版者購買了一個註冊序列號並公佈於天下,則全部用戶使用這個註冊號均可進行正常使用;後者對用戶來講註冊手段稍顯複雜些,對開發者來講也須要必定的編程真功夫,但其具備"八點鎖緊"功能,防盜性倒是無可置疑。本文根據本身的實踐,將後者的實現過程介紹給想要製做發佈共享軟件的讀者。 html
1、註冊源 算法
在WIN98/95的保護模式下,要根據硬件信息造成註冊碼可不是一件容易的事,在實模式下可經過硬盤端口1F6H和1F7H直接讀取硬盤的序列號等信息做爲註冊的數據源,但這一方法在保護模式下卻被亮出了紅牌。利用BIOS中的主板序列號、BIOS版本序列號或主機出廠日期和標誌等,徹底能夠做爲註冊碼的註冊源。如ROMBIOS中F000H-FFFFH區域中就存在與硬件配置有關的信息,還能夠採集其它一處或幾處主板等的信息做爲註冊碼的生產基地。例如可根據F000H:FFF5H-F000H:FFFFH 中存放的主機出廠日期和主機標誌值,產生應用程序的註冊碼。因爲計算機產品的更新換代比較快,並且全部用戶使用的計算機不可能配置都徹底相同,因此註冊碼產生的源也不會徹底相同。並且這些硬件信息內容在任何操做系統下均徹底相同,兼容性很是好,更不會由於操做系統的更新而形成註冊功能失效。 編程
註冊源肯定以後,關鍵的問題就是共享軟件安裝程序如何採集註冊源信息,並讓用戶將其返回給開發者。最簡單的方法就是將採集到的註冊源信息通過位操做加密後存放到一個文本中,造成註冊碼的數據源資料。這個註冊源數據串可稍長一些,但不宜過長,使用戶可以經過電子郵箱、電話或信件順利轉給開發者爲宜。如筆者安裝程序是用C語言編制的,若是將上述內存地址做爲註冊源,數據串文本文件名爲KEYID.DOC,長度爲20個字符。其示例代碼以下: 安全
代碼:
網絡
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); |
2、註冊機 函數
開發者獲得用戶提供的註冊源數據以後,就須要利用註冊機生成註冊碼並返回給用戶。註冊機利用既定的位操做和不可逆算法,造成用戶比較容易操做的字符串註冊碼,註冊碼的長度通常爲8-16位爲宜,用戶只需註冊一次就能夠長期使用,因此註冊碼的長度不會影響用戶的註冊操做。固然註冊機的算法應與共享軟件中的算法部分基本相同。對於遠程用戶,註冊機應該具備從鍵盤和內存兩種取得註冊源數據的功能,因此註冊機的加密算法實際爲兩個分支:第一個分支是從鍵盤獲取註冊源數據後直接根據註冊算法造成註冊碼的過程,是直接給遠程用戶反饋註冊碼的過程;第二個分支是直接從ROM BIOS中根據註冊源算法取得註冊源數據,再根據註冊算法造成註冊碼的過程,是直接讀取本地機註冊碼的。 post
用戶獲得註冊碼後,根據共享發佈軟件的註冊方法進行一次註冊,應用程序會自動將這個註冊碼存放到軟件的特定位置處,當應用程序被他人拷貝到其它機器中去後,因爲註冊碼因不一樣機器而異,因此應用程序的功能或使用次數仍然受限,要在其它機器中使用該應用程序,還必須進行從新註冊,達到共享軟件發佈目的。同時因爲註冊源數據的算法和註冊碼算法都可因人而異,所以這種方法很是可靠。本人實現的註冊機帶參數時接受鍵盤輸入註冊源;不帶任何參數時從本地機器內直接採集註冊源數據。個人註冊機示例程序以下: 學習
代碼: 網站
#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);//接受鍵盤輸入遠程註冊源 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; //進行移位異或處理 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;//進行移位異或處理 sprintf(Buff+4*sumj,"%04x",sum); sprintf(Buff1+4*sumj,"%04x",sum1); } printf("\nWIN-KEY:"); printf(Buff); printf("\nDOS-KEY:"); printf(Buff1); } } |
3、註冊碼 加密
當用戶註冊成功後,註冊碼就被寫到共享軟件的相應位置。這時共享軟件必須對用戶註冊碼進行實時檢測與判斷,才能實現註冊限制功能。這時要求共享軟件必須內部取得註冊源數據,並利用註冊機中相同的算法產生內部註冊碼。這就要求共享軟件直接讀取ROM BIOS的註冊源信息,並在共享軟件中須要限制的功能處增長註冊碼檢測判斷功能,這須要根據共享軟件的實際須要、軟件大小和實現的難易程度來肯定限制的數量,使盜版者很難進行解密。這樣既使計算機中多個共享軟件使用相同的註冊源,也不會發生註冊衝突問題;既使是使用了相同的註冊源數據,因爲註冊算法的不一樣註冊碼也不會相同;即便解密者知道註冊算法的註冊源地址,因爲沒法知道註冊算法並且註冊點遍及整個共享軟件,也很難進行盜版。所以,這一註冊方法使共享軟件有效地跨越各類系統平臺。
要在共享軟件內部產生註冊碼,必須在共享軟件中讀取ROM BIOS數據源內存數據。WINDOWS保護模式下必須利用段選擇符方法和API編程接口提供的函數才能實現:
1.AllocSelector(Selector)分配一個與參數相同的空選擇器
2.FreeSelector(Selector) 釋放分配的選擇器
3.SetSelectorBase() 設置選擇器描述符物理起始地址
4.GetSelectorBase() 獲取選擇器描述符物理起始地址
5.SetSelectorLimit() 設置選擇器描述符訪問界限
6.GetSelectorLimit() 獲取選擇器描述符訪問界限
其中函數AllocSelector(Selector) 是保護模式下物理內存訪問的關鍵,Selector是分配空選擇器的段寄存器模板,能夠利用GlobalAlloc()函數分配內存,再利用GlobalHandleToSel()函數將內存句柄轉換爲相應選擇器,內存單元訪問結束後再利用GlobalFree()釋放分配的內存。最簡單的方法就是將系統的數據段寄存器__DS直接做爲模板參數,這個參數在通常應用程序中徹底能夠正常使用。而後利用SetSelectorBase()和SetSelectorLimit( )函數分別設置內存的物理起始地址和訪問界限值,利用正常的指針操做*pt=Value和Value=*pt訪問物理內存單元,訪問結束後必須使用FreeSelector()函數釋放分配的選擇器,由於WINDOWS 並不自動釋放無用的選擇器,並且系統的選擇器共享資源是很是有限,只有8192個供使用。根據以上原理及註冊機中的註冊源和註冊碼算法,就不難實現共享軟件內部註冊碼函數:
代碼:
UINT ImeCmpkey(void) { //共享軟件內部註冊碼產生函數 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);//分配一個新選擇符 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;//進行移位異或處理 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); } } |
5、註冊口
對於共享軟件,無論其實現何種功能,最好採起再線註冊方式,這樣能夠減小用戶不少重複操做。同時應該採起多個註冊入口,如本人軟件能夠在增長或刪除詞組等時進行註冊,只要一處註冊成功整個軟件就算註冊成功,並注意對註冊口輸入的註冊碼進行再加密處理。
筆者共享軟件中註冊口代碼示例代碼以下:
代碼:
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){//回車鍵處理 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){//寫入註冊碼 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)"程序打開出錯!"); 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); } |
出處:http://www.cnblogs.com/erwin/archive/2007/06/13/782770.html