.net 为什么可以';在调试/反汇编期间,我是否单步执行调用指令?

.net 为什么可以';在调试/反汇编期间,我是否单步执行调用指令?,.net,x86-64,cil,reflection.emit,disassembly,.net,X86 64,Cil,Reflection.emit,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

分解过程如下所示:

                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”是在.NET中使用Reflection.Emit动态创建的。它将两个Int32参数作为“byRef”值。这将作为“发布模式”版本进行调试

我可以一步一步地完成组装,直到“调用”指令。R8和RDX(参数)指向的内存内容看起来很好。我不知道是什么样的魔法允许JIT使用寄存器来调用而不是堆栈,但这不是重点

当我尝试“单步执行”call指令时,调试器将“单步执行”该指令。确实调用了例程-该方法正确地执行了其功能。但我似乎无法分解,也无法进入方法

在调用之前的一点上,RAX包含值000000000 25C67A8H。当向该地址添加18h时,间接寻址的地址变为000000000 25C67C0H。此地址的QWORD为00000000 1B64DC48H

如果我尝试反汇编此地址(00000000 1B64DC48H),调试器将返回“无法显示指定的地址。在提供的位置没有代码”

作为一次万岁的尝试,我尝试在RAX上无需间接地反汇编代码,但正如我预期的那样,这也失败了


有谁能告诉我如何访问地址中的任何代码,或者在分解地址中的代码之前是否需要对地址(RAX+18h)执行类似LEA的操作?

您需要记住,调试器正试图避免浪费您数小时的生命
methShort()
是一个委托调用,在该调用完成之前还需要做很多工作。您将不会喜欢单步执行将动态方法编译为机器代码、CAS检查链接请求、CLR创建代理存根并将其绑定到调用站点的过程

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

  • 项目+属性,调试选项卡,勾选“启用本机代码调试”选项
  • 您必须将调试器从托管模式切换到非托管模式。没有明确的命令,最简单的方法就是使用调用堆栈窗口。双击底部框架(__RtlUserThreadStart@8)
  • 单击源代码不可用弹出窗口中的“查看反汇编”链接,以便返回反汇编窗口。调试器现在处于非托管模式。请注意,新的调试引擎现在显示正确的代码地址,因此您无法再轻松地分辨
  • 现在,您可以在地址框中输入您发现的地址。一定要在它前面加上“0x”
否则,调试动态方法可能不会有用,尽管我尝试时很快就放弃了。通过两次调用该方法,您可能会领先,这样就可以绕过第二次调用的所有开销


另一种完全不同的方法是临时向方法发出对Debugger.Break()的调用,现在更容易了。

在x64上,方法调用的前两个参数位于RCX中,RDX.VS拒绝向您显示运行时的某些部分。这必须是运行时帮助程序调用。我一直不明白为什么他们绝对确保调试器甚至不能反汇编运行时。