怎樣讓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在設計上的一個問題,希望在以后的版本中能夠解決這個問題。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM