求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
  
 
 
     
   
分享到
.NET核心代码保护策略:隐藏核心程序集
 
作者 Zengg,火龙果软件    发布于 2014-01-06
 

这次写点有实际意义的,说说我是怎么保护我的代码的,个人心得,如有不妥的地方请见谅。

我们都知道.NET的代码容易被反编译出来,站在破解者的角度来看,破解一个软件情况可分为几种:

1.修改注册验证的代码,达到绕过注册的效果;

2.破解出注册核心算法,写出注册机;

3.完全复制代码,做成一个可编译修改的项目;

从危害是上来看,第三种是最严重的,如果你的软件被实力更强的竞争对手复制了,那简直是毁灭性的打击,理论上.NET没有什么软件是不能破解复制的,只要有高手死了心要破你的软件,保护再好也是无意义的,那时只能认命,当然一般高手也不屑于破解一些没意义的东西。我们虽然做不到完全保护代码,但是我们可以增加他们的破解难度,防不了高手,至少我们要防住像我这种普通的程序员,不能随便什么阿猫阿狗都能染指我们的代码,这简直是对程序员智商的侮辱嘛。

一般我们保护软件的几个重要过程:

1.设计机器码;

2.根据机器码生成注册码;

3.验证注册码(本地+远程验证);

4.给程序集加强命名;

5.对程序集进行代码混淆;

当然这么多步骤一次性也说不完,当前我主要就说说怎么隐藏自己的核心程序集,也就是.DLL动态库。我们平时写项目的时候,里面肯定会有各种各样的动态库出现,当我们编译的时候都会有一个*.DLL的动态库出现,DLL里包含了许多元数据,这就很容易被人用反编译软件完全看到里面的代码,所以隐藏程序集就很重要了,思路是:把程序集存入内存中,到该用的时候才会去用它,这样反编译软件就不能轻易的看到我们的代码了,但是一个比较大的项目,把全部程序集存入内存是不现实的,我们只能把几个比较核心的程序集,如注册验证过程,登陆过程,核心算法过程等隐藏,由于你隐藏了一些重要的程序集,一些人就算得到了你的其他代码,想要软件正确运行还是需要很大的工夫的。

花了一点时间写了一个隐藏程序集的的小DEMO,如下图:

这个DEMO的主要功能是:

1.从外部打开某个DLL,并列出该DLL里一共有多少个类;

2.列出某个类里有多少个可执行的公开方法;

3.执行某个方法,会显示执行的返回结果,前提是该方法具有返回值;

4.把从外部打开的DLL转换成C#可用的代码,方便大家存入内存;

5.从内存里读取已经存入内存的程序集,并且执行它的某个类的方法;

下面我们看看项目的VS结构:

我们假设AssemblyTest这个类库是存放我们注册验证的地方,AssemblyWPFDemo就是我们的主程序,一般来说,我们如果要用到AssemblyTest类库的功能时,肯定要由主程序先引用该类库,才能使用这个类库的功能,这里面就存在一个问题,只要引用了类库,在本地就会生成该类库对应的DLL,这个就暴露了我们的代码,下面的过程就是解释,如何在不引用的情况下使用该类库的功能。

AssemblyTest类库情况:

有两个测试类,和一个WPF窗体。

AssemblyTest1

namespace AssemblyTest
{
public class AssemblyTest1
{
public string AssemblyTest1GetString()
{
return "这是Assembly1";
}
}
}

AssemblyTest2

namespace AssemblyTest
{
public class AssemblyTest2
{
public string AssemblyTest2GetString()
{
return "这是Assembly2";
}
}
}

WPF窗体预览

这个窗体就是上面DEMO图里弹出的那个窗口。

下面我们看看主要代码是怎么实现的,在这之前你们可能得去了解C# Assembly的应用,里面我只写一些简单的注释,大家自行去看:

核心代码:

AssemblyControl

// ***********************************************************************
// Assembly : AssemblyWPFDemo
// Author : 曾意恒
// Created : 09-25-2013
//
// Last Modified By : 曾意恒
// Last Modified On : 09-25-2013
// ***********************************************************************
//
// Copyright 2013(c) . All rights reserved.
//
// 程序集操作类
// ***********************************************************************
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
namespace AssemblyWPFDemo
{
///
/// Class AssemblyControl
///
public class AssemblyControl
{
///
/// 把指定程序集转化为二进制流
///
/// 路径程序集
/// 二进制源
public byte[] GetAssemblyToByte(string assemblyFileName)
{
byte[] assemblySource=null;
if (File.Exists(assemblyFileName))
{
FileStream fStream = new FileStream(assemblyFileName, FileMode.Open);
BinaryReader bReader = new BinaryReader(fStream);
assemblySource = bReader.ReadBytes(Convert.ToInt32(fStream.Length));
fStream.Close();
bReader.Close();
}
return assemblySource;
}
///
/// 二进制源转换为C#代码,方便我们新建一个静态类存入数据
///
/// 字段名
/// 二进制源
/// System.String.
public string ByteConverToString(string memberNmae, byte[] assemblySource)
{
string strAssemblySource = "public byte[] " + memberNmae + "={";
foreach (byte b in assemblySource)
{
strAssemblySource += b.ToString() + ",";
}
strAssemblySource = strAssemblySource.Substring(0, strAssemblySource.Length - 1);
strAssemblySource += "};\n";
return strAssemblySource;
}
///
/// 通过二进制数据源转换为程序集
///
/// 二进制源
/// 程序集
/// assembly为空,请检查二进制数据源
public Assembly GetAssemblyBySource(byte[] assemblySource)
{
Assembly assembly = Assembly.Load(assemblySource);
if (assembly == null)
throw new NullReferenceException("assembly为空,请检查二进制数据源");
return assembly;
}
///
/// 获取该程序集里有多少个类
///
/// 程序集
/// 类列表
public Type[] GetTypesByAssembly(Assembly assembly)
{
if (assembly == null)
return null;
Type[] types = assembly.GetTypes();
return types;
}
///
/// 获取类名
///
/// 类
/// 类名
public string GetTypeName(Type type)
{
if (type == null)
return "";
return type.Name;
}
///
/// 获取类里的所有公开方法的信息
///
/// The type.
/// MemberInfo[][].
public MemberInfo[] GetMemberInfosByType(Type type)
{
if (type == null)
return null;
return type.GetMethods();
}
///
/// 获取当前方法的名称
///
/// The menber info.
/// System.String.
public string GetMemberInfoName(MemberInfo menberInfo)
{
return menberInfo.Name;
}
///
/// 执行无返回值的方法
///
/// 方法体
/// 类实体
public void InvokeMember(MemberInfo menberInfo,object obj)
{
obj.GetType().InvokeMember(menberInfo.Name, BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance, null, obj, null);
}
///
/// 执行有返回值的方法
///
/// 方法体
/// 类实体
/// 返回结果
public object InvokeMemberHaveResult(MemberInfo menberInfo, object obj)
{
return obj.GetType().InvokeMember(menberInfo.Name, BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance, null, obj, null);
}
}
}

AssemblyWPFDemo里的AssemblyTestSource类里存放的就是我们AssemblyTest类库的二进制流,这个二进制流是通过我写的这个DEMO转换来的,可看上图,里面有个“转换代码”的按钮,当你打开了一个DLL后就可以把它转换为C#代码,通过加载这个数据流就能获得AssemblyTest的程序集,有了程序集你就能使用程序集里的功能。由于AssemblyTestSource的代码比较多,贴上来会影响排版,大家自行下载DEMO来看。

其他的就是一些简单的逻辑处理了,不一一贴上来,我这里只是起着抛砖引玉的作用,写得比较粗糙,大家要注意的是,程序集的二进制流要存放的隐蔽,要有艺术,不然别人还是能把二进制流取出来存入文件,这就又变成DLL了,有心研究的可以去研究一下把这些二进制数据进行个加密,尽量增加反编译的难度。

这里我又要唠叨一下破解者和软件开发者的关系,我们写软件保护就是和破解者斗智斗勇的过程,这两个是良性的竞争关系,如果你输了只能说明你技不如人,回去回炉一下卷土再来,一味的指责并不能使你的技术提高,你得了解破解人的思路是怎么想的才能设计出更好的保护手段,我相信一个高明的破解者,也一定是一个高明的软件开发者,这两个关系并没有什么分明的界限。。。

 
相关文章

深度解析:清理烂代码
如何编写出拥抱变化的代码
重构-使代码更简洁优美
团队项目开发"编码规范"系列文章
 
相关文档

重构-改善既有代码的设计
软件重构v2
代码整洁之道
高质量编程规范
 
相关课程

基于HTML5客户端、Web端的应用开发
HTML 5+CSS 开发
嵌入式C高质量编程
C++高级编程
 
分享到
 
 


使用decj简化Web前端开发
Web开发框架形成之旅
更有效率的使用Visual Studio
MVP+WCF+三层结构搭建框架
ASP.NET运行机制浅析【图解】
编写更好的C#代码
10个Visual Studio开发调试技巧
更多...   


.NET框架与分布式应用架构设计
.NET & WPF & WCF应用开发
UML&.Net架构设计
COM组件开发
.Net应用开发
InstallShield


日照港 .NET Framework & WCF应用开发
神华信息 .NET单元测试
北京 .Net应用软件系统架构
台达电子 .NET程序设计与开发
赛门铁克 C#与.NET架构设计
广东核电 .Net应用系统架构
更多...