前言¶
接手一个 LabVIEW 项目时常见一种情况:前面板能打开,框图却提示需要密码。传统解法是装几 GB 的 NI 运行时,或者改用 pylabview、第三方解密脚本,前者笨重,后者依赖 Python 和特定版本环境。
下面介绍的 dsh-labview 是社区插件,它用一个无依赖的 JavaScript 引擎直接解析 LabVIEW 文件的 RSRC 容器,在字节层面把密码记录抹掉,不需要 LabVIEW、不需要 Python、不需要联网。
这是什么¶
dsh-labview 由维护者 yxfvvv 开发并托管在 yxfvvv/dsh-labview,许可证为 MIT,分类为 admin-security。它处理的问题很具体:把 *.vi、*.ctl、*.vit、*.ctt(以及配置扩展名中的 .xctl)文件里被锁定的框图解除保护,原地修补少量字节。
先说清它的本质:LabVIEW 的“密码保护”并不是加密。框图本身以明文存在文件里,挡住你的只是一小段记录。插件把这段记录改成“空密码 + 正确哈希”,再把版本块里的一个保护标志位清掉,LabVIEW 打开时就不再询问密码。
工作原理¶
锁定信息存在两个块里:
| 块 | 含义 |
|---|---|
BDPW |
md5(password) + hash_1 + hash_2,即密码记录 |
LVSR |
版本块;execFlags 的 bit 13(LibProtected,0x2000)标记该库受保护 |
插件要做的就是四步:
password_md5→md5(""),即d41d8cd98f00b204e9800998ecf8427ehash_1→md5(md5("") ‖ LIBN ‖ LVSR ‖ salt)hash_2→md5(hash_1 ‖ contentHash(BDH))- 清掉
LVSR里的VI_EXEC_FLAGS.LibProtected
唯一未知量是 12 字节 salt,LabVIEW 由连接器面板上各端子类型的数量推导。引擎不去解析类型描述符,而是对这三个小整数做暴力枚举——典型计数值小于 20,多数文件几毫秒内就能解出。文件其余部分不动:不重新序列化、不重新压缩、不改变长度;原本没锁的文件保持字节一致。
安装与启用¶
以下命令来自 README。第一种是从 npm 安装,但 README 注明要等包发布之后才可用;第二种指向本地检出。
dsh plugin --profile web add dsh-labview # from npm, after it is published
dsh plugin --profile web add link:/path/to/dsh-labview # local checkout
装好后重启 harness(或在市场页面切换插件开关),三个工具会在每个会话里出现。运行环境要求 Node >=20.19.0,DSH 侧要求 >=0.1.0-rc.6 <0.3.0。
README 提到 lib/engine.js 是独立模块,也可以直接 import 进来使用解析和修补逻辑,绕过 harness。
使用¶
插件暴露三个工具,可以用自然语言调用,也可以直接调用。
lv_scan(dir?, lockedOnly?) 是只读检查,逐文件报告 LabVIEW 版本、框图是否被保护、能否解锁以及不能解锁的原因。它不写任何内容,可以在生产副本上安全运行。
lv_unlock(dir?, dryRun?, hashSource?, limit?) 先把每个原件备份到 <dir>/_lv_unlock_backup/…,再修补文件,并且在落盘前先在内存里校验修补后的字节。已经打开的文件会被计数并跳过。dryRun: true 只报告将要做什么、不做任何写入,大目录建议先跑一遍。
lv_restore(dir?, dryRun?, limit?) 把备份复制回工作文件,撤销解锁。
一个典型会话是:
扫描
D:\SCADA\FC MAS V1里被锁的 LabVIEW 文件,然后解锁,并告诉我需要哪个 LabVIEW 版本才能打开这个项目。
模型会调用 lv_scan,看到 140 × LabVIEW 2015 / 83 × LabVIEW 8.5,再运行 lv_unlock,把两者一并汇报。原件保留在 _lv_unlock_backup/,直到你手动删除。
安全模型与配置¶
lv_unlock 的安全顺序是:先备份、再校验、后落盘。具体约束有几条:
- 第一字节写入之前先复制原件到备份树,且已有备份永不覆盖,第二次
lv_unlock不会破坏原始副本。 - 修补后的缓冲区会被重新解析,必须报告“无密码”才写入文件,任何异常都会被拒绝。
- 写入通过
<file>.lvunlock.tmp+ rename 完成,中断运行不会留下半个 VI。 - 每个文件只改两处,并报告确切的字节区间;不需要改动的文件永远不会被重写。
- 全程不需要 LabVIEW,引擎直接解析
RSRC,不运行 VI、不解编译框图、不需要许可证。
所有配置项都有可用默认值,仅在需要时覆盖:
| 配置项 | 默认值 | 含义 |
|---|---|---|
backupDir |
_lv_unlock_backup |
备份目录,相对扫描根目录 |
extensions |
.vi .ctl .vit .ctt .xctl |
要处理的文件类型 |
maxFiles |
20000 |
单次扫描的上限 |
concurrency |
4 |
并行处理的文件数 |
hashSource |
auto |
新哈希覆盖哪个版本块(auto/final/original) |
对应 profile 配置:
# profile cordis.patch.yml
- id: labview-unlock
config:
backupDir: _lv_backup
concurrency: 8
版本处理上有一处细节:删除密码也会清掉 LibProtected,而 LabVIEW 是在这之后才对版本块做哈希,所以 hash_1 覆盖的是已清除标志位的块;旧版本保存的 VI 使用遗留版本块布局,标志位从未进入哈希。引擎通过 hashSource: "auto" 自动识别两种情况并复现对应行为,"final" 或 "original" 可强制其中一种。
支持范围¶
支持 LabVIEW 8.0 到 2019 保存的文件(以及最后由这些版本保存的旧文件)。LabVIEW 2020 及以上改变了框图布局,这类文件会被报告为不支持,而不是被写入。
适用场景与注意¶
适合接手了遗留 LabVIEW 项目、需要批量恢复框图访问权限、又不方便铺开整套 NI 环境的人。需要注意几点:
- 插件以当前 dsh 进程的权限运行。安装前应先检查源码与许可证(本插件为 MIT),确认来源可信。
- 插件无法打开 LabVIEW,所以“已解锁”只表示文件自身的密码记录如此。对不可替代的项目做批量解锁前,先用 LabVIEW 打开一个文件验证。
- 动词暴力搜索有上限,每个计数最多搜到 255(约 1670 万种组合)。连接器面板超出此范围的文件会被报告为不可解锁,而不会被静默误处理。
- 解除
*.ctl的密码同样会暴露其框图;原本被当作不透明黑盒使用的类型,现在可以被编辑。 - 伦理边界:只对你拥有或被授权维护的软件使用本工具。恢复公司已付费项目的访问属于维护,破解厂商的授权限制则不是。
README 里的验证数据来自插件自述:在真实工业代码上,重新解锁后与原件逐字节比较 223 / 223 一致;此前一个 601 文件项目随机抽查 597 个已解锁、0 个仍被锁;密码盐恢复 223 / 223 正确;误改文件 0 个,每个差异都只是密码块加一个标志字节。平均每文件约 35 ms,最慢的一个约 0.9 s。
结尾¶
dsh-labview 的价值在于把一个依赖重运行时的逆向维护任务,收敛成纯 JavaScript 的字节修补,并配了备份、校验、原子写入这三层保护。它目前星标为 0,属于早期阶段的社区项目,功能与限制都写在 README 里。
目录页在 https://www.skillhub.cn/plugins/yxfvvv/dsh-labview ,源码在 https://github.com/yxfvvv/dsh-labview 。需要说明的是,skillhub 是社区维护的独立目录站点,与 DeepSeek 及幻方没有官方从属关系,不是官方应用商店。