简体   繁体   中英

ArgumentException in Mono.Cecil while saving assembly

I have two assemblies. "patchsrc.exe" and "Assembly-CSharp.dll"
I get all IL-instructions from patchsrc.exe::TXDLLLoader.Program::Main()
And all IL-instructions from Assembly-CSharp.dll::Class::Method()

I remove 'ret' opcode from first code and then merge them into one function.

When I try to save it, I get this:

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

I'm using this code:

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");

How can I resolve everything?

As explained in Cecil documentation on importing member references like the one to LoadFile() that you're using are scoped to modules. If you want to use the reference in another module, you need to import it first. Since you don't know what instructions you'll encounter in patchsrc, you need to be able import any operands of any instruction. To do that, you can write a helper method:

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;
}

And then use it when adding patchsrc instructions:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM