VC++中利用磁盘序列号识别正版软件
摘要:本文讲述了获取磁盘序列号的方法,并利用所读取的磁盘序列号来作为甄别正版软件的一种手段。
关键字:磁盘序列号、正版软件、识别
一、 引言
作为程序员,不希望看到自己辛辛苦苦编制的软件被盗版,虽然国家为了打击盗版和保护知识产权出台了一系列的相关法律法规,但仍有众多的地下盗版商在利益的驱使下置国法于不顾,对众多的软件进行非法复制、传播,所以身为程序员有必要加强自我保护意识、利用自身在编程方面的优势对自己编写的软件进行保护,防止或尽量减少被盗版的可能性。本文就针对这个问题提出了一种简便易行的对正版软件进行甄别的方法。
二、 设计思路与实现原理
虽然可以用纯软件的方式通过对输入的产品序列号进行判断的方式来验证该软件是否为正版,但由于序列号要由用户手工输入,位数较少的话达不到保护软件的目的,太长又不便用户输入,而且盗版商可以用Soft-ICE等调试跟踪软件查找到需要输入序列号的对话框被调用的地址,通过对该地址的修改,可以很轻松的跳过对产品序列号的验证过程,使之发挥不了作用。相比之下,利用硬件或采用软件兼有硬件的加密方式可以达到较好的保护效果,因为硬件毕竟不象软件那样容易被复制、分析、更改。而且采用这种方式一般不需要用户做什么工作,判别工作都是由软件在后台自动完成的,使用起来比较方便。在此领域做的较好的北京江民公司的KV300系列杀毒软件就是通过对正版杀毒盘人为对载体软盘的某扇区的数据进行更改,使之具有特殊的标记,由于该扇区被人为设置为"坏扇区"所以不会随数据拷贝到其他软盘,而没有该特殊扇区的磁盘是无易做图常运行程序的,这种结合硬件来保护软件的方法还是比较可靠的。
虽然利用磁盘扇区对正版软件进行标注的可靠性较高,但实现起来较烦琐。本文采用一种通过对磁盘的序列号进行判别的方式简单而又可靠地保护软件不被非法传播。在微软的操作系统中,对每一个磁盘都在其格式化时设置有一个随机的8字节长的序列号,虽然理论上有重复的可能,但实际上要找到两个相同的磁盘序列号是很困难的,根据概率论的知识可以算出遇到两个相同磁盘序列号的概率为0.00000000023283,即大约5亿多张磁盘中才会遇到相同的序列号,对我们来说这已经足够了 。而且磁盘序列号也不会随着磁盘上的软件内容的拷贝而拷贝,所以我们在此把磁盘序列号作为唯一识别码应用于我们的软件中,可以用API函数GetVolumeInformation来很方便的获取磁盘的序列号,其原型声明如下:
BOOL GetVolumeInformation( LPCTSTR lpRootPathName,
LPTSTR lpVolumeNameBuffer,
DWORD nVolumeNameSize,
LPDWORD lpVolumeSerialNumber,
LPDWORD lpMaximumComponentLength,
LPDWORD lpFileSystemFlags,
LPTSTR lpFileSystemNameBuffer,
DWORD nFileSystemNameSize );
在这里只需通过lpRootPathName来设置我们需要检测的磁盘驱动器号,执行后结果保存在lpVolumeSerialNumber中,其他参数我们不关心,均设为空(NULL)。
我们必须在程序开始执行时马上进行检测工作,验证为正版的才可以继续执行,否则就终止程序的运行使盗版无法使用,从而达到保护正版软件的目的。我们允许将同一份正版软件安装到有限的多台计算机中,将识别用的唯一标识符用软盘的磁盘序列号来表示,这张软盘就作为该套软件的钥匙盘,首先检测在硬盘中是否有认证文件,如果没有就把钥匙盘插入到软驱,在验证序列号正确的前提下把C盘的序列号保存到认证文件中,下次运行程序就会检测到认证文件,通过对认证文件内容和C盘序列号的动态比较来识别是否是正版,如果不能匹配,需要再插入钥匙盘再做一个认证文件。所以整个验证系统只需该套软件在某台计算机上首次运行程序时需要使用一次钥匙盘,或是在认证文件发生损坏后需要使用钥匙盘对其进行恢复,其余时间均由认证文件对正版进行保证。下面的流程图展示了程序对正版的唯一标识符进行验证的全部过程:
三、 程序的具体实现
在程序正式编写之前,需要预先获取到作为钥匙盘的软盘的磁盘序列号,使之作为我们判断软件是否为正版的依据。可用前面提到的API函数GetVolumeInformation()来实现:
DWORD dwSerialNum;
GetVolumeInformation("A:\",NULL,NULL,&dwSerialNum,NULL,NULL,NULL,NULL);
双字型变量dwSerialNum内保存有钥匙盘的序列号。并把该值在正式程序中用#define宏定义为一个常量,作为正版的标识。由于需要在程序正式运行前对程序的正版与否进行识别所以需要在程序的入口函数,应用程序类的InitInstance()函数中对其进行编程,并且应该在识别完毕之前用m_pMainWnd->ShowWindow(SW_HIDE);函数隐藏程序主界面,直至判定为正版之后方可改变其参数为"SW_SHOW"将主界面正常显示。下面是对认证文件进行判别的主要代码:
……
if(file.Open("Logo.ini",CFile::modeReadWrite)==FALSE)
{
AfxMessageBox("请将正版钥匙盘插入到软驱!");
MakeKey();
}
else
{
file.Read(logo,20);
file.Close();
DWORD LogoNum=atol(logo);
GetVolumeInformation("C:\",NULL,NULL,&dwIDESerial,NULL,NULL,NULL,NULL);
if(LogoNum!=dwIDESerial)
{
AfxMessageBox("请将正版钥匙盘插入到软驱!");
MakeKey();
}
else
m_bCanRun=true;
}
……
if(m_bCanRun==true)
{
m_pMainWnd->ShowWindow(SW_SHOWMAXIMIZED);
m_pMainWnd->UpdateWindow();
}
else
PostQuitMessage(0);
跟据设计的流程图,当没有发现认证文件(首次运行程序)时或是认证文件内容与C盘的序列号不匹配(认证文件出错)时就要利用钥匙盘创建/恢复认证文件,下面是实现该功能的MakeKey函数的部分关键代码: ……
GetVolumeInformation("A:\",NULL,NULL,&dwSerialNum,NULL,NULL,NULL,NULL);
if(dwSerialNum==SerialNum)//SerialNum就是我们预先获取的作为正版标识的序列号
{
GetVolumeInformation("C:\",NULL,NULL,&dwIDESerial,NULL,NULL,NULL,NULL);
ltoa(dwIDESerial,logo,10);
while(logo[i]!=’’)
i++;
……
file.Open("Logo.ini",CFile::modeCreate | CFile::modeReadWrite);
file.Write(logo,i);
file.Close();
AfxMessageBox("已通过认证,下次使用时不必再插入钥匙软盘!");
m_bCanRun=true;
}
else
{
AfxMessageBox("请插入正版钥匙软盘再执行本程序!");
m_bCanRun=false;
}
四、 小结
本文介绍的只是此类软件中的一种实现方法,在理解本文编程思想的基础下也可以采用其他类似的方法对软件进行保护,比如也可以用网卡的标识号作为认证的标识等等。而且也可以用类似的方式发布共享软件,以用户反馈回的硬件参数作为输入来产生注册码等等。总之,具体如何应用仍要根据实际的需求而灵活的作出决定。本程序在Windows 2000 Professional下,由Microsoft Visual C++ 6.0编译调试通过。