保护 C# 代码可以防止未经授权的访问、复制或盗窃其专有代码,从而保护其时间、精力和资源投入。如果没有适当的保护,C# 代码可以进行逆向工程,让其他人能够理解其逻辑、算法和设计。甚至还有一些工具可以反编译编译的 C# 代码并重建源代码,使任何人都可以重新构建它!
一个好的 C# 保护程序应该支持所有 .NET 版本,并且是跨平台的,以便允许在 Windows、Linux 和 macOS 上构建应用程序。当然,它还应该提供多个级别的保护,以有效地保护 C# 代码。
选择保护程序时,还可以考虑其他有用的功能,例如与 MSBuild 和 Visual Studio 的集成、对 GitHub Workflow 和 Azure DevOps 的支持、与旧版 .NET 框架(包括 .NET 2.0 和 4.0)的兼容性,以及对 .NET Core 的支持。
.NET 应用程序被编译为中间语言 (IL),然后被实时 (JIT) 编译为运行它的平台上的本机代码。由于 IL 比本机代码更容易进行逆向工程,因此攻击者可以更轻松地了解应用程序的工作原理、查找漏洞并修改 IL。
可以将 IL 转换回 C# 代码。此转换通常称为反编译。反编译涉及将 IL 代码(.NET 程序集编译成的一组较低级别、独立于平台的指令)转换回高级语言代码(如 C#)。
混淆技术概述
针对C#代码保护不免需要涉及到混淆技术的话题。
针对C#代码有几个级别的保护:类名、方法等的混淆、控制流混淆和虚拟化。通过混淆名称,我们隐藏了应用程序的逻辑,因为类、方法和字段名称包含有关每段代码负责的内容的信息。此方法不会直接更改方法的代码,因此名称混淆提供最弱级别的应用程序保护。
控制流混淆和虚拟化会以不同的方式修改方法代码:控制流混淆通过将方法的代码转换为执行每个代码“分支”的单个循环来隐藏条件和无条件分支和循环的逻辑。代码的 “分支” 是一系列没有任何转换的指令,这意味着它总是按顺序执行。
虚拟化代表了更高级的保护级别:控制流混淆在代码“分支”上运行,而虚拟化则单独处理每个 MSIL 指令。每条指令都有一个原始逻辑:从堆栈中获取值,对它们执行一些操作,将结果放回堆栈上,或将控制权转移给特定指令。但是,没有实际的堆栈(它是虚构的,方法参数和变量也是如此);JIT 编译器可以生成任何代码来执行处理器,只要结果与指令的逻辑一致。
ArmDot 是适用于 .NET 的跨平台混淆器,具有一组丰富的混淆选项,支持以下选项
C#代码保护实践
创建一个控制台应用程序来验证输入的密码。它会将输入文本的哈希值与正确的文本进行比较,因此有效密码不会存储在代码中。但是,我们也需要隐藏算法,不被窥探,尤其是哈希值。
要创建控制台应用程序,请执行以下步骤:
1、创建名为 'CheckPassword' 的目录。
2、输入目录并从命令行运行以下命令:
dotnet new console --use-program-main
打开Program.cs并添加以下代码,该代码显示输入文本的哈希值:
构建并运行程序。键入 'ArmDot' 以获取其哈希值:
现在有了正确密码的哈希值,让我们通过比较哈希来修改 Main 方法:
构建并运行它以测试它是否按预期工作:
这段代码的问题在于任何人都可以轻松查看它。要确保这一点,通过运行 ILSpy 并加载 CheckPassword.dll(最好删除 CheckPassword.pdb,这样 ILSpy 就无法使用调试信息):
C# 保护程序来救援!让我们看看它是如何工作的。
首先,需要添加两个 NuGet 包:ArmDot.Client 和 ArmDot.Engine.MSBuildTasks:
ArmDot.Client 包含可用于指示保护程序使用哪种保护技术的属性(稍后将提供有关它们的详细信息)。ArmDot.Engine.MSBuildTasks 为 MSBuild 提供了一个任务,执行该任务是为了保护正在编译的程序集(此任务读取这些属性以正确保护代码的各个部分)。
添加 ArmDot.Engine.MSBuildTasks 后,修改项目文件以启用保护任务,如下所示:
重新生成项目;将看到 Protector 完成了它的工作(但实际上并没有保护任何东西,因为没有指定任何 protection 属性)。这就是为什么需要添加保护属性:没有它们,保护者根本不知道该怎么做。但在添加属性之前,再简单了解一下保护技术和相应的属性。
名称混淆是一种保护技术,它更改类、方法等的名称,以使其毫无意义。使用下面的方法进行保护
https://www.armdot.com/docs/obfuscation-attributes.html
控制流混淆是一种通过加密条件和无条件分支来隐藏方法逻辑的技术。使用控制流模糊进行保护
https://www.armdot.com/docs/api/ArmDot.Client.ObfuscateControlFlowAttribute.html
虚拟化是一种保护方法的技术,该方法将其原始代码转换为新虚拟 CPU 的一组指令。使用虚拟化方法
https://www.armdot.com/docs/api/ArmDot.Client.VirtualizeCodeAttribute.html
通过将 ArmDot.Client.VirtualizeCode 应用于整个程序集来虚拟化所有方法。另外,让我们混淆名称,因为 'GetHash()' 似乎太明显了:
重新生成项目并运行以确保其正常工作。
在 ILSpy 中打开程序集。现在代码完全不可读了,也达到了保护代码的效果。
小结
代码保护混淆程度需要结合具体业务需求,也并不是混淆强度越强越好。混淆的强度和程序的性能挂钩的。
进行对C#保护功能后,需要完成对项目和代码功能做下系统性的测试验证,因为混淆后任何依赖于其原始的代码都失效了。
原文始发于微信公众号(编码安全):C#代码保护的杂谈
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论