【问题标题】:Undo IL CLR Method Injection?撤消 IL CLR 方法注入?
【发布时间】:2018-06-15 11:35:31
【问题描述】:

我有一段代码需要两个方法,并在 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_Inject”方法。不过,它似乎仍然引用了新注入的代码。
  • 唯一的写入发生在*tarSrc = (((int) injInst + 5) + *injSrc) - ((int) tarInst + 5);。在此之前保存*tarSrc的值,稍后使用它来恢复它。

标签: c# unit-testing pointers clr jit


【解决方案1】:

我曾尝试保存目标源,但是一旦替换了目标方法,我就无法简单地重新分配先前的源,而代码不进入中断模式。 结果证明这是一个相当简单的答案。我所要做的就是在运行完交换(注入)的方法后第二次运行相同的命令。使用 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" ) );
  }

必须对 Interceptor 类进行的更改:

     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;
           }

        }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-06-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-30
    • 2011-04-07
    • 2010-10-25
    • 2013-07-29
    相关资源
    最近更新 更多