限制程序在指定U盘运行

工作中有这样一个需求:

1)制作一个U盘离线版阅读器,只能访问U盘中对应的书本内容;

2)如果将U盘中的内容拷贝到其他磁盘,则无法打开阅读器。

以下是我的一个方案思路,因为一些原因未在实际工作中采用,故在此抛砖引玉权作交流。

环境:Windows

框架:Electron

语言:JS

一、方案思路

        要让程序具有排他性,就要让它与U盘建立绑定关系。整体思路是:

        1)当启动程序时,获取到程序所在磁盘的物理ID,然后与程序绑定的ID做对比,相等则通过,不相等则直接关闭程序;

        2)因为此处使用的磁盘物理ID通过第三方工具是可以修改的,为防止别人通过“修改物理ID”加“拷贝资源文件”克隆更多U盘实例,于是又增加了时间校验,多一层防护;

        3)既然上述步骤需要校验磁盘物理ID,就必须在程序中预设U盘ID,此处使用一个单独的加密文件(disk.disc)来存储敏感信息,当启动程序时,在内存中解密该文件数据来使用。

、生成加密文件

        为方便U盘制作者,编写了一个小工具,该工具也是用Electron开发的(nodejs环境),没有直接获取磁盘物理ID的API,需要使用child_process模块执行DOS命令来获取。

wmic logicaldisk get Caption,VolumeSerialNumber,Description /format:list

        制作者可以选择目标盘符来生成特定加密文件(disk.disc)。

、程序启动时的校验

        实现比较简单,直接看主要代码:

function checkDiskDisc() {

// disk.disc内容

let diskDiscPath = 'X:\\xx\\disk.disc' // disk.disc文件路径

let diskDiscInfo = {} //用于存储解密后的disk.disc文件数据

//当前程序所在磁盘信息

let baseDataPath = 'X:\\xx\\xx' //程序中的某个特定文件路径,用于时间校验

let baseDataInfo = fs.statSync(baseDataPath) //特定文件信息

let baseDataDiskCode = baseDataPath.slice(0, 2).toUpperCase() //当前程序所在磁盘盘符

  let command = 'chcp 65001 & wmic logicaldisk get Caption,VolumeSerialNumber /format:list'

  return new Promise((resolve, reject) => {

if (!fs.existsSync(diskDiscPath)) {return reject('资源文件不存在')}

    try {

      let data = fs.readFileSync(diskDiscPath)

diskDiscInfo = decrypt(data) //解密disk.disc文件,假设解密后得到一个JSON对象

      exec(command, {encoding: 'buffer'}, (error, stdout, stderr) => {

if (error) { return reject('命令执行失败,请以管理员身份运行程序后重试') }

let str = iconv.decode(stdout, 'utf8') || '' //命令执行结果-编码处理

        let lst = str.match(/.*=.*/g)

let result = [] //用于存储电脑上所有磁盘的信息

        let count = {}

        lst && lst.forEach(v => {

          let arr = v.split('=')

          arr = arr.map(v => (v || '').trim())

          if (!count[arr[0]] && count[arr[0]] !== 0) {count[arr[0]] = 0}

          else {count[arr[0]] = count[arr[0]] + 1}

          let index = count[arr[0]]

          if (!result[index]) {result[index] = {}}

          result[index][arr[0]] = arr[1]

        })

        result = result.map(v => {

          return {

            label: (v.Caption || '').toUpperCase(),

            value: v.VolumeSerialNumber

          }

        })

        let diskList = result.filter(v => v.value)

        let diskItem = diskList.filter(v => v.label === baseDataDiskCode)[0] || {}

        let timeDiff = diskDiscInfo.testDate - baseDataInfo.ctimeMs

//物理ID校验、时间校验(规则根据实际需求调整)

        if (diskItem.value === diskDiscInfo.deviceId && timeDiff > 0 && timeDiff < 2592000000) {

resolve(true) //通过

        } else {

resolve(false) //不通过

        }

      })

    } catch (error) {

reject('读取资源文件失败')

    }

  })

}

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容