在 STFLD 导致 InvalidProgramException 之前添加对方法的调用
Adding a call to a method before a STFLD causes an InvalidProgramException
我可以访问这样的函数体中间语言:
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: 'Common Language Runtime detected an invalid program.'
或一个
System.BadImageFormatException: 'Index not found. (Exception from HRESULT: 0x80131124)'
如果我从 ExceptionHandlingService
中删除 postsharp 和服务接口
我想我以导致无效代码流的方式编辑了 Il 代码,但查看 call
和 stfld
文档 here(p368 和 p453)我不知道不知道我做错了什么。
对于那些想知道魔法发生了什么的人
InjectionHelper.UpdateILCodes(NestedFooInfo, newIlCodes);
您可以查看 this link,其中显示了如何在运行时编辑 Il 代码。
以下异常是由执行程序集外部的 OnChangeField() 方法的元数据标记引起的。
System.BadImageFormatException: 'Index not found. (Exception from
HRESULT: 0x80131124)'
下面的异常是由应用到 NestedFoo 函数的 postsharp 方面引起的,而且还因为我没有将任何参数压入堆栈以传递给 OnchangeField 参数:
System.InvalidProgramException: 'Common Language Runtime detected an invalid program.'
IL 有点脆弱,它并不是真的要手工组装 :) 无论如何,您无法仅通过查找单个字节值来找到 stfld
- 您需要确保您实际上是在读取操作码,而不是参数等。
如果您没有绝对的理由不这样做,我建议您使用 Expression
树来编译“原始”IL - 这应该会让您省去很多麻烦。无论如何,尝试以非常小的步骤移动 - 例如首先尝试在方法的开头注入一个调用,尝试一个没有参数的方法,然后尝试找到 stfld
,处理更复杂的情况(装箱等)...
要前进,使用编译器和反编译器会有所帮助。编译调用您的方法的普通 C# 代码,并将其反编译为 IL。尝试注入,并反编译生成的完整 IL 字节数组——您可能会发现错误(尽管我不会说“足够简单”,因为它需要一点 IL 经验才能发现大多数错误 :))。
当场跳出来的一件事是您保留了 stfld
和 call
,但只传递了一次参数。同样,作为确定问题的一部分,它有助于一次做一件事情——将 stfld
替换为 call
以确保该部分有效;然后处理 dup
堆栈上的值以允许您调用这两个操作。
我可以访问这样的函数体中间语言:
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: 'Common Language Runtime detected an invalid program.'
或一个
System.BadImageFormatException: 'Index not found. (Exception from HRESULT: 0x80131124)'
如果我从 ExceptionHandlingService
我想我以导致无效代码流的方式编辑了 Il 代码,但查看 call
和 stfld
文档 here(p368 和 p453)我不知道不知道我做错了什么。
对于那些想知道魔法发生了什么的人
InjectionHelper.UpdateILCodes(NestedFooInfo, newIlCodes);
您可以查看 this link,其中显示了如何在运行时编辑 Il 代码。
以下异常是由执行程序集外部的 OnChangeField() 方法的元数据标记引起的。
System.BadImageFormatException: 'Index not found. (Exception from HRESULT: 0x80131124)'
下面的异常是由应用到 NestedFoo 函数的 postsharp 方面引起的,而且还因为我没有将任何参数压入堆栈以传递给 OnchangeField 参数:
System.InvalidProgramException: 'Common Language Runtime detected an invalid program.'
IL 有点脆弱,它并不是真的要手工组装 :) 无论如何,您无法仅通过查找单个字节值来找到 stfld
- 您需要确保您实际上是在读取操作码,而不是参数等。
如果您没有绝对的理由不这样做,我建议您使用 Expression
树来编译“原始”IL - 这应该会让您省去很多麻烦。无论如何,尝试以非常小的步骤移动 - 例如首先尝试在方法的开头注入一个调用,尝试一个没有参数的方法,然后尝试找到 stfld
,处理更复杂的情况(装箱等)...
要前进,使用编译器和反编译器会有所帮助。编译调用您的方法的普通 C# 代码,并将其反编译为 IL。尝试注入,并反编译生成的完整 IL 字节数组——您可能会发现错误(尽管我不会说“足够简单”,因为它需要一点 IL 经验才能发现大多数错误 :))。
当场跳出来的一件事是您保留了 stfld
和 call
,但只传递了一次参数。同样,作为确定问题的一部分,它有助于一次做一件事情——将 stfld
替换为 call
以确保该部分有效;然后处理 dup
堆栈上的值以允许您调用这两个操作。