NulledCoder
NulledCoder

Reputation: 265

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?

Upvotes: 1

Views: 665

Answers (1)

svick
svick

Reputation: 244998

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

Upvotes: 2

Related Questions