Author Archive
Triggering a Fault From a Publisher in The Peach Fuzzing Platform
by sirus on Feb.06, 2012, under Fuzzing
Often times when fuzzing obscure targets one finds a need to trigger a fault from within a publisher. Peach itself does not provide a direct facility to do this but extending Peach to provide this functionality is simple.
As Publishers are designed to function as independent entities tasked with only getting mutated data to and from a target they have no direct ability outside of the PublisherSoftException to communicate exceptional conditions. However, a facility exists that allows Publishers to raise events with any attached Monitors directly using the OnPublisherCall method.
This facility allows us to raise specific signals within Monitors which we in-turn can use to raise faults once the Monitor is queried:
from Peach.Engine.engine import Engine from Peach.agent import Monitor class PublisherMonitor(Monitor): def __init__(self, args): self._name = "PublisherMonitor" self._fault = False def OnTestStarting(self): self._fault = False pass def DetectedFault(self): return self._fault def PublisherCall(self, method): if method == "TriggerFault" self._fault = True pass
Given this monitor we can simply invoke the following call to trigger a fault from within a Publisher:
Engine.context.agent.OnPublisherCall("TriggerFault")
Declassify on: 25X1-human
by sirus on Nov.22, 2011, under Uncategorized
Received one of these from Mudge himself for the CINDER work myself and Tim Carstens have been working on the past year as a thank you. Pure awesome and I cannot wait to get working on proposals for Cyber Fast Track.


The schadenfreude of unreliable exploits: Going beyond Dr. Watson
by sirus on Oct.13, 2011, under Security
I recently had the wonderful pleasure of presenting with Tim Carstens at Toorcon 13 about a DARPA CINDER Initiative project we’ve been working on for the past year at Leviathan Security. The description and video follow.
In a world where executables are designed to thwart exploitation, attackers are often forced to take chances: the work-arounds for many modern defenses are often good enough to succeed, but not without generating some crashes along the way.
Building on this premise, we have been engineering tools for collecting Windows crash dumps from networked systems, and building an analytics framework designed to answer the following question: was the crash caused by a routine malfunction, or by a failed exploit?
TLS Renegotiation: Explanation & Exploitation
by sirus on Apr.15, 2011, under TLS
Last night I have a talk on the TLS renegotiation authentication gap vulnerability at the iSec Partners Open Forum.
You can grab a copy of my slide deck here.
The Double-Edged Sword of HSTS Persistence and Privacy
by sirus on Apr.03, 2011, under HSTS, Privacy, Security, TLS
HTTP Strict Transport Security or more commonly known as HSTS is a draft policy by the IETF WebSec working group currently proposed that would extend the standard list of HTTP response headers to allow domain owners to enroll complying browsers into exclusively secure communications with the web server for an asserted period of time.
This is accomplished by rewriting all HTTP requests to that particular domain regardless of entry (be it via link, image or manually typed in the address bar) over HTTPS and validating the certificate chain. If a secure connection cannot be established or the certificate chain cannot be verified then the request fails with a transport level error and is abandoned.
The actual implementation of this is nearly trivial. Over a secure connection the server simply has to return the header specifying how long the browser should exclusively attempt HTTPS connections and a flag whether it should include sub-domains:
Strict-Transport-Security: max-age=31536000; includeSubDomains
Under normal circumstances as long as the user has been to that domain within the max-age of the policy, this is an effective mitigation against sslstrip type attacks which rely on users to initiate an HTTP connection to perform a man-in-the-middle attack against the browsers.
One of the less understood implications of this proposal is the role that wildcard SSL certificates play. When purchasing an SSL certificate the domain owner must decide between a standard certificate that covers only one particular FQDN such as store.domain.com or a (more expensive) wildcard certificate issued to *.domain.com that would encompass multiple sub-domains such auth.domain.com and store.domain.com.
As the certificate wildcard feature is decoupled from the HSTS includeSubDomains flag it leads to interesting behavior that allows an actor such as an advertising company or any other entity to store, retrieve, and edit data in the browser’s database. When a wildcard SSL certificate is used it allows the owner to have a near unlimited number of entires in the HSTS databases as currently implemented by supporting browsers.
An entry in the HSTS database can grant a single-bit of information to an interested party that can be retrieved at a later time. Lets look at an example where we want to store and retrieve the word “HELLO” in a browser’s HSTS database using nothing but forum image tags and a trivial encoding.
To set the bits we would simply need to create a post with the following tags:
[img]https://charcount-5.trackingdomain.com/setbit.png[/img]
[img]https://0-H.trackingdomain.com/setbit.png[/img]
[img]https://1-E.trackingdomain.com/setbit.png[/img]
[img]https://2-L.trackingdomain.com/setbit.png[/img]
[img]https://3-L.trackingdomain.com/setbit.png[/img]
[img]https://4-O.trackingdomain.com/setbit.png[/img]
When a browser goes to each of these URLs over HTTPS the web server would see the /setbit.png key and include a HSTS header with a large max-age value in the response and create an entry in the browser’s HSTS table for each of the sub-domains.
To read this data back out a javascript block on a different domain than the original forum would first brute force the character count by creating resource requests enumerating possible values and having the server respond whether the request came in over HTTP or HTTPS as the requests would have been rewritten by the browser if the sub-domain is present in HSTS database. These requests would look like:
http://charcount-1.trackingdomain.com/getbit.png [ Server: HTTP ]
http://charcount-2.trackingdomain.com/getbit.png [ Server: HTTP ]
http://charcount-3.trackingdomain.com/getbit.png [ Server: HTTP ]
http://charcount-4.trackingdomain.com/getbit.png [ Server: HTTP ]
http://charcount-5.trackingdomain.com/getbit.png [ Server: HTTPS! ]
The same brute-force enumeration process would be performed to retrieve the individual characters of the message body. This enumeration is more effective than the current history enumeration attacks via CSS (here.)
At first this approach looks like a Bloom filter. Seemingly akin to burning in bits permanently and not having the ability to change them but thanks to the max-age specifier of the header it is possible to also clear bits by setting their maximum age to 0:
Request URL: https://charcount-5.trackingdomain.com/clearbit.png
Strict-Transport-Security: max-age=0;
Initially this doesn’t look worse than standard tracking cookie as long as it is cleared on a regular basis but clearing the HSTS database frequently renders it much less effective in preventing the very attacks it sought guard against. Therein lies the classic trade-off of security versus privacy. Of the currently two HSTS supporting browsers there is no consensus on this topic. Chrome opts for increased privacy by clearing HSTS database when cookies are cleared while Firefox 4 opts to store HSTS settings in the separate and infrequently cleared site-preferences database.
So what can be done about this?
My proposal is to amend the draft to force the includeSubDomains flag on wildcard certificates. This would limit them to only one entry in the browsers HSTS database and make the technique above prohibitively expensive to non-CA owners as a separate signed SSL certificate would be needed for every bit of information stored and limit encoding options. That way we can have the best of both worlds, privacy and security.
[PROTIP] Get an Interactive Python Shell in Peach
by sirus on Sep.30, 2010, under Code, Fuzzing
Peach being the monolith that it is, breaks most python debugging tools. As such it makes debugging pit files extremely difficult. I came across the RC of the IPython interactive shell and found that it can actually handle breaking in the middle of cracking input in peach!
Download and install IPython 0.10.1.rc1.
Create a code-behind file for peach with this simple function:
import inspect,IPython
def db():
uframe = inspect.currentframe().f_back
return eval('IPython.Shell.IPShellEmbed([])()', dict(globals().items() + uframe.f_globals.items()),uframe.f_locals)
Now anywhere you have an evaluated field you can execute a db() call and break into a python shell with the full context so you have access to the cracker and the DataEmelent! A great feature of IPython is that is also has tab autocompletion and element inspection.
And here is some example usage:
<Import import="*" from="peach_debug"/>
...
<DataModel name="SampleModel">
<Number name="foo">
<!-- always break -->
<Relation type="when" when="db() or True" />
</Number>
<Blob name="bar">
<!-- always break but maintain a conditional so parsing continues -->
<Relation type="when" when="( db() and False) or int(self.find('foo').getInternalValue()) & 0x1 == 0x1" />
</Blob>
<Blob name="bas">
<!-- break only on specific conditional -->
<Relation type="when" when="( int(self.find('foo').getInternalValue()) == 0x03 and db()) or True" />
</Blob>
</DataModel>
Once you are in the IPython shell try the following:
self?
self??
self.getInternalValue()
self.parent?
self.find('foo')
self.find('foo').getInternalValue()
To break out of the shell and resume execution of peach press Ctrl+Z.
Heap Heap Hurray: Proper C++ Heap Debugging
by sirus on Apr.03, 2009, under Code
In this post I will show you how to make a C++ heap debugger with the following capabilities:
- Detect all memory leaks on program termination with exact file and line of allocation.
- Immediately break the debugger on buffer overflow / underflow.
- Immediately break the debugger on read/writes to freed memory.
- Immediately break the debugger on delete/vector-delete mismatch and double delete.
- Integrate into a project by just compiling in 1 cpp file.
The most common way that people debug the MSVC heap is by redefining new to their down new function in a manner such as this:
#ifdef __DEBUG #define new DEBUG_NEW(__FILE__,__LINE__) #endif
While this seems to be one of the easiest ways to achieve the desired functionality, it is one of the worst ways to do it since the DEBUG_NEW above completely hoses the placement new ( foo*ptr = new (buffer) foo(); ) which is used all over the STL and in any memory manager.
instead what one must do is overload the following 8 functions:
void * operator new(size_t size) throw (std::bad_alloc); void * operator new(size_t size, const std::nothrow_t&) throw (); void * operator new[](size_t size) throw (std::bad_alloc); void * operator new[](size_t size, const std::nothrow_t&); void operator delete(void * ptr) throw (); void operator delete(void * ptr, const std::nothrow_t&) throw (); void operator delete[](void * ptr) throw (); void operator delete[](void * ptr, const std::nothrow_t&) throw ();
Note the existence of nothrow deletes even though delete under no circumstances will throw.
To achieve the functionality of breaking immediately on buffer overflow/underflow will be using virtual pages. Virtual pages have the advantage of having customizable access rights. Meaning that we can mark certain pages PAGE_READWRITE and certain pages PAGE_NOACCESS. When bad code tries to read or write an address that is within a page that is marked PAGE_NOACCESS a structured exception is throw which we will catch.
First we need to align our allocated buffer on the edge of a page marked PAGE_READWRITE next to a page marked PAGE_NOACCESS.
Since page sizes are fixed we can’t get our buffer to have PAGE_NOACCESS pages on both sides of it unless the buffer size is an exact multiple of the size of a page.
For example in the case of overflow protection we’d be setting up the following scenario:

Our internal Heap classes will keep track of a unique heap size. Meaning that we will have a Heap for objects of size 4, a heap for objects of size 8, etc.
Lets define a structure to keep track of individual allocations:
struct HeapListEntry
{
HeapListAllocType Type;
ProtectionLevel Protection;
unsigned __int64 AllocatingAddress;
byte* Data;
};
- The Type field is defines if its a Single or a Vector allocation.
- The Protection field keeps track of the page protection level between None, Partial, and full.
- The AllocatingAddress field is the address of the code where the allocation was initially made. (More on this later)
- the Data pointer is the data we will be handing out to the user.
Now to manage these allocations we will need to keep track of three lists:
//Define out collection. typedef std::vector<HeapListEntry,Mallocator<HeapListEntry> > HLEVECTOR; HLEVECTOR m_Allocated; HLEVECTOR m_Distributed; HLEVECTOR m_Free;
- The Allocated list is the memory we received from the operating system but have not yet distributed to the program.
- The Distributed list is what the program currently has ownership of.
- The Free list is what we gave to the program but it then freed and gave it back.
The reason we have we define a std::allocator for the vectors is because otherwise the vector will use new to allocate that memory and hit our memory debugger again causing an infinite recursion. In this instance the Mallocator simply calls malloc and free for the given size of HeapListEntry.
We want to have the ability to toggle whether we want to detect overflows, underflows, or not use virtual pages at all. Also we want to specify some variables.
//0 off, 1 overflow, 2 underflow #define ACCESSVIOLATIONS 1 #define PAGESIZE 0x4000 #define ALLOCD 0xA0 #define FREED 0xDF #define NOMAND 0xFE #define NOMANSIZE 0x10
- PAGESIZE is the size of the operating system’s pages.
- ALLOCD is the fill byte we use when we have allocated memory but not used it.
- FREED is the fill byte we use when we get memory back from the program.
- NOMAND is the fill byte we use to pad the available data array on either side of our buffer allowing us to detect both underflow and overflow at some point.
- NOMANSIZE is how big the no-man’s land is.
Globally we need to define a New and a Delete function for our operator overloads to call.
typedef std::map<size_t,Heap*,std::less<size_t> ,Mallocator<std::pair<size_t,Heap*> > > HEAPMAP;
HEAPMAP g_Heaps;
void* Alloc(size_t s, bool Vector = false)
{
HEAPMAP::iterator itr = g_Heaps.find(s);
if(itr == g_Heaps.end())
{
Heap* pHeap = new (malloc(sizeof(Heap))) Heap(s);
g_Heaps.insert(std::make_pair(s,pHeap));
}
return g_Heaps.find(s)->second->Alloc(Vector);
}
void Delete(void *ptr,bool Vector = false)
{
if(!ptr)
return;
for (HEAPMAP::iterator i=g_Heaps.begin();i!=g_Heaps.end();++i)
{
if(i->second->IsOwned((byte*)ptr))
{
i->second->Delete(ptr,Vector);
return;
}
}
_ASSERTE(0 && "POINTER NOT OWNED BY MEMORY MANAGER.");
}
This code maps allocation sizes to there individual heaps. If a heap is not found it is created for that size and the code calls their Alloc and Delete functions as necessary.
Our Alloc function looks as follows:
void* Heap::Alloc(bool Vector, unsigned stacklookup /* = 5 */)
{
HeapListEntry hle = GetAvailable();
if(hle.Data)
{
Protect(&hle,PL_PARTIAL);
hle.AllocatingAddress = GetCallingFunction(stacklookup);
hle.Type = Vector?HLAT_VECTOR:HLAT_SINGLE;
m_Distributed.push_back(hle);
}
return hle.Data;
}
We call GetAvailable which first tries to exhaust its free list, then its distributed list and if it still can’t find any allocated regions it will call the operating system to get more memory. Then we initialize our structure, add it to our distributed list and return the data pointer.
The Delete function looks as follows:
void Heap::Delete(void* ptr,bool Vector)
{
if(IsInList(&m_Free,(const byte*)ptr))
{
_ASSERTE(0 && "DOUBLE DELETE DETECTED");
}
else if(IsInList(&m_Distributed,(const byte*)ptr))
{
HeapListEntry* pEntry;
FindHLE(ptr,NULL,&pEntry);
if(pEntry->Type != (Vector?HLAT_VECTOR:HLAT_SINGLE))
{
_ASSERTE(0 && "delete/delete[] missmatch");
}
if(!Validate(pEntry))
{
_ASSERTE(0 && "BUFFER OVERFLOW / UNDERFLOW DETECTED! Turn on ACCESSVIOLATIONS to break immediately!");
}
HeapListEntry entry;
for(HLEVECTOR::iterator i = m_Distributed.begin();i!=m_Distributed.end();++i)
if(i->Data == pEntry->Data)
{
entry = *i;
m_Distributed.erase(i);
break;
}
memset(entry.Data,FREED,m_Size);
Protect(&entry,PL_FULL);
m_Free.push_back(entry);
}
else
{
_ASSERTE(0 && "ATTEMPTING TO DELETE UNOWNED POINTER.");
}
}
Here what we do is check for double frees, make sure that the proper delete is being called and check validate the boundaries before finally adding it to our Free list.
Now lets look at how we actually allocate the virtual pages from the operating system. When we exhaust our Free and Allocated lists we call the Grow() function bellow:
#define TOTALSIZE (NOMANSIZE + m_Size + NOMANSIZE)
#define PAGESPAN ((TOTALSIZE / PAGESIZE) + 1)
//...
void Heap::Grow()
{
unsigned newblocks = (m_Distributed.size() + 1)*2;
for(unsigned i=0;i<newblocks;++i)
{
#if ACCESSVIOLATIONS == 0
byte* ptr = (byte*)malloc(TOTALSIZE);
#else
byte* ptr = (byte*)VirtualAlloc(NULL,(PAGESPAN+1)*PAGESIZE,MEM_COMMIT,PAGE_READWRITE);
#endif
_ASSERTE(ptr && "COULD NOT ALLOCATE MORE MEMORY");
if(ptr)
{
m_SystemAllocated.push_back(ptr);
#if ACCESSVIOLATIONS == 0
// dont need to do anything
#endif
#if ACCESSVIOLATIONS == 1
//detect overflows adjust so we straddle edge of last page
ptr += (PAGESPAN)*PAGESIZE;
ptr -= m_Size + NOMANSIZE;
#endif
#if ACCESSVIOLATIONS == 2
//detect underflows adjust so we straddle edge of first page
ptr += PAGESIZE - NOMANSIZE;
#endif
memset(ptr,NOMAND,NOMANSIZE);
memset(ptr+NOMANSIZE,ALLOCD,m_Size);
memset(ptr+NOMANSIZE+m_Size,NOMAND,NOMANSIZE);
HeapListEntry hle;
hle.Data = ptr+NOMANSIZE;
Protect(&hle,PL_PARTIAL);
m_Allocated.push_back(hle);
}
}
}
Here what we are doing, is allocating the memory from the operating system either via VirtualAlloc or via malloc if ACCESSVIOLATIONS are off. If we are using virtual pages we need to allocate enough pages to span all our data plus one page that will be marked PAGE_NOACCESS.
We then memset our allocation with the proper fill bytes and adjust our data pointer accordingly. Next we Protect the region so that our edge page would be marked PAGE_NOACCESS.
This function SHOULD be expanded to support releasing the free lists on other heaps when allocations fail.
Lets look at the Protect function now:
void Heap::Protect(HeapListEntry* hle, ProtectionLevel level)
{
hle->Protection = level;
#if ACCESSVIOLATIONS == 0
return;
#else
DWORD protlevel;
void* start = NULL;
size_t size;
switch(level)
{
case PL_NONE:
protlevel = PAGE_READWRITE;
start = hle->Data - NOMANSIZE;
size = TOTALSIZE;
break;
case PL_PARTIAL:
{
Protect(hle,PL_NONE);
ProtectedRegion pr = GetProtectedRegion(hle);
protlevel = PAGE_NOACCESS;
start = pr.base;
size = pr.len;
break;
}
case PL_FULL:
protlevel = PAGE_NOACCESS;
start = hle->Data - NOMANSIZE;
size = TOTALSIZE;
break;
}
DWORD oldProt = 0;
if(!VirtualProtect(start,size,protlevel,&oldProt))
{
_ASSERTE(0 && "FAILED TO PROTECT PAGE.");
}
#endif
}
Here we simply find out the regions we need to protect and call virtual protect, pretty straight forward. Theres a trick that happens in the call to GetProtectedRegion though:
//...
Heap::ProtectedRegion Heap::GetProtectedRegion(const HeapListEntry* hle) const
{
ProtectedRegion ret(NULL,0);
#if ACCESSVIOLATIONS == 0
ret.base = hle->Data;
ret.len = 0;
#endif
#if ACCESSVIOLATIONS == 1
ret.base = hle->Data + m_Size ;
ret.len = 1;
#endif
#if ACCESSVIOLATIONS == 2
ret.base = hle->Data - 1;
ret.len = 1;
#endif
return ret;
}
You can’t partially protect pages so its enough to just pass a length of 1 to alter an entire page!
We also want to find out where the allocations happened. Since we can’t use the __FILE__ and __LINE__ macros we have to do something a little more sneaky:
unsigned __int64 Heap::GetCallingFunction(unsigned depth)
{
CONTEXT c;
#ifdef _WIN64
RtlCaptureContext(&c);
#else
c.ContextFlags = CONTEXT_CONTROL;
__asm
{
LABEL: mov eax, [LABEL];
mov c.Eip, eax;
mov c.Ebp, ebp;
mov c.Esp, esp;
}
#endif
STACKFRAME64 stack_frame = {0};
stack_frame.AddrPC.Mode = AddrModeFlat;
stack_frame.AddrPC.Offset = c.Eip;
stack_frame.AddrStack.Mode = AddrModeFlat;
stack_frame.AddrStack.Offset = c.Esp;
stack_frame.AddrFrame.Mode = AddrModeFlat;
stack_frame.AddrFrame.Offset = c.Ebp;
for(unsigned i=0;i<depth;++i)
StackWalk64(IMAGE_FILE_MACHINE_I386,GetCurrentProcess(),GetCurrentThread(),&stack_frame,&c,NULL,SymFunctionTableAccess64,SymGetModuleBase64,NULL);
return stack_frame.AddrPC.Offset;
}
What this code lets us do is find the exact address that made the allocation. This works by walking the actual stack. We capture the current context in one of two ways depending on whether we are compiling in 32bit or 64bit. Using this data we can call StackWalk64 to walk up the stack and find the exact address of the call. We will use this later to find the file and line number of the call.
There is an obvious benefit to doing this as opposed to using the macros. You only need to store a pointer as opposed an entire character array for each allocation and you can ship this code (not that you would want to) without any information disclosure to any one trying to reverse engineer your application.
This function could be expanded to also store the full stack for more intensive debugging.
To make the stack walking work as well as to catch the access violations we are going to throw we will need to do a little bit of initialization. The best way to do this is with global constructor that is guaranteed to be called before any other global. This is achieved with the init_seg(user) pragma.
class SEHInit
{
public:
SEHInit()
{
SymInitialize(GetCurrentProcess(),NULL,true);
SymSetOptions(SymGetOptions() | SYMOPT_LOAD_LINES);
SetUnhandledExceptionFilter(SEHHandler);
}
~SEHInit()
{
for(HEAPMAP::iterator i=g_Heaps.begin();i!=g_Heaps.end();++i)
{
i->second->~Heap();
free(i->second);
}
g_Heaps.clear();
}
};
#pragma init_seg(user)
SEHInit SEH;
In the constructor we call SymInitialize to enable symbol resolution and SymSetOptions to allow us to get the actual file names and line numbers. There is also a call SetUnhandledExceptionFilter to catch our access violations and print a useful message.
I will cover SetUnhandledExceptionFilter and its uses in a future post.
In our destructor we destroy our heaps and free the memory we allocated for them and dump associated memory leaks in the destructors.
Here is the code for our structured exception handler:
LONG WINAPI SEHHandler(EXCEPTION_POINTERS* pException)
{
#if ACCESSVIOLATIONS != 0
EXCEPTION_RECORD* walker = pException->ExceptionRecord;
//get to original exception.
while(walker->ExceptionRecord)
walker = walker->ExceptionRecord;
if(walker->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
{
for(HEAPMAP::iterator i=g_Heaps.begin();i!=g_Heaps.end();++i)
{
if(i->second->IsProtected((byte*)walker->ExceptionAddress))
{
_ASSERTE(0 && "BUFFER OVERFLOW / UNDERFLOW / WRITE AFTER DELETE DETECTED.");
}
}
}
#endif
return EXCEPTION_CONTINUE_SEARCH;
}
We walk the exceptionrecord until we reach the base exception. Then we make sure that the exception itself is an access violation and proceed to print out an error message if the address is on one of our heaps.
On program termination we will dump information about any items that have been allocated but not yet freed as well as validate our memory in case access violations were disabled. This is accomplished via the heap class destructor:
Heap::~Heap()
{
for(HLEVECTOR::iterator i=m_Free.begin();i!= m_Free.end();++i)
if(!Validate(&(*i)))
_ASSERTE(0 && "WRITE AFTER DELETE DETECTED! Turn on ACCESSVIOLATIONS to break imediately!");
for(HLEVECTOR::iterator i=m_Distributed.begin();i!= m_Distributed.end();++i)
{
if(!Validate(&(*i)))
_ASSERTE(0 && "BUFFER OVERFLOW / UNDERFLOW DETECTED! Turn on ACCESSVIOLATIONS to break immediately!");
PrintLeak(i->AllocatingAddress,m_Size);
}
for(VOIDVECTOR::iterator i=m_SystemAllocated.begin();i!=m_SystemAllocated.end();++i)
{
#if ACCESSVIOLATIONS == 0
free(*i);
#else
VirtualFree(*i,0,MEM_RELEASE);
#endif
}
}
Here we walk through our free list and check to make sure that the FREED fill byte is still there. Then we walk through our distributed list and print out every item that is still there for it is now a leak. Then we finally return our memory to the operating system.
Code for this can be downloaded here: heapdebugger.zip
To include it into your existing project just add the MemoryDebugger.cpp file to your project and rebuild.
Lies of The Debugger
by sirus 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:

Even the registers are reporting correct values:

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.
Well I caved.
by sirus on Feb.07, 2009, under Uncategorized
I’ve finally started a blog. First real entry coming in the next couple of days.