2006年12月13日星期三

Sentinel SuperPro加密锁编程开发指南

  本文将简要介绍Sentinel SuperPro软件加密锁的编程开发知识,可供需要进行软件加密锁开发的软件开发商快速学习掌握这种类型的加密锁的编程开发,快速保护自己的软件不受侵害,防止加密狗被破解。本文资源来源于Sentinel SuperPro加密锁开发套件中的开发手册,仅供大家学习,文字版权属于Sentinel SuperPro所有。

  一、加密锁简介

  Sentinel SuperPro加密锁是一个硬件/软件保护系统,功能主要是保护软件,即防止未经允许非法使用开发商的软件。如未把正确的加密锁与计算机连接,则被保护的应用程序将无法实现全部功能。因此,只有合法用户才能使用开发商的产品。

  使用SuperPro软件保护锁完成软件保护,就是将软件开发商的未被保护的应用程序与SuperPro软件保护锁加密算法相绑定过程。最终实现绑定后的应用程序没有软件保护锁不能单独运行,只有插入软件开发商自己定义算法的软件保护锁,应用程序才能正常运行。当软件开发商的软件销售给最终用户后,软件即使被非法复制,而没有软件开发商提供的软件保护锁软件不能被使用。这样一来,从技术上防止盗版起到软件保护的作用。

  应用程序和Sentinel Superpro软件保护锁加密算法的绑定,是通过API函数调用来校验软件保护锁是否存在来实现的。API函数验证分由三部分组成:

  1)询问部分:开发商的应用程序通过API调用向软件保护锁的驱动程序发出的“查询串”, 驱动程序自动将“查询串”传给并口或USB口上的SuperPro软件保护锁上的算法单元。

  2)运算部分:加密锁通过内部的算法芯片计算“查询串”,并将运算结果“响应串”返回给驱动程序。

  3)程序鉴定、行动部分:驱动程序将“响应串”返回给应用程序中的API函数调用。应用程序对返回值—“响应串”进行比较判断,判断与预知运算结果是否相等,根据判断的结果进行相应的行动。

  如果返回值—“响应串”与预知运算结果相等,则说明保护应用程序的软件保护锁存在,使用程序的用户为合法用户。不相等则说明保护应用程序的软件保护锁不存在,用户为非法用户,应用程序将终止被使用。

  SentinelSuperPro 系统一个主要优点是可对一个加密锁编程,以提供多种驱动程序类型,包括固定响应和可变响应。这样,所创建的软件锁具有非常多的类型。

  例如,可用单元存储固定数据(如序列号、用户名称)或存储控制功能询问的代码。然后,可简单地读取这些数据,以检验是否附加了加密锁。也可使用存储的数据控制程序流程或应用程序的函数。数据字组可以定义为只读的(锁定)或可读/写的。

  每个加密锁的前 8 个单元为系统信息保留。除了某些限制外,可以任意方式使用其它 56 个单元。

  二、加密锁编程接口

  下面以VB语言为例,说明使用SuperPro软件保护锁时,有关API调用的数据结构、函数以及必要的步骤。

  1.基本信息

  DEVELOPER ID:用户ID为加密编号,为固定数字,没有它就不能使用加密锁。

  地址:SuperPro 的地址从00H到3FH,以十六进制数表示。

  访问权限:SuperPro的每一个存储单元可以单独设置成不同的操作权限。

  1- 可读/写数据;2- 只读数据;3- 计数器;4- 算法

  2.数据结构

  APIPACKET

  Type APIPACKET

    data(4096) As Byte

  End Type

  除了sproInitialize, 所有的函数都使用APIPACKET数据结构,该结构在调用sproFindFirstUnit 时被初始化,在调用sproFindNextUnit的时候会根据下一个加密锁的信息进行必要修改。当函数正常返回时,APIPACKET中存放必要的返回信息。

  SuperPro驱动程序使用该结构中的信息与加密锁进行通讯。开发者不要自行修改其中的数据。SuperPro驱动程序不负责分配APIPACKET数据单元,开发者在程序中必须分配APIPACKET结构变量,并且把指针传递给不同的函数。

  3.API函数

  ● RNBOsproFindFirstUnit()

  RNBOsproFindFirstUnit() 函数定位具有指定的开发者ID的SentinelSuperPro 加密锁。在调用 RNBOsproInitialize() 之后,使用其他调用之前,必须首先调用该函数。如果找到了加密锁,讯息包或UNITINFO 记录中将包含有效的数据,如果未找到加密锁,讯息包或 UNITINFO 记录将被标记为无效的。

  RNBOsproFindFirstUnit() 将搜索所有连接在并行端口上的所有级联单位。如果存在多个加密锁具有相同的开发者 ID,RNBOsproFindFirstUnit() 存取找到的第一个加密锁。在需要的时候,可以使用 RNBOsproFindNextUnit()查找具有相同开发者 ID 的其他加密锁。

  参数

  RNBOsproFindFirstUnit() 调用需要以下参数:

  ■由 Rainbow Technologies 或者其他发布者为您指定的开发者 ID。

  ■指向讯息包记录或者 UNITINFO 结构的指针。

  返回值

  如果执行成功,函数返回成功。如果发生了错误,函数将返回附录中所列的状态码。

  ● RNBOsproFindNextUnit()

  RNBOsproFindNextUnit() 函数搜索具有给定开发者 ID 的下一个加密锁。

  开发者ID是由第一次调用的 RNBOsproFindFirstUnit() 指定的。(要找到具有不同的开发者ID的加密锁,需要再 次调用RNBOsproFindFirstUnit()。)

  如果 RNBOsproFindNextUnit() 成功了,讯息包或者 UNITINFO 记录将包含下一个加密锁的数据。如果未成功,讯息包或者 UNITINFO 记录将被标记为无效的。要重新对讯息包或 UNITINFO 记录初始化,需要再次使用 RNBOsproFindFirstUnit() 以及 RNBOsproFindNextUnit()。

  RNBOsproFindNextUnit() 将搜索连接到任何并行端口上的所有级联单位。如果同时连接了几个加密锁,应用程序可 以多次调用RNBOsproFindNextUnit()。

  参数

  RNBOsproFindNextUnit()函数需要一个参数:指向讯息包记录或者UNITINFO 结构的指针。

  返回值

  如果执行成功,函数返回成功。如果发生了错误,函数将返回附录中所列的状态码。

  ● RNBOsproFormatPacket()

  RNBOsproFormatPacket() 函数检验讯息包记录大小的有效性,并且初始化它的缺省值。该调用必须在调用其他 API 函数之前进行,否则将返回错误码 2 (INVALID PACKET)。

  参数

  RNBOsproFormatPacket() 需要下列参数:

  ■指向讯息包记录 (RBP_RNBOspro_APIPACKET) 的指针。

  ■PacketLen 的值是一个整数, 包含了讯息包的长度 (1028 个字节) 。

  返回值

  所有的函数都返回一个无符号的 16 位的值。值为 0 表示操作成功了,其他的值都表明发生了错误。如果发生了错误,函数将返回附录中列出的状态码之一。如果返回的状态码为非 0 的值,那么函数返回的其他数据将是无意义的。

  ● RNBOsproGetVersion()

  RNBOsproGetVersion() 函数返回与 SentinelSuperPro 驱动程序有关的信息,应用程序可以使用该信息。

  参数

  RNBOsproGetVersion() 函数需要下列参数:

  ■指向讯息包记录或者 UNITINFO 结构的指针。

  ■指向用来返回主版本号的位置的指针。

  ■指向用来返回次版本号的位置的指针。

  ■指向用来返回修订版本号的位置的指针。

  ■指向用来返回驱动程序类型标识符的位置的指针。

  返回值

  如果执行成功,函数返回成功。如果发生了错误,函数将返回附录中所列的状态码。

  ● RNBOsproInitialize()

  RNBOsproInitialize() 函数使驱动程序执行所需的初始化操作。在调用任何其他的 API 之前,应用程序必须首先调用 RNBOsproInitialize() 一次。

  参数

  RNBOsproInitialize() 函数不需要任何参数。

  返回值

  如果执行成功,函数返回成功。如果发生了错误,函数将返回附录中所列的状态码。

  ● RNBOsproOverwrite()

  RNBOsproOverwrite() 函数使应用程序能够改变字组的值与存取码,但是被限制的单元(0 到 7)除外。在使用该函数的时候,需要同时提供写密码和重写密码。

  注意重写密码的能力是相当强大的。它的使用应该仅限于工作室中,而不应该在正式发布的应用程序中出现。如果您的保护方案不需要在应用程序中使用重写密码,Rainbow 可以通过编程使您的每一个加密锁具有一个“随机重写密码”。每个加密锁中将包含不同的重写加密锁密码,并且不能在最终用户实际使用中进行修改。

  参数

  RNBOsproOverwrite() 函数需要下列参数:

  ■指向讯息包记录或者 UNITINFO 结构的指针。

  ■您的写密码。

  ■您的两个字组长的重写密码。

  ■需要写入的定址。

  ■赋予该字组的存取码:0 (可读/写数据)、1 (只读数据)、2 (计数器)、3 (算法/隐藏的)。

  ■希望写到该定址上的值。

  返回值

  如果执行成功,函数返回成功。如果发生了错误,函数将返回附录中所列的状态码。

  ● RNBOsproQuery()

  利用RNBOsproQuery() 函数,应用程序可以发送询问字串到SentinelSuperPro 加密锁。应用程序还可以指定用来对字串进行转换的算法描述符。

  加密锁将使用自己的算法和指定的算法描述符中储存的信息对输入字串进行转换。然后加密锁将转换之后的字串返回到应用程序。应用程序将返回的字串与希望得到的字串相比较,即可知道所需要的加密锁是否仍连接在并行端口上。

  在软件开发阶段,高级编程序的鉴定 API:询问选项可以用来确认加密锁的算法描述符对给定的输入字串进行转换的结果。

  参数

  RNBOsproQuery() 函数需要下列参数:

  ■指向讯息包记录或者 UNITINFO 结构的指针。

  ■询问字串中的字节个数(最大值 56)。

  ■指向包含询问字串的缓冲区的指针(偏移量在前,段定址在后)。

  ■指向用来供驱动程序返回结果字串的缓冲区的指针。该缓冲区需要足够大,以便容纳整个的结果字串。另外一种可取的办法是,将某个位置的指针传递给驱动程序,但驱动程序仅返回结果字串的最后 32 个位。

  ■询问中使用的算法描述符的首(较低的)字组的定址。该定址必须是偶数。一般来说,较长的询问字串能够提供更好的保护。推荐的询问字串长度至少为 8 个十六进制字符(32 个位)。驱动程序允许的最长的询问字串的长度为 56 个字节。如果发送的字串长度小于 32 位,驱动程序将在添加一种已知的模式之后再将其发送出去。

  返回值

  如果执行成功,函数返回成功。如果发生了错误,函数将返回附录中所列的状态码。

  ● RNBOsproRead()

  应用程序可以使用 RNBOsproRead() 函数读取非隐藏的记忆体字组的值。非隐藏的字组的存取码为 0 (可读/写数据)、1 (只读数据)或 2(计数器)。算法/隐藏的字组的存取码为 3,它是不能被读取的。如果试图读取一个隐藏的字组,驱动程序将返回拒绝存取。通过检查该状态即可判断字组是不是算法字组。RNBOsproRead() 返回字组的值,而不是其存取码。要获得字组的存取码,可以使用 RNBOsproExtendedRead()。

  参数

  RNBOsproRead() 函数需要下列参数:

  ■指向讯息包记录或者 UNITINFO 结构的指针。

  ■被读取的定址。

  ■一个指针,在调用成功的时候,该指针指向驱动程序返回的指定字组的内容。

  返回值

  如果执行成功,函数返回成功。如果发生了错误,函数将返回附录中所列的状态码。

  ● RNBOsproDecrement()

  RNBOsproDecrement() 函数将可读/写的数据字组(存取码为 0)或者计数器字组(存取码为 2)的值减量 1。在使用该函数时需要提供写密码。如果试图对被锁定或者隐藏的字组减量,驱动程序将返回拒绝存取。如果字组中的值已归零,驱动程序将返回已归零。对这两种状态码应用程序都需要进行检查。RNBOsproDecrement() 可以用来限制演示程序能够被执行的次数。首先将一个计数器与一个算法描述符联系起来,然后在每次程序执行的时候将计数器减一。当计数器为 0 时,算法将自动被去激活。以后的询问将返回无效的响应。

  参数

  RNBOsproDecrement() 函数需要下列参数:

  ■指向讯息包记录或者 UNITINFO 结构的指针。

  ■您的写密码。

  ■需要被减量的数据字组或计数器的定址。

  返回值

  如果执行成功,函数返回成功。如果发生了错误,函数将返回附录中所列的状态码。

  ● RNBOsproExtendedRead()

  RNBOsproExtendedRead() 函数读取非隐藏的记忆体字组的值和存取码。未隐藏的字组的存取码为 0 (可读/写数据)、(只读数据)或 2 (计数器)。算法/隐藏字组的存取码为 3,它们是不可读的。如果试图读取一个隐藏的字组,驱动程序返回拒绝存取。通过检查该状态,能够确认一个字组是否是算法字组。

  参数

  RNBOsproExtendedRead() 函数需要下列参数:

  ■指向讯息包记录或者 UNITINFO 结构的指针。

  ■要读取的定址。

  ■指向指定字组的内容的指针,在调用成功时由驱动程序返回内容。

  ■指向指定字组的存取码的指针,在调用成功时由驱动程序返回存取码。存取码可能是 0 (可读/写数据)、1 (只读数据)或 2 (计数器)。调用不可能返回存取码 3 (算法/隐藏的)。

  返回值

  如果执行成功,函数返回成功。如果发生了错误,函数将返回附录中所列的状态码。

  4.必要的初始化步骤

  在调用API函数进行加密工作以前,必须在应用程序中完成以下有关加密的初始化工作。

  分配APIPACKET数据结构

  调用RNBOsproFormatPacket初始化结构(仅Win32程序使用)

  调用RNBOsproInitialize函数

  调用RNBOsproFindFirstUnit函数找到软件保护锁

  以上工作完成以后,开发者可以根据各自的需要在程序的任何部分进行读写、查询等工作。所有函数没有必然的前后顺序。

  当利用查询响应对,调用RNBOSproQuery进行查询时,需要对查询响应对进行适当转换。

  三、备注

  如果想了解更多有关软件保护锁的信息,请参阅加密锁手册SentinelSuperPro Developer Guide。

  附录.API状态码返回数值

  0 success - 调用顺利地完成。

  1 invalid function code - 指定了一个无效功能码。对于有效的 API 功能码,请查看您所使用的语言的包含档案(例如:SUPERPRO.H )。如果使用 Rainbow 提供的接口程序与驱动程序通讯,通常不应该产生该错误。

  2 invalid packet - 在命令讯息包中检测到核对和错误,该错误表明内部不一致。因是讯息包记录或 UNITINFO 结构还没有被初始化,或者可能已被篡改了。如果使用Rainbow 提供的接口程序与驱动程序通讯,通常不应该产生该错误。

  3 unit not found - 或者 RNBOsproFindFirstUnit(),或者 RNBOsproFindNextUnit() 无法找到指定的 SentinelSuperPro 加密锁。请确信发送了正确的开发者 ID。如果该加密锁消失(也就是说,已?被取消),该错误是其它函数返回的。

  4 access denied - 企图对字组执行非法操作。例如,您可能试图从算法/隐藏字组中读取、向锁定的字组中写入,或者将不是计数器字组或者数据字组的字组减量。

  5 invalid memory address - 指定了无效的 SentinelSuperPro 记忆体定址。有效的定址是十进制的 0-63(十六进制的 0-3F)。对于许多操作,单元 0-7 是无效的。必须使用第一个(偶数)定址引用算法描述符。

  6 invalid access code - 指定了一个无效的存取码。存取码必须是 0(读/写数据)、1(只读数据)、2 (计数器)或者 3(算法/隐藏)。

  7 port is busy - 因为端口被占用,所以请求的操作不能完成。导致的原因可能是有大量的打印任务,或者该端口上的某个单位正在执行写操作并阻塞了该端口。请再试一次该函数。

  8 write not ready - 由于暂时缺少足够的能力,不能执行写入或者减量操作。请再试一次该操作。

  9 no port installed - 在工作站上没有发现并行端口。

  10 already zero - 试图将已?包含 0 的计数器字组或者数据字组减量。如果使用该计数器控制演示程序执行,当对应的算法描述符被起动密码重新激活之后,可能会出现这种情况。

  12 driver not installed - 没有安装或者没有检测到系统设备驱动程序。不可能与该单位通讯。请验证设备驱动程序的加载是否正确。

  13 communications error - 系统设备驱动程序与该单位通讯时碰到问题。请验证设备驱动程序的安装是否正确。

  18 version not supported - 当前的系统设备驱动程序过期。请更新驱动程序。

  19 OS environment not supported - 客户程序库不支持该操作系统或者环境。请与技术支持联系。

  20 query too long - 发送的询问字串超过 56 个字符。请发送较短的字串。

  30 driver is busy - 系统驱动程序繁忙。请再试一次该操作。

  31 port allocation failure - 通过操作系统的并行端口争用处理器来分配并行端口时失败。

  32 port release failure - 通过操作系统的并行端口争用处理器来释放先前分配的并行端口时失败。

  39 acquire port timeout - 在规定的超时时间内请求使用并行端口失败。

  42 signal not supported - 特定的机器不支持信号线。例如,企图在一台 NEC 9800 计算机上使用 ACK 线。

  57 init not called - 没有初始化加密锁。在调用产生该错误的函数之前,请先调用RNBOsproInitialize() 函数。

  58 driver type not supported - 对于定义的操作系统和客户程序库,不支持驱动程序的存取类型。该驱动程序的存取类型无论是直接 I/O,还是系统驱动程序。

  59 fail on driver comm - 客户程序库与 Rainbow 的系统驱动程序通讯失败。

  60 API status unavailable - 扩充的 API 状态函数不可用。

  255 invalid status - 返回的状态码无效。