PLC动态加密功能块实战:S7-1200/1500程序防复制与授权管理
发布时间:2026/10/11 0:30:11 锦皓数字建站

干自动化这些年最扎心的场景不是现场调试到凌晨而是设备刚交出去半年就发现客户厂里多了一台和你做的设备一模一样的机器运行逻辑连定时器参数都没改。S7-1200/1500 在国内项目里太常见上载、反编译、复制项目的门槛远比想象中低。所以很多做非标设备和 OEM 整机的工程师都在琢磨同一个问题怎么让程序离开授权就跑不起来。这就要说到“动态加密功能块”。它不是博途菜单里那个简单的 Know-how 密码而是把授权码校验、时间锁、运行时长锁、动态码生成全部封装成一个独立功能块设备方掌握授权算法和密钥客户现场只能通过动态码申请授权。这篇我就把这个块的原理、选型、代码结构、坑点全部拆开讲适合做设备保护、项目交付、OEM 标准机型以及被客户催着“解个锁”的同行参考。先说结论如果只想挡外行一个静态密码就够了。但如果要防的是同行、是能拿上载软件反编译的人就必须让授权逻辑“动”起来。下面从设计思路开始一步一步把这个块搭出来。1. 动态加密功能块的设计思路与选型1.1 为什么“动态”而不是“静态”静态加密主要有两个死穴。第一密码是固定的只要被记录一次就等于永久有效哪怕你三天两头换密码也可能被人盯着 HMI 操作界面抄走。第二固定日期锁太容易被绕过现场改一下 PLC 系统时间授权就到手了。动态加密的思路完全不同。它让 PLC 每次上电、每次运行都根据当前日期、运行时长、上电次数、设备编号等信息在块内部计算出一个不断变化的“动态码”。操作人员把这个动态码发给设备厂商厂商用自己手里的密钥和算法离线算出“授权码”再填回 HMI 解锁。因为动态码每次都变即使被完整记录下来下一次也无法复用因为授权码和动态码是一一对应的拿到一个授权码也推算不出生成规则。这个逻辑用生活里的例子解释最直观动态码相当于一分钟换一次的随机锁芯授权码是开这一分钟锁芯的钥匙。锁芯每次都换钥匙就必须现配别人就算抢走一把钥匙下一分钟也打不开门。1.2 为什么选用 FB 而不是 FC动态加密功能块我用 FB 封装不用 FC。原因很简单FB 自带背景数据块可以把授权状态、上电计数、上次正常运行日期、授权期限这些数据稳稳地存起来而且可以设置掉电保持。FC 没有独立的背景数据所有状态数据都得靠外部 DB 传入调用多了容易乱也容易被人在外部直接改写。FB 做成标准块之后项目里用起来非常省事。OB1 里一个调用把 HMI 上的授权码输入和动态码显示连上接口就行。内部逻辑越复杂外部接口越简单越好。我习惯只开放四个口使能、授权码输入、动态码输出、锁机状态输出内部那些盐值、CRC 算法、时间判断全藏在块体里再配合博途的 Know-how 保护别人上载回来只能看到接口看不到内部实现。1.3 S7-1200 与 S7-1500 的选型差异同样一套动态加密块S7-1200 和 S7-1500 基本都能跑但有几个前置条件要确认。S7-1200 的 Know-how 保护从固件 V4.0 开始才比较完善如果现场还是老版本固件建议先升级再谈加密。S7-1500 全系都支持块保护而且加密强度更高。保持性数据方面S7-1200 的保持性存储区有限动态加密块占用的保持变量不多通常够用但如果一个项目里要放十个八个授权块就得精打细算。S7-1500 的保持区大得多跑起来也更从容。性能上两者都没压力别听人说 CRC 运算在 1200 上跑不动一个 CRC16 循环几十个字节扫描周期增加可以忽略不计。真正要权衡的是项目定位标准单机、小型产线用 S7-1200 完全没问题大型 OEM 设备、需要频繁改动授权策略的建议直接上 S7-1500。对比项S7-1200S7-1500Know-how 保护V4.0 以上支持全系支持保持性数据区有限更大复杂运算能力够用更从容适合场景单机、小产线大型设备、频繁升级2. 核心算法与关键指令解析2.1 动态码生成CRC 加盐值动态码是整个授权体系的心脏。我的做法是把设备编号、当前日期、上电计数、累计运行小时数拼成一个字节数组然后用 CRC16 算出校验值再和藏在块里的盐值做一次异或得到最终动态码。这里有个关键点动态码必须可复现。也就是说PLC 算出来的动态码设备厂商在办公室用同一套算法、同样的输入数据也必须能算出同一个值否则后续授权码根本无法生成。所以动态码里的所有因子必须是确定性的。CRC16 本身不是安全哈希防不了博士级逆向但配合盐值和授权码两步校验已经能挡住绝大多数图省事的仿制者。盐值的保存方式比算法本身更重要一定放在加过 Know-how 保护的块内部不能出现在 HMI 变量表或上位机配置文件里。我常用的盐值处理是双层生成动态码时用盐值 A 参与异或计算预期授权码时再用盐值 B 参与 CRC两把盐不放在同一个上下文里。哪怕有人通过通讯抓包拿到了动态码和授权码也无法直接反推出盐值。2.2 授权码校验逻辑授权码不能只是动态码的简单反转那样太容易被人看出来。我设计的校验链路是设备厂商拿到动态码后把动态码、客户编号、授权天数、密钥拼接在一起做第二次 CRC得到授权码。PLC 端每次校验时用同样的因子重新计算一遍预期授权码再和 HMI 输入值比对。比对通过后PLC 把授权状态置为有效同时把“到期日期”和“剩余运行时长”写入保持性变量。授权码里还可以编码授权等级比如低字节表示天数高字节表示功能权限这样同一个算法既能做整机锁定也能做功能模块的按需开通。有一点必须提醒PLC 端和 PC 端授权工具必须使用同一套 CRC 实现连初始值和多项式都要一致。实际项目里最常见的问题就是两边 CRC 算法版本不一致导致授权码死活校验不过。测试阶段先用一组固定输入数据分别跑 PLC 和 PC 工具比对输出确认一致再上线。2.3 时间锁与运行时长锁单纯的自然日期锁有漏洞现场改一下 PLC 时钟就能绕过。所以我一直用“双锁”方案一个锁看自然日期另一个锁看累计运行时长两个锁哪个先到期就哪个生效。累计运行时长可以用西门子的 RUNTIME 指令实现它专门累加 CPU 运行小时数配合保持性 DB 能做到掉电不丢。每次执行授权校验时读取当前累计小时和授权时写入的“授权运行秒数”做比较。就算客户把 PLC 时间改回十年前运行时长锁照样在跑。自然日期锁也有存在的必要它负责管那些“按自然年卖服务”的场景。但日期锁必须加防篡改块里存一个“上次正常运行日期”每次上电时检查当前日期如果发现比上次记录提前了超过一天说明时钟被往回改过直接标记授权异常。这个方法不是绝对防破解但显著提高了绕过成本。2.4 防止被强制和在线改值的几个细节S7-1500 支持变量强制S7-1200 不支持强制但对背景 DB 的直接写入很难拦住。所以我做动态加密块时有几个固执的习惯授权状态不放在 HMI 可以直接写的全局 DB 里而是放在 FB 的 Multi-instance 背景数据块深处背景 DB 设置优化访问外部拿不到可靠地址。更关键的是把锁机动作分散开。别让所有设备输出都依赖同一个“解锁标志位”否则有人就在线强制这个位为 TRUE整机就通了。我会在设备主逻辑里多处引用授权块的输出同时在某些关键中断 OB 里也做校验思路是哪怕有人绕过一个地方其他地方还会跳故障。这些手段防不了专业逆向但足够让想抄程序的人觉得“不划算”。后面我会细说为什么技术防护只能做到这一步。3. 实操从零搭建一个动态加密功能块3.1 准备工作与块接口定义我用 TIA Portal V17 演示S7-1200 固件 V4.5S7-1500 固件 V2.8 以上都适用。先把项目库建好建议建一个“AuthLib”库文件夹放 FB_DynLock 和 FC_CRC16 两个块以后新项目直接拖。FB 接口我这样定义FUNCTION_BLOCK FB_DynLock VAR_INPUT bEnable : BOOL; // 加密功能总开关 dwAuthCodeIn : DWORD; // 来自HMI的授权码输入 END_VAR VAR_OUTPUT bLocked : BOOL; // 1锁机状态 dwDynCode : DWORD; // 动态码输出给HMI显示 iRemainDays : INT; // 剩余授权天数用于HMI提示 END_VAR VAR_IN_OUT sDeviceSN : STRING[16]; // 设备/客户编号授权时写入 END_VAR VAR udtDynBuf : ARRAY[0..31] OF BYTE; // 动态因子缓冲区 holdCnt : DINT; // 上电计数掉电保持 lastDate : DWORD; // 上次正常日期 tRunHours : LREAL; // 累计运行时长 authStatus : BOOL; // 授权有效标志 END_VAR接口能少则少。实际项目里有人把授权做得能剩下 20 个引脚结果自己接线都容易出错完全没必要。3.2 CRC16 函数实现FC_CRC16 我用标准 Modbus CRC16 算法初始值 16#FFFF多项式 16#A001。核心代码如下FUNCTION FC_CRC16 : WORD VAR_INPUT data : ARRAY[0..255] OF BYTE; len : INT; END_VAR VAR i : INT; j : INT; crc : WORD; END_VAR BEGIN crc : 16#FFFF; FOR i : 0 TO len - 1 DO crc : crc XOR data[i]; FOR j : 1 TO 8 DO IF (crc AND 2#0001) 2#0000 THEN crc : (crc / 2) XOR 16#A001; ELSE crc : crc / 2; END_IF; END_FOR; END_FOR; FC_CRC16 : crc; END_FUNCTIONSCL 里 WORD 除以 2 等价于右移一位逻辑上没问题。如果你用的是查表法那就注意查找表字节序不同实现之间很容易差出高低字节交换的结果。这个函数我会单独做测试输入一串已知值比对网上现成的 Modbus CRC16 在线计算结果一致了再往 FB 里引。3.3 FB 核心逻辑结构FB_DynLock 的扫描逻辑我分成四段生成动态码、校验授权码、检查到期、输出锁机状态。伪代码如下// 1. 每次扫描累加上电计数 holdCnt : holdCnt 1; IF holdCnt 16#7FFFFFF0 THEN holdCnt : 0; // 防溢出同时防止授权“永久化” END_IF; // 2. 填充动态因子缓冲区 // 把 sDeviceSN、当前日期 YYYYMMDD、holdCnt、运行小时取整值 // 依次写入 udtDynBuf注意按 BYTE 数组方式处理 // 3. 第一次 CRC生成动态码 dwDynCode : FC_CRC16(udtDynBuf, 实际长度) XOR 16#A5B6; // 盐值A // 4. 第二次 CRC计算预期授权码 // 把 dwDynCode、客户编号、盐值B 拼接后再次 CRC expectedAuth : FC_CRC16(拼接缓冲区, 实际长度); // 5. 与输入授权码比对 IF bEnable AND (dwAuthCodeIn expectedAuth) THEN authStatus : TRUE; // 写入授权期限日期锁和运行时长锁 ELSE authStatus : FALSE; END_IF; // 6. 到期检查 IF authStatus TRUE THEN IF 当前日期 授权到期日 THEN bLocked : TRUE; END_IF; IF 累计运行小时 授权运行小时 THEN bLocked : TRUE; END_IF; ELSE bLocked : TRUE; END_IF;这里有个容易踩的坑上电计数和授权状态都放在保持性存储区如果现场把 CPU 恢复出厂保持区一清授权就自动失效。这个特性既是保护措施也有点麻烦——换电池、换存储卡、恢复出厂都会导致重新授权。所以块上要加一个“重新激活”的识别机制检测到保持数据异常时动态码会附加一个特殊标记告诉厂商“这是保持区丢失后的补授权请求”。3.4 OB100 初始化与 OB1 调用OB100 里要做几件事检查保持区数据完整性、同步初始上电时间、确认授权块首次使能。特别是上电计数第一次下载程序后应对其初始化否则第一次上电就是 1问题不大但若发现计数为 0 而授权状态有效就说明保持区被清过需要进入补授权流程。OB1 里的调用非常简洁FB_DynLock_DB( bEnable : TRUE, dwAuthCodeIn : HMICfg.AuthCode, bLocked DeviceCtrl.Locked, dwDynCode HMICfg.DynCode, iRemainDays HMICfg.RemainDays, sDeviceSN : DeviceParam.SN );锁机状态要不要直接切断输出我建议不要一刀切。直接断输出容易造成设备停在危险位置我通常做法是先报警、再进安全停车流程、三分钟后确认无人干预才彻底锁机。这个“缓冲锁机”的细节在设备保护里比加密本身还重要。3.5 HMI 授权画面组态HMI 上我放三个元素动态码显示框、授权码输入框、激活按钮。动态码可以从 dwDynCode 直接显示最好再用日期转换脚本把它显示成更易读的格式。授权码输入框要限制输入长度类型选字符串或十六进制整数具体看 HMI 型号。HMI 和 PLC 的通讯要特别注意如果触摸屏和 PLC 跨网段MCGS、昆仑通泰这些组态屏跟 S7-1500 通讯时不仅要 IP 能通PLC 侧还得开启“允许来自远程对象的 PUT/GET 通信”否则授权画面读不到动态码也写不进授权码。这是很多人现场折腾半天的原因后面故障排查我再展开。3.6 如何设置 Know-how 保护FB 写完后右键 FB_DynLock进入属性在“保护”选项卡里勾选“专有技术保护”也就是 Know-how Protection设置密码。设置完成后下载到 PLC再从 PLC 上载回来验证看到内部代码为空、只剩接口变量就说明保护生效了。密码管理千万注意这个密码不是给客户看的是给你自己公司内部维护用的。密码丢了你连自己的程序都改不了只能整个块删掉重写所以公司内部必须有人专门管授权密码不能只有现场工程师一个人知道。另外不要把加密块的源文件随便放。博途支持导出块源文件导出的源文件如果没加密等同于把算法交出去。我习惯把含加密块的整个库文件放在公司内部版本服务器上现场只放编译后的程序。4. 常见问题与排查技巧实录4.1 授权码总是校验失败这是反馈最多的问题。第一检查动态码生成因子是否一致尤其是日期。PLC 时钟和 PC 端授权工具的时钟不一致比如 PLC 时区设置不对导致“今天”差了一天动态码就不同授权码自然对不上。我遇到过客户把 PLC 时区设成 UTC结果显示时间比北京时间慢 8 小时授权码怎么输都失败折腾了一下午。第二检查字符串拼接顺序。sDeviceSN、日期、计数这些因子的拼接顺序在 PC 工具和 PLC 里必须完全一致有一端多了一个空格或者回车换行CRC 就变了。我建议在两端同时打印出拼接后的十六进制字节流逐字节比对这是最直接的排查方式。第三检查 DWORD 高低字节顺序。HMI 输入授权码时如果按十六进制整数处理又碰到了大小端问题输入值会被翻转。我在实际项目里为了省事把授权码统一按字符串输入处理避开字节序问题。4.2 时间锁无故触发或授权期异常最典型的场景是现场停电时间太长PLC 保持电池耗尽系统时间回到默认值日期锁立刻失效。如果你的授权策略完全依赖自然日期这时候客户一定会打电话来骂人。所以我才坚持用运行时长锁为主、日期锁为辅。第二个场景是 RUNTIME 保持区被清。S7-1200 的保持性存储区有限如果项目里保持变量比较多新增授权块后可能导致保持区分配失败又不报错上电后运行时长归零。排查方法是上电后监控 RUNTIME 输出检查是否为 0若是就要调整保持区设置或改用专门保持 DB。第三个场景是授权剩余天数显示为负数。HMI 上 iRemainDays 用的是 INT如果授权期超过 32767 天直接爆表。这类参数用 DINT 更稳妥HMI 显示时再转换成“X 年 X 月”。4.3 更换 PLC 或存储卡后授权失效这取决于你有没有做硬件绑定。如果授权只绑定设备编号设备编号是写在 HMI 或上位机里手动配置的换 PLC、换存储卡都不影响授权如果绑定的是 CPU 序列号这类硬件信息那就必须重新走授权流程。我的经验是常规 OEM 设备不要做 CPU 序列号绑定维护成本太高客户换一个 CPU 就得管你要一次授权体验很差。真正需要防仿制的高价专机才值得做硬件绑定。即便要做也推荐通过 RDREC 指令读取模块诊断信息来取序列号而不是手动把序列号写进 DB否则照样被人从 DB 里改掉。4.4 上载后程序保护能不能被绕过说实话不能把 Know-how 保护想成绝对安全。S7-1200/1500 上传回来的加密块块内部代码确实不可见接口和调用关系却还看得到。有经验的工程师可以通过观察外部接口的调用时机、强制变量、监控 DB 数值变化反推出锁机条件然后想办法绕过。防这种行为的务实方案是把关键算法彻底打散授权块里只做校验和输出最终状态真正决定设备动不动的判断分散在其它几个普通块里每个普通块再通过读取授权背景 DB 的部分变量做二次判断。这样即使有人监控到一个点也会被另一个点拦住。再配合合同里写明“程序及授权机制的知识产权归供方所有擅自破解视为违约”大多数想动手的人会掂量一下投入产出。4.5 通讯相关HMI 读不到动态码或写不进授权码这个问题在热词里反复出现和加密块直接相关。触摸屏跨网段连 S7-1500 时最常见的坑是 PLC 侧的 PUT/GET 通讯没有打开。在 CPU 属性里找到“防护与安全”把“允许从远程伙伴使用 PUT/GET 通讯访问”勾上HMI 才能正常读写。MCGS 触摸屏跟 S7-1500 通讯还有一个特点它经常走 S7 协议这个协议映射的 DB 区和你博途里优化访问的 DB 可能对不上。所以我给 HMI 通讯用的变量统一放在专门为 HMI 建的非优化访问 DB 里授权块的动态码输出和授权码输入再往这个 DB 中转一层。虽然多一步但通讯稳定性好很多。5. 工程落地建议与后续扩展5.1 把加密块做成标准库而不是一次性代码动态加密块最适合沉淀成公司标准库。我见过一种很好的做法库文件里放 FB_DynLock、FC_CRC16、授权工具 PC 程序、授权记录模板四件套每个新项目复制一份只改盐值和设备号前缀。这样做的收益很明显新项目不用重新开发授权逻辑遇到问题只要维护一套代码。换盐值要谨慎。盐值一变所有已出厂的设备授权全部失效必须重新授权。所以我把盐值按产品线分开每个产品系列一套盐值单独建档管理。不要让现场工程师随便改盐值改之前必须走内部变更流程。5.2 授权管理流程比加密算法更重要加密块只是强制执行机构真正决定授权体系能不能跑顺的是流程。我见过最惨的项目加密逻辑写得很牛但厂商客服系统里没有授权码计算工具客户周末打电话要授权没有人能算出来最后设备停了两天还是技术总监从家里电脑算好发过去。建议至少准备两套授权工具一个 PC 端离线工具给技术支持和售后用能根据动态码、设备号、天数自动生成授权码一个手机端简化工具给值班人员应急用只支持最常用的“按天授权”。授权记录统一登记到项目管理系统至少能追溯到“哪天、哪个设备、授权到什么时候”。5.3 我踩过的几个坑第一个坑是“动态码里用了随机数”。当时想着更“动态”PLC 每次上电生成随机因子结果授权工具根本没法复现因为随机数没有同步到厂商侧授权流程直接卡死。后来改成时间加计数的确定性因子问题才解决。动态不等于随机动态指的是随时间、随次数变化但变化规律必须可复现。第二个坑是“授权状态放在 HMI 可写 DB”。第一版设计时图方便把授权标志放在 HMI 公共数据区结果客户现场一位“高手”半小时就找到了这个位直接在线改成 TRUE整机鎖定形同虚设。后来我把授权状态藏进 FB 背景 DB 深处才消停。第三个坑是“没有给客户留缓冲期”。最早版本授权一到期立刻锁机停输出客户正在生产货还在机器里直接投诉到老板那里。后来加了“到期前 7 天报警、到期后允许紧急运行 30 分钟、进入安全停机流程再锁定”的缓冲逻辑客户体验好了很多也没有影响保护效果。5.4 还能怎么扩展这套动态加密功能块的思路可以延伸出很多玩法。比如把动态码换成二维码HMI 上生成二维码客户用微信小程序一扫直接把动态码发到厂商后台后台自动返回授权码全程不用人工介入。或者在块里增加功能分级字段同一台设备基础版授权只开放部分工艺高级版授权通过另一个授权码解锁剩余功能实现“硬件一台、软件分层卖”。再进一步如果用的是 S7-1500还可以利用 OPC UA 把授权状态和到期时间上抛到上位机管理系统设备厂商在总部就能看到所有已交付设备的授权健康度提前一周提醒客户续费。这套东西做扎实了其实已经不只是一个加密功能块而是设备厂商产品化管理的一部分。说到底动态加密功能块真正的价值不在代码有多神秘而在于它把“设备控制权”这件事收回到开发者手里。我在多个项目里反复验证过保护越简单越不容易出问题流程越规范越不需要跟客户扯皮。最后再分享一个小技巧给块内部加一个软件版本号上电时把版本号输出到 HMI 隐藏页面以后远程排查授权问题时开口先问版本号能少走很多弯路。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。