zoukankan      html  css  js  c++  java
  • 携手Delphi,保护我们的.NET程序

    大家都知道,对于用.NET开发的应用程序而言,是很容易被反编译的。如果我们的应用程序中有一些比较隐秘的东西(如注册算法),我们是很不希望被其它人知道的,所以我们需要保护自己写的.NET程序。

    目前保护.NET应用程序主要还是靠混淆,并且也不乏一些很强大的混淆软件,本文主要是从另外一个方向下手,基于文件的保护,而不是代码层面的。

    话不多说,我们从实际操作开始,边操作我会边解释为什么是基于文件的保护。

    1、首先我们打开Visual Studio,建立一个解决方案 slnNProtect 。
    2、在解决方案中添加一个 Windows窗体应用程序项目 Client,这个Client项目便是我们要保护的.NET应用程序。
    3、为了验证保护后的应用程序是否能够正确的读取配置文件,我们给Client添加一个App.config文件。

    最后项目目录结构如下:

    image

    FrmMain 是我将默认的 Form1 重新命名了一下,该窗体的设计界面如下:

    image

    为了方便读写配置文件(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。
    2、将自动生成的 Form1 删除。
    3、将 Client 项目输出的 Client.exe 用资源的方式添加到 Host 项目中。
    4、添加App.config文件。

    最后项目目录如下:

    image

    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。
    2、将 Client 项目输出的 Client.exe 用资源的方式添加到 NLibraryCom 项目中。
    3、添加一个类文件 ClassMain.cs。

    最后项目目录如下:

    image

    从项目的名称上来看就知道,我们要将这个类库以 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 的命令行定位到该文件的目录下,然后执行下面的命令:
    regasm NLibraryCom.dll tlb:NLibraryCom.tlb


    image
    注意:如果 UAC 打开了,则必须用管理员的身份来运行 VS 的命令行。

    到这里,我们 DotNet 平台上要做的事情就结束了,下面我以 Delphi 2010 为例,来调用我们写的这个 COM 组件。

    1、打开 Delphi 2010,如果 UAC 打开了,请以管理员身份来打开 Delphi 2010(这点很重要)。
    2、新建一个 VCL Forms Application。
    image

    3、删除自动创建出来的 Form,打开 Project 的源码,将begin 和 end 之间的代码清空。
    image
    4、载入DotNet 开发的 Com 组件。
    image
    在弹出的向导中,选择第一个 Import a Type Library,点击Next
    image
    在 Registered Type Libraries 页中,我们选择添加,然后选择我们注册 NLibraryCom.dll 时生成的 NLibraryCom.tlb。
    image
    然后一直 Next 即可。

    经过上面的操作,我们便将一个 DotNet 的 COM 组件添加到了 Delphi 中,最后,我们调用即可:

    Project 中代码相当简单:
    image

    运行程序,一切如期而至,并且 App.config 依然可以正确读取使用,至此,我们的 Client 变成了一个类似 原生的Windows应用程序,但是并不完美。

    思考:

    1、我们的应用程序 Client 目前依然是在一个 DotNet 应用程序中(NLibraryCom.dll),如果反编译该类库,依然可以很轻松的得到它,如何解决这个问题呢?

    2、如此保护,维护应用程序会很困难,除了发布时要包裹,部署时还要注册 COM,如何避免这么多麻烦?

    解释:

    1、我们可以将 Client 放到任何地方(比如 Delphi 程序的资源文件中),只要修改暴露出的 COM 接口,让它接受一个字节数组即可(Run(byte[] rawDatas);)。

    2、部署时的 COM 注册,可以让宿主程序自动完成(本例中的 Delphi 程序),至于包裹程序,要知道,目前大多数混淆软件,也是需要做额外的混淆操作的,为了程序的安全,我们完全可以接受这一点点的小麻烦。并且,我们可以让宿主程序做更多的事情,比如检测有没有 DotNet 的运行环境,或则直接在宿主程序写入注册逻辑,毕竟目前大多数加壳软件针对的都是 原生的Windows应用程序。

    最后,本篇文章就到这里了,只能算是抛砖引玉,若有不对的地方,欢迎指正,大家一起交流。

    源代码下载(访问密码:cca8)

    作者:Sun.M
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
  • 相关阅读:
    Poj2155Matrix二维线段树
    二维树状数组模板
    PAT-1014 Waiting in Line (30 分) 优先队列
    PAT-1012 The Best Rank (25 分) 查询分数对应排名(包括并列)
    PAT-1003 Emergency (25 分) 最短路最大点权+求相同cost最短路的数量
    PAT-1001 A+B Format (20 分) 注意零的特例
    利用requests和BeautifulSoup爬取菜鸟教程的代码与图片并保存为markdown格式
    菜鸟教程上的设计模式代码合集
    用python将项目中的所有代码(或txt)合并在一个文件中
    POJ 2485 Prim 找最长的边
  • 原文地址:https://www.cnblogs.com/prinsun/p/2977260.html
Copyright © 2011-2022 走看看