我可以访问函数体中间语言,如下所示:
byte[] ilCodes = NestedFooInfo.GetMethodBody().GetILAsByteArray();
我希望能够修改其IL代码,以便每当有stfld
IL命令时,我都会调用以下名为OnChangeField
:的方法
public static void OnChangeField(object obj, object value)
{
Console.WriteLine("VICTORY");
return;
}
到目前为止,我是这样做的:
我定义了要调用的方法的调用指令:
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);
我修改了原始的(NestedFoo(...)
(方法体代码,如下所示:
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
}
}
在我的测试用例中更改的方法体是:
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();
}
[...]
}
我把这个方法叫做
var a = new ExceptionHandlingService();
var b = new SampleClass("bonjour", 2, 3L); // Not really relevant
a.NestedFoo(b);
我得到了一个:
System.InvalidProgramException:"公共语言运行时检测到无效程序。">
或
System.BadImageFormatException:'未找到索引。(HRESULT:0x80131124中的异常('
如果我从ExceptionHandlingService
中删除postsharp和服务接口
我想我编辑Il代码的方式会导致无效的代码流,但看看这里的call
和stfld
文档(p368和p453(,我不知道我做错了什么。
对于那些想知道魔法发生在哪里的人InjectionHelper.UpdateILCodes(NestedFooInfo, newIlCodes);
您可以查看此链接,该链接显示如何在运行时编辑Il代码。
IL有点脆弱,它并不是真正的手工组装:(在任何情况下,你都不能仅仅通过寻找一个单字节值来找到stfld
——你需要确保你真正读取的是操作码,而不是参数等。
如果你没有一个压倒一切的理由不这样做,我建议使用Expression
树来编译";生的";IL-这应该可以帮你省去很多头痛。无论如何,尝试通过非常小的步骤进行移动-例如,首先尝试在方法开始时注入调用,尝试一个没有参数的方法,然后尝试找到stfld
,处理更复杂的情况(装箱等(…
为了向前发展,使用编译器和反编译器会有所帮助。编译调用方法的普通C#代码,并将其反编译为IL。尝试注入,并反编译生成的完整IL字节数组-你可能会发现错误(尽管我不会说"足够容易",因为它需要一点IL经验才能发现大多数错误:(。
有一点很突出,那就是您保留了stfld
和call
,但只传递了一次参数。同样,作为识别问题的一部分,它有助于一次做一件事——用call
替换stfld
,以确保该部分正常工作;然后处理堆栈上的值的CCD_ 15,以允许您调用这两个操作。
以下异常是由执行程序集外部的OnChangeField((方法的元数据标记引起的。
System.BadImageFormatException:'未找到索引。(来自的异常HRESULT:0x80131124('
下面的异常是由应用于NestedFoo函数的postsharp方面引起的,但也因为我没有在堆栈上推送任何要传递给OnchangeField参数的参数:
System.InvalidProgramException:"公共语言运行时检测到无效程序。">