撤消 IL CLR 方法注入?
Undo IL CLR Method Injection?
我有一些代码采用两种方法并在 IL 中注入一种 (injectionMethod) 代替另一种 (replacedMethod)。这用于操纵 replacedMethod 的结果,以在单元测试中给出 injectionMethod 提供的结果。
public static void Inject<T>( Func<T> replacedMethod, Func<T> injectionMethod )
{
string replacedMethodName = replacedMethod.Method.Name;
Type replacedMethodType = replacedMethod.Method.ReflectedType;
string injectionMethodName = injectionMethod.Method.Name;
Type injectionMethodType = injectionMethod.Method.ReflectedType;
MethodInfo methodToReplace = replacedMethodType.GetMethod( replacedMethodName );
MethodInfo methodToInject = injectionMethodType.GetMethod( injectionMethodName );
RuntimeHelpers.PrepareMethod( methodToReplace.MethodHandle );
RuntimeHelpers.PrepareMethod( methodToInject.MethodHandle );
ReplacedMethod = methodToReplace;
unsafe {
if( IntPtr.Size == 4 ) {
int* inj = (int*) methodToInject.MethodHandle.Value.ToPointer() + 2;
int* tar = (int*) methodToReplace.MethodHandle.Value.ToPointer() + 2;
if( System.Diagnostics.Debugger.IsAttached ) {
//Version x86 Debug
byte* injInst = (byte*) *inj;
byte* tarInst = (byte*) *tar;
int* injSrc = (int*) (injInst + 1);
int* tarSrc = (int*) (tarInst + 1);
*tarSrc = (((int) injInst + 5) + *injSrc) - ((int) tarInst + 5);
}
else {
//Version x86 Release
*tar = *inj;
}
}
else {
long* inj = (long*) methodToInject.MethodHandle.Value.ToPointer() + 1;
long* tar = (long*) methodToReplace.MethodHandle.Value.ToPointer() + 1;
if( System.Diagnostics.Debugger.IsAttached ) {
//Version x64 Debug
byte* injInst = (byte*) *inj;
byte* tarInst = (byte*) *tar;
int* injSrc = (int*) (injInst + 1);
int* tarSrc = (int*) (tarInst + 1);
*tarSrc = (((int) injInst + 5) + *injSrc) - ((int) tarInst + 5);
}
else {
//Version x64 Release
*tar = *inj;
}
}
}
}
该代码有效,但我不完全理解它是如何工作的。一旦 IL 被 jitted,注入的方法似乎被永久缓存,任何其他引用原始方法的代码都会失败,因为它正在引用注入的方法。
我不打算重构被测的原始代码,也不想 运行 一次使用 Inject 方法进行一个测试。有没有办法 "undo" 在这里完成注射?
我曾尝试保存目标源,但是一旦目标方法被替换,我就不能简单地重新分配以前的源,而代码不会进入中断模式。
结果证明这是一个相当简单的答案。我所要做的就是 运行 在完成 运行 交换(注入)方法后第二次执行相同的命令。 运行 使用 ToLongString() 方法对对象进行的 NUnit 测试将通过:
public string InterceptorToString()
{
return "Injected Text";
}
[Test]
[Category( "Interceptor" )]
public void InjectionTest()
{
MyObject obj = new MyObject();
string objString1 = obj.ToLongString();
string intString1 = InterceptorToString(); //returns "Injected Text"
Interceptor.Inject( obj.ToLongString, InterceptorToString );
string objString2 = obj.ToLongString(); //returns "Injected Text"
string intString2 = InterceptorToString();
Interceptor.Inject( obj.ToLongString, InterceptorToString );
string objString3 = obj.ToLongString();
string intString3 = InterceptorToString(); //returns "Injected Text"
Assert.That( objString2, Is.Not.EqualTo( intString2 ) );
Assert.That( objString3, Is.Not.EqualTo( intString3 ) );
Assert.That( objString2, Is.EqualTo( "Injected Text" ) );
}
必须对拦截器进行的更改class:
unsafe {
if( IntPtr.Size == 4 ) {
int* inj = (int*) methodToInject.MethodHandle.Value.ToPointer() + 2;
int* tar = (int*) methodToReplace.MethodHandle.Value.ToPointer() + 2;
if( System.Diagnostics.Debugger.IsAttached ) {
//Version x86 Debug
byte* injInst = (byte*) *inj;
byte* tarInst = (byte*) *tar;
int* injSrc = (int*) (injInst + 1);
int* tarSrc = (int*) (tarInst + 1);
Replaced = (((int) tarInst + 5) + *tarSrc) - ((int) injInst + 5);
*tarSrc = (((int) injInst + 5) + *injSrc) - ((int) tarInst + 5);
*injSrc = Replaced;
}
else {
//Version x86 Release
Replaced = *tar;
*tar = *inj;
*inj = Replaced;
}
}
我有一些代码采用两种方法并在 IL 中注入一种 (injectionMethod) 代替另一种 (replacedMethod)。这用于操纵 replacedMethod 的结果,以在单元测试中给出 injectionMethod 提供的结果。
public static void Inject<T>( Func<T> replacedMethod, Func<T> injectionMethod )
{
string replacedMethodName = replacedMethod.Method.Name;
Type replacedMethodType = replacedMethod.Method.ReflectedType;
string injectionMethodName = injectionMethod.Method.Name;
Type injectionMethodType = injectionMethod.Method.ReflectedType;
MethodInfo methodToReplace = replacedMethodType.GetMethod( replacedMethodName );
MethodInfo methodToInject = injectionMethodType.GetMethod( injectionMethodName );
RuntimeHelpers.PrepareMethod( methodToReplace.MethodHandle );
RuntimeHelpers.PrepareMethod( methodToInject.MethodHandle );
ReplacedMethod = methodToReplace;
unsafe {
if( IntPtr.Size == 4 ) {
int* inj = (int*) methodToInject.MethodHandle.Value.ToPointer() + 2;
int* tar = (int*) methodToReplace.MethodHandle.Value.ToPointer() + 2;
if( System.Diagnostics.Debugger.IsAttached ) {
//Version x86 Debug
byte* injInst = (byte*) *inj;
byte* tarInst = (byte*) *tar;
int* injSrc = (int*) (injInst + 1);
int* tarSrc = (int*) (tarInst + 1);
*tarSrc = (((int) injInst + 5) + *injSrc) - ((int) tarInst + 5);
}
else {
//Version x86 Release
*tar = *inj;
}
}
else {
long* inj = (long*) methodToInject.MethodHandle.Value.ToPointer() + 1;
long* tar = (long*) methodToReplace.MethodHandle.Value.ToPointer() + 1;
if( System.Diagnostics.Debugger.IsAttached ) {
//Version x64 Debug
byte* injInst = (byte*) *inj;
byte* tarInst = (byte*) *tar;
int* injSrc = (int*) (injInst + 1);
int* tarSrc = (int*) (tarInst + 1);
*tarSrc = (((int) injInst + 5) + *injSrc) - ((int) tarInst + 5);
}
else {
//Version x64 Release
*tar = *inj;
}
}
}
}
该代码有效,但我不完全理解它是如何工作的。一旦 IL 被 jitted,注入的方法似乎被永久缓存,任何其他引用原始方法的代码都会失败,因为它正在引用注入的方法。
我不打算重构被测的原始代码,也不想 运行 一次使用 Inject 方法进行一个测试。有没有办法 "undo" 在这里完成注射?
我曾尝试保存目标源,但是一旦目标方法被替换,我就不能简单地重新分配以前的源,而代码不会进入中断模式。 结果证明这是一个相当简单的答案。我所要做的就是 运行 在完成 运行 交换(注入)方法后第二次执行相同的命令。 运行 使用 ToLongString() 方法对对象进行的 NUnit 测试将通过:
public string InterceptorToString()
{
return "Injected Text";
}
[Test]
[Category( "Interceptor" )]
public void InjectionTest()
{
MyObject obj = new MyObject();
string objString1 = obj.ToLongString();
string intString1 = InterceptorToString(); //returns "Injected Text"
Interceptor.Inject( obj.ToLongString, InterceptorToString );
string objString2 = obj.ToLongString(); //returns "Injected Text"
string intString2 = InterceptorToString();
Interceptor.Inject( obj.ToLongString, InterceptorToString );
string objString3 = obj.ToLongString();
string intString3 = InterceptorToString(); //returns "Injected Text"
Assert.That( objString2, Is.Not.EqualTo( intString2 ) );
Assert.That( objString3, Is.Not.EqualTo( intString3 ) );
Assert.That( objString2, Is.EqualTo( "Injected Text" ) );
}
必须对拦截器进行的更改class:
unsafe {
if( IntPtr.Size == 4 ) {
int* inj = (int*) methodToInject.MethodHandle.Value.ToPointer() + 2;
int* tar = (int*) methodToReplace.MethodHandle.Value.ToPointer() + 2;
if( System.Diagnostics.Debugger.IsAttached ) {
//Version x86 Debug
byte* injInst = (byte*) *inj;
byte* tarInst = (byte*) *tar;
int* injSrc = (int*) (injInst + 1);
int* tarSrc = (int*) (tarInst + 1);
Replaced = (((int) tarInst + 5) + *tarSrc) - ((int) injInst + 5);
*tarSrc = (((int) injInst + 5) + *injSrc) - ((int) tarInst + 5);
*injSrc = Replaced;
}
else {
//Version x86 Release
Replaced = *tar;
*tar = *inj;
*inj = Replaced;
}
}