zoukankan      html  css  js  c++  java
  • C# 编译器 和 反编译器,你要哪个(歪头)? 我全都要(捏拳)!

    前言

           从 C# 6.0 开始,C# 编译器就从以前由 C++ 实现的 csc.exe 换成了用 C# 重新实现的开放式 API 式编译服务 Roslyn。这个编译器到现在已经替代了老式编译器,从前 WebForm 的动态编译基础 CSharpCodeProvider 就是对 csc.exe 的调用包装,当然也继承了 csc.exe 的所有毛病。比如这是一个完全的黑盒编译器,把源码传进去返回程序集,中间的符号解析,词法、语法分析,优化和生成这些中间步骤究竟发生了什么我们无从知晓。无法作为脚本引擎把 C# 代码作为脚本执行,曾经有过第三方脚本引擎。当然还有不支持 C# 6.0 或更高版本源代码的编译,巨硬是铁了心要强推 Roslyn 逼死 csc.exe 啊,虽然都 9102 年了,csc.exe 早都透心凉了。

           到 VS 2019,C# 又多了一个功能,导航到反编译的源代码,虽然之前已经有各种第三方反编译工具和 VS 集成扩展,但总归没有自带的来的方便。之前大名鼎鼎的 dotPeek 也在使用中发现无法反编译框架为 .NetStandard 2.1 和 .NetCoreApp 3.1 的程序集,不知道现在怎么样了。刚好在使用中发现了 “由 ICSharpCode.Decompiler 提供反编译服务”字样,抱着好玩的心态去 Nuget 搜索了一下,居然真的找到了!既然这样,那岂不是能:把 Roslyn 编译的程序集交给 ICSharpCode.Decompiler 反编译的源代码交给 Roslyn 编译的程序集交给 ICSharpCode.Decompiler 反编译的源代码交给…… (→_→)

    正文

           这两个包的基本使用其实很简单。在项目的管理 Nuget 程序包中搜索并安装 ICSharpCode.Decompiler 和 Microsoft.CodeAnalysis.CSharp.Scripting。

           1、Microsoft.CodeAnalysis.CSharp.Scripting 基本使用:

              引用命名空间 using Microsoft.CodeAnalysis.CSharp.Scripting;

              开始使用:

    1 //执行表达式,返回强类型结果
    2 int result = await CSharpScript.EvaluateAsync<int>("1 + 2");

              就这么简单,已经能执行一个简单的表达式了,连 C# 语句要分号这种规则都不用 (@^0^@)/

              其他各种花式用法:要引用更多命名空间,包括但不限于

    1 using Microsoft.CodeAnalysis;
    2 using Microsoft.CodeAnalysis.CSharp;
    3 using Microsoft.CodeAnalysis.CSharp.Scripting;
    4 using Microsoft.CodeAnalysis.Scripting;
     1             //处理编译错误
     2             try
     3             {
     4                 Console.WriteLine(await CSharpScript.EvaluateAsync("2+"));
     5             }
     6             catch (CompilationErrorException e)
     7             {
     8                 Console.WriteLine("2+ : " + string.Join(Environment.NewLine, e.Diagnostics));
     9             }
    10 
    11             //添加程序集引用
    12             var result1 = await CSharpScript.EvaluateAsync("System.Net.Dns.GetHostName()",
    13                 ScriptOptions.Default.WithReferences(typeof(System.Net.Dns).Assembly));
    14             Console.WriteLine("System.Net.Dns.GetHostName() : " + result1);
    15 
    16             //导入命名空间 using
    17             var result2 = await CSharpScript.EvaluateAsync("Directory.GetCurrentDirectory()",
    18                 ScriptOptions.Default.WithImports("System.IO"));
    19             Console.WriteLine("Directory.GetCurrentDirectory() : " + result2);
    20 
    21             //导入静态类型 using static
    22             var result3 = await CSharpScript.EvaluateAsync("Sqrt(2)",
    23                 ScriptOptions.Default.WithImports("System.Math"));
    24             Console.WriteLine("Sqrt(2) : " + result3);
    25 
    26             //参数化脚本
    27             var globals = new Globals {X = 1, Y = 2};
    28             Console.WriteLine("X + Y : " + await CSharpScript.EvaluateAsync<int>("X+Y", globals: globals));
    29 
    30             //编译缓存并多次执行脚本
    31             var script = CSharpScript.Create<int>("X*Y", globalsType: typeof(Globals));
    32             script.Compile();
    33             for (int i = 0; i < 10; i++)
    34             {
    35                 Console.WriteLine("No " + (i + 1) + " X*Y : " + (await script.RunAsync(new Globals { X = i, Y = i })).ReturnValue);
    36             }
    37 
    38             //编译脚本为委托
    39             script = CSharpScript.Create<int>("X/Y", globalsType: typeof(Globals));
    40             ScriptRunner<int> runner = script.CreateDelegate();
    41             for (int i = 1; i < 11; i++)
    42             {
    43                 Console.WriteLine("No " + (i + 1) + " X/Y : " + await runner(new Globals { X = new Random().Next(1,i), Y = new Random().Next(1, i) }));
    44             }
    45 
    46             //运行脚本片段并检查已定义的变量
    47             var state = await CSharpScript.RunAsync<int>("int answer = 42;");
    48             foreach (var variable in state.Variables)
    49                 Console.WriteLine($"{variable.Name} = {variable.Value} of type {variable.Type}");
    50 
    51             //连接多个片段为一个脚本
    52             var script1 = CSharpScript.
    53                 Create<int>("int x = 1;").
    54                 ContinueWith("int y = 2;").
    55                 ContinueWith("x + y");
    56             Console.WriteLine("x + y : " + (await script1.RunAsync()).ReturnValue);
    57             //获取编译对象以访问所有Roslyn API
    58             //var compilation = script1.GetCompilation();
    59 
    60             //从之前的状态继续执行脚本
    61             var state1 = await CSharpScript.RunAsync("int x = 1;");
    62             state1 = await state1.ContinueWithAsync("int y = 2;");
    63             state1 = await state1.ContinueWithAsync("x+y");
    64             Console.WriteLine("x + y : " + state1.ReturnValue);
    65 
    66             //读取代码文件并执行编译
    67             var file = @"C:UsersAdministratorsource
    eposConsoleApp2ConsoleApp2/GenericGenerator.cs";
    68             var originalText = File.ReadAllText(file);
    69             var syntaxTree = CSharpSyntaxTree.ParseText(originalText);//获取语法树
    70             var type = CompileType("GenericGenerator", syntaxTree);//执行编译并获取类型
    71 
    72             var transformer = Activator.CreateInstance(type);
    73             var newContent = (string)type.GetMethod("Transform").Invoke(transformer,
    74                 new object[] { "某个泛型类的全文,假装我是泛型类 Walterlv<T> is a sb.", 2 });
    75 
    76             var aa = new GenericGenerator();
    77             var str = aa.Transform("某个泛型类的全文,假装我是泛型类 Walterlv<T> is a sb.", 25);
    View Code
     1         private static Type CompileType(string originalClassName, SyntaxTree syntaxTree)
     2         {
     3             // 指定编译选项。
     4             var assemblyName = $"{originalClassName}.g";
     5             var compilation = CSharpCompilation.Create(assemblyName, new[] { syntaxTree },
     6                     options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
     7                 .AddReferences(
     8                     // 这算是偷懒了吗?我把 .NET Core 运行时用到的那些引用都加入到引用了。
     9                     // 加入引用是必要的,不然连 object 类型都是没有的,肯定编译不通过。
    10                     AppDomain.CurrentDomain.GetAssemblies().Where(x=>!string.IsNullOrEmpty(x.Location)).Select(x => MetadataReference.CreateFromFile(x.Location)));
    11 
    12             // 编译到内存流中。
    13             using (var ms = new MemoryStream())
    14             {
    15                 var result = compilation.Emit(ms);
    16 
    17                 if (result.Success)
    18                 {
    19                     ms.Seek(0, SeekOrigin.Begin);
    20                     var assembly = Assembly.Load(ms.ToArray());
    21                     return assembly.GetTypes().First(x => x.Name == originalClassName);
    22                 }
    23                 throw new Exception(string.Join(Environment.NewLine, result.Diagnostics));
    24             }
    25         }
    View Code

              当然还有这种四不像代码也是可以滴

     1 //不仅可以在脚本引擎中引用命名空间,还可以在脚本中引用,当然前提是在脚本引擎中引用了相关程序集
     2 using System;
     3 
     4 //没有 namespace Xxx { /* code */ } 的定义,直接在外面定义一个类
     5 public class Point
     6 {
     7     public int X {get; set;}
     8     public int Y {get; set;}
     9 
    10     public Point(int x, int y)
    11     {
    12         X = x;
    13         Y = y;
    14     }
    15 
    16     public override string ToString()
    17     {
    18         return $"({X}, {Y})";
    19     }
    20 }
    21 
    22 //变量没有定义在任何类或类的方法中,正常的 C# 代码一定会报错,但骚断腿的 CSharpScript.EvaluateAsync<TResult>(string code) 能正常执行
    23 var random = new Random();
    24 
    25 int a = random.Next(0, 1000);
    26 int b = random.Next(0, 1000);
    27 Func<int, int, int> plus = (l, r) => l + r;
    28 //最后这个表达式没有分号结尾,也没有使用变量接收结果。当然,最后这个带返回值的表达式的结果会在 string result = await CSharpScript.EvaluateAsync<string>(code); 的 result 里收到
    29 $"{a}+{b}={plus(a, b)}
    Point : {new Point(random.Next(0, 1000), random.Next(0, 1000))}"

           2、ICSharpCode.Decompiler 的基本使用

    1 using ICSharpCode.Decompiler;
    2 using ICSharpCode.Decompiler.CSharp;
    3 using ICSharpCode.Decompiler.TypeSystem;
    4 
    5 var decompiler = new CSharpDecompiler(typeof(string).Assembly.Location, new DecompilerSettings());
    6 var name = new FullTypeName(typeof(string).FullName);
    7 var code = decompiler.DecompileTypeAsString(name);

           ICSharpCode.Decompiler 的最新版 6.0.1 预览版1 支持最新框架的程序集反编译,同时支持了大多数 C# 特性,包括部分C# 8.0 刚出的新特性。

           现在的 .Net Core 真是可以玩出各种骚操作,太好玩了。当然,在下面的演示代码中有一个集成在网站项目的演示,下载后运行 IdentityServer 项目并访问 /OnlineCode/CSharp 和 /DeCompiler/CSharp 就可以查看效果。C# 脚本大多数演示代码在 CSharpScriptDemo 项目中。

           效果预览

    脚本运行

    反编译代码

           转载请完整保留以下内容,未经授权删除以下内容进行转载盗用的,保留追究法律责任的权利!

      本文地址:https://www.cnblogs.com/coredx/p/12045104.html

      完整源代码:Github

      里面有各种小东西,这只是其中之一,不嫌弃的话可以Star一下。

  • 相关阅读:
    增加正则项Regularization to Prevent Overfitting
    feature_column、fc.input_layer以及各种类型的column如何转化
    input_fn如何读取数据
    matplotlib.pyplot如何绘制多张子图
    机器学习之杂乱笔记
    Adobe Flash Player
    LSTM/GRU-讲得非常细致
    anaconda python36 tensorflow virtualenv
    畅通工程-HZNU寒假集训
    食物链-HZUN寒假集训
  • 原文地址:https://www.cnblogs.com/coredx/p/12045104.html
Copyright © 2011-2022 走看看