为什么我不能在调试/反汇编期间进入调用指令?

Why can't I step into a Call instruction during Debug / Disassembly?

反汇编看起来像:

                methShort( ref x, ref y );
000007FF00163F67  lea         r8,[rsp+34h]  
000007FF00163F6C  lea         rdx,[rsp+30h]  
000007FF00163F71  mov         rcx,qword ptr [rsp+20h]  
000007FF00163F76  mov         rcx,qword ptr [rcx+8]  
000007FF00163F7A  mov         rax,qword ptr [rsp+20h]  
000007FF00163F7F  call        qword ptr [rax+18h]  

方法 "methShort" 是使用 Reflection.Emit 在 .NET 中动态创建的。它采用两个 Int32 参数作为 "byRef" 值。这正在作为 "release-mode" 构建进行调试。

我可以单步执行汇编直到 "call" 指令。 R8 和 RDX(参数)指向的内存内容看起来没问题。我不知道是什么魔法允许 JIT 使用寄存器而不是堆栈进行调用,但这不是重点。

当我尝试 "Step Into" 调用指令时,调试器 "steps over" 改为执行它。例程确实被调用了——该方法正确地执行了它的功能。但是我似乎无法反汇编也无法进入该方法。

在调用前一刻,RAX 包含值 00000000025C67A8h。当向其添加 18h 时,间接地址变为 00000000025C67C0h。该地址的 QWORD 是 000000001b64dc48h。

如果我尝试反汇编此地址 (000000001b64dc48h),调试器会返回 "The specified address cannot be displayed. There is no code at the supplied location"。

作为一次万岁尝试,我尝试在没有间接访问的情况下在 RAX 上反汇编代码,但正如我所料,这也失败了。

任何人都可以告诉我如何到达地址处的任何代码,或者是否需要在反汇编那里的代码之前对地址 (RAX+18h) 执行类似于 LEA 的操作?

您需要记住,调试器正在努力避免您浪费几个小时的生命。 methShort() 是委托调用,在调用完成之前还需要完成 lot 的工作。您不会享受通过抖动将动态方法编译为机器代码、CAS 检查 link 需求、CLR 创建委托存根并将其绑定到调用站点的单步执行。

我会按照发布的方式回答问题,调试器不会向您显示调用目标代码,因为它是非托管代码。你可以从地址中看出,远离你的 jitted 代码的位置。说服它你想看到它需要几个技巧:

  • 项目+属性,调试选项卡,勾选"Enable native code debugging"选项
  • 您必须将调试器从托管模式切换到非托管模式。没有明确的命令,我知道如何做到这一点的最简单方法是使用调用堆栈 window。双击底部框架(__RtlUserThreadStart@8)。
  • 单击“源不可用”弹出窗口中的 "view disassembly" link,以便您返回反汇编 window。调试器现在处于非托管模式。请注意,您无法再使用新的调试引擎轻松分辨,因为它现在显示正确的代码地址。
  • 现在您可以在“地址”框中输入您发现的地址。一定要在前面加上“0x”。

除此之外不太可能对调试动态方法有用,尽管我在尝试时很快就认输了。您可能通过两次调用该方法领先,因此您可以绕过第二次调用的所有开销。

一种完全不同的方法是临时发出对 Debugger.Break() 的调用到方法中,现在要容易得多。