【问题标题】:Protecting Code in an Excel Workbook?保护 Excel 工作簿中的代码?
【发布时间】:2013-05-03 16:31:28
【问题描述】:

我想制作一个包含专有公式和其他知识产权的 Excel 工作簿。我很可能会在 Visual Studio 2010 中使用 C# 制作此工作簿。

这些项目中的代码受保护程度如何? C# 中的反射问题是否仍然适用于这些工作簿项目?如果有,是否有专门针对这些类型的工作簿的混淆工具?

补充一下这个问题 - 好的 'ole VBA 怎么样?如果我们要走 VBA 路线,是否也有办法保护该代码?

【问题讨论】:

  • 您是否在 Excel 中使用标准保护工作簿?密码本身是加密的,但在 Excel 2007 及更高版本中很容易绕过。我不确定 2003 年及之前的情况。只需几分钟,我就可以绕过工作表锁定、工作簿锁定或 VBAproject 密码。 Excel 本身并没有设计得那么安全。我对 C# 不够熟悉,无法回答这部分。

标签: c# visual-studio excel


【解决方案1】:

正如其他答案所暗示的,Excel 2003 (.xls) 中的 VBA 安全性存在缺陷;密码只是有点烦人,通常对于开发人员本人而言。恕我直言,即使 Excel 2007+ (.xlsx) 中的 VBA 安全性要好得多,但使用 VSTO 绝对会更好,更不用说几个世纪以来都没有改进的 VBA IDE(尽管不是真的with the right VBE add-in...)。

一种解决方案可能是在您的 VSTO 项目调用的单独库中编写合理的代码;除了复杂的公式,你可以公开函数,它们会显示为“黑盒子”(即公式功能的抽象);没有源代码的任何人都无法使用它的实现方式),没有 dll 就无法计算工作簿(到处都是#NAME?)。

例如,用户看到的不是=some proprietary formula returning a Smurf,而是=Smurf(SomeTableColumnName, SomeRangeName, SomeRangeReference, SomeCellReference)。当然,这是以性能为代价的,所以我只会对那些真正需要保护的东西这样做。

我认为设法从这样一个侧边库中提取专有公式的用户应该离开它们 - 在这和 Alt+F11 之间,有相当大的一步;专有代码与任何其他 .net 程序集一样受到“保护”。

更新

仍然不使用任何第 3 方库,如果 专有代码 不需要 Excel 互操作性,也许更好的解决方案可能是创建 函数 在 VBA 中(即在 Excel 中保留 Excel interop 部分)并且只传递原始类型(VBA Integer => .net Int16; VBA Long => .net @987654328 @、字符串等)到一个 COM 可见的库,它甚至不需要使用 Excel 互操作;它将是 COM 互操作而不是 Excel 互操作,并且性能影响将显着降低,尽管仍然存在(它仍然是 .net 托管代码与非托管 COM“对话”)。

VBA 代码的职责是读取和写入工作表,COM 可见库的作用是执行由 VBA 代码传递的值进行的计算;当然,用户可以轻松访问 VBA 代码(Alt+F11),但他们也无法访问实际的计算实现。然后VBA代码可以被密码保护,没有DLL的计算结果将是0#VALUE!,这取决于VBA代码是如何实现的。

关键是在 Excel 中保留 Excel-specifics 并让 .net 库在不引用 Excel 互操作程序集的情况下提取 - 例如,VBA 函数将获取单元格引用,抓取它的值(也许验证它)并将其传递给 .net 程序集,该程序集将返回 VBA 将返回给 Excel 的另一种原始类型。

另一个优点是,如果您曾经将 Excel 工作簿“转换”为 Web 应用程序,则可以重复使用带有专有计算的库代码。

【讨论】:

    【解决方案2】:

    供将来参考:这里有 2 种新的创新产品可帮助您解决确切的问题:

    1) https://HiveLink.io: - 使用 HiveLink,您可以创建电子表格的“轻量级”版本,将其提供给您的用户,但它不包含任何敏感的 VBA 或计算公式。您从轻量级电子表格中剔除知识产权,并向您的用户发送下载此电子表格的邀请。当您的用户输入他们的输入数据时,HiveLink 会将数据传送到您的原始电子表格以处理数据,然后自动将结果返回给用户。

    这种方法的好处是不可能使用 .NET 反射甚至汇编代码进行逆向工程 - 因为您完全从他们的计算机中删除了代码。这是最安全的选择,如果您的模型适合输入/输出往返设计,那就太好了——如果您需要即时发生的客户端交互式代码,那就不是很好了。如果您确实需要快速的客户端交互代码,通常这不太敏感,您可以使用工作簿保护,甚至可以使用 DoneEx Excel Compiler 之类的东西编译基本代码,并使用 Excel 编译器和 HiveLink 的组合进行保护。

    2) http://FCell.io:这允许您将 .NET 代码直接构建到电子表格中。它甚至在电子表格中带有一个代码编辑器窗口,有点像用 .NET 代码编辑器替换现有的 VBA 代码编辑器。代码编辑器中的对象模型比普通的对象模型稍微不全面,但您也可以创建任务窗格并将它们嵌入到工作簿中进行分发,这样您的用户甚至不需要安装任何东西!我不确定您的代码嵌入到工作簿中的安全性如何,但我 100% 确信如果您的代码对他们来说足够重要,人们可以访问您的代码。

    关于 Re Mat's Mug 的进一步回复:

    Re: 性能成本 - 实际上,您可能会注意到通过在 .NET 中实现的东西可以显着提高性能,尤其是在您利用多线程时,Excel 根本不会使用 UDF 或宏。我已经看到在 .NET 库中重写我的一些客户代码(用于繁重的处理计算)获得了 100 倍的收益。您可能还会发现 .NET 代码比 VBA 更容易维护和改进。我已经将 VBA 代码和功能转换为 .NET 代码进行了大量转换,并且在正确完成时从未遇到过明显的性能问题。

    Re:用户值得通过反射 .NET 库获得访问权限?? - 我完全不同意这一点。 非常很容易使用反射访问 .NET 代码,甚至可能比 cracking Excel's weak passwords 更容易。如果您确实有想要保护的敏感计算,那么最好使用HiveLink 之类的东西来完全消除逆向工程的可能性,或者使用混淆来使其更难/非常烦人。对于混淆,我使用 CryptoObfuscator($) 和 Dotfuscator($$$)。如果没有这个,我不会在 .NET 库中为 excel 共享我客户的敏感模型 VBA 代码。我有时也使用 CryptoLicensing 让他们通过为每个用户创建许可证来控制谁可以访问他们的 DLL 功能。

    Re:让 VBA 代码负责向/从工作表写入/读取 - 我也强烈反对这一点。我建议在 VBA 中保留尽可能少的代码并对 .NET 进行高级调用,将读/写留给 .NET 插件。在工作表中使用命名范围来识别输入/输出和数据位置,然后将这些命名范围定义为 .NET 库中的常量。在您的库中,您可以非常轻松地读取范围并写入范围。以这种方式维护起来更清洁、更容易。

    在为 Excel 构建任何您想从 VBA 调用外部功能的扩展库时 - 您必须使您的库 COM 可见。有几种方法可以做到这一点:我强烈建议避免使用 regsvr32 在 Windows COM 列表中注册您的库 - 不惜一切代价避免它!

    注册库的最佳方法是使用ExcelDna 加载它,这允许您在每次启动 Excel 时动态加载 DLL,而不必在注册表中使用 COM 接口永久定义每个类(当你更新版本)。您最终会创建一个 XLL 文件,该文件可以将 DLL 打包在其中 - 您可以告诉 Excel 在每次启动时加载 XLL,方法是在注册表中放置一个键。这样做的好处是不需要您将整个 COM 接口存储在注册表中,因此管理新版本非常容易。

    因此,您的 VBA 代码可能类似于:

    Sub Button_Click()
        Call ThisWorkbook.EnsureLibraryInitialized
        Call ThisWorkbook.MyLibrary.ReadRangesAndWriteResults(someInputParam)
    End Sub
    

    请注意,VBA 会在运行时查找此方法 ReadRangesAndWriteResults。如果您在 VBA 中公开编译时方法,那么您必须在注册表中注册其 COM 接口签名 - 值得不惜一切代价避免!!!

    ThisWorkbook 模块:

    Public MyLibrary as Object
    
    public sub Workbook_Open()
        EnsureLibraryInitialized
    End Sub
    
    public Sub EnsureLibraryInitialized()
        Dim addin As COMAddIn
        If MyLibrary is Nothing Then
            For Each addin In Application.COMAddIns
                If InStr(addin.Description, "MyLibrary.COMHelper") Then
                    If addin.object Is Nothing Then
                        'complain it can't connect to library, tell user to reinstall it
                    Else
                        Set MyLibrary = addin.object
                    Exit For
                End If
            Next
        End if
    End Sub
    

    Re:Excel 互操作和 .NET 库 - 好点 Mat's Mug,当使用普通的 Microsoft.Office.Interop.Excel 时,这可能是一场噩梦。因为您正在从托管 .NET 环境访问本机代码,所以您必须确保正确清理您的 .NET COM 包装器对象,否则即使您关闭 Excel 并且看起来它已经消失了,您也会获得 Excel 的挂起/僵尸进程/已关闭(查看任务管理器,它们可能还在!)

    我发现处理此问题的最佳方法是使用 NetOffice,它基本上是 Excel 互操作模型的克隆,但创建它是为了安全地为您管理所有这些问题。最好同时注意 COM 对象的良好安全处理,例如herehere

    祝你好运!

    【讨论】:

    • 使用 VBA 宏的典型 Excel 用户在看到 VBE 时会惊慌失措,即使是简单、高质量且可读/可维护的 VBA 代码,一切看起来都像是被混淆的乱码;我的观点是,典型的 Office 用户对于 .NET 和逆向工程没有任何线索 - 所以是的,在 99% 的情况下,如果他们反编译 .NET DLL 并找到“受保护”函数,让他们拥有它。至于多线程,当然 - 除了 Excel 本身在 STA 线程上运行,因此如果您对工作表进行并发访问,您遇到问题。
    【解决方案3】:

    如果您使用密码保存工作簿以以 XLSB 形式打开(请注意此不保护工作簿结构等),则工作簿将使用相当安全的加密进行加密(在 Excel 2013 中更强)。
    但用户必须提供密码才能打开它,这不适合某些使用场景。

    所有其他形式的 Excel 保护都相对较弱。

    最好使用 C++ XLL 来保护 Excel 中的代码。
    下一个最好的是 VB6 DLL(但现在这是一种死技术,在 64 位 Excel 中不起作用)
    下一个最好的是混淆的 .NET,但你确实需要使用那种 .NET由 Addin Express 或 XL DNA 提供的 XLL 接口,以避免 .NET Interop 和 VSTO 性能不佳的特性。

    【讨论】:

    • 虽然是一个基本正确的答案,但我认为指出第二项在发布本文时并不完全正确 - 根据 MSDN (msdn.microsoft.com/en-US/vstudio/ms788708.aspx): '仅 WOW 仿真环境支持 32 位 VB6 应用程序和组件。 32 位组件还必须托管在 32 位应用程序进程中。所以是的,它是一种正在衰落的技术,但仍然在 64 位机器中用作 32 位进程。
    • 我编辑了我的答案以澄清我的意思是 VB6 DLL 不能在 64 位 Excel 中工作
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多