Как я могу вызвать метод вне исполняемой сборки в MSIL?

#c# #hook #clr #jit #cil

#c# #перехват #clr #jit #cil

Вопрос:

У меня есть доступ к промежуточному языку тела функции, подобному этому :

 byte[] ilCodes = NestedFooInfo.GetMethodBody().GetILAsByteArray();
 

Я могу изменить его IL-код, чтобы перед выполнением тела метода я вызывал следующий метод с именем OnChangeField :

 public static void OnChangeField()
{
    Console.WriteLine("VICTORY");
    return;
}
 

Пока я делаю это так :

Я определяю инструкцию вызова для метода, который я хочу вызвать:

 MethodInfo OnStfld = typeof(MethodBoundaryAspect).GetMethod("OnChangeField");
byte[] callIL = new byte[5];
callIL[0] = (byte)OpCodes.Call.Value;
callIL[1] = (byte)(OnStfld.MetadataToken amp; 0xFF);
callIL[2] = (byte)(OnStfld.MetadataToken >> 8 amp; 0xFF);
callIL[3] = (byte)(OnStfld.MetadataToken >> 16 amp; 0xFF);
callIL[4] = (byte)(OnStfld.MetadataToken >> 24 amp; 0xFF);
 

Обратите внимание, что OnChangeField находится в классе MethodBoundaryAspect . Этот класс находится вне сборки редактируемого метода.

И вот как я изменяю исходное NestedFoo(...) тело метода () :

 byte[] ilCodes = NestedFooInfo.GetMethodBody().GetILAsByteArray();
InjectionHelper.UpdateILCodes(NestedFooInfo, callIL.Concat(ilCodes).ToArray());
 

И я получаю :

Система.Исключение BadImageFormatException: ‘Индекс не найден. (Исключение из HRESULT: 0x80131124) ‘

Но только если метод подключения OnChangeField находится вне (или, как я понял) исполняемой сборки. Если я перейду OnChangeField в тот же класс, что и NestedFoo, или в другой класс NestedFoo сборки, он работает отлично.

Я понимаю, что metadatatoken указывает на ячейку памяти, которая недействительна. Есть ли способ это изменить?

Для ссылок здесь: как выглядит тело метода измененного метода:

 public class ExceptionHandlingService : IExceptionHandlingService
{
        public static string var1 = "initialValue";
        public static string Var2 { get; set; } = "initialValue";
        public string var3 = "initialValue";
        public string Var4 { get; set; } = "initialValue";

        public string NestedFoo(SampleClass bar)
        {
            var1 = "value set in NestedFoo()";
            Var2 = "value set in NestedFoo()";
            var3 = "value set in NestedFoo()";
            Var4 = "value set in NestedFoo()";
            AddPerson("From", "NestedFoo", 2);
            return Foo();
        }
        [...]
}
 

и как я вызываю измененный метод :

 var a = new ExceptionHandlingService();
var b = new SampleClass("bonjour", 2, 3L); // Not really relevant
a.NestedFoo(b);
 

Для тех, кому интересно, что за волшебство происходит,
InjectionHelper.UpdateILCodes(NestedFooInfo, newIlCodes); вы можете проверить эту ссылку, которая показывает, как вы можете редактировать Il-код во время выполнения.

Ответ №1:

Вам нужно добавить записи в MemberRef и последовательно TypeRef или TypeSpec таблицы метаданных, чтобы содержать ссылки на типы и ссылаться на эти токены. Это также включает в себя правильное написание больших двоичных объектов подписи.

См. Раздел II 22.38, 22.25 ECMA-335