为了达到目的我们必须首先能够得到硬盘的序列号,其次,为了安全起见我们最好再选择一种加密算法,将加密后的硬盘的序列号作为密文公开存放,软件通过解密得到明文,即硬盘的序列号,通过将解密后的硬盘序列号和实际的硬盘序列号相比较得出程序是否合法。这一步当然是由应用程序秘密运行,用户根本不知道,从而达到软件的二次加密目的,同时也隐藏了软件的合法性识别过程,使破译者无从下手。下面就来谈谈如何具体实现。
一、如何读取硬盘序列号
要读取硬盘序列号我们可以用汇编来实现,但毕竟不容易,况且也不能有效的结合到PB脚本中。在PB中我们可以通过调用Windows提供的外部函数GetVolumeInformationA()来实现。这相对来说比较简单。
该函数的原型为:
BOOL GetVolumeInformation(
LPCTSTR lpRootPathName,
LPTSTR lpVolumeNameBuffer,
DWORD nVolumeNameSize,
LPDWORD lpVolumeSerialNumber,
LPDWORD lpMaximumComponentLength,
LPDWORD lpFileSystemFlags,
LPTSTR lpFileSystemNameBuffer,
DWORD nFileSystemNameSize
上述原型中,参数类型只要是以"LP-"开头的表明该参数用的是长指针(Long Pointer)类型,即在PB中调用时的参数传递是通过引用传递。在8个参数中对我们真正有用的只有两个LPCTSTR lpRootPathName和LPDWORD lpVolumeSerialNumber。其中参数lpRootPathName是指向文件系统根目录的地址,我们需要用它来指明所要获取序列号的硬盘盘符;参数lpVolumeSerialNumber是返回的硬盘序列号的地址,这正是我们需要的。
众所周知,PB在调用任何外部函数前都要首先进行函数声明,可以将声明放在全局或局部函数声明中。具体声明如下:
Function Boolean GetVolumeInformationA( &
ref String ls_Rootpath, &
ref String ls_volumnename, &
Ulong lul_VolumeNameSize, ref Ulong lul_VolumeSerialNumber, &
ref Ulong lul_MaximumComponentLength, &
ref Ulong lul_FileSystemFlags, &
ref String ls_FileSystemNameBuffer, &
Ulong lul_FileSystemNameSize &
) Library "Kernel32.dll"
上述声明中,"ref"指明是该参数是通过引用传递的,有关函数引用的详细内容请参见有关教程。声明完毕我们不能马上进行调用,还必需确保已为它分配足够的内存空间,即使是参数引用传递也是这样,否则的话将会出现调用错误,这跟C语言的引用调用不同,这一点往往被忽视,希望读者能够注意。也就是为什么我在调用该函数前将有些字符串参数给它预先分配了多达256个字符空间以及给一些整型类型的参数赋初始值256。完整的读取硬盘序列号的程序代码如下:
/******************* 程序代码 ************************/
String ls_Rootpath, ls_volumnename
ls_Rootpath = "C:" // 指定要得到序列号的硬盘,
// 一般情况都是C盘,除非你能保证用户存在其它逻辑盘或物理盘
ls_volumnename = Space(256) // 分配足够的空间,下同
Ulong lul_VolumeNameSize
lul_VolumeNameSize = 256
Ulong lul_VolumeSerialNumber, lul_MaximumComponentLength, lul_FileSystemFlags
lul_MaximumComponentLength = 256
String ls_FileSystemNameBuffer
ls_FileSystemNameBuffer = space(256)
Ulong lul_FileSystemNameSize
lul_FileSystemNameSize = 256
beep(1)
boolean lb_rtn
lb_rtn = False
lb_rtn = GetVolumeInformationA(ls_Rootpath, ls_volumnename, lul_VolumeNameSize,
lul_VolumeSerialNumber, lul_MaximumComponentLength, lul_FileSystemFlags,
ls_FileSystemNameBuffer, lul_FileSystemNameSize)
if lb_rtn = true then
MessageBox("提示","函数调用成功!")
else
MessageBox("提示","函数调用失败!")
end if
sle_1.text = String(lul_VolumeSerialNumber) // 得到硬盘序列号
/********************* 结束 *************************/
一旦读取成功我们的任务也就完成了近一半,接下来要做的是怎样选择一个合适的加密算法。
二、选取一个优秀的加密算法
1、数据加密概述
早在几千年前人类就已经有了通信保密的思想和方法。但直到1949年,信息论创始人香农发表著名文章,论证了一般经典加密方法得到的密文几乎都是可破译的。密码学才得以进入了一个新的发展时期。70年代后期,美国的数据加密标准DES和公开密钥密码体制的出现成为近代密码学发展史上的两个重要里程碑。
公开密钥密码体制的概念是由Difie与Hellman于1976年提出。所谓公开密钥密码体制就是加密密钥与解密密钥不同,是一种由已知加密密钥推导出解密密钥在计算上是不可行的密码体制。其中,基于数论中大数分解问题的RSA体制曾被ISO/TC97的数据加密技术委员会SC20推荐为公开密钥数据加密标准。
2、RSA体制的基本原理
该体制是根据寻求两个大素数比较简单,而将它们的乘积分解开则极其困难这一原理来设计的。在已提出的公开密钥算法中它是最容易理解和实现的。RSA在世界上许多地方已成事实上的标准。ISO几乎(但没有明确)已指定RSA用作数字签名标准。该算法已经经受住了多年深入的密码分析,虽然密码分析者既不能证明也不能否定RSA的安全性,但这恰恰说明了该算法有一定的可信度。它的安全性是与大数分解密切相关的。我想通过下表你将会对它的安全性有一个较好的认识,它给出了在计算机每一微妙做一次 *** 作的假定下分解不同大小的N所需要的时间。
N的十进位数 5075100 200
时间 3.9小时 104天 74年 3.8X1015年
RSA加密算法具体如下:
(1)选取两个大素数,p和q。为了获得最大程序的安全性,两个素数的长度一样。并计算乘积N(N=pq)。
(2) 随后计算出N的欧拉函数ф(N)=(p-1)(q-1),ф(N)定义为不超过N并与N互素的数的个数。
(3)从[0,ф(N)- 1]中随机选取加密密钥e,使得e和ф(N)互为素数。
(4)计算出满足公式ed=1 modф(N)的d,d为解密密钥。
(5)若用整数X表示明文,整数Y表示密文(X,Y均小于N),则加解密运算为:
加密:Y = Xe mod N
解密:X = Yd mod N
注意,其中的d和N也互素。e和N是公开密钥,d是秘密密钥。两个素数p和q应舍弃,但千万不要泄密哦。
3、相关数学背景知识
(1)素数:素数是一个比1大,其因子只有1和它本身,没有其它数可以整除它的数。素数是无限的。例如,2,3,5,7……等。
(2)两个数互为素数:指的是它们除了1之外没有共同的因子。也可以说这两个数的最大公因子是1。例如,4和9,13和27等。
(3)模变换:两个数相模,如A模N运算,它给出了A的余数,余数是从0到N-1的某个整数,这种运算称为模运算。
4、算法的具体实现
从RSA的基本原理我们得知,对明文进行加密选择一个合适的e很重要,如果你选择合适的话,RSA的加密速度将快得多,并且也不会因为用户机器的限制而要做更多的变换(指在计算中为了避免数据的溢出所进行的转换,毕竟我们用的是PC机再说也用不着很高的安全性)。最常用的三个e值是3,17,65537。在这里我们取的e等于3,当然到底选取哪个e值并没有规定,这里只是为了演示方便罢了。
根据算法定义,
(1)为了方便起见我们选取素数p = 3和q = 11,则N = pq = 3 * 11 = 33。
(2)ф(N)=(p-1)(q-1)= 2 * 10 = 20。
(3)从[0,ф(N) - 1]中,即,[0, 19]之间任意选取加密密钥e = 3,且e和ф(N)互素。
(4)如何从公式ed=1 modф(N)求出解密密钥d?
由模的定理我们可以将公式ed=1 modф(N)转换成形式ed= k * ф(N)+ 1,即3d = k * 20 + 1,将0,1,2,3…依次代入k,求出d。取k = 1,得d = 7。
读者可以通过编程实现随机选取p和q来求出相应的N,e,d。
(5)进行加解密。
对明文进行加密
根据定义,我们首先要根据N的值对明文进行分组,每个分组的值应小于N。如果要加密固定的消息分组,那么可以在它的左边填充一些0(零)并确保该值比N小。例如,我们要对数据X=172035594进行加密(在我的计算机上C盘的序列号是0A41-0E0A,转换成十进制就是172035594),我们首先要将它分成小于N(N=33)的若干小组。可以分成,X1=17,X2=20,X3=3,X4=5,X5=5,X6=9,X7=4。对第一分组X1运用加密公式得到加密密文Y1=X1e mod N = 173 mod 33 = 29,依次将其余分组进行加密得到,Y2=14,Y3=27,Y4=26,Y5=26,Y6=3,Y7=31。即密文Y= 2914272626331。我们可以将密文存储在文件或注册表中,每当应用程序启动时先读取密文,并将其解密,再将解密后的结果与硬盘序列号进行比较,以此来判断软件是否合法。在实际运用中我们可以随时通过程序修改密文,比如,将密文去掉一位或将密文颠倒等,就可以实现诸如测试版软件的使用限制问题,
对密文进行解密
对密文进行解密同样要首先对密文进行分组,使每个分组都小于N。将密文Y=2914272626331分组成:
Y1=29,Y2=14,Y3=27,Y4=26,Y5=26,Y6=3,Y7=31
这时我们一定要注意,不要急于将将各分组代入解密公式X=Yd mod N,如果这样做了我们所得到的明文将是X=1202811913,并不是加密时的明文!是不是加密算法有错?绝对不是。回顾加解密的公式,我们不难发现它们做的都是先将一个数进行n次方运算然后在做模运算。问题就出在"n次方运算"上,千万不要忽略PowerBuilder中数值的取值范围,在其它的编程语言中也是如此。在本例中我给明文和密文用的都是unsigned long类型,它的32位所允许最大值是4294967295,的确很大,但我们不能保证一个数在进行了7次方后不超过该最大值。其实,这种情况在对明文加密时也是会发生的,只是33的3次方是35937,远小于最大值,我们将其忽略罢了。
好在问题并不像我们想象的那么复杂。由模的运算规律得知,模运算像普通的运算一样,它是可交换的、可结合的、可分配的。而且,简化运算每一个中间结果的模n运算,其作用与先进行全部运算,然后再简化模n运算是一样的。比如,
(A * B) mod N = ((A mod N) * (B mod N)) mod N。
因此,
X = Y7 mod N
= (Y3 * Y4)mod N
= ((Y3 mod N)*(Y4 mod N))mod N
当然,我们也可以将Y7分解成更多项的乘积。将分组后的密文Y1至Y7依次代入上式得出密文为X = 17 20 3 5 5 9 4。即为正确明文,解密成功。
在实际的运用中考虑到PB没有现成的乘方运算函数,为了便于读者理解原程序是如何实现RSA加密算法的本文所采用的方法是通过FOR…NEXT语句循环来实现乘方运算,读者可以将其做成一个函数,在使用的时候调用。RSA加解密算法的完整程序代码如下:
/**************** 加密程序代码 ***************/
// 以下参数由RSA加密算法得来
integer li_e, li_d, li_n 、
li_e = 3 // 设置指数e,加密密钥
li_d = 7 // 设置指数d,解密密钥
li_n = 33 // 设置N:两个素数得乘积
string ls_str
ls_str = Trim(sle_1.text) // 将明文转换成字符串,以便随后进行分组
ulong lul_temp
lul_temp = 0
ulong lul_x, lul_y // lul_x: 加密明文lul_y: 加密密文
int I
do until ls_str = ""
lul_temp = Integer(left(ls_str, 2))
if lul_temp >= li_n then // 将明文分组,且每组均小于N(N=33)
lul_temp = Integer(left(ls_str, 1))
ls_str = right(ls_str, len(ls_str)-1)
else
ls_str = right(ls_str, len(ls_str)-2)
end if
lul_y = 1
for I = 1 to li_e // 进行乘方运算
lul_y = lul_y * lul_temp
next
lul_y = mod( lul_y, 33) // 根据加密公式计算密文
sle_2.text = trim(sle_2.text) + string(lul_y) // sle_2.tex中存放的是加密后的密文
loop
/******************** 结束 *****************/
/**************** 解密程序代码 ***************/
ls_str = Trim(sle_2.text) // 与加密同理,将密文转换成字符串,以便随后进行分组
ulong lul_x0, lul_x1
do until ls_str = ""
lul_temp = Integer(left(ls_str, 2))
if lul_temp >= li_n then // 将密文分组,且每组均小于N(N=33)
lul_temp = Integer(left(ls_str, 1))
ls_str = right(ls_str, len(ls_str)-1)
else
ls_str = right(ls_str, len(ls_str)-2)
end if
// 由于考虑到乘方运算得结果可能会超出数值所允许得最大取值,
// 因此对解密公式进行适当转换,lul_x = lul_x0 * lul_x1
lul_x0 = 1
lul_x1 = 1
// 假如解密密钥是7,则先进行数的4次方运算取模,在进行数的3次方运算取模
for I = 1 to 4
lul_x0 = lul_x0 * lul_temp
next
lul_x0 = mod( lul_x0, 33)
for I = 1 to li_d - 4
lul_x1 = lul_x1 * lul_temp
next
lul_x1 = mod( lul_x1, 33)
lul_x = mod(lul_x0 * lul_x1, 33) // 根据解密公式计算明文
sle_3.text = trim(sle_3.text) + string(lul_x) // sle_3.tex中存放的是解密后的明文
loop
/******************** 结束 *****************/
总结,文中所提供的程序代码已经在PowerBuilder 7.0,Windows 98环境下运行通过。本文所讨论的通过读取硬盘序列号方法来实现程序加密技术同样可以运用到其它程序语言中,只是具体的实现方法略有差异罢了。由于本人水平有限,错误和不足之处再所难免,因此还敬请同行们给予指教。
方法如下。1、系统封装前的软件部署安装
关于软件的安装部署,可以是预装,也可以是预封装,预装就是直接将软件安装好,然后封装,预封装是将软件安装包部署好,在系统安装部署过程中或过程后进行安装。
请大家根据自己的需要选择部署安装,常用的软件:输入法、QQ、浏览器、QQ电脑管家、视频软件等。
预装软件时都默认安装在系统盘(C盘)即可,这个过程很简单,这里就不多做介绍了。
安装完软件后,用QQ管家进行一下简单的垃圾清理和启动项、系统的优化就可以了。
预安装会在下面的教程中提及,请看下文。
接下来将VMwaretools工具卸载掉,重启下虚拟机,切记,卸载前请确保封装必须的工具已拷贝至虚拟机D盘。
2、使用EasySysprep_4.5进行系统封装的第一阶段
打开EasySysprep_4.5,
点击“开始”按钮,
这个界面可以改下“用户”和“组织”,然后点击“下一步”,
保持默认的“使用当前用户登录”,然后“下一步”,
在“封装完成后”处选择“关闭计算机”,然后点击“完成”。
点击“确定”,
等待EasySysprep_4.5配置完成即可。
3、使用EasySysprep_4.5进行系统封装的第二阶段
封装的第二个阶段需要在PE环境下完成,所以我们需要将虚拟机启动为PE,系统天地官网小编使用的是老毛桃的PEISO镜像,我们通过光驱进行加载即可。
在虚拟机界面,点击“CD/DVD”,如下图所示,
然后在d出的窗口右侧勾选“启动时连接”,然后在“使用ISO镜像文件处”通过“浏览”选择老毛桃PE,LMT.ISO。
设置完成后,点击“确定”。
接下来我们要设置虚拟机的光驱为第一启动项,点击下图中的大三角后的小三角,
在下拉菜单中选择“打开电源时进入固件”,如下图,
切换至“boot”选项卡,然后通过小键盘上的“+”“-”将“CD-ROMDrive”调整至第一位,然后按F10保存并重新启动。
再次启动后,就进入了老毛桃PE系统,
我们通过键盘上的“↑”“↓”选择“运行老毛桃Win8PEx64增强版”,然后回车,即可进入PE系统。
进入PE系统后,我们再次打开EasySysprep_4.5,
然后点击“开始”,
这里的信息可以自己YY,那个OEM图片直接勾选掉就可以了,没什么用处,直接“下一步”,
这一步也没什么好说的,就是优化的最后一项需要注意,如果不需要设置主页,就将其勾选掉,然后“下一步”,
这个界面保持默认即可,继续“下一步”,
继续“下一步”,
这一步最主要是设置部署中驱动的安装,我们可以先在C盘下建立一个tools的文件夹,然后将win10万能驱动放进去,然后在上图中选择万能驱动的运行程序就可以了。
关于综合运行库,系统天地官网小编觉得没必要部署,直接勾选掉就可以了
其他的选项,大家可以根据自己的需要进行调整,如果不明白,直接按图中所示设置。
接着“下一步”
这一步用于添加部署任务,添加的任务可以是:系统部署前、系统部署中、系统部署后、第一次登陆、首次进入桌面等,
比如我们要在部署后安装软件,就可以在“运行”的“程序”处选择我们要安装的软件,然后在“添加”后面选择“系统部署后”,再点击“添加”按钮即可。
关于“删除”,比如我们的安装包和万能驱动在部署安装好系统后就不需要了,我们需要将其删除,那么我们就在“目录”处选择要删除的文件夹,比如tools,然后选择“添加”后面的“首次进入桌面”,再点击“添加”即可。
设置好以后,我们继续“下一步”,
我们在“设置完成后”处选择“退出本程序”,然后点击“完成”。
点击“确定”,配置完成。
接下来我们将封装好的系统进行ghost备份,打开PE系统里的老毛桃一键装机,如下图,
选择“备份分区”,将其备份至虚拟机的D盘,然后点击“确定”。
点击“是”,开始备份,
等待备份完成,
备份完成后,点击“是”重启计算机就可以看到我们制作的封装系统在安装部署时的效果了。
跟软件开发公司签订委托协议,协议里说明权利归属,在申请软件著作权时将软件的说明书和源代码一并提交。如后期发现软件代码有相同或者近似,可通过司法途径要求该公司出示软件目标程序。如侵权,可提出诉讼。希望帮到您,望采纳!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)