• native compile 保护的dotNet本地程序还原成dotNet IL程序集


    前面讨论了 .Net 保护中的 native compile 方式 。
    提到了 native compile的两种方式 伪编译 和 ngen 编译。仍然没有像C++那样的完全native的编译。

    这里要讨论的就是 ngen编译 生成的  ni 文件。
    前面讨论时我们提到了fetion框架中的 fetionvm.srm文件。
    注意到它是使用了ngen编译保护模式。
    Remotesoft在评论中予以了证实,今回就以 fetionvm.srm这个文件为例,尝试ni文件的还原。

    首先用reflector直接打开这个 srm文件。
    这个文件里面就只有一个
    [STAThread]
    public static void Main(string[] argv)
    {
    }

    空函数 ,再没有其它内容。很明显这只是一个壳,而且元数据和原始的也肯定不一样。

    但是它配合ni文件能够正常运行,显然有一个地方应该会包含原始的元数据。
    srm文件只有2k大小,应该没有可能隐藏原始的元数据。

    在前面已经确认了原始元数据就隐藏在 ni 文件中。理论上说ni文件应该有个地方记录原始元数据的rva和size,由于对 ni 文件的格式不是完全清楚,所以这里采用暴力搜索的模式确定原始元数据的rva。
    用编辑器(如UE)打开文件
    \C\WINDOWS\assembly\NativeImages_v2.0.50727_32\FetionVM\6e39d95b1cb7d342a0ad2b892350dc65\FetionVM.ni.exe
    搜索 BSJB ,
    我们会发现有两个结果,其中一个就是目前cli header中指定的metadata。推测另外一个就是原始的元数据,在文件偏移的0x3000处。
    根据元数据结构计算出这个原数据的大小是 0x0970。
    把它提取出来查看其结构为:
    namespace FetionVM
    {
        internal static class Program
        {       
            private static string GetTimeString(DateTime dateTime);
            private static void Log(string message);
            [STAThread]
            private static void Main(params string[] args);
        }
    }
    看到这样的结构基本上可以肯定它就是原始的元数据了。

    元数据有了,只能得到程序的类型结构,还是无法获取到方法体的代码。

    前面推测ni 文件也包含了 原始的IL代码,今回就做个测试,
    通过强制放法重新Jit编译,然后在Jit层进行捕获,得到了想要的IL代码。
    强制方法重新Jit最简单的方式是使用profile api。
    这个程序集很简单,经过手动还原,可以得到如下:
    internal static class Program
    {
        // Methods
        private static string GetTimeString(DateTime dateTime)
        {
            return (dateTime.ToLongTimeString() + ":" + dateTime.Millisecond);
        }

        private static void Log(string message)
        {
            try
            {
                File.AppendAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "VMDotNet.log"), "[" + (DateTime.Now.ToString() + "] " + message + "\r\n"));
            }
            catch
            {
            }
        }

        [STAThread]
        private static void Main(params string[] args)
        {
            if (args.Length != 0)
            {
                string path = args[0];
                if (!File.Exists(path))
                {
                    Log("未找到要运行的程序 " + path);
                }
                else
                {
                    string assemblyFile = path;
                    if (path.IndexOf(@"\") > 0)
                    {
                        Environment.CurrentDirectory = Path.GetDirectoryName(path);
                        assemblyFile = Path.GetFileName(path);
                    }
                    AppDomainSetup info = new AppDomainSetup();
                    info.PrivateBinPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "System");
                    AppDomain domain = AppDomain.CreateDomain(Path.GetFileNameWithoutExtension(path), AppDomain.CurrentDomain.Evidence, info);
                    try
                    {
                        string[] destinationArray = new string[args.Length - 1];
                        Array.Copy(args, 1, destinationArray, 0, destinationArray.Length);
                        domain.ExecuteAssembly(assemblyFile, AppDomain.CurrentDomain.Evidence, destinationArray);
                    }
                    catch (Exception exception)
                    {
                        Log("运行程序 " + path + " 出现错误!" + exception.Message);
                    }
                }
            }
        }
    }

    到这里我们可以确定,在ni 文件中包含了原始的元数据和原始的IL代码。所以理论上ni文件是可以被还原的。

    不知道除了Jit层捕获,是否还可以使用net2.0中的反射获取方法体代码?
    有兴趣可以自己试试。

    需要注意的是这些测试需要在那个虚拟框架中进行,另外看Main函数的代码,我们会注意到进程中至少包含两个应用程序域。

  • 相关阅读:
    Linux 笔记 —— SVN和FTP的安装
    在.NET中使用Javascript作为脚本语言(v8最新版)
    回调的经典应用
    Javascript.NET(V8Wrapper) 更新,自定义映射别名
    SQLServer 的视图自带缓存特效?!
    关于 Twing Hot Link 的一些事
    Twing Hot Link For PSP 开发笔记(1)
    新闻发布系统
    Spring MVC入门讲解
    f(f(x))=x, 纯数学理解
  • 原文地址:https://www.cnblogs.com/rick/p/871316.html
Copyright © 2020-2023  润新知