
先说个真实场景。几年前我给别人定制过一批Excel业务工具开发周期最长的一个花了一个多月里面带数据清洗、自动对账、邮件定时发送逻辑算不上高深但足够繁琐。交付后第二天对方发来一句“这几个函数我抄下来改成我们自己用的了没意见吧”说实话功能摆在那替代方案也多我并没有太心疼。真正让我下定决心做VBA工程加密工具是后来连续看到几个VBA作品被人扒了源码、改了署名再低价出售的案例。VBA开发者大多知道“工程保护密码”这回事但也都听说过它并不安全。这篇文章不聊怎么设置常规密码而是拆解我折腾出来的“破坏式锁定”思路在标准VBA工程密码基础上故意破坏vbaProject.bin里的关键校验信息使VBEVBA编辑器无法加载和展示模块源码甚至在别人绕过密码之后代码依然处于不可查看状态。全文会讲清楚原理、手把手的操作流程、可固化成小工具的自动化方案以及我实测中踩过的那些坑。安全提示本文所有操作只适用于你本人创建、拥有完整处理权限的VBA工程。在他人交付的加密文件上做类似操作属于绕过他人保护不在本文讨论范围内。1. 被高估的VBA工程密码先理解它到底锁住了什么1.1 常规密码保护的工作路径在VBE里点击菜单“工具 - VBAProject属性”切换到“保护”页勾选“查看时锁定工程”再输入密码保存工作簿。之后别人拿到文件按AltF11打开VBE系统会先读取工程属性发现带锁就直接弹密码框。密码不对代码区就是一片空白。这套流程背后藏着VBA工程加密的一个核心特点保护信息不是存在某个独立服务器或密钥管理器里而是跟着文件走。具体来说Office把VBA工程的所有内容——代码模块、工程属性、密码校验信息——都打包在了一个叫vbaProject.bin的文件中。对于xlsm格式它藏在压缩包的xl目录下面对于老式xls格式它则作为OLE复合文档的一部分存在。VBE打开工程时会先解析这个bin文件里的工程目录流读取锁定状态和密码校验数据。校验通过才继续加载各个模块的源代码校验不通过或没有密码就直接在UI层把模块列表藏起来。这就像小区门禁先刷卡验证验不过去就不放你进单元楼。1.2 为什么“有密码”不等于“安全”常规保护不安全的根源在于这个密码校验信息是跟文件待在同一个房间里的。它不是一道需要去远方比对的服务端验证而是本地数据。任何能读文件的工具只要按自己的方式去解析这段数据就可以尝试绕过程序本身的验证机制。这些年市面上流传的各种“VBA密码移除器”“VBA密码恢复工具”本质上都是围绕vbaProject.bin里的密码校验字段做文章。很多工具的使用方式甚至简单到“一键操作”根本不需要理解VBA内部结构。这意味着哪怕对方完全不懂代码也能花两分钟找到工具把你的工程锁解开。所以我的结论一直很明确把安全寄托在一层本地保护的密码上等于把家门钥匙挂在门框上。这不是说密码完全没用它至少能挡住90%的普通用户但在真正想把代码扒走的人面前这层保护只能算礼貌性劝阻。1.3 破坏式锁定的定位与边界破坏式锁定的思路跟常规密码保护完全不同我不去加固那层密码而是把密码校验这个环节直接“焊死”。对合法拥有者而言密码作为“开启凭证”已经没有意义因为焊死之后谁都进不去对试图绕过密码的人而言即便他把密码校验段清理干净VBE也无法正常解析工程目录拿不到可阅读的源码。想改代码的人只能通过原始备份文件走正规发布流程。这带来的文件形态变化是可编辑母版master.xlsm正常密码保护你保留。加密工具执行定位、备份、破坏、重打包的自动化小工具。对外发布版release.xlsm宏能跑但VBE进不去源码不可读。这里要强调一个边界破坏式锁定要解决的是“防止代码被查看”不是“防止宏被禁用”。如果Excel本身禁用了宏或者公司安全策略不允许运行无签名宏那发布版照样跑不起来。加密工具再强也不能替你把宏观安全策略的问题解决掉。2. 破坏式锁定原理把保险柜锁芯焊死而不是换一把锁2.1 两层锁的思路我的加密工具实际上做了两次动作。第一层是标准的“工程密码锁定”这很容易在VBE属性窗口里就能完成。它的作用是拦住那些老老实实按AltF11想去翻代码的人。第二层才是破坏式锁定的核心修改vbaProject.bin里的密码校验字段。操作之后VBE在打开工程时会在校验阶段直接判定“工程不可识别”或“工程损坏”然后停止后续加载。注意它不会弹密码框也不会显示空白代码区而是直接拒绝进入工程解析流程。此时的体验通常有两种一是弹出“无法访问VBA工程”的错误对话框二是工程仍然挂在左侧但双击模块时会报错窗口里什么内容都看不到。第二层的存在让第一层密码被绕过的风险大幅下降。因为绕过密码的工具通常是把校验字段清掉或替换掉而我们现在先把校验字段改成了非法状态那些工具面对的是一个“连格式都不对”的工程自然也就不知道该怎么把代码还原出来。2.2 目标文件里到底改什么vbaProject.bin内部是OLE复合文档结构对不熟悉的人来说可以直接把它当成一个“装了很多二进制碎片的大盒子”。VBA工程的密码校验信息在十六进制里最明显的特征就是文本标记Office 2007到2013时代常见标记是DPB。Office 2016及之后的版本常见标记是DPx。打开旧格式文件时两种标记都可能出现因为文件会按原样保留。这些标记后面跟着的是一段经过特殊处理的校验数据。VBE看到这段数据就能判断出工程有没有密码、密码对不对。破坏操作不需要理解里面每一位的含义只需要让VBE不认这段数据。最简单的做法是把标记字符改掉。比如DPB改成DPA。DPx改成DPy。别看只改一个字母VBE按特征字符串来识别密码字段时会直接认为这不是一个合法的VBA工程密码存储段。后续校验流程自然就断了。如果需要加一道保险还可以处理dir流中的模块记录。模块记录里存着模块名称、模块对应的代码流名称、代码偏移等信息。在bin文件里搜索Module1、ThisWorkbook这样的Unicode字符串把第一个字节改成无效字符VBE在加载模块入口时就会失败。但我不建议默认这么干原因后面在踩坑部分会说。2.3 破坏之后的文件状态VBE打不开宏为什么还能跑这是很多人问我的第一个问题你都把工程校验信息破坏了文件还能正常打开吗宏还能执行吗能。原因要从Excel的运行链路说起。Excel在打开工作簿时由VBA运行时负责装载工程并准备宏执行环境这个过程不依赖VBE的UI层。换句话说宿主业务链路用的是“运行时装载”而VBE里能不能看到代码是“开发环境解析”两条路并不重合。密码校验字段损坏后受影响的是“开发环境解析”这一路。VBE就读不到代码窗口了但VBA运行时仍然按自己的方式从vbaProject.bin里找到模块数据并构建可执行环境所以按钮、快捷键、Workbook_Open事件等该触发还是触发。我用一个不太严谨但好理解的类比工程目录就像是快递柜的柜门编号VBE必须按编号打开每个柜门看里面的包裹而Excel运行时像是从这个快递柜直接走了内部传输带它知道包裹怎么流转不需要经过柜门编号那套系统。你把柜门编号涂掉查包裹的人懵了但传输带照常运转。当然这个结论不是百分百适用于所有破坏方式。如果你坏掉的是模块名或代码流偏移那运行时也可能跟着找不到模块。所以加密工具的默认策略是只动密码校验字段模块名那类破坏做成了可选项默认关闭。3. 一次完整的破坏式锁定实操3.1 第一步准备一个干净的发布母本很多人拿到一个能用的xlsm就直接加密发布结果把测试代码、无用模块、临时按钮全锁进去了。这不算错但会让发布版体积变大行为也可能不稳定。我习惯先做一次清理删除调试期写的测试宏和临时模块。清除代码里的断点和调试输出语句。检查是否有硬编码的本机路径比如C:\Users\你的名字。确认工作簿打开时默认不显示VBE窗口。在VBE属性里设置好“查看时锁定工程”密码用一段长随机字符串并记录到密码管理器。如果你想在锁定后还有机会自己维护代码这一步特别关键。我见过有人把密码设成1234结果别人用字典秒破也见过有人设了超长密码但没备份破坏式锁定之后自己也进不去只能从版本库里捞老代码。3.2 第二步把xlsm拆包拿到vbaProject.binxlsm本质上是一个ZIP压缩包内部有一堆XML文件和二进制部件。最核心的VBA工程文件位于xl/vbaProject.bin。操作如下把要锁定的文件复制一份例如finish.xlsm改名为finish.zip。用7-Zip或WinRAR打开这个zip。进入xl目录把vbaProject.bin单独解压出来。如果你打开zip后找不到vbaProject.bin说明这个文件要么没包含宏要么另存时选了xls或xlsx格式。需要先回到Excel里确认宏存在并另存为“启用宏的工作簿xlsm”。这里有个小习惯解压时不要把所有文件都解压到桌面上一堆散文件夹里。后面重新打包时目录结构一旦错位Excel就会提示“文件格式和扩展名不匹配”到时候排查很费时间。单独解压一个bin出来改最稳妥。3.3 第三步用十六进制编辑器做定向破坏推荐用HxD免费、轻量、打开几十MB的文件也不卡。打开vbaProject.bin后按Ctrl F切换到搜索输入DPB查找类型选“文本字符串”。如果文件比较新可能搜不到DPB就继续搜DPx。搜到之后把标记改成非法值DPB改成DPA。DPx改成DPy。保存文件。如果两种标记都搜不到说明这个bin文件的工程保护结构比较特殊通常出现在老版本Excel 2003文件里。遇到这种情况我建议先放弃手工修改回到Excel里把文件另存为xlsm再重新走流程。注意这一步修改的是密码校验字段目的是让VBE无法识别工程密码存储格式。请再次确认你正在处理的是自己拥有完整处理权限的文件。如果想尝试第二道保险可以搜索模块名的Unicode字节串。比如Module1在十六进制里通常表现为4D 00 6F 00 64 00 75 00 6C 00 65 00 31 00把第一个字节4D改成01保存。修改之后VBE加载这个模块时会直接失败。但这个方法副作用很大我建议先只改密码校验字段跑通全流程再说。3.4 第四步按正确姿势重新打包改完vbaProject.bin接下来把它塞回压缩包。推荐两种方式方式一在7-Zip窗口里直接拖放。把修改好的vbaProject.bin拖回7-Zip窗口里的xl目录确认覆盖然后把finish.zip改回finish.xlsm。方式二全量重新压缩。把整个zip解压到同一个文件夹确认最外层有[Content_Types].xml和_rels目录然后把文件夹里的所有内容全选添加到新压缩包压缩格式选ZIP最后改扩展名为xlsm。第二种方式更干净但要注意千万不要用Windows右键菜单里的“发送到压缩文件夹”直接把解压出来的那个外层文件夹打包这样会多出一层目录Excel会报错。正确的是先进入解压后的目录里全选内容再压缩。最后一步验证打开这个新的xlsm文件确认没有“文件损坏”之类的错误。如果有通常都是zip目录结构错位造成的不是VBA工程本身的问题。3.5 验证清单锁没锁上跑不跑得起来加密工具做出来后我每发布一版都会跑一遍下面的验证清单检查项操作方法预期结果文件能否打开双击发布版xlsm正常打开无格式损坏提示宏能否运行点击核心功能按钮或触发事件功能正常计算结果正确能否进入VBE按AltF11弹出错误提示或工程模块不可读能否修改代码尝试双击模块查看代码代码窗口为空、报错或无法加载文件能否复制复制到另一台电脑再打开表现一致不依赖本机环境我一般还会多做一步把发布的文件发给另外一个没装任何开发工具的朋友让他双击打开并跑一遍核心功能。这一步能同时验证文件传输过程中有没有损坏以及目标电脑上的Office版本能不能兼容。4. 把流程固化成一个可用工具4.1 从手工到半自动Python脚本先行手工流程适合偶尔锁一个文件但如果每个月都要给客户发布新版就必须把步骤固化。我最初做了一个Python脚本核心逻辑很简单用zipfile模块读xlsm拿到xl/vbaProject.bin做字符串替换再写回新的zip文件。示例代码如下import zipfile import shutil import os import time def lock_vba(src, dst): if not os.path.exists(src): print(源文件不存在) return backup fbackup_{time.strftime(%Y%m%d_%H%M%S)}.xlsm shutil.copyfile(src, backup) print(f原始文件已备份到 {backup}) with zipfile.ZipFile(src, r) as zin: names zin.namelist() payload {n: zin.read(n) for n in names} if xl/vbaProject.bin not in payload: print(未找到 xl/vbaProject.bin请先确认文件包含VBA工程) return blob payload[xl/vbaProject.bin] count 0 for old, new in [(bDPB, bDPA), (bDPx, bDPy)]: if old in blob: blob blob.replace(old, new, 1) count 1 payload[xl/vbaProject.bin] blob print(f处理密码校验段 {count} 处) with zipfile.ZipFile(dst, w, zipfile.ZIP_DEFLATED) as zout: for name in names: zout.writestr(name, payload[name]) print(f发布版已生成{dst}) if __name__ __main__: lock_vba(source.xlsm, locked.xlsm)这个脚本没有依赖第三方库Python 3.6以上就能跑。它检查了DPB和DPx两种标记并自动备份原始文件。代码里的备份路径和时间戳逻辑就是我在手工流程里被坑过一次后补上的。需要注意这只是一个演示脚本真实工具还需要处理文件格式校验、zip内压缩算法兼容性、异常回滚、批量处理等细节。用它处理简单场景没问题但别直接拿去给商业产品用。4.2 备份、时间戳和失败回滚我最早做脚本时没有备份直接在原文件上改。有一次同事拿生产文件测试改完发现VBE进不去又忘了密码版本库里只有上周的代码最后硬生生返工了一周。那次之后我把备份逻辑做成了不可跳过的步骤每次处理前自动复制原始文件。备份文件名带时间戳格式统一为master_YYYYMMDD_HHMMSS.xlsm。处理过程中一旦发现bin文件里没有可替换的标记立即终止不生成发布版。生成发布版后对比原始文件和发布版的文件大小如果偏差过大则提示检查。这些逻辑加进去之后就不用再担心“手滑把唯一一份文件锁死”的问题了。4.3 给工具包一层GUI方便同事和客户使用脚本写好后我自己用命令行没问题但交给不写代码的同事就费劲了。所以我又用Tkinter包了一个极简界面一个按钮选择xlsm文件一个输入框指定输出目录一个“开始锁定”按钮再加一个日志区域显示处理过程。然后用PyInstaller把整个脚本打包成单个exe这样同事双击就能用不用装Python环境。打包完的体积大约8MBWindows Defender偶尔会把它当未知文件扫描第一次运行可能多等几秒但只要代码签名不做这个现象无法完全避免。真正打包成exe时最大的坑不是界面而是zip操作的范围。办公环境下有些xlsm文件体积可能到几十MB里面还会塞图片、嵌入对象如果脚本把这些非VBA部件也重新压缩一遍耗时很长。更好的做法是只替换vbaProject.bin这个单独条目其他文件原样复制这样处理速度快很多。5. 实测兼容性与翻车记录5.1 Office版本差异比想象中大我在Office 2013、2016、2019和365上分别测过破坏式锁定表现有差异Office 2013下修改DPB后打开VBE会直接弹出“工程不可查看”之类的错误模块窗口完全不显示。Office 2016及之后部分文件里找到的是DPx修改之后VBE的表现类似但错误文案更偏向“无法加载工程”。Office 365连续更新版本上偶尔会有文件同时包含DPB和DPx两个标记这时候脚本里的count会记录为2。这提醒我不能假设所有用户都基于同一套二进制结构。工具里最好把“找到了几个标记、分别改了什么位置”作为日志输出方便排查问题。5.2 WPS用户特别提醒国内不少客户用的是WPS而WPS的VBA组件来自微软老版本授权对vbaProject.bin的解析逻辑和Office 2016并不完全一致。实测下来我用破坏式锁定处理过的文件在WPS里经常出现两类问题第一类整个工作簿的宏直接被禁用点按钮没有反应。第二类文件打开时提示“VBA工程损坏”连正常数据功能都受影响。如果你的交付对象明确会用WPS我建议放弃破坏式锁定回到“工程密码 只读建议”的保守路线。如果一定要保护就把核心逻辑放到服务端Excel只做数据展示和参数传递。5.3 杀毒软件、网络路径和文件结构带来的坑重新打包后的xlsm由于文件级数字签名变了很多杀毒软件会把它当成新文件重新扫描第一次打开可能多等几秒。这不算致命问题但如果在客户现场演示时遇到“打开卡住”会比较尴尬。更麻烦的是网络路径。有人把xlsm放在共享盘上直接打开再调用加密工具处理结果Office文件句柄没释放工具读到的bin还是不完整的旧版本。我的工具规定处理前必须先确认文件不在Excel中打开状态脚本开头会检查文件是否被占用如果占用则直接报错退出。另一个结构问题是zip压缩算法。Office生成的xlsm内部绝大多数条目用的是Deflate算法zipfile可以正常读写。但有些压缩软件会往zip里写入额外的描述信息导致用Python写回后Office不认。遇到这种情况最稳妥的办法是回到7-Zip手工重压。5.4 修改模块名导致宏罢工的现场复盘我做模块名破坏测试时把Module1的第一个字节改成0xFF保存后打开工作簿Excel提示“无法找到宏FIRSTBUTTON_Click”按钮点击没有任何反应。后来排查发现这个宏是从窗体按钮控件直接绑定到Module1.FirstButton_Click的。宿主加载按钮控件时需要按Module1这个名称去定位代码流。模块名被改坏相当于员工的工牌被撕了前台就不知道该放谁进来。所以我在工具里把模块名破坏这个选项默认关闭。就算真的要加这道保险也只建议针对那些“不参与界面绑定、只由其他模块间接调用”的纯函数模块。但即便如此不同Office版本的解析差异依然很大不建议在生产环境里滥用。6. 什么时候我劝你放弃破坏式锁定6.1 企业内部工具不要自找麻烦企业内部分发的Excel工具迭代频率通常很高一个月可能要改好几版。如果在内部工具上用破坏式锁定每次更新都要走母版、加密、验证、分发这条完整流程反而拖慢效率。内部场景更合理的做法是普通工程密码保护 公司宏安全策略 规范的项目文档。同事之间本来就应该有基本的信任边界加密工具不是用来防同事的。6.2 客户关系比代码值钱有些项目客户虽然只付了开发费但对系统长期维护有依赖。这种情况下主动交付源码反而能建立信任后续维护合同也更容易谈。如果你一上来就“破坏式锁定”客户第一反应往往是“你是不是留了什么后门”。所以我在对外交付时会先问清楚客户需要源码做二次开发还是只要功能可用只要功能可用才考虑上破坏式锁定。6.3 更优的保护是让代码不值得偷我在做了几年VBA之后有一个体会真正核心的算法和业务逻辑最好不要全部放在Excel本地。哪怕你做了破坏式锁定手熟的逆向工程师依然可以通过内存调试、COM调用跟踪等手段还原大部分逻辑。更稳的方案是把核心计算放到服务端Excel只负责发起请求和展示结果。这样就算有人把VBA代码整个扒走拿到的也只是一堆一堆的JSON解析和按钮调用核心价值仍然在服务器上。6.4 我现在的选择做过的三十多个Excel自动化工具里我最终只对三种场景使用破坏式锁定对外出售的单机工具、封装好的标准化模板、以及试用版演示文件。其余内部工具统统只用普通密码保护。原因很简单内部工具生命周期短、迭代勤锁死了自己也麻烦。破坏式锁定是给“发布品”用的不是给自己人用的枷锁。最后分享一句踩坑换来的经验任何加密方案的强度都不如一份妥善备份让你安心。工具做得再花哨备份丢了一切都白搭。