Adding a call to a method before a STFLD causes an InvalidProgramException

-1

I have access to a function body Intermediate Language like this :

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

I want to be able to modify its IL code so that whenever there is a stfld IL command I call the following method named OnChangeField:

public static void OnChangeField(object obj, object value)
{
    Console.WriteLine("VICTORY");
    return;
}

So far I do it like this :

I define the call instruction to the method I want to call:

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

And I alter the original(NestedFoo(...)) method body code like this :

byte[] ilCodes = NestedFooInfo.GetMethodBody().GetILAsByteArray();
var stfldOpCode = (byte)OpCodes.Stfld.Value;
for (int i = 0; i < ilCodes.Length; i++)
{
    if (ilCodes[i] == stfldOpCode)
    {
         byte[] newIlCodes = ilCodes.Take(i).Concat(callIL).Concat(ilCodes.Skip(i)).ToArray(); // Insert the call instruction before the s
         InjectionHelper.UpdateILCodes(NestedFooInfo, newIlCodes); // Explanation below
         break; // Currently I just want to hook the first stfld as a PoC
     }
}

The method body altered in my test case is this :

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();
        }
        [...]
}

I call the method like this :

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

And i get a :

System.InvalidProgramException: 'Common Language Runtime detected an invalid program.'

or a

System.BadImageFormatException: 'Index not found. (Exception from HRESULT: 0x80131124)'

If I remove postsharp and the services Interface from ExceptionHandlingService

I suppose that I edited the Il code in a way that lead to an invalid codeflow but looking at the call and stfld documentation here (p368 and p453) I don't know what I did wrong.

For those who wonder what magic happens in InjectionHelper.UpdateILCodes(NestedFooInfo, newIlCodes); you can check this link which shows how you can edit Il code at runtime.

c#
jit
cil
asked on Stack Overflow Nov 13, 2020 by Amon • edited Nov 13, 2020 by SE_net4 the downvoter

2 Answers

2

IL is a bit fragile, it's not really meant to be assembled by hand :) In any case, you can't find stfld just by looking for a single byte value - you need to make sure you're actually reading an opcode, and not an argument etc.

If you don't have an overriding reason not to, I'd recommend using Expression trees to compile the "raw" IL - that should save you a lot of headache. Regardless, try to move by very small steps - e.g. first try injecting a call at the start of the method, try a method with no arguments, then try finding stfld, handle the more complicated cases (boxing etc.)...

To move forward, it helps to use compilers and decompilers. Compile normal C# code calling your method, and decompile it into IL. Try the injection, and decompile the resulting full IL byte array - you'll likely find the mistake (though I wouldn't say "easily enough", since it needs a bit of IL experience to spot most mistakes :)).

One thing that jumps out on the spot is that you're keeping both the stfld and the call, but only passing the arguments once. Again, as part of identifying the issue, it helps to do things one at a time - replace the stfld with the call to make sure that part works; then deal with duping the values on the stack to allow you to invoke both operations.

answered on Stack Overflow Nov 13, 2020 by Luaan
1

The following exception was caused by the metadata token of the OnChangeField() method which was outsite the executing assembly.

System.BadImageFormatException: 'Index not found. (Exception from HRESULT: 0x80131124)'

And the following exception was caused by a postsharp aspect beeing applied to the NestedFoo function but also because I didn't push any argument on the stack to be passed to OnchangeField arguments:

System.InvalidProgramException: 'Common Language Runtime detected an invalid program.'

answered on Stack Overflow Nov 13, 2020 by Amon

User contributions licensed under CC BY-SA 3.0