haxsys.net

Tag: breakpoint

Lies of The Debugger

by on Feb.08, 2009, under Code

While I was working on a class to create debugging minidumps I noticed something quite interesting about the Visual Studio debugger and the way it handles breakpoints and how much it actually hides from the user. Lets take a look at the following sample program:

#include <cstdio>

#define ENTRYPOINT ((intptr_t)main + *(intptr_t *)((char *)main+ 1) + 5)

int main(int, char**)
{
	printf("Main RealEntryPoint = 0x%X \r\n", ENTRYPOINT);
	printf("Main RealEntryPoint[0] = %02X\r\n", *(char *)ENTRYPOINT);
	return 0;
}

class StaticClass
{
public:
	StaticClass()
	{
		printf("SaticClass RealEntryPoint = 0x%X \r\n", ENTRYPOINT);
		printf("SaticClass RealEntryPoint[0] = %02X\r\n", *(char *)ENTRYPOINT);
	}
} GLOBAL;

This program takes advantage of the fact that globals are constructed before any other user code has a chance to execute. My original plan was to hook the main() function and call some error-handling code which would set up structured exception handlers before any of the users code has a chance to crash. In theory this would have allowed very simple integration by simply including the .cpp file in the project.

This program performs a virtual-function-table resolution on the entry point and prints the address and the first byte stored there. Once during the construction of the StaticClass GLOBAL and once during the execution of main().  When run outside of the Visual Studio debugger the output is just as we expected:

SaticClass RealEntryPoint = 0x413460
SaticClass RealEntryPoint[0] = 55
Main RealEntryPoint = 0x413460
Main RealEntryPoint[0] = 55

However, if we run this code with the Visual Studio debugger attached something quite interesting happens:

SaticClass RealEntryPoint = 0x413460
SaticClass RealEntryPoint[0] = CC
Main RealEntryPoint = 0x413460
Main RealEntryPoint[0] = 55

Our code has apparently changed between the execution of the class constructor and main()!  However, if we follow up and look at the memory we see that the value is still completely unchanged and is what we were originally expecting:

lies2

Even the registers are reporting correct values:

lies1

So what’s going on here?  Well, if we look into what that reported value means and the fact that it happens only when under the Visual Studio debugger and not under any other debuggers such as IDA Pro we can get a much clearer picture. 0xCC is the x86 opcode for the assembly instruction int 3 which is a hardware interrupt that signals the operating system that a breakpoint was hit. Unannounced to the user, Visual Studio sets up a breakpoint hook at the beginning of main() immediately after spawning the process.

Being a somewhat decent debugger, Visual Studio knows about where it has set its breakpoints, be it by the user’s actions or the debugging engine itself.  As a result it knows specifically when to lie.  That’s why we see the correct value in the memory view as well as the watch window and the disassembly.   This isn’t necessarily a bad thing most of the time since the abstractions are usually harmless until you start start going into advanced areas such as trying to hook your own functions in a non-obvious manner.

Trust your debugger, just don’t rely on it 100% of the time to actually tell you what is happening behind the scenes.

Leave a Comment :, , , more...

Looking for something?

Use the form below to search the site:

Still not finding what you're looking for? Drop a comment on a post or contact us so we can take care of it!

Visit our friends!

A few highly recommended friends...