Search code examples
visual-studiovisual-studio-2015hookvisual-studio-debugging

Strange memory content display in Visual Studio debug mode


I am writing some multi-thread C program. I tried to modify the few instructions at the beginning of a function's body to redirect the execution to somewhere else.

But I noticed that when debugging within Visual Studio 2015, some memory location seems to be unchangeable as displayed in the Memory window.

For example:

In below picture, a function ApSignalMceToOs() begins at 0x7FFBBEE51360. I have unprotected the memory range 0x7FFBBEE51360 to 0x7FFBBEE5136E to modify it.

Line 305 to 312 modify the address range 0x7FFBBEE51360 ~ 0x7FFBBEE5136E.

Everything is fine until 0x7FFBBEE51369. At line 311, the (uint32_t(((uintptr_t)dst) >> 32 is 0x00007ffb. After line 311 is executed, I was expecting the memory range in 0x7FFBBEE51369 ~ 0x7FFBBEE5136C will be filled as fb 7f 00 00. But as shown below, Visual Studio says it is 48 7f 00 00, where the 48 is the old value.

enter image description here

Then I went to check the disassembly code of the function ApSignalMceToOs(). And not surprisingly, the instruction at 00007FFBBF171365 is mov dword ptr [rsp+4], 7F48h, which should be 7FFB. As shown below in the red box below.

enter image description here

So until this point, Visual Studio 2015 is telling me that my modification would fail.

But as the yellow arrow in above picture shows, after the mov dword ptr [rsp+4], 7F48h is executed, I checked the content in the stack area. Surprisingly it is indeed 7f fb got moved onto the stack (shown in the green box in above picture).

And after the ret instruction is executed, the RIP register does change to 00007FFBBEEAD940, which is no surprise. See below:

enter image description here

And in another function, the same location is being read. Shown as below:

enter image description here The code[len] or byte ptr [rax] is the memory location holding 48 or fb. But it reads 0xcc, which is neither 0x48 nor 0xfb.

Visual Studio disassembly code is decoded from the memory content. So the memory content or how VS2015 read/refresh it is the key point.

Based on above observation, I came to 2 conclusions with VS 2015 debug mode:

  • Some memory content is not correctly shown (or refreshed in GUI).
  • Some memory read operation doesn't work correctly.

But the program runs smoothly when not debugging.

Does anyone know why this is happening?

ADD 1 - 5:08 PM 10/14/2019

Thanks to @MichaelBurr. I guess I can explain it now.

The root cause is I added a breakpoint at 0x00007FFB...369 at the disassembly code level, not the C source level.

enter image description here

When I did this, the VS Debugger did add a 0xCC instruction at the location 0x00007FFB...369. But it seems Visual Studio 2015 goes to great lengths to hide this fact. Below is the show of the memory content with the breakpoint at 0x00007FFB...369, we can see 0x00007FFB...369 still holds the old value 0x48.

enter image description here

But after I manually copied the memory from 0x00007FFB...360 to 0x00007FFB...36e to somewhere else. The 0xCC instruction at the offset 0x9 is unveiled. See below:

enter image description here

When I modify the content at 0x00007FFB...369, Visual Studio seemed to be alerted and it just restored the content to the old preserved one, i.e. 0x48. Not my newly written one.

But I think this very restoration doesn't make any sense. The restoration of the preserved byte content shouldn't be triggered at this moment in any way. A more reasonable action is to update the breakpoint's location a little bit and insert the 0xCC instruction to a new location. Because the newly modified code may change the "instruction boundary". This way, the debug experience of the self-modifying code can be best preserved. But this will require the Visual Studio to disassemble the new code in the nearby. And the new instruction content could be invalid if the programmer made a mistake.


Solution

  • I think you are essentially fighting with the debugger's breakpoint/single step handling. Breakpoints are often implemented with the int 3 instruction which has the encoding 0xCC. When the debugger sets the 0xCC for the breakpoint it has to save the original value, then replace it when the debugger has stopped program execution.

    In a normal situation (code that isn't self-modified) this makes things appear as you expect when examining the code memory region. However if your program modifies the memory that is being managed by the debugger you can get confusing results since the debugger will restore the value it had saved when it set the breakpoint (overwriting your modification).