С# Mono.Cecil ввел IL-код, который не выполняется

Я хочу сделать небольшой пример кода с Mono.Cecil. Цель состоит в том, чтобы манипулировать IL. Метод X печатает что-то на консоли. Чтобы проверить манипулирование кодом IL, я удаляю все инструкции из метода. Но когда я запускаю External2.dll, вывод консоли по-прежнему печатается (но инструкции по выводу консоли удаляются из External2.dll). Как я могу решить эту проблему?

var module = ModuleDefinition.ReadModule("External.dll");
var types = module.Types;
foreach (var type in types) {
    foreach (var method in type.Methods) {
        if (method.Name == "X") {
            Console.WriteLine(method.Body.Instructions.Count);
            method.Body.Instructions.Clear();
            Console.WriteLine(method.Body.Instructions.Count);
        }
    }
}
module.Write("External2.dll");

person MjeOsX    schedule 31.08.2020    source источник


Ответы (1)


Я думаю, вам нужно использовать AssemblyDefinition вместо ModuleDefinition

Кроме того, вы не можете просто удалить все инструкции из тела метода. У вас должен быть хотя бы один ret. Например, приведенный ниже код работает:

using System;
using System.Linq;
using Mono.Cecil;
using System.IO;

namespace cecil
{
    class Program
    {
        static void Main(string[] args)
        {
            X();

            var assembly = AssemblyDefinition.ReadAssembly(typeof(Program).Assembly.Location);
            var method = assembly.MainModule.Types.SelectMany(t => t.Methods).SingleOrDefault(m => m.Name == "X");

            if (method == null)
            {
                System.Console.WriteLine("Method X not found.");
            }
            else
            {
                Console.WriteLine(method.Body.Instructions.Count);
                method.Body.Instructions.Clear();
                var ilProcessor = method.Body.GetILProcessor();
                ilProcessor.Append(ilProcessor.Create(Mono.Cecil.Cil.OpCodes.Ret));

                Console.WriteLine(method.Body.Instructions.Count);

                var newAssemblyFileName= "External2.dll";
                assembly.Write(newAssemblyFileName);
                System.Console.WriteLine($"Assembly saved to {Path.GetFullPath(newAssemblyFileName)}");
            }
        }

        private static void X()
        {
            System.Console.WriteLine("From X");
        }
    }
}

кстати, вы всегда можете использовать https://cecilifier.me, чтобы помочь вам.

person Vagaus    schedule 01.09.2020
comment
Да, рет должен быть еще там. Как я мог это проглядеть :) - person MjeOsX; 01.09.2020
comment
Еще раз спасибо за вашу помощь. Но у меня все та же проблема. Код IL манипулируется вашим кодом. Но по какой-то причине, когда я запускаю External2.dll, я все еще получаю вывод консоли из X, как будто там есть какой-то механизм кэширования. Вы знаете, как это могло быть возможно? Я запускаю его с помощью dotnet ‹fullPath›\External2.dll - person MjeOsX; 01.09.2020
comment
Когда я копирую External2.dll (со всеми ссылками) в другую папку, он работает :) Так что должен быть файл, который я должен удалить - person MjeOsX; 01.09.2020
comment
Мне нужно больше подробностей о том, как вы строите / работаете, чтобы помочь больше. Например, я протестировал это с ядром dotnet, и для запуска External2.dll мне потребовалось скопировать его поверх исходного. - person Vagaus; 02.09.2020