Как внедрить IL в метод во время выполнения - PullRequest
8 голосов
/ 30 января 2011

Название более или менее говорит само за себя. Основываясь на этой статье, я придумал следующее:

public static unsafe void Replace(this MethodBase destination, MethodBase source)
{
    IntPtr srcHandle = source.MethodHandle.GetFunctionPointer();
    IntPtr dstHandle = destination.MethodHandle.GetFunctionPointer();

    int* dstPtr = (int*)dstHandle.ToPointer();
    *dstPtr = srcHandle.ToInt32();
}

Это на самом деле работает ... иногда -.-

Например, это работает.

public static class Program
{
    public static void Main(string[] args)
    {
        MethodInfo methodA = typeof(Program).GetMethod("A", BindingFlags.Public | BindingFlags.Static);
        MethodInfo methodB = typeof(Program).GetMethod("B", BindingFlags.Public | BindingFlags.Static);

        methodA.Replace(methodB);

        A();
        B();
    }

    public static void A()
    {
        Console.WriteLine("Hai World");
    }

    public static void B()
    {
        Console.WriteLine("Bai World");
    }
}

Однако, это не так (SEHException). Все, что я сделал, это изменил порядок, в котором были определены функции.

public static class Program
{
    public static void Main(string[] args)
    {
        MethodInfo methodA = typeof(Program).GetMethod("A", BindingFlags.Public | BindingFlags.Static);
        MethodInfo methodB = typeof(Program).GetMethod("B", BindingFlags.Public | BindingFlags.Static);

        methodA.Replace(methodB);

        A();
        B();
    }

    public static void B()
    {
        Console.WriteLine("Bai World");
    }

    public static void A()
    {
        Console.WriteLine("Hai World");
    }
}

Что касается кода в статье ... Я не мог заставить его работать вообще.

Есть идеи / альтернативы?

1 Ответ

8 голосов
/ 30 января 2011

Это делает много плохих предположений, которые будут кусать вас в задницу.

Во-первых, во-первых, структура, возвращаемая посредством отражения, никоим образом не гарантирует, что она вообще указывает на какие-либо структуры времени выполнения. Таким образом, попытка изменить указатели, которые он содержит или возвращает, просто ошибочна.

Во-вторых, если вы хотите сделать что-то вроде внедрения инвариантов вызова до / после метода (в качестве примера), то вам, вероятно, следует создать прокси-объекты времени выполнения и внедрить их вместо этого. Или используйте динамическое построение метода через пространство имен Emit. Попытка манипулировать вещами с помощью недокументированного / неизвестного поведения (такого как приведенный выше код) просто не сработает.

Вы также должны понимать, что JIT решает, когда он будет работать. Иногда он работает против целого класса, а иногда просто против одного метода. Ваш код не пытается определить, был ли метод JITted, и вслепую предполагает, что возвращаемый указатель функции может быть изменен.

...