Meditation, The Art of Exploitation

Thinking? At last I have discovered it--thought; this alone is inseparable from me. I am, I exist--that is certain. But for how long? For as long as I am thinking. For it could be, that were I totally to cease from thinking, I should totally cease to exist....I am, then, in the strict sense only a thing that thinks.

Tuesday, May 16, 2006

WIN32 SEH and Memory Management considered harmful (Part 2)

Originally composed on May 27, 2004, editted formatting.

We could pretty much exclude direct win32 Virtual Memory management out of the picture now and try to rely on another win32 memory type Heap to make this program work. Now our code looks like this:


except_handler(...){

HeapReAlloc(hhwnd, HEAP_REALLOC_IN_PLACE_ONLY, cur_mem, 2*cur_size);

return EXECUTION_CONTINUE;

if anything is wrong, return EXECUTION_SEARCH;
}

patcher(...){

hhwnd = HeapCreate(0,0,0)
cur_mem = HeapAlloc(hhwnd, initial_size);

__try{
read_data(FILE, tmp_data);
cur_mem[counter]->data = tmp_data;
counter++;
}
__except(except_handler(...)){
do_something;
}
}

Remember that this is just pseudocode skeleton, you must do proper error checking and handling if you are writing real code. It looks nice that we are granted with a HeapReAlloc function to dynamically adjust the allocated block size. However there are two gotchas here.

First, the realloc function is unlikely to conserve the memory content of the previously allocated memory space. Second, this code is simply flatout not working. What's wrong? We have avoided the C-ASM incoherence by reallocating at fixed memory address with the so called HEAP_REALLOC_IN_PLACE_ONLY flag. We also ensure the memory space is large enough. If you debug this code though, you will discover HeapReAlloc will forever return NULL. And you will also notice the exception address is way behond cur_mem+size


|-----------| <- cur_mem | | | | <- cur_mem->size
|-----------|
| |
| |
| |
| | <- Exception happening here | |

The reason of all these is that heap is designed to be a heavy duty memory block and heap manager DOES NOT take care of out of bound memory access. A more detailed Heap structure looks like this:


+-----------+ <- Heap Real Start address
| |
| | <- Heap control structure
|-----------| <- some heap control block (the infamous first_free arena)
| | <- some random block if cur_mem wasn't allocated first
| |
|-----------| <- cur_mem
|header info|
| | <- cur_mem->size
|-----------|
|header info|
| |

Because heap manager does not care about writing beyond block boundary, our code is actually writing way over it's initially allocated memory space and corrupted header info of contigent memory block, thus preventing HeapReAlloc from expanding its size. A simple heap allocation code will reveal that blocks allocated one after another do not have their addresses form a continuous space. Thus when our code triggers the exception, it has already overwritten through its boundary and corrupted the heap. Clearly SEH and Heap memory are mutually exclusive in win32 programming.


Conclusions:

As good an idea as SEH may sound like, in Win32 enviroment, we have demonstrated that SEH will not cooperate Heap memory manage and direct Virtual Memory allocation makes SEH redudent and actually not working. The fact that a single C line of code cannot be guranteed to execute atomically makes SEH debugging very difficult. Same code, may produce different result depending on how it's compiled. The requirement of a fixed base address when working with virtual memory and SEH makes the effort of using SEH seem vain. In conclusion, SEH should be avoided when memory management is invovled, although ironically one of the major reasons for SEH is to handle invalid memory access. Also due to volitile nature of SEH and C interaction, extreme caution should be taken to ensure reproducibile and predicatble program execution.