mono - 篡改程序集时,为什么无法删除原始指令?

标签 mono il mono.cecil tampering ilspy

为了能够test legacy code which relies on SharePoint ,我需要模拟SharePoint的一些对象。我通过篡改 SharePoint 程序集、即时用我的方法替换他们的方法来实现此目的。

这适用于某些情况,但不适用于其他情况。我遇到的一个奇怪的情况就是这个。

我想用我自己的实现替换SPContext.Current的getter;为了简单起见,我的实现只是抛出一个异常:

.property class Microsoft.SharePoint.SPContext Current()
{
  .get class Microsoft.SharePoint.SPContext Proxy.SPContextProxy::get_Current()
}

.method public hidebysig specialname static 
  class Microsoft.SharePoint.SPContext get_Current () cil managed 
{
  // Method begins at RVA 0x877e68
  // Code size 12 (0xc)
  .maxstack 8

  IL_0000: nop
  IL_0001: ldstr "Proxy don't have an effective implementation of this property."
  IL_0006: newobj instance void [mscorlib]System.NotImplementedException::.ctor(string)
  IL_000b: throw
} // end of method SPContextProxy::get_Current

当篡改原始程序集时,如果我替换SPContext.Current getter对应的IL代码,该属性将无法再使用。我什至无法在 ILSpy 中可视化其内容,因为显示的是以下内容:

System.NullReferenceException: Object reference not set to an instance of an object.
   at Mono.Cecil.Cil.CodeReader.ReadExceptionHandlers(Int32 count, Func`1 read_entry, Func`1 read_length)
   at Mono.Cecil.Cil.CodeReader.ReadSection()
   at Mono.Cecil.Cil.CodeReader.ReadFatMethod()
   at Mono.Cecil.Cil.CodeReader.ReadMethodBody()
   at Mono.Cecil.Cil.CodeReader.ReadMethodBody(MethodDefinition method)
   at Mono.Cecil.MethodDefinition.<get_Body>b__2(MethodDefinition method, MetadataReader reader)
   at Mono.Cecil.ModuleDefinition.Read[TItem,TRet](TRet& variable, TItem item, Func`3 read)
   at Mono.Cecil.MethodDefinition.get_Body()
   at ICSharpCode.Decompiler.Disassembler.ReflectionDisassembler.DisassembleMethodInternal(MethodDefinition method)
   at ICSharpCode.ILSpy.ILLanguage.DecompileProperty(PropertyDefinition property, ITextOutput output, DecompilationOptions options)
   at ICSharpCode.ILSpy.TextView.DecompilerTextView.DecompileNodes(DecompilationContext context, ITextOutput textOutput)
   at ICSharpCode.ILSpy.TextView.DecompilerTextView.<>c__DisplayClass16.<DecompileAsync>b__15()

另一方面,当我在原始指令之前插入我的指令时,我可以成功调用 getter,并在 ILSpy 中查看其内容:

.property class Microsoft.SharePoint.SPContext Current()
{
  .custom instance void [Microsoft.SharePoint.Client.ServerRuntime]Microsoft.SharePoint.Client.ClientCallableAttribute::.ctor() = (
    01 00 00 00
  )
  .get class Microsoft.SharePoint.SPContext Microsoft.SharePoint.SPContext::get_Current()
}

.method public hidebysig specialname static 
  class Microsoft.SharePoint.SPContext get_Current () cil managed 
{
  // Method begins at RVA 0x33e2d8
  // Code size 61 (0x3d)
  .maxstack 1
  .locals init (
    [0] class Microsoft.SharePoint.SPContext,
    [1] class [System.Web]System.Web.HttpContext,
    [2] class Microsoft.SharePoint.SPContext
  )

...遵循我插入的说明:

  IL_0000: nop
  IL_0001: ldstr "Proxy doesn't implement this property yet."
  IL_0006: newobj instance void [mscorlib]System.NotImplementedException::.ctor(string)
  IL_000b: throw

...遵循原始说明:

  IL_000c: ldnull
  IL_000d: stloc.0
  IL_000e: call class [System.Web]System.Web.HttpContext [System.Web]System.Web.HttpContext::get_Current()
  IL_0013: stloc.1
  IL_0014: ldloc.1
  IL_0015: brfalse.s IL_0039
  .try
  {
    IL_0017: ldloc.1
    IL_0018: call class Microsoft.SharePoint.SPWeb Microsoft.SharePoint.WebControls.SPControl::GetContextWeb(class [System.Web]System.Web.HttpContext)
    IL_001d: brtrue.s IL_0023

    IL_001f: ldnull
    IL_0020: stloc.2
    IL_0021: leave.s IL_003b

    IL_0023: leave.s IL_002a
  } // end .try
  catch [mscorlib]System.InvalidOperationException
  {
    IL_0025: pop
    IL_0026: ldnull
    IL_0027: stloc.2
    IL_0028: leave.s IL_003b
  } // end handler
  .try
  {
    IL_002a: ldloc.1
    IL_002b: call class Microsoft.SharePoint.SPContext Microsoft.SharePoint.SPContext::GetContext(class [System.Web]System.Web.HttpContext)
    IL_0030: stloc.0
    IL_0031: leave.s IL_0039
  } // end .try
  catch [mscorlib]System.IO.FileNotFoundException
  {
    IL_0033: pop
    IL_0034: leave.s IL_0039
  } // end handler
  catch [mscorlib]System.InvalidOperationException
  {
    IL_0036: pop
    IL_0037: leave.s IL_0039
  } // end handler

  IL_0039: ldloc.0
  IL_003a: ret

  IL_003b: ldloc.2
  IL_003c: ret
} // end of method SPContext::get_Current

在插入新指令之前删除原始指令时,什么会阻止 ILSpy 加载代码?

注释:

  • 通过使用 MethodDefinition.Body.Instructions 集合(以及相应的 InsertRemove 方法)对 Mono.Cecil 进行篡改.)

  • Microsoft.SharePoint 程序集的一些其他方法和属性已成功篡改:ILSpy 显示生成的 IL 代码。

  • 我认为 .maxstack 指令可能是一个问题(原始属性中为 1,代理属性中为 8,结果中为 1)。在单独的项目上测试了几次后,似乎没有效果。

  • 我还怀疑异常可能是原因(原始代码抛出的异常与新代码不同)。经过几次测试on a separate project ,看来也没有什么效果。

最佳答案

当 IL 以文本形式显示时,异常处理 block (.trycatch 等)显示为实际的 IL 指令 block ,就像它们在C#。

但在二进制形式中,异常处理 block 是单独存储的(请参阅 §II.25.4.6 Exception handling clauses of ECMA-335 )并使用偏移量引用 IL 指令。在 Cecil 中,异常处理程序使用 MethodBody.ExceptionHandlers 属性来表示。

因此,如果您用自己的指令替换旧的 MethodBody.Instructions,则旧异常处理程序的偏移量现在很可能无效,从而导致问题。 (Cecil 抛出 NullReferenceException 对我来说听起来像是一个错误,请考虑报告它。)

您链接到的未出现此问题的另一个示例有所不同,因为原始方法不包含异常处理程序,它抛出异常。并且 throw 只是一个普通的 IL 指令,它没有像例如这样的特殊表示。 .try/catch 确实如此。

关于mono - 篡改程序集时,为什么无法删除原始指令?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31969111/

相关文章:

C# 在运行时或使用 Mono.Cecil 向方法添加代码

c# - 确定 Mono.Cecil.MethodDefinition 是否引用与给定 EnvDTE.CodeFunction 相同的函数

macos - 如何使用 MStests 和 NUnit 的通用测试工具并在 Mono 中运行它们?

c# - 如何使用 Mono 在 XSP 上运行 .NET 4.5?

c# - PEVerify 关于重复方法的警告在这里是错误的吗?

.net - 查看 .NET 应用程序中所有调用的最简单方法(分析/检测)

c# - ldobj 和 ldind.<type> 有什么区别,为什么 ldobj 更快?

c# - Mono.Cecil - 如何获取方法体的简单示例

c# - 如何使用 Mono 和 mkbundle 将一个简单的 .Net 控制台项目转换为可移植的 exe?

.net - 编译时引用.NET dll(单声道)