zoukankan      html  css  js  c++  java
  • 怎样让1+1=3?

    如下所示的是一个.NET程序。我们在这段程序中定义了一个作整数加法运算的Add方法,但是我希望将针对这个方法的调用转移到另一个Add2方法上,为此我定义了一个Override方法。

    class Program
    {
        static void Main()
        {
            Override(() => Add(default, default), () => Add2(default, default));
            Console.WriteLine($"Add(1, 1) == {Add(1, 1)}");
            Console.ReadLine();
        }
    
        public static int Add(int x, int y) => x + y;
        public static int Add2(int x, int y) => x + y + 1;
        public static void Override(Expression<Action> originalCall, Expression<Action> targetCall);
    }

    从如下所示的输出可以看出:虽然源程序我们调用的是Add方法,实际上最终的调用被转移到Add2方法上。

    image

    我们知道通过C#编写的.NET程序在编译后会转化成IL Code,在运行时以及时编译的方式转化成机器指令。如果想“篡改”某个方法的实现,要么在JIT之前改变IL代码,要么直接修改最终的机器指令。Override方法采用的是第二种解决方案,如下所示的该方法的实现,基本的思路就是将将原方法的机器指令修改为JUMP(对应x86二进制为0xE9)指令实现向目标方法的跳转。

    public static void Override(Expression<Action> originalCall, Expression<Action> targetCall)
    {
        var originalMethod = ((MethodCallExpression)originalCall.Body).Method;
        var targetMethod = ((MethodCallExpression)targetCall.Body).Method;
    
        RuntimeHelpers.PrepareMethod(originalMethod.MethodHandle);
        RuntimeHelpers.PrepareMethod(targetMethod.MethodHandle);
    
        var sourceAddress = originalMethod.MethodHandle.GetFunctionPointer();
        var targetAddress = (long)targetMethod.MethodHandle.GetFunctionPointer();
    
        int offset = (int)(targetAddress - (long)sourceAddress - 4 - 1); 
    
        byte[] instruction = {
            0xE9, // JUMP
            (byte)(offset & 0xFF),
            (byte)((offset >> 8) & 0xFF),
            (byte)((offset >> 16) & 0xFF),
            (byte)((offset >> 24) & 0xFF)
        };
    
        Marshal.Copy(instruction, 0, sourceAddress, instruction.Length);
    }

    这个方式有时候会很有用,我最近应用的场景是希望篡改.NET Core应用中针对IHostEnvironment的如下三个扩展方法的实现,因为我们的部署环境并没有按照默认的命名约定(Development、Staging和Production)这样导致了这三个方法返回错误的结果。但是IsDevelopment方法的返回结果在.NET Core服务承载系统中很重要,所以不得不篡改它的实现逻辑。

    public static class HostEnvironmentEnvExtensions
    {
        public static bool IsDevelopment(this IHostEnvironment hostEnvironment)
        =>hostEnvironment.IsEnvironment(Environments.Development);
    
        public static bool IsProduction(this IHostEnvironment hostEnvironment)
        =>hostEnvironment.IsEnvironment(Environments.Production);
    
        public static bool IsStaging(this IHostEnvironment hostEnvironment)
        =>hostEnvironment.IsEnvironment(Environments.Staging);
    }
    
    public static class Environments
    {
        public static readonly string Development = "Development";
        public static readonly string Production = "Production";
        public static readonly string Staging = "Staging";
    }

    从某种意义上讲,这也体现了.NET Core Hosting System在设计上的一个问题,希望在以后的版本中能够解决这个问题。

  • 相关阅读:
    hdu 6702 ^&^ 位运算
    hdu 6709 Fishing Master 贪心
    hdu 6704 K-th occurrence 二分 ST表 后缀数组 主席树
    hdu 1423 Greatest Common Increasing Subsequence 最长公共上升子序列 LCIS
    hdu 5909 Tree Cutting FWT
    luogu P1588 丢失的牛 宽搜
    luogu P1003 铺地毯
    luogu P1104 生日
    luogu P1094 纪念品分组
    luogu P1093 奖学金
  • 原文地址:https://www.cnblogs.com/artech/p/11354583.html
Copyright © 2011-2022 走看看