大家都知道,对于用.NET开发的应用程序而言,是很容易被反编译的。如果我们的应用程序中有一些比较隐秘的东西(如注册算法),我们是很不希望被其它人知道的,所以我们需要保护自己写的.NET程序。 目前保护.NET应用程序主要还是靠混淆,并且也不乏一些很强大的混淆软件,本文主要是从另外一个方向下手,基于文件的保护,而不是代码层面的。 话不多说,我们从实际操作开始,边操作我会边解释为什么是基于文件的保护。 1、首先我们打开Visual Studio,建立一个解决方案 slnNProtect 。 最后项目目录结构如下: FrmMain 是我将默认的 Form1 重新命名了一下,该窗体的设计界面如下: 为了方便读写配置文件(App.config),我们增加对 System.Configuration.dll 的引用,FrmMain 的后台代码如下: using System; using System.Configuration; using System.Windows.Forms; namespace Client { public partial class FrmMain : Form { public FrmMain() { InitializeComponent(); } private void btnHelloWorld_Click(object sender, EventArgs e) { MessageBox.Show("Hello World!!!"); } private void FrmMain_Load(object sender, EventArgs e) { txtFromConfig.Text = ConfigurationManager.AppSettings["KEY"].ToString(); } } } App.config 内容如下: <?xml version="1.0" encoding="utf-8" ?> <configuration> <appSettings> <add key="KEY" value="Hello World!!"/> </appSettings> </configuration> 程序相当的简单,我们只是在窗体加载的时候,将配置文件中的内容显示到文本框里,并且在点击按钮的时候弹出一个经典的 Hello World,除此之外,我们没有做任何事情。 介于这个程序过于简单,我们就不做太多深入的讨论,下面我们来谈谈如何保护它。 如果对 PE 结构了解的朋友应该都知道,将一个 原生的Windows应用程序 通过内存释放运行是相当困难的,很多加壳软件都是针对 PE 内容进行压缩,然后在运行时解压释放,如 UPX。相比于 原生的Windows应用程序 ,DotNet 的释放运行就要简单的多,因为框架已经帮我们实现了。 下面就来给我们的应用程序做一个简单的包裹。 1、在解决方案中添加一个新的 Windows窗体应用程序项目 Host。 最后项目目录如下: Program.cs 文件中,我们写下如下代码: using System; using System.Collections.Generic; using System.Windows.Forms; using System.Reflection; namespace Host { static class Program { /// <summary> /// 应用程序的主入口点。 /// </summary> [STAThread] static void Main() { //以字节的方式加载 Assembly asm = Assembly.Load(Properties.Resources.Client); MethodInfo mi = asm.EntryPoint; mi.Invoke(null, null); } } } 代码还是相当简单,这里最重要的便是: Assembly.Load(Properties.Resources.Client); 通过这句,我们便将一个字节数组转换成了一个 DotNet 程序集,通过反射找到该程序集的入口函数(必须是 DotNet 下的可执行程序),然后调用,便启动了我们包裹的这个应用程序。 通过测试,我们发现配置文件依然是可以有效使用的。 现在我们需要保护的应用程序已经得到了比较简单的保护,这种保护并不安全,我们可以很容易的将 Host 反编译,并提取其中的资源。为了更好的保护我们的应用程序,我们需要采取更加强硬的保护措施。 1、添加一个类库项目到解决方案 NLibraryCom。 最后项目目录如下: 从项目的名称上来看就知道,我们要将这个类库以 COM 的方式对外暴露,所以,我们要让这个项目对 COM 可见,Properties 目录下的 AssemblyInfo.cs 文件中,添加这样一句(或修改为)[assembly: ComVisible(true)] ,即可。 ClassMain.cs 文件中的内容如下: using System; using System.Collections.Generic; using System.Text; using System.Reflection; using System.Runtime.InteropServices; namespace NLibraryCom { [Guid("CBBF9F0A-373D-4827-A1C8-83C16853868E")] public interface INApplication { void Run(); } [Guid("5622CA35-F02D-47cc-A228-AE372C5959F6")] [ClassInterface( ClassInterfaceType.None)] public class NApplication : INApplication { #region INApplication 成员 public void Run() { Assembly asm = Assembly.Load(Properties.Resources.Client); MethodInfo mi = asm.EntryPoint; mi.Invoke(null, null); } #endregion } } 关于 COM 的理论和编程,我这里不作太多赘述,这里大家只要清除,我们对外暴露了一个 INApplication 的接口,可以给其它任何支持 COM 的编程语言来调用,这个接口中只有一个 Run 的方法,而这个 Run 便是调用我们包裹的程序。 我们生成这个 NLibraryCom.dll,然后注册它,通过 VS 的命令行定位到该文件的目录下,然后执行下面的命令:
到这里,我们 DotNet 平台上要做的事情就结束了,下面我以 Delphi 2010 为例,来调用我们写的这个 COM 组件。 1、打开 Delphi 2010,如果 UAC 打开了,请以管理员身份来打开 Delphi 2010(这点很重要)。 3、删除自动创建出来的 Form,打开 Project 的源码,将begin 和 end 之间的代码清空。 经过上面的操作,我们便将一个 DotNet 的 COM 组件添加到了 Delphi 中,最后,我们调用即可: Project 中代码相当简单: 运行程序,一切如期而至,并且 App.config 依然可以正确读取使用,至此,我们的 Client 变成了一个类似 原生的Windows应用程序,但是并不完美。 思考: 1、我们的应用程序 Client 目前依然是在一个 DotNet 应用程序中(NLibraryCom.dll),如果反编译该类库,依然可以很轻松的得到它,如何解决这个问题呢? 2、如此保护,维护应用程序会很困难,除了发布时要包裹,部署时还要注册 COM,如何避免这么多麻烦? 解释: 1、我们可以将 Client 放到任何地方(比如 Delphi 程序的资源文件中),只要修改暴露出的 COM 接口,让它接受一个字节数组即可(Run(byte[] rawDatas);)。 2、部署时的 COM 注册,可以让宿主程序自动完成(本例中的 Delphi 程序),至于包裹程序,要知道,目前大多数混淆软件,也是需要做额外的混淆操作的,为了程序的安全,我们完全可以接受这一点点的小麻烦。并且,我们可以让宿主程序做更多的事情,比如检测有没有 DotNet 的运行环境,或则直接在宿主程序写入注册逻辑,毕竟目前大多数加壳软件针对的都是 原生的Windows应用程序。 最后,本篇文章就到这里了,只能算是抛砖引玉,若有不对的地方,欢迎指正,大家一起交流。
源代码下载(请点击) 作者:MKiller
出处:http://www.cnblogs.com/prinsun/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
分类: Delphi, C#
|
|
来自: 昵称10504424 > 《C#》