MySQL, Oracle, Linux, 软件架构及大数据技术知识分享平台

网站首页 > 精选文章 / 正文

限制程序在指定U盘运行(限制usb软件)

2025-03-25 13:46 huorong 精选文章 2 ℃ 0 评论

工作中有这样一个需求:

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('读取资源文件失败')
    }
  })
}

Tags:difftime

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言