在运行时修改导致 InvalidProgramException 的 IL 代码

Posted

技术标签:

【中文标题】在运行时修改导致 InvalidProgramException 的 IL 代码【英文标题】:Modifying IL code causing InvalidProgramException at runtime 【发布时间】:2018-11-20 15:12:06 【问题描述】:

我试图在 IL 代码中删除对 Tick 事件的订阅,以便它永远不会触发。

这是 IL 代码:

IL_0e19:  ldftn      instance void App.Framework.MainForm::mTimer_Tick(object, class [mscorlib]System.EventArgs)
IL_0e1f:  newobj     instance void [mscorlib]System.EventHandler::.ctor(object, native int)
IL_0e24:  callvirt   instance void [System.Windows.Forms]System.Windows.Forms.Timer::add_Tick(class [mscorlib]System.EventHandler)
IL_0e29:  ldarg.0

所以我在想我应该能够删除最后两行来删除订阅,它应该没问题。

IL_0e24:  callvirt   instance void [System.Windows.Forms]System.Windows.Forms.Timer::add_Tick(class [mscorlib]System.EventHandler)
IL_0e29:  ldarg.0

我成功编译了 IL 代码:

ilasm.exe c:\Framework.il /32bitpreferred /dll

现在,当我尝试启动程序时,它会抛出异常:

System.InvalidProgramException: JIT Compiler encountered an internal limitation.

如果我在没有任何修改的情况下编译 IL,则程序运行时不会出现任何异常,因此我正在做出更改。

【问题讨论】:

这听起来像是一种非常复杂的方法。反正。 callvirt 期望 this 参数在堆栈上,所以这是问题的一部分。在程序集上运行 PEVerify 以获取 .NET 对您做错了什么的描述。要真正做到这一点,请在 C# 中编写您想要的代码,然后查看 ildasm(或其他 IL 反汇编程序)显示的内容。 您仍然需要调用ldarg.0 提示:在 dll 上运行 peverify @500-InternalServerError 如果您知道更简单的方法,请告诉我。 想到了更改原始高级源代码。 【参考方案1】:

ldarg0 无关,可能与 下一个 操作有关;所以你不应该放弃它。您还在堆栈上留下了一些东西,因此您应该删除 callvirt,而不是弹出两个值 - 目标实例(未显示加载)和事件处理程序实例(来自ldftn/newobj - 注意还有一个未显示的负载与此步骤相关)。或者 - 首先不要加载这两个值。大概在IL_0e19 之前发生了很多事情,可以做一些清理工作。

基本上,给定的代码如下:

someTimer.Tick += target.SomeMethod;

这是:

加载 someTimer [现在堆栈 someTimer] 加载目标 [stack now someTimer, target] 将 SomeMethod 作为函数指针加载 [现在堆栈 someTimer、目标、函数指针] 为该对象/指针创建一个委托 [stack now someTimer, delegate] 虚拟调用 Tick_add 方法 [堆栈现在为空]

您展示的 IL 以“将 SomeMethod 作为函数指针加载”开始,并包含一个与下一个操作无关的加载。

【讨论】:

以上是关于在运行时修改导致 InvalidProgramException 的 IL 代码的主要内容,如果未能解决你的问题,请参考以下文章

Guard 导致“错误:无法修改字符串;暂时锁定”

Python版本更新导致的 yumex 运行错误

如何修改IDEA的内存

Centos7:修改selinux错误导致服务器起不来

linux 修改硬件时间

Linux单用户模式(修改密码运行级别)方法详解