📄 如何在pb中通过读取硬盘序列号实现软件加密.htm
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<!-- saved from url=(0074)http://www.sybase.com.cn/cn/content/developer/exp_kfzly_jqhz_PB_secure.htm -->
<HTML><HEAD><TITLE>如何在PB中通过读取硬盘序列号实现软件加密</TITLE>
<META content="text/html; charset=gb2312" http-equiv=Content-Type><LINK
href="如何在PB中通过读取硬盘序列号实现软件加密.files/stylesheet.css" rel=stylesheet>
<META content="MSHTML 5.00.2614.3500" name=GENERATOR></HEAD>
<BODY bgColor=#ffffff leftMargin=0 topMargin=0 marginheight="0" marginwidth="0">
<TABLE background=如何在PB中通过读取硬盘序列号实现软件加密.files/logo_bg.jpg border=0 cellPadding=0
cellSpacing=0 width="100%">
<TBODY>
<TR>
<TD width=260><IMG height=49 src="如何在PB中通过读取硬盘序列号实现软件加密.files/logo_01.jpg"
width=260></TD>
<TD> </TD>
<TD align=right width=320><IMG height=49
src="如何在PB中通过读取硬盘序列号实现软件加密.files/logo_02.jpg" width=320></TD></TR>
<TR>
<TD colSpan=3> </TD></TR></TBODY></TABLE>
<TABLE align=center border=0 cellPadding=0 cellSpacing=0 width="90%">
<TBODY>
<TR align=middle>
<TD class=p2 height=28>如何在PB中通过读取硬盘序列号实现软件加密</TD></TR>
<TR align=middle>
<TD height=28>
<HR noShade SIZE=1 width="98%">
</TD></TR>
<TR>
<TD>
<P> 大家知道,每当我们格式化软盘或硬盘时系统都会给它分配一个序列号,即用DOS命令dir显示出的"Volume Serial Number
is
0A41-0E0A"。该序列号是随机产生的,且具有唯一性。也就是因为这个原因许多软件的测试版本利用该项技术使测试版一旦过了限定期限就不能再使用,即使将该软件重新安装也无济于事。另外,有些共享软件的注册码也是通过这个序列号来生成的。同样的方法我们也可以运用到软件的防拷贝技术。那么如何才能实现上述功能呢?
</P>
<P
align=left> 为了达到目的我们必须首先能够得到硬盘的序列号,其次,为了安全起见我们最好再选择一种加密算法,将加密后的硬盘的序列号作为密文公开存放,软件通过解密得到明文,即硬盘的序列号,通过将解密后的硬盘序列号和实际的硬盘序列号相比较得出程序是否合法。这一步当然是由应用程序秘密运行,用户根本不知道,从而达到软件的二次加密目的,同时也隐藏了软件的合法性识别过程,使破译者无从下手。下面就来谈谈如何具体实现。
</P>
<P align=left><B>一、如何读取硬盘序列号 </B></P>
<P
align=left> 要读取硬盘序列号我们可以用汇编来实现,但毕竟不容易,况且也不能有效的结合到PB脚本中。在PB中我们可以通过调用Windows提供的外部函数GetVolumeInformationA()来实现。这相对来说比较简单。
</P>
<P align=left> 该函数的原型为: </P>
<P align=left> BOOL GetVolumeInformation( </P>
<P align=left> LPCTSTR lpRootPathName, </P>
<P align=left> LPTSTR lpVolumeNameBuffer, </P>
<P align=left> DWORD nVolumeNameSize, </P>
<P align=left> LPDWORD lpVolumeSerialNumber, </P>
<P align=left> LPDWORD lpMaximumComponentLength, </P>
<P align=left> LPDWORD lpFileSystemFlags, </P>
<P align=left> LPTSTR lpFileSystemNameBuffer, </P>
<P align=left> DWORD nFileSystemNameSize </P>
<P align=left> 上述原型中,参数类型只要是以"LP-"开头的表明该参数用的是长指针(Long
Pointer)类型,即在PB中调用时的参数传递是通过引用传递。在8个参数中对我们真正有用的只有两个LPCTSTR
lpRootPathName和LPDWORD
lpVolumeSerialNumber。其中参数lpRootPathName是指向文件系统根目录的地址,我们需要用它来指明所要获取序列号的硬盘盘符;参数lpVolumeSerialNumber是返回的硬盘序列号的地址,这正是我们需要的。
</P>
<P align=left> 众所周知,PB在调用任何外部函数前都要首先进行函数声明,可以将声明放在全局或局部函数声明中。具体声明如下: </P>
<P align=left> Function Boolean GetVolumeInformationA( & </P>
<P align=left> ref String ls_Rootpath, & </P>
<P align=left> ref String ls_volumnename, & </P>
<P align=left> Ulong lul_VolumeNameSize, ref Ulong
lul_VolumeSerialNumber, & </P>
<P align=left> ref Ulong lul_MaximumComponentLength, & </P>
<P align=left> ref Ulong lul_FileSystemFlags, & </P>
<P align=left> ref String ls_FileSystemNameBuffer, & </P>
<P align=left> Ulong lul_FileSystemNameSize & </P>
<P align=left> ) Library "Kernel32.dll" </P>
<P align=left> </P>
<P
align=left> 上述声明中,"ref"指明是该参数是通过引用传递的,有关函数引用的详细内容请参见有关教程。声明完毕我们不能马上进行调用,还必需确保已为它分配足够的内存空间,即使是参数引用传递也是这样,否则的话将会出现调用错误,这跟C语言的引用调用不同,这一点往往被忽视,希望读者能够注意。也就是为什么我在调用该函数前将有些字符串参数给它预先分配了多达256个字符空间以及给一些整型类型的参数赋初始值256。完整的读取硬盘序列号的程序代码如下:
</P>
<P align=left>/******************* 程序代码 ************************/ </P>
<P align=left>String ls_Rootpath, ls_volumnename </P>
<P align=left>ls_Rootpath = "C:" // 指定要得到序列号的硬盘, </P>
<P align=left>// 一般情况都是C盘,除非你能保证用户存在其它逻辑盘或物理盘 </P>
<P align=left>ls_volumnename = Space(256) // 分配足够的空间,下同 </P>
<P align=left>Ulong lul_VolumeNameSize </P>
<P align=left>lul_VolumeNameSize = 256 </P>
<P align=left>Ulong lul_VolumeSerialNumber, lul_MaximumComponentLength,
lul_FileSystemFlags </P>
<P align=left>lul_MaximumComponentLength = 256 </P>
<P align=left>String ls_FileSystemNameBuffer </P>
<P align=left>ls_FileSystemNameBuffer = space(256) </P>
<P align=left>Ulong lul_FileSystemNameSize </P>
<P align=left>lul_FileSystemNameSize = 256 </P>
<P align=left>beep(1) </P>
<P align=left>boolean lb_rtn </P>
<P align=left>lb_rtn = False </P>
<P align=left>lb_rtn = GetVolumeInformationA(ls_Rootpath, ls_volumnename,
lul_VolumeNameSize, </P>
<P align=left>lul_VolumeSerialNumber, lul_MaximumComponentLength,
lul_FileSystemFlags, </P>
<P align=left>ls_FileSystemNameBuffer, lul_FileSystemNameSize) </P>
<P align=left>if lb_rtn = true then </P>
<P align=left>MessageBox("提示","函数调用成功!") </P>
<P align=left>else </P>
<P align=left>MessageBox("提示","函数调用失败!") </P>
<P align=left>end if </P>
<P align=left>sle_1.text = String(lul_VolumeSerialNumber) // 得到硬盘序列号 </P>
<P align=left>/********************* 结束 *************************/ </P>
<P align=left> 一旦读取成功我们的任务也就完成了近一半,接下来要做的是怎样选择一个合适的加密算法。 </P>
<P align=left><B>二、选取一个优秀的加密算法 </B></P>
<P align=left> 1、数据加密概述 </P>
<P
align=left> 早在几千年前人类就已经有了通信保密的思想和方法。但直到1949年,信息论创始人香农发表著名文章,论证了一般经典加密方法得到的密文几乎都是可破译的。密码学才得以进入了一个新的发展时期。70年代后期,美国的数据加密标准DES和公开密钥密码体制的出现成为近代密码学发展史上的两个重要里程碑。
</P>
<P
align=left> 公开密钥密码体制的概念是由Difie与Hellman于1976年提出。所谓公开密钥密码体制就是加密密钥与解密密钥不同,是一种由已知加密密钥推导出解密密钥在计算上是不可行的密码体制。其中,基于数论中大数分解问题的RSA体制曾被ISO/TC97的数据加密技术委员会SC20推荐为公开密钥数据加密标准。
</P>
<P align=left> 2、RSA体制的基本原理 </P>
<P
align=left> 该体制是根据寻求两个大素数比较简单,而将它们的乘积分解开则极其困难这一原理来设计的。在已提出的公开密钥算法中它是最容易理解和实现的。RSA在世界上许多地方已成事实上的标准。ISO几乎(但没有明确)已指定RSA用作数字签名标准。该算法已经经受住了多年深入的密码分析,虽然密码分析者既不能证明也不能否定RSA的安全性,但这恰恰说明了该算法有一定的可信度。它的安全性是与大数分解密切相关的。我想通过下表你将会对它的安全性有一个较好的认识,它给出了在计算机每一微妙做一次操作的假定下分解不同大小的N所需要的时间。
</P>
<P align=left> N的十进位数 50 75 100 200 </P>
<P align=left> 时间 3.9小时 104天 74年 3.8X1015年 </P>
<P align=left> RSA加密算法具体如下: </P>
<P align=left> (1)选取两个大素数,p和q。为了获得最大程序的安全性,两个素数的长度一样。并计算乘积N(N=pq)。 </P>
<P align=left>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -