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.
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 dup
ing the values on the stack to allow you to invoke both operations.
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.'
User contributions licensed under CC BY-SA 3.0