列举工作以来遇到的各种类型的软件所采用的代码保护技术,只讲原理不涉及技术细节实现,以避免产生法律问题。有些朋友说直接把代码放在Github开源下载,开源可以促进技术交流与进步,然而值钱的代码都积压在硬盘里面,即使很烂的代码都卖了很多钱,赢得了许多客户与市场。珍惜爱护自己写的代码,他们都是宝贵的财富。

以下保护技术主要测重于脱机验证与保护,不涉及联网(连接到许可证服务器)验证。

1 程序集混淆 Asembly obfuscate

CLR代码的运行是即时编译执行的,.NET编译器只是将源代码文件编译成中间语言,中间语言包含丰富的元数据。使用.NET Reflector/ILSpy/JustCode这类的反编译器几乎可以还原整个源代码,所以用.NET开发软件第一个要做的任务是混淆编译后的中间语言代码。这也是我见得最多的.NET代码保护技术。

2 写注册表 Registry

虽然.NET时代强调XCOPY部属,但很多很程序依然依赖于注册表来做保护技术的基础。可以把自己想要隐藏的信息放到很深的注册表键中。

一些程序会将注册表键做一个字符串处理,比如将字符串改成16进制值,或是做一个DES混淆处理,在不知道解密键(KEY)的情况下很难看出它的意思。

3  RSA 签名  RSA Signature

RSA签名技术可以生成二个密匙,私匙用于生成许可文件,公匙用于验证许可。

string publicKey = "<BitStrength>1024</BitStrength><RSAKeyValue><Modulus>wDSWDT4ujpa+B7VkwdXGDvIEDc18O6qSnGtL38prVR18B7sjqbhR2Uq1C7hksADnF

DH+PAHv9BAWiqa1cWeaVgCwC2LNMClaJu4jZg0aFDIzhZUNHV56KS5aaVD+MHH+iEGmPok9XRz7Jd8hyjD38jHl439Uaf6oltxMRdKS5KU=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";

string privateKey = "<BitStrength>1024</BitStrength><RSAKeyValue><Modulus>wDSWDT4ujpa+B7VkwdXGDvIEDc18O6qSnGtL38prVR18B7sjqbhR2Uq1C7hksADnFDH+PAHv9B

AWiqa1cWeaVgCwC2LNMClaJu4jZg0aFDIzhZUNHV56KS5aaVD+MHH+iEGmPok9XRz7Jd8hyjD38jHl439Uaf6oltxMRdKS5KU=</Modulus><Exponent>AQAB</Exponent><P>82nuszkXKLx

+NDtQLiruBlDGozAUaJ50PqHKq7jglLV740jz521+v2O6EVA+59gpGSwFrX6E7HnUrkKBQGbY/w==</P><Q>yiTO+1+xaNvmEcAyKdFboNRbP1/toUnakkREKyYCg/KLnjgpkb22gMtu

u5ocbfY2vIdyDZbYYeXmLYSJ9Io+Ww==</Q><DP>ds7Zx3iyKRv3rZ7Vv/MMQuDiU3yAOaA9tORbe/29AFiko4dUJT14hAo1I4Y7bgY/6R1nmAsM7i9486VaWQjaGQ==</DP><DQ>sRU

1zbiy7i1Vi09XopKpNmdR2F7tCVJti50KKtKNeZHNBbolkGslqgaM5wPGy/3ZTadKHuV6gaio0E8/m15P6Q==</DQ><InverseQ>YsZNcptrcalrlR5TS+rJ+m9G8+xUIJGGLSB3Czjw

q28rFgXa//avpx05E3FIN0tfAOKJhy84VKElqPNyQyacNA==</InverseQ><D>jaNy3Clxl6QgP3/90xWc0ZCpSh6eKT4GsnwjOrRpKh0DNJNEsaJhtpXmGs/0avsPToOUzVXEJP/iDKTTWtG

1GdkqZp+gaqKbp73RP21Nmd+lZ/S1WvmUTe3Ge5I5FP/7zf9KnTXBm1yLel/9N5UL6o5EncPNjqUt2+oKQ+q7vJU=</D></RSAKeyValue>";

  
因为私匙只存在于自己的电脑中,不会发布到程序中,只要私匙不泄露出去,这种加密方法是相当安全的。

破解RSA签名的唯一方法是替换公匙,用自己生成的私匙生成许可文件。

4 参数保护 Parameter Protection

基于第3条RSA签名技术,签名之后产生的字符串是只读的,在它的基础上我们可以增加许多控制点,比如可使用的用户数,过期时间,最大账套数等。

在我的Enterprise Solution开发框架中,提供基于第3条和第4条为原理的许可保护技术的例子,在一定程度上保护产品不被未授权用户使用。

5 机器硬件识别 Hardware Recognition

为了控制软件不会被客户随意拷贝分发,一方面通过法律合同条文明确禁止,但通常这没有什么卵用,另一方面则必须做硬件识别。在生成许可文件之前,要依赖于客户的机器配置,将客户的机器硬件信息完整的保存到许可文件中,运行时逐条检测,发现有不一致的地方立刻退出应用程序。

.NET 提供了WMI接口用于读取机器相关的信息,参数如下的代码例子:

  private static string GetDiskDriveSize()
        {
            return WmiHelper.GetWmiPropertyValue("Win32_DiskDrive", "Size");
        }

        private static string GetDiskDriveTotalSectors()
        {
            return WmiHelper.GetWmiPropertyValue("Win32_DiskDrive", "TotalSectors");
        }

        private static string GetDiskDriveTotalTracks()
        {
            return WmiHelper.GetWmiPropertyValue("Win32_BaseBoard", "Manufacturer");
        }

三个方法分别读取硬盘大小,硬盘分区,主板制造商,将这些信息组合起来放到一个字符串文件中,即是电脑的硬件信息,再对这个字符串做一个DES对称加密或是RSA签名处理,处理之后,客户的硬件信息看起来是这样的字符串。

lxocnnn7ofk2+Nheb7Qo+cBRDlCbVWHCfVSFG/XnyT3F7q/avyRL3Lj7gotPMtuo41A33AjSryt5a1ydvu7tTOnsOsizy8HJIrP13T5jHbgyHZgZaQk54eGLN3eft5zijKT3hyirZ5w2Mf6mWmv/9F

68jFxsnNh//OUUAVI3lx0=KMw2gRnZ8BqL4lNw4A5j+tuDlvbsj5gp3CGZNbAXlFlGW188uliAZexb6GhcvB0VSwTV37R6NUstT/VT5MJ7wyf/zyyHpa1uHGx4/UziLCAwawnap90BkQWlH1aVz

2lTX3I93iKKYOdAJF0y5TM9njlVvqToj2cBBWLUG+sYXxo=DUjIpSbrp5cl8L//WbIs7ErSX8RBTeVMFO7OCIYoTh7yogVzJNH3D3QZHYKnNfGmJDMSleJSgdLb0GdiWdEgiLTf0uv7lQsoA076

WM5VrtG7O/h0E3DvmisP7OkkuJIMEIpExaM+3GDlIHGvHYRK0nDfzBQM8AXM2k0ue8wj0xY=k5CRv+IkEdB6yZEy65iLXsU0lQT1FbHwSH7LawWBoOZHJt1TwIAajHuqH0AHAWZgRHYDbHCxnlJFv9t

OepsJdNGL6CvjUEnyuNA3RlhKNB6K51rOjIQ1ROKRiy9z+YsbHbJxdbsj3km/kITzfHvoDqQIg4zrTR0XfSn49j0rMkI=C4oBw14/k1OWPzMC1SUe+YxrJtyUqzL2k/cKz/feAhZ0DELbUiCUcbjhHg

+ryg+E7P5Bfy53n3kH5SBud4rVBpRUZGx4xZFPB8eXQ40SUCMxs74cbQRUPh7+zQo6qF/AtrQkXKbzoiZKHCz50n6h/afaa+jwuOplYUx191269w4=RGDYBw6j5VXJO7phILX1zLRcbO3+6SrBuCLEFwk

sitOk/z+OGtFEoIfHD5DZRp9t6GVYtpdI7jPVz7lTV4DL5xfCQ8woJjpqUdB++pYaIw3gx9EL6PD+ucRfLKtiNng6ujKe0r6A51y9eGJRic9p8uAPGSEj8OtNLmO0mVWcwhE=GJg9mdr/YdDMZCVe0WPb3

S8YGKfzdPYj3xJTQtRYdFLV9E6GHzQj8FFgIrxjdoD3n9FGthhF82MTpjS3kOdIyQEc7moienSbzqevcUPs4ZmDwmveH/F4DSynS7JAiPqCcnAj7N4EI/PkXZWAtdHSra2yWBkEBw5PlVhLTLbcLGE=FL

+JSw0yGUIpIZUgq8VRt27e5J+D0vRoEw9t5NKPROysnGw=YN1yhy2reEhQPs30aCnOpNDz2

He2E9re26QBwipNgivHM8Z/FcLk0hjALeiC8zY4Dcn+0PKag6DH7tmIBwTpBus2+ZqjLlgIx7uKMPB49+yPC5qOV8vsJGV/s81xVEA+prgNbh9Sa3TSsBV3qMSyZDEyvkHc+1QbBoLd/1h2WUI=p0/G

lIKMQ7XoRom9njDq5XPn3sj4V8P137v6eDDq7uTpRcTXD/qOWi/IZMcNX56LqVb1PJko0BCUYXa8h0zzEvsmTJNXUzggZlpkzk4EOzq4GekTwM6hXcn0Es/6iZ2J5Et1h5kSOOk1MLZS3cmUHeSRn0js

Ed0O/+L8+qBNpYapf6GL2iYf5j/Qfi+2djOwqCspHawZcBZ/e/WtK0b/0F4=lmHD5BVilXA7KIQk8yma7d4dP5mawdNhMEuYG68sN15SJOUMZlGiFT17rh0Y7Z

54itnjVmhaRVXBsc/LDKYPIwsD+TZBTGDbSOebmxvDe+Jdh6ZLsmGUIdREH4/L19y1713PyJ2r6HYi9v/8GobkWWg4chIKggBhSYUJFP8jj4Q=JHNsEDYacXEIwWhR

nDfiQoWZbrZjmCgF7lo9g4BCTYD0KbLLRKdrDERNPTQs+e3kLf0/fhc0x0BsE/yCuYcnj8rh9/iA08ZUxO+TZkz/p5rseWklWboc45o90q/ZXfuu5f+TCVPnfTXCxMjwdH3

F4I7EotPzweUS/P++YDvHHWA=d8l5s9fNa0fUX4eo9LoHVT4Wb8YbHKDvDW7o1P+wTDykIrOCLoCKklrSGdHjOi8EWw=LDrOGw3pq7rv4L7YoGLaZAmXfRdcw1wp2Zi/Hn2

这样既避免了客户隐私信息泄露,也可以解决当前软件氛围环境下软件分发的困扰。

6  序列号/注册码 Serial Key

相当多的软件还是采取这一传统的序列号保护方法,比如Windows Server 2012的注册码:

NB4WH-BBBYV-3MPPC-9RCMV-46XCB

一组看似无序的数字与字母的组合,对软件保护起了很重要的作用。如果是在软件安装时验证序列号,则破解难度会更大。

序列上面的字母数字组合,可以包含很多信息,比如客户名称,过期时间,最大用户数。将这样的序列号分享给同行的其它客户,一旦软件公司追查起来很容易就能识别这个序列号是否合法。

在我的博客下载工具中用到的一个组件aspNetMHT,它就是采用这个方法来分发序列号的,序列号中包含过期时间。aspNetMHT的序列号看起来是这样的:

SM48Z-FMXGZ-25P67-4ZJKF-GS211-AQYA4-7VHUX-KCF1C-RD4RC-RU7XS-XK8JY-6JT54-M9CX

7  强名称 Strong Name

强名称不是一种代码保护技术,而是程序集标识技术。每个经过strong name签名后的程序集都是唯一的,在运行时可以验证这个文件的签名是否被改动过。

验证强名称的关键地方在于调用CLR中的基础方法:

[DllImport("mscoree.dll", CharSet = CharSet.Unicode)]
public static extern bool StrongNameSignatureVerificationEx(string wszFilePath, bool fForceVerification, ref bool pfWasVerified);

所以开发.NET程序,不仅仅要给程序签名,还需要在运行时验证所给的签名是否被改动过。

.NET CLR支持对程序集跳过强名称验证,不过这样做的风险太大,直接修改了CLR的行为,未见有很多实例。

8 内存保护 Memory  Protection

Delphi XE采取了这种方法,运行时会生成一段验证代码,验证结束后代码销毁,因为验证在内存中进行,不会在硬盘上留下痕迹,破解的难度相对困难了很多。

Win32支持动态加载和卸载程序集,所以Win32 API中有接口LoadLibrary和UnloadLibrary供调用,可实现内存保护。

.NET也支持动态加载程序集,但不支持卸载(unload)程序集,程序集的卸载由CLR控制,这一点给破解内存保护技术提供了方便。

在我的Enterprise Solution开发框架中,将第7条和第8条组合一下,简单的解释如下:创建一个类型定义文件,然后将这个文件用强名称签名,之后将这个签名的程序集经过混淆,字符串加密等处理,附加到启动程序的资源文件中去,运行时,我从资源文件中将此程序集解析释放出来,同时强制验证程序集的强名称,运行类型定义文件中指定的验证方法,实现简单的内存保护技术。

破解这种保护需要知道签名程序集的强名称,字符串加密处理的全流程和相应的键,给破解增加了不少难度。要验证什么和怎么验证都是在我设计的类型定义文件中指定的,启动程序只是一个壳,运用反射调用要验证方法。而且这个经过签名后的程序集是附加到主程序中去的,要替换这个资源文件,也需要一些方法和技巧。

9  源代码混淆 Source code obfuscate

经过编译混淆后的.NET程序集,在运行时依旧可以被调试器附加调试。如果我能将源代码中的敏感字符串替换成乱七八糟的字符串,减少明文的出现,虽然降低了性能,但这样可以增加安全性。比如,我运行时检测找不到许可文件时,抛出以下异常:

string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory,"license.lic");
if (!File.Exists(path))
  throw new LicenseException("License file not found");

经过源代码敏感字符串替换后的程序如下:

string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory,"license.lic");
if (!File.Exists(path))
  throw new LicenseException(B("/jNloqd4U59LBT64eX1hb9eO8kYDD7tD"));
经过一个B方法的调用,无法看到异常的明文,增加了破解难度。如果代码中这样的B方法有很多,而且B方法的字符串处理方式各不相同,无疑提高了系统的安全性。

现在市场上有很多JavaScript源代码混淆工具,就是把JavaScript代码的格式打乱,让它不容易读取。

C#源代码也可以采用此方法,不过,读强大的Visual Studio有文档格式化功能(Edit-Advanced- Format Document ,Ctrl + E,D) ,瞬间变为可读的格式。

10  本机代码 Native Code

即将发布的Visual Studio 2015提供.NET Native功能,可将.NET代码编译为本机代码。然而经过近一年的等待,.NET Native目前也是仅仅限定于Windows应用程序商店项目,真正的Windows Forms/WPF/ASP.NET要实现编译为本机代码可能不现实。

这种保护方法的核心是许可验证逻辑放在本机代码中,直接编译成机器码。一般采用Visual C++/Delphi/VB6之类的工具,将重要的算法与验证编译成机器码,这样会增加破解难度。本机代码难于处理的一个方面是.NET Any CPU的问题。当我们设定.NET编译为Any CPU时,CLLRU会依据程序运行的平台(x86,x64)编译为本机代码。

在x64的系统中加载32位的本机代码,常常会抛出无效的程序集异常。破解这种保护的方法可直接修改.NET程序中的调用方法,可以忽略本机代码的逻辑。

相对于前面的几种技术,这种保护技术破解难度更大,要懂的知识点也非常多。

大型.NET商业软件代码保护技术 技术与实践相结合保护辛苦创造的劳动成果的更多相关文章

  1. 转:Android 2.3 代码混淆proguard技术介绍

    ProGuard简介 ProGuard是一个SourceForge上非常知名的开源项目.官网网址是:http://proguard.sourceforge.net/. Java的字节码一般是非常容易反 ...

  2. 大型B2C网站高性能可伸缩架构技术探秘

    大型B2C网站高性能可伸缩架构技术探秘 2010-07-21 08:51 狂放不羁 JavaEye 字号:T | T 向您介绍大型B2C网站高性能的网站架构技术,包括缓存的使用.应用程序和数据库的拆分 ...

  3. 可能对Flutter应用程序开发有用的代码/库/专有技术列表

    当我开始使用Flutter实施该应用程序时,我开始担心“如何最好地编写?”以及“如何使其更好地放置?”. 在这种情况下,您将需要参考GitHub上发布的代码和应用程​​序. 因此,我收集了似乎对Flu ...

  4. 如何快速读懂大型C++程序代码

    要搞清楚别人的代码,首先,你要了解代码涉及的领域知识,这是最重要的,不懂领域知识,只看代码本身,不可能搞的明白.其次,你得找各种文档:需求文档(要做什么),设计文档(怎么做的),先搞清楚你即将要阅读是 ...

  5. Atitit.ide技术原理与实践attilax总结

    Atitit.ide技术原理与实践attilax总结 1.1. 语法着色1 1.2. 智能提示1 1.3. 类成员outline..func list1 1.4. 类型推导(type inferenc ...

  6. 腾讯技术分享:微信小程序音视频与WebRTC互通的技术思路和实践

    1.概述 本文来自腾讯视频云终端技术总监rexchang(常青)技术分享,内容分别介绍了微信小程序视音视频和WebRTC的技术特征.差异等,并针对两者的技术差异分享和总结了微信小程序视音视频和WebR ...

  7. XPages访问关系型数据库技术与最佳实践

    XPage 对于 Domino 开发人员的一大好处就是能够很方便和高效的访问关系型数据库.本文通过实例代码展现了在 XPage 中访问关系型数据库的具体步骤 , 同时讲解了一些在 XPage 中高效访 ...

  8. 转 Redis集群技术及Codis实践

    转  Redis集群技术及Codis实践 转自 :http://blog.51cto.com/navyaijm/1637688 codis开源地址:https://github.com/CodisLa ...

  9. 《SaltStack技术入门与实践》——执行结果处理

    执行结果处理 本章节参考<SaltStack技术入门与实践>,感谢该书作者: 刘继伟.沈灿.赵舜东 Return组件可以理解为SaltStack系统对执行Minion返回后的数据进行存储或 ...

随机推荐

  1. Object-C内存管理的理解总结

    今天看到了OC的内存管理这块,觉得很亲切. 自己的习惯是尽量自己掌控程序的空间和时间,有点强迫症的感觉.用C和C++做项目的时候,时时刻刻都在操心这new和delete的配对使用和计数,学习stl和b ...

  2. react-native-http请求后navigator导航跳转

    琢磨react-native有一段时间了.对于我来说,它的确是前端开发工作者的福音,因为我可以利用它来写app的代码,而且基本可以一套代码,多个平台使用. 早就想写一篇随笔记录一下react nati ...

  3. springMVC静态文件访问

        web.xml文件  <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xs ...

  4. Deploying JRE (Native Plug-in) for Windows Clients in Oracle E-Business Suite Release 12 (文档 ID 393931.1)

    In This Document Section 1: Overview Section 2: Pre-Upgrade Steps Section 3: Upgrade and Configurati ...

  5. Mac OS X 常用快捷键

       

  6. diff详解,读懂diff结果

    1.概述 本文将要讨论的是diff命令,diff用来比较两个文件.当然文件比较的工具很多,windows系统下面就有不错的工具可以使用,例如常用的Beyond Compare,WinMerge都是图形 ...

  7. [Xamarin] 動態載入Fragment (转帖)

    這篇我們來動態加入,一樣務求好懂簡單 1.一樣先將專案調整成3.0以上版本 2.首先建立自定Control的Layout \Resources\Layout\MyControlLayout1.axml ...

  8. C#中使用OpenSSL的公钥加密/私钥解密

    在C#中进行公钥加密/私钥解密,需要用RSACryptoServiceProvider,但是它不支持由OpenSSL生成的公钥/私钥字符串. 比如这样的公钥/私钥对( 公私钥生成方法见 http:// ...

  9. Sql Server tempdb原理-缓存机制解析实践

    Tempdb就像Sqlserver的临时仓库,各式各样的对象,数据在里面进行频繁计算,操作.大量的操作使得tempdb可能面临很大压力,tempdb中缓存的设计就是为了缓解这些压力.这次就为大家介绍下 ...

  10. 接口分离原则(Interface Segregation Principle)

    接口分离原则(Interface Segregation Principle)用于处理胖接口(fat interface)所带来的问题.如果类的接口定义暴露了过多的行为,则说明这个类的接口定义内聚程度 ...