保存程序集时 Mono.Cecil 中的 ArgumentException

ArgumentException in Mono.Cecil while saving assembly

我有两个程序集。 "patchsrc.exe" 和 "Assembly-CSharp.dll"
我从 patchsrc.exe::TXDLLLoader.Program::Main()
获得所有 IL 指令 以及来自 Assembly-CSharp.dll::Class::Method()

的所有 IL 指令

我从第一个代码中删除 'ret' 操作码,然后将它们合并到一个函数中。

当我尝试保存它时,我得到了这个:

An unhandled exception of type 'System.ArgumentException' occurred in Mono.Cecil.dll

Additional information: Member 'System.Reflection.Assembly System.Reflection.Assembly::LoadFile(System.String)' is declared in another module and needs to be imported

我正在使用此代码:

var assembly = AssemblyDefinition.ReadAssembly("./game_Data/Managed/Assembly-CSharp.dll");
var assembly_patchsrc = AssemblyDefinition.ReadAssembly("./patchsrc.exe");


Console.WriteLine("searching..");

Collection<Instruction> instrForPatch = new Collection<Instruction>();

foreach (var methodDefinition in from type in assembly_patchsrc.MainModule.Types from methodDefinition in type.GetMethods() where methodDefinition.FullName.Contains("TXDLLLoader.Program::Main()") select methodDefinition)
{
    Console.WriteLine("Found some patch instructions!");

    var instr_patchsrc = methodDefinition.Body.Instructions;

    instr_patchsrc.Remove(instr_patchsrc.Last());

    for (var i = 0; i <= instr_patchsrc.Count - 1; i++)
    {
        instrForPatch.Add(instr_patchsrc[i]);
    }
}

Console.ReadLine();

foreach (var instr in from typeDef in assembly.MainModule.Types
          from method in typeDef.Methods
          where typeDef.Name.Equals("Class") && method.Name.Equals("Method")
          select method.Body.Instructions)
{
    Collection<Instruction> oldList = new Collection<Instruction>();

    for (var i = 0; i<=instr.Count-1; i++)
    {
        oldList.Add(instr[i]);
    }

    instr.Clear();

    Console.WriteLine($"Begin injecting patch instructions.. [{instrForPatch.Count}]");

    foreach (var instruction in instrForPatch)
    {
        Console.WriteLine($"Adding instruction: [{instruction}]");
        instr.Add(instruction);
    }

    Console.WriteLine($"Begin injecting old instructions..  [{oldList.Count}]");

    foreach (var instruction in oldList)
    {
        Console.WriteLine($"Adding instruction: [{instruction}]");
        instr.Add(instruction);
    }

    Console.WriteLine("patched!");
}

Console.WriteLine("saving asssembly..");
assembly.Write("./game_Data/Managed/Assembly-CSharp_patched.dll");

我该如何解决所有问题?

Cecil documentation on importing 中所述,您正在使用的 LoadFile() 成员引用的范围仅限于模块。如果你想在另一个模块中使用引用,你需要先导入它。由于您不知道在 patchsrc 中会遇到什么指令,因此您需要能够导入任何指令的任何操作数。为此,您可以编写一个辅助方法:

static Instruction ImportInstruction(Instruction instruction, ModuleDefinition module)
{
    object operand = instruction.Operand;

    var fieldOperand = operand as FieldReference;
    if (fieldOperand != null)
        return Instruction.Create(instruction.OpCode, module.Import(fieldOperand));

    var methodOperand = operand as MethodReference;
    if (methodOperand != null)
        return Instruction.Create(instruction.OpCode, module.Import(methodOperand));

    var typeOperand = operand as TypeReference;
    if (typeOperand != null)
        return Instruction.Create(instruction.OpCode, module.Import(typeOperand));

    return instruction;
}

然后在添加patchsrc指令时使用:

foreach (var instruction in instrForPatch)
{
    Console.WriteLine($"Adding instruction: [{instruction}]");
    instr.Add(ImportInstruction(instruction, assembly.MainModule));
}