在 Visual Studio 调试模式下显示奇怪的内存内容

Strange memory content display in Visual Studio debug mode

我正在编写一些多线程 C 程序。我试图修改函数体开头的几条指令,以将执行重定向到其他地方。

但我注意到在 Visual Studio 2015 内调试时,某些内存位置似乎无法更改,如 Memory window.

中所示

例如:

在下图中,函数 ApSignalMceToOs()0x7FFBBEE51360 开始。 0x7FFBBEE513600x7FFBBEE5136E的内存范围我已经取消保护修改了

305~312行修改地址范围0x7FFBBEE51360~0x7FFBBEE5136E

0x7FFBBEE51369 之前一切都很好。在第 311 行,(uint32_t(((uintptr_t)dst) >> 320x00007ffb。 执行完第311行后,本以为0x7FFBBEE51369~0x7FFBBEE5136C的内存范围会被填充为fb 7f 00 00。但是如下图,Visual Studio表示是48 7f 00 00,其中48是旧值。

然后我去查看函数ApSignalMceToOs()的反汇编代码。毫不奇怪,00007FFBBF171365 处的指令是 mov dword ptr [rsp+4], 7F48h 应该7FFB。如下图红色框内

所以直到现在,Visual Studio 2015 告诉我我的修改 失败。

但是如上图黄色箭头所示,mov dword ptr [rsp+4], 7F48h执行完后,我查看了栈区的内容。 令人惊讶的是它确实7f fb被移到了堆栈上(显示在上方[=136]中的绿色框中=] 图片).

ret指令执行后,RIP寄存器确实变成了00007FFBBEEAD940,这并不奇怪。见下文:

并且在另一个函数中,正在读取相同的位置。如下图:

code[len]byte ptr [rax] 是保存 48fb 的内存位置。但它显示为 0xcc,既不是 0x48 也不是 0xfb

Visual Studio反汇编代码是从内存内容中解码出来的。所以内存内容还是VS2015如何read/refresh呢才是重点。

基于以上观察,我对 VS 2015 调试模式得出了 2 个结论:

  • 某些内存内容未正确显示(或在 GUI 中刷新)。
  • 某些内存读取操作无法正常工作。

但是程序在不调试的时候运行很流畅

有人知道为什么会这样吗?

添加 1 - 5:08 下午 10/14/2019

感谢@MichaelBurr。我想我现在可以解释了。

根本原因是我在 0x00007FFB...369 在反汇编代码级别添加了一个断点,而不是 C 源代码级别。

当我这样做时,VS 调试器 确实 在位置 0x00007FFB...369 添加了一条 0xCC 指令。但似乎 Visual Studio 2015 年竭尽全力掩盖这一事实。下面是断点在0x00007FFB...369的内存内容显示,我们可以看到0x00007FFB...369仍然保持旧值0x48

但是在我手动将内存从0x00007FFB...360复制到0x00007FFB...36e到其他地方之后。偏移 0x9 处的 0xCC 指令是 公开的 。见下文:

当我修改0x00007FFB...369处的内容时,Visual Studio似乎被提醒,它只是将内容恢复到旧的保留内容,即0x48。不是我新写的

但我认为这种修复没有任何意义。此时不应以任何方式触发保留字节内容的恢复。更合理的做法是稍微更新断点的位置,将 0xCC 指令插入到新位置。因为新修改的代码可能会改变"instruction boundary"。这样,可以最好地保留自修改代码的调试体验。但这将需要 Visual Studio 在附近反汇编新代码。并且如果程序员出错,新的指令内容可能无效。

我认为您实际上是在与调试器的 breakpoint/single 步骤处理作斗争。断点通常使用编码为 0xCCint 3 指令实现。当调试器为断点设置 0xCC 时,它必须保存原始值,然后在调试器停止程序执行时替换它。

在正常情况下(未自行修改的代码),这会使检查代码内存区域时出现您预期的情况。但是,如果您的程序修改了由调试器管理的内存,您可能会得到令人困惑的结果,因为调试器将恢复它在设置断点时保存的值(覆盖您的修改)。