Home

Awesome

<img src="logos/jitex.svg" width="100" align="left"/> Jitex Jitex Build Nuget

A library to modify MSIL/Native code at runtime.

It's a library built in .NET Standard 2.0, works on all version >=.NET Core 2.0.

.NET Core (2.1 ~ 3.1).NET 5~8.NET FrameworkMono
Windows (x86/x64/ARM64):heavy_check_mark::heavy_check_mark::x: Not supported:x: Not supported
Linux (x86/x64/ARM64):heavy_check_mark::heavy_check_mark::x: Not supported:x: Not supported
MacOS (x86/x64/ARM64):heavy_check_mark::heavy_check_mark::x: Not supported​:x: Not supported

Jitex can help you replace code at runtime easily.

using System;
using Jitex;

JitexManager.MethodResolver += context =>
{
    if (context.Method.Name.Contains("Sum"))
        context.ResolveMethod(Mul); //Replace Sum by Mul
};

int result = Sum(5, 5); 
Console.WriteLine(result); //Output is 25

static int Sum(int n1, int n2) => n1 + n2;
static int Mul(int n1, int n2) => n1 * n2;

Support

Intercept call

using System;
using Jitex;

JitexManager.MethodResolver += context =>
{
    if (context.Method.Name.Contains("Sum"))
        context.InterceptCall();
};

//Every call from Sum, will be pass here.
JitexManager.Interceptor += async context =>
{
    //Get parameters passed in call
    int n1 = context.GetParameterValue<int>(0);
    int n2 = context.GetParameterValue<int>(1);

    n1 *= 10;
    n2 *= 10;

    //Override parameters value
    context.SetParameterValue(0, n1);
    context.SetParameterValue(1, n2);

    //Or we can just set return value
    context.SetReturnValue(100);
};

int result = Sum(5, 5); //Output is 100
Console.WriteLine(result);

int Sum(int n1, int n2) => n1 * n2;

Replace Method

/// <summary>
///     Take sum of 2 random numbers
/// </summary>
/// <returns></returns>
public static int SumReplace () 
{
  const string url = "https://www.random.org/integers/?num=2&min=1&max=999&col=2&base=10&format=plain&rnd=new";
  using HttpClient client = new HttpClient ();
  using HttpResponseMessage response = client.GetAsync (url).Result;
  string content = response.Content.ReadAsStringAsync ().Result;
    
  string[] columns = content.Split ("\t");
    
  int num1 = int.Parse (columns[0]);
  int num2 = int.Parse (columns[1]);
    
  return num1 + num2;
}

private static void MethodResolver (MethodContext context) 
{
  if (context.Method.Name == "Sum") {
    //Replace Sum to our SumReplace
    MethodInfo replaceSumMethod = typeof (Program).GetMethod (nameof (SumReplace));
    context.ResolveMethod (replaceSumMethod);
  }
}

Detour Method

private static void MethodResolver (MethodContext context) {
  if (context.Method.Name == "Sum") {
    //Detour by MethodInfo
    MethodInfo detourMethod = typeof (Program).GetMethod (nameof (SumDetour));
    context.ResolveDetour (detourMethod);
    //or
    context.ResolveDetour<Action> (SumDetour);

    //Detour by Action or Func
    Action<int, int> detourAction = (n1, n2) => {
      Console.WriteLine ("Detoured");
      Console.WriteLine (n1 + n2);
    };
    context.ResolveDetour (detourAction);

    //Detour by Address
    IntPtr addressMethod = default; //Address of method to execute.
    context.ResolveDetour (addressMethod);
  }
}

Replace MSIL

private static void MethodResolver (MethodContext context) 
{
  if (context.Method.Name == "Sum") {
    //num1 * num2
    byte[] newIL = {
    (byte) OpCodes.Ldarg_0.Value, //parameter num1
    (byte) OpCodes.Ldarg_1.Value, //parameter num2
    (byte) OpCodes.Mul.Value,
    (byte) OpCodes.Ret.Value
    };
      
    MethodBody body = new MethodBody (newIL, context.Method.Module);
    context.ResolveBody (body);
  }
}

Replace Native Code

private static void MethodResolver (MethodContext context) 
{
  if (context.Method.Name == "Sum") {
    Assembler assembler = new Assembler (64);

    //Replace with fatorial number:
    //int sum = num1+num2;
    //int fatorial = 1;
    //for(int i = 2; i <= sum; i++){
    //    fatorial *= i;
    //}
    //return fatorial;
    assembler.add (edx, ecx);
    assembler.mov (eax, 1);
    assembler.mov (ecx, 2);
    assembler.cmp (edx, 0x02);
    assembler.jl (assembler.@F);
    assembler.AnonymousLabel ();
    assembler.imul (eax, ecx);
    assembler.inc (ecx);
    assembler.cmp (ecx, edx);
    assembler.jle (assembler.@B);
    assembler.AnonymousLabel ();
    assembler.ret ();
      
    using MemoryStream ms = new MemoryStream ();
    assembler.Assemble (new StreamCodeWriter (ms), 0);
      
    byte[] asm = ms.ToArray ();
      
    context.ResolveNative (asm);
  }
}

Inject custom MetadataToken

You can inject a custom metadata too, in this way, you can "execute" metadatatoken not referenced in compile-time:

/// <summary>
///     Example of a external library to replace Sum.
/// </summary>
/// <remarks>
///     We replace Sum to return the PID of process running. To do this, normally we need
///     reference assembly (System.Diagnostics.Process) and class Process.
///     In this case, the original module, dont have any reference to namespace System.Diagnostics.Process.
///     As we pass the MetadataToken from Process.GetCurrentProcess().Id, its necessary resolve that manually,
///     because CLR dont have any information about that in original module.
/// </remarks>
public static class ExternLibrary
{
    private static MethodInfo _getCurrentProcess;
    private static MethodInfo _getterId;

    static ExternLibrary()
    {
        LoadAssemblyDiagnostics();
    }

    public static void Initialize()
    {
        JitexManager.AddMethodResolver(MethodResolver);
        JitexManager.AddTokenResolver(TokenResolver);
    }

    private static void LoadAssemblyDiagnostics()
    {
        string pathAssembly = Path.Combine(Directory.GetCurrentDirectory(), "../../../../", "System.Diagnostics.Process.dll");
        Assembly assemblyDiagnostics = Assembly.LoadFrom(pathAssembly);
        Type typeProcess = assemblyDiagnostics.GetType("System.Diagnostics.Process");
        _getCurrentProcess = typeProcess.GetMethod("GetCurrentProcess");
        _getterId = _getCurrentProcess.ReturnType.GetProperty("Id", BindingFlags.Public | BindingFlags.Instance).GetGetMethod();
    }

    private static void TokenResolver(TokenContext context)
    {
        if (context.TokenType == TokenKind.Method && context.Source.Name == "Sum")
        {
            if (context.MetadataToken == _getCurrentProcess.MetadataToken)
            {
                context.ResolveMethod(_getCurrentProcess);
            }
            else if (context.MetadataToken == _getterId.MetadataToken)
            {
                context.ResolveMethod(_getterId);
            }
        }
    }
    
    private static void MethodResolver(MethodContext context)
    {
        if (context.Method.Name == "Sum")
        {
            List<byte> newIl = new List<byte>();
            newIl.Add((byte)OpCodes.Call.Value);
            newIl.AddRange(BitConverter.GetBytes(_getCurrentProcess.MetadataToken));
            newIl.Add((byte)OpCodes.Call.Value);
            newIl.AddRange(BitConverter.GetBytes(_getterId.MetadataToken));
            newIl.Add((byte)OpCodes.Ret.Value);
            MethodBody methodBody = new MethodBody(newIl.ToArray(), _getCurrentProcess.Module);
            context.ResolveBody(methodBody);
        }
    }
}
static void Main (string[] args) {
    ExternLibrary.Initialize ();
    int result = Sum (1, 7);
    Console.WriteLine (result); //output is PID
}

Replace content string

private static void TokenResolver (TokenContext context) {
  if (context.TokenType == TokenKind.String && context.Content == "Hello World!")
    context.ResolveString ("H3110 W0RLD!");
}
static void Main (string[] args) {
    ExternLibrary.Initialize ();
    HelloWorld (); //output is H3110 W0RLD!
}

static void HelloWorld () {
    Console.WriteLine ("Hello World!");
}

Modules

Jitex can support modules. To create your own module, just extend JitexModule:

public class ModuleJitex : JitexModule
{
    protected override void MethodResolver(MethodContext context)
    {
        //...
    }

    protected override void TokenResolver(TokenContext context)
    {
        //...
    }
}

And load module:

JitexManager.LoadModule<ModuleJitex>();
//or...
JitexManager.LoadModule(typeof(ModuleJitex));

ASP.NET Core support

To load module in ASP.NET Core, just call UseModule in Configure (Startup.cs):

app.UseModule<ModuleJitex>();
app.UseModule<ModuleJitex1>();
app.UseModule<ModuleJitex2>();
//or
app.UseModule(typeof(ModuleJitex);

Proof of Concept

Nitter - A easy mocker for .NET

AutoMapper Patcher - A simple remover AutoMapper at runtime.

InAsm - Run assembly directly from a method.

Thanks

Replace methods was an idea to increase performance in .NET Applications. Searching a way to do that, i've found this hook implementation from @xoofx Writing a Managed JIT in C# with CoreCLR, which became core of Jitex.

Support

Logo

<div>Icon made by <a href="https://www.flaticon.com/authors/iconixar" title="iconixar">iconixar</a> from <a href="https://www.flaticon.com/" title="Flaticon">www.flaticon.com</a></div>
I'm trying be better in english language too, so probably you will see some grammatical errors... Feel free to notify me.