来自:http://www.vckbase.com/index.php/wv/1661
开发软件的实践中,通常将软件分为几种版木:①release;②demo;③anti_copy:④register;⑤dog版本。release为正常的无限制发行版本,后几种受版权保护版本,其中demo版本为限制时间或者功能的版本;anti_copy是防复制版本,只有安装的版本才能使用,实现时安装程序将某个动态库放在指定的路径下,程序运行时验证该文件是否存在;register版本是用户通过向软件开发者提供软件注册申请码,由软件提供者生成注册码,返回给用户,即通过软件中提供的注册码验证机制实现软件系统功能的开放;dog版本通过硬件加密狗实现软件的版权保护。
1 限制软件部分功能
做法一是在软件的功能菜单中将该菜单所对应的功能实现函数全部置空,这样在执行该功能时会弹出提示对话框或者什么动也没有。做法二是为了方便起见,直接将菜单的属性设置为变灰或者非激活状态,这样在运行系统时,该菜单项就会变灰或者没有激活。这种方法虽然简便,但是如果用Visual C++直接以资源方式打开可执行文件的话,就可以将将变灰或者非辙活状态去掉,从而去掉软件部分功能的限制。
2 限制软件试用时间
这里介绍记录时间的时问限制方法。
2.1设置三个时间变量
起始时间:int nYear0,nMonth0,nDate0;
终止时间:int nYear,nMonth,nDate;
当前时间:int nYearCur,nMonthCur,nDateCur;
2.2初次运行时给上述变量的赋值
对于当前时间:
1.
SYSTEMTIME stCur;
2.
GetSystemTnne(&stCur);
3.
nYearCUFstCur.wYear.nMonthCur=stCur.wMonth;
4.
nDateCur=stCur.wDay;
起始时间和终止时间的赋值是在程序第一次运行时将当前时间和预设的期限写入某个文件当中,以后将这两个时间同当前的系统时间进行比较,判断软件是否过期或者试用时间是否到期:
01.
String sFn=
"c:\\muxuanshe.sys"
;
02.
FII.E *fp=
fopen
(sFn,
"rb"
);
03.
{
04.
nDateO=nDateCur;
05.
nYear0=n YearCur;
06.
nMonthO=nMonthCur;
07.
nDate=nDate0;
08.
nYear=nYear0;
09.
nMonth=nMonth0+1;
10.
if
(nMonth>12){
11.
nMonth-=12;
12.
nYcar++;
13.
}
//跨年度计算依次将起始时间、到期时间和当前时间及文件的字节数写入c:\\muxuanshe sys
14.
}
2.3比较时间
每次运行时比较当前时间与文件记录中的时间,有两种情况,一是判断是否将系统系统时间提前:
if(IsPrcTime(nYear0,nMonth0,nDate0)||IsPreTime(nYear01d,nMo nthOld,nDateOJd)))其中nYearOld,nMonthOld,nDateOld为上次运行的时间:
01.
BOOL
JsOverTime(
int
nYear,
int
nMonth,
int
nDate)
02.
{
03.
SYSTEMTIME st;
04.
GetSysten/rime(&st);
05.
if
(st.wYear>nYear)
06.
return
TRUE;
07.
else
if
(st.wYearnMonth)
08.
return
TRUE;
09.
else
if
(st.wMonth==nMonth&&st.wDay>nDate)
10.
return
TRUE;
11.
return
FALSF;
12.
}
如果您前移了机器时间则将终止时间nYear=l并写入c:\\muxuanshe.sys,软件以后终止运行!
另一种情况是对于正常的剩余时问计算:IsOverTime(nYear,nMonth,nDate),不再赘述,具体实现请参见示例代码。
这里介绍的方法主要是要注意存放时间的文件要保密,如果知道了时间存放位置,那么时间限制就很容易被解除。也可以用其他更为稳妥的方法将时间存放,如存放在注册表中。
3根据机器码实现软件注册
机器码指与计算机硬件(CPU、网号、硬盘)有关的串号,如硬盘序列号、MAC地址等,编写软件的人常用机器码作为产生软件序列号的依据,目的是区分用户,确保自己的软件使用受控,下面通过读取用户计算机硬盘卷序列号,经一定的加密算法进行换算后,返回给用户一个产品注册码,由于硬盘卷序列号是唯一的,提供的产品注册码也是唯一的,用户利用该注册码通过验证后获得软件全部功能。
在笔者的编程实践中发现有时候由于所使用的函数不正确,常常不能正确获取硬盘的序列号,这里就这一问题进行讨论。
3.1不正确的序列号
利用GetVolumelnformation获取的计算机硬盘卷序列号,不是正确的唯一序列号:
01.
void
CWJGISApp::Register()
02.
{
03.
DWORD
VolumeSerialNumber;
04.
GetVolumeInformation(
"c:\\"
,NULL,NULL,&VolumeSeriaINumber,NULL,NULL,NULL,NULL);
05.
char
charVolumeSerialNumber[l0];
06.
itoa(VolumeSerlalNumber,charVoIumeSerialNumber,10);
07.
CString strTemp;
08.
strTemp.Format(
"%s"
,charVolumeSerialNumber);
09.
}
该函数获取的序列号在硬盘格式化后会发生变化。
3.2正确获取硬盘的序列号
正确获取硬盘序列号代码如下:
01.
//读般硬盘序列号函数
02.
char
* CGetHDSerial::GetHDSerial()
03.
{
04.
m_buffer[0]=
'\n'
;
05.
//得到当前操作系统版本
06.
OSVERSIONINFO OSVersionInfo;
07.
OSVersionInfo.dwOSVersionInfoSize =
sizeof
(OSVERSIONINFO);
08.
GetVersionEx(&OSVersionInfo);
09.
if
(OSVersionInfo.dwPlatformld != VER_PLATFORM_WIN32_NT)
10.
{
11.
//Windows 9x/ME下读取硬盘序列号
12.
WORD
m_wWin9xl'HDSerial[256];
13.
Win9xReadHDSerial(m_wWin9xHDSerial);
14.
strcpy
(m_buffer, WORDToChar(m _wWin9xHDSerial, 10, 19));
15.
}
16.
else
17.
{
18.
//Windows NT/2000/XP下读取硬盘序列弓
19.
DWORD
m_wWinNTHDSerial[256];
20.
//判断是否有SCSI硬盘
21.
if
(!WinNTReadIDEHDSerial(m_wWinNTHDSerial))
22.
WinNTReadSCSIHDSerial(m_wWinNTHDSerial);
23.
strcpy
(m_buffer,DWORDToChar(m_wWinNTHDSerial,10, 19));
24.
}
25.
return
m_buffer;
26.
}
27.
//Windows NT/2000/XP下读取IDE硬盘序列号
28.
BOOL
CGetHDScriaI::WinNTReadIDEHDSerial(
DWORD
* buffer)
29.
{
30.
BYTE
JdOutCmd [
sizeof
(SENDCMDOUTPARAMS)+IDFNTIFY_BUFFER_SIZE - 1];
31.
BOOL
bFlag = FALSE;
32.
int
drive = 0;
33.
Char driveName[256];
34.
HANDLE
hPhysicalDrivelOCTL=0;
35.
36.
Sprintf(driveName,
"\\\.\\PhysicalDrive%d"
,drive);
37.
//Windows NT/2000/XP 下创建文件需要管理员权限
38.
hPhysicalDriveIOCTL=CreateFile(driveName,GENERIC_READ|GENERIC_WRITE,
39.
FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL);
40.
41.
if
(hPhysicalDriveIOCTL!=INVALID_HANDLE_VALUE)
42.
{
43.
GETVERSIONOUTPARAMS VersionParams;
44.
DWORD
cbBytesReturned=0;
45.
//得到驱动器的I/O控制器版本
46.
memser((
void
*)&VersionParams,0,
sizeof
(VersionParams));
47.
if
(DeviceIoControl(hPhysicalDriveIOCTL,IOCTL_GET_VERSION,NULL,
48.
0,&VersionPaiams,
sizeof
(VersionParams),
sizeof
(VersionParams),
49.
&cbBytesReturned,NULL))
50.
{
51.
if
(VersionParams.bIDEDeviceMap>0)
52.
{
53.
BYTE
bIDCmd=0;
//IDE或者ATAPI识别命令
54.
SENDCMDINPARAMS scip;
55.
//如果驱动器是光驱,采用命令IDE_ATAPI_IDENTIFY,command,
56.
否则采用命令IDE_ATA_IDENTIFY读取驱动器信息
57.
bIDCmd=(VersionParams.bIDEDeviceMap>>drive&0x10)?
58.
IDE_ATAPI_IDENTIFY : IDE_ATA_IDENTIFY;
59.
60.
memset
(&scip,0,
sizeof
(scip));
61.
memset
(IdOutCmd,0,
sizeof
(IdOutCmd));
62.
//获取驱动器信息
63.
if
(WinNTGetIDEHDInfo(hPhysicalDrivelOCTL,&scip,(PSENDCMDOUTPARMS)&
64.
IdOutCmd,(
BYTE
)bIDCmd,(
BYTE
)drive,&cbBytesReturned))
65.
{
66.
int
m=0;
67.
USHORT
*pldSector=(
USHORT
*)((PSENDCMDOUTPARAMS)IdOutCmd)->bBuffer;
68.
for
(m=0;m<256;m++)
69.
buffer[m]=pIdSector[m];
70.
bFlag=TRUE;
//读取硬盘信息成功
71.
}
72.
}
73.
}
74.
CloseHandle(hPhysicalDriyeIOCTL);
//关闭句柄
75.
}
76.
Return bFlag;
77.
}
4 软件版权保护方法调用
在程序初始化时,根据实际需要调用不同软件保护方法,具体时间如下所示:
01.
BOOL
g_nSoftType=0;
//0,relese;1,demo;2 anti_copy;3 dog;
02.
BOOL
CTestApp::InitInstance()
03.
{
04.
if
(g_nSoftType==1){
05.
if
(DemoIsOverTime())
06.
return
FALSE;
07.
}
08.
else
if
(g_nSoftType==2){
09.
if
(!IsInstalledSoft())
10.
return
FALSE;
11.
}
12.
else
if
(g_nSoftType==3){
13.
if
(!Dog()){
14.
AfxMessageBox(“您没有安装软件狗,请购买正版软件。\n\n抱歉……”);
15.
return
FLASE;
16.
}
17.
}
18.
if
(!g_bDemo){
19.
CCommandLineInfo cmdInfo;
20.
ParseCommandLine(cmdInfo);
21.
}
22.
}
以上简单介绍了在Visual C++中实现软件版权保护的方法,显然,保护有效性从高到低依次为硬件dog方法版本、注册码注册方法、功能限制方法、时间限制方法,用户可以根据工作实际需要进行选择。