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