篡改装配时,为什么我不能删除原始说明?
When tampering an assembly, why can't I remove original instructions?
为了能够 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
集合(以及相应的 Insert
和 Remove
方法)对 Mono.Cecil 进行篡改。
Microsoft.SharePoint
程序集的一些其他方法和属性被成功篡改:ILSpy 显示生成的 IL 代码。
我认为 .maxstack
指令可能有问题(原始 属性 中有 1 个,代理中有 8 个,结果中有 1 个)。在单独的项目上测试了几次,似乎没有效果。
我还怀疑异常可能是原因(原始代码抛出的异常与新代码不同)。经过几次测试on a separate project,似乎也没有效果。
当 IL 以文本形式显示时,异常处理块(.try
、catch
等)显示为实际的 IL 指令块,就像它们在 C# 中一样。
但在二进制形式中,异常处理块是单独存储的(参见§II.25.4.6 Exception handling clauses of ECMA-335)并使用偏移量引用 IL 指令。在 Cecil 中,异常处理程序使用 MethodBody.ExceptionHandlers
属性.
表示
因此,如果您将旧的 MethodBody.Instructions
替换为您自己的指令,很可能旧的异常处理程序的偏移量现在无效,这就是导致问题的原因。 (Cecil 抛出 NullReferenceException
的事实对我来说听起来像是一个错误,请考虑报告它。)
您链接到的另一个未出现此问题的示例是不同的,因为原始方法不包含异常处理程序,它抛出 异常。而 throw
只是一个普通的 IL 指令,它没有特殊的表示形式,例如.try
/catch
是。
为了能够 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
集合(以及相应的Insert
和Remove
方法)对 Mono.Cecil 进行篡改。Microsoft.SharePoint
程序集的一些其他方法和属性被成功篡改:ILSpy 显示生成的 IL 代码。我认为
.maxstack
指令可能有问题(原始 属性 中有 1 个,代理中有 8 个,结果中有 1 个)。在单独的项目上测试了几次,似乎没有效果。我还怀疑异常可能是原因(原始代码抛出的异常与新代码不同)。经过几次测试on a separate project,似乎也没有效果。
当 IL 以文本形式显示时,异常处理块(.try
、catch
等)显示为实际的 IL 指令块,就像它们在 C# 中一样。
但在二进制形式中,异常处理块是单独存储的(参见§II.25.4.6 Exception handling clauses of ECMA-335)并使用偏移量引用 IL 指令。在 Cecil 中,异常处理程序使用 MethodBody.ExceptionHandlers
属性.
因此,如果您将旧的 MethodBody.Instructions
替换为您自己的指令,很可能旧的异常处理程序的偏移量现在无效,这就是导致问题的原因。 (Cecil 抛出 NullReferenceException
的事实对我来说听起来像是一个错误,请考虑报告它。)
您链接到的另一个未出现此问题的示例是不同的,因为原始方法不包含异常处理程序,它抛出 异常。而 throw
只是一个普通的 IL 指令,它没有特殊的表示形式,例如.try
/catch
是。