Click here to Skip to main content
15,896,111 members
Articles / Desktop Programming / MFC

Import/Export Registry Sections as XML

Rate me:
Please Sign up or sign in to vote.
4.33/5 (16 votes)
21 Jan 20033 min read 168K   6.1K   73  
Export registry sections as XML to simplify registry diffs
This article details a tool aimed to import/export registry sections in XML format, to make registry diff easier in practice.
/*////////////////////////////////////////////////////////////////////////////
 *  Project:
 *    Memory_and_Exception_Trace
 *
 * ///////////////////////////////////////////////////////////////////////////
 *	File:
 *		Stackwalker.cpp
 *
 *	Remarks:
 *    Dumps the stack of an thread if an exepction occurs
 *    Dumps memory leaks (unreleased allocations)
 *
 *	Note:
 *    Usage as AllocChecking:
 *    (only available in Debug-Versions (if _DEBUG is defined)):
 *    If you change the source, then it can also be used in Release-Version
 *    if you create also the PDB-File
 *
 *    At the beginning of the programm you have to call 
 *    InitAllocCheck();
 *    The (optinal) parameters are:
 *    BOOL bSetUnhandledExeptionFilter
 *      If set to TRUE (default) it will set an unhandled exception filter
 *    ULONG ulShowStackAtAlloc: See below.
 *
 *    or the more complicated way:
 *
 *    InitAllocCheckWN(pszLogFileName, ulShowStackAtAlloc)';
 *    pszLogFileName:     Name of the logfile 
 *    ulShowStackAtAlloc: Level of detail at allocation:
 *       0 = Do not write any output during 
 *           runtime-alloc-call (default)
 *       1 = Write only the alloc action (malloc, 
 *           realloc, free)
 *       2 = Write alloc action and callstack only 
 *           for malloc/realloc
 *       3 = Write alloc action and callstack for 
 *           all actions
 *
 *   At the end of the programm you have to call 'DeInitAllocCheck()',
 *   then all Memory leaks (for the C-Runtime heap) will be dumped with 
 *   an stackdump starting with the allocation address.
 *   This is very helpfull if you make use of STL (which internal alloc memory
 *   and the buildin leak-detection will only give you the STL-Line/File)
 *
 *   If the LogFileName could not be opened or created, then the info will be 
 *   redirected to 'stdout'
 *   InitAllocCheck could only be called once!
 *
 *  Warning:
 *    - Works with VS C++ 5/6/7
 *    - Tested OS: W-NT4, W-2000, W-XP, should also work with Win9x
 *    - For W-NT4 you have to install the new version of IMAGEHLP.DLL (5.0.2195.1) 
 *      and DBGHELP.DLL 5.0.2195.1) from W2K (or install NT4 SP6a).
 *      If you do not install this DLLs you are not able to get the 
 *      callstack, because the old version of imagehlp.dll are not 
 *      able to read the new PDB-Files from MS VC++ 6.0
 *      If you link with COFF-Symbols (or without using PDB-Files)
 *      you can at least see the callstack (without line numbers) at 
 *      runtime, but you are not able to see the callstacks at 
 *      the output of the leaks
 *    - Generates some warnings if you compile with /Wp64 (Detect 64-Bit Portability Issues)
 *
 * Example:
 *      InitAllocCheck();  // Init alloc checker
 *
 *      char *pTest1, *pTest2;
 *      pTest1 = malloc(100);    // malloc memory for test
 *      pTest2 = malloc(200);    // malloc memory for test
 *      free(pTest1);            // free only one memory pointer
 *      DeInitAllocCheck();      // will report the memory leak and 
 *                               // close the file
 *
 * Usage as try/except (C/C++):
 * InitAllocCheck has to be called first!
 *
 *       InitAllocCheck(_T("C:\\AllocCheck.log"));  // Init alloc checker
 *       __try
 *       {
 *         char *p = NULL;
 *         *p = 'A'; // BANG!
 *       }
 *       __except ( StackwalkFilter( GetExceptionInformation(), 
 *         EXCEPTION_CONTINUE_SEARCH, _T("<LogFileName and Path>") ) )
 *       {
 *         printf( "Handled exception.\n" );
 *       }
 *
 *
 *  Example of Output for Memory-Leaks:
 *
 *  
 *  Known bugs:
 *    - If the allocation-RequestID wrap, then allocations will get lost...
 *
 *	Author:
 *		Jochen Kalmbach, Germany
 *
 *//////////////////////////////////////////////////////////////////////////////

#include <windows.h>
#include <string>
#include <vector>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <crtdbg.h>
#include <tchar.h>

#include "Stackwalker.h"

// If the following is defined, only the used memories are stored in the hash-table. 
// If the memory is freed, it will be removed from the hash-table (to reduce memory)
// Consequences: At DeInitAllocHook, only Leaks will be reported
#define HASH_ENTRY_REMOVE_AT_FREE


// 0 = Do not write any output during runtime-alloc-call
// 1 = Write only the alloc action (malloc, realloc, free)
// 2 = Write alloc action and callstack only for malloc/realloc
// 3 = Write alloc action and callstack for all actions
static ULONG g_ulShowStackAtAlloc = 0;


// Size of Hash-Table
#define ALLOC_HASH_ENTRIES 1024


// Size of Callstack-trace in bytes (0x500 => appr. 5-9 functions, depending on parameter count for each function)
#define MAX_ESP_LEN_BUF 0x500


// Normally we can ignore allocations from the Runtime-System
#define IGNORE_CRT_ALLOC

// MaxSize: 128 KByte (only for StackwalkFilter)
#define LOG_FILE_MAX_SIZE 1024*128



#ifdef _IMAGEHLP_
#error "'imagehlp.h' should only included here, not before this point! Otherwise there are some problems!"
#endif
#pragma pack( push, before_imagehlp, 8 )
#include <imagehlp.h>
#pragma pack( pop, before_imagehlp )
#if API_VERSION_NUMBER < 7  // ImageHelp-Version is older.... so define it by mayself
// The following definition is only available with VC++ 6.0 or higher, so include it here
extern "C" {
//
// source file line data structure
//
typedef struct _IMAGEHLP_LINE
{
    DWORD                       SizeOfStruct;           // set to sizeof(IMAGEHLP_LINE)
    DWORD                       Key;                    // internal
    DWORD                       LineNumber;             // line number in file
    PCHAR                       FileName;               // full filename
    DWORD                       Address;                // first instruction of line
} IMAGEHLP_LINE, *PIMAGEHLP_LINE;
#define SYMOPT_LOAD_LINES        0x00000010
}  // extern "C"
#endif



// Forward definitions of functions:
static void ShowStackRM( HANDLE hThread, CONTEXT& c, FILE *fLogFile, PREAD_PROCESS_MEMORY_ROUTINE ReadMemoryFunction, HANDLE hProcess);
static void ShowStack( HANDLE hThread, CONTEXT& c, FILE *fLogFile);

static void AllocHashOut(FILE*);
static ULONG AllocHashOutLeaks(FILE*);



// Globale Vars:
static TCHAR *g_pszAllocLogName = NULL;
static FILE *g_fFile = NULL;

// AllocCheckFileOpen
//  Checks if the log-file is already opened
//  if not, try to open file (append or create if not exists)
//  if open failed, redirect output to stdout
static void AllocCheckFileOpen(void) {
  // is the File already open? If not open it...
  if (g_fFile == NULL)
    if (g_pszAllocLogName != NULL)
      g_fFile = _tfopen(g_pszAllocLogName, _T("a"));
  if (g_fFile == NULL)
    g_fFile = stdout;
}

// Write Date/Time to specified file (will also work after 2038)
static void WriteDateTime(FILE *fFile) {
  TCHAR pszTemp[11], pszTemp2[11];

  if (fFile != NULL) {
    _tstrdate( pszTemp );
    _tstrtime( pszTemp2 );
    _ftprintf(fFile,  _T("%s %s"), pszTemp, pszTemp2 );  // also ok after year 2038 (asctime is NOT ok)
  }
}  // WriteDateTime



/*******************************************************************************
 * Hash-Tabelle
 *******************************************************************************/
// Memory for the EIP-Address (is used by the ShowStack-method)
#define MAX_EIP_LEN_BUF 4

#define ALLOC_ENTRY_NOT_FOUND 0xFFFFFFFF
typedef struct AllocHashEntryType {
  long                       lRequestID;    // RequestID from CRT (if 0, then this entry is empty)
  size_t                     nDataSize;     // Size of the allocated memory
  char                       cRemovedFlag;  // 0 => memory was not yet released
  struct AllocHashEntryType  *Next;
  // Callstack for EIP
  DWORD                      dwEIPOffset;
  DWORD                      dwEIPLen;
  char                       pcEIPAddr[MAX_EIP_LEN_BUF];
  // Callstack for ESP
  DWORD                      dwESPOffset;
  DWORD                      dwESPLen;
  char                       pcESPAddr[MAX_ESP_LEN_BUF];
} AllocHashEntryType;

static AllocHashEntryType AllocHashTable[ALLOC_HASH_ENTRIES];
static ULONG AllocHashEntries = 0;
static ULONG AllocHashCollisions = 0;
static ULONG AllocHashFreed = 0;
static ULONG AllocHashMaxUsed = 0; // maximal number of concurrent entries
static ULONG AllocHashCurrentCount = 0;

static ULONG AllocHashMaxCollisions = 0;
static ULONG AllocHashCurrentCollisions = 0;


static void AllocHashInit(void) {

  memset(AllocHashTable, 0, sizeof(AllocHashTable));
  AllocHashEntries = 0;
  AllocHashCollisions = 0;
  AllocHashFreed = 0;
  AllocHashCurrentCount = 0;
  AllocHashMaxUsed = 0;

  AllocHashMaxCollisions = 0;
  AllocHashCurrentCollisions = 0;
  return;
}  // AllocHashInit


// AllocHashDeinit
// Returns the number of bytes, that are not freed (leaks)
static ULONG AllocHashDeinit(void) {
  ULONG ulRet = 0;
  AllocCheckFileOpen();  // open global log-file

  _ftprintf(g_fFile, _T("\n##### Memory Report ########################################\n"));
  WriteDateTime(g_fFile);
  _ftprintf(g_fFile, _T("\n"));

#ifndef HASH_ENTRY_REMOVE_AT_FREE
  // output the used memory
  _ftprintf(g_fFile, _T("##### Memory used: #########################################\n"));
  AllocHashOut(g_fFile);
#endif

  // output the Memoty leaks
  _ftprintf(g_fFile, _T("\n##### Leaks: ###############################################\n"));
  ulRet = AllocHashOutLeaks(g_fFile);

  // output some statistics from the hash-table
  _ftprintf(g_fFile, _T("***** Hash-Table statistics:\n"));
  _ftprintf(g_fFile, _T("      Table-Size:     %i\n"), ALLOC_HASH_ENTRIES);
  _ftprintf(g_fFile, _T("      Inserts:        %i\n"), AllocHashEntries);
  _ftprintf(g_fFile, _T("      Freed:          %i\n"), AllocHashFreed);
  _ftprintf(g_fFile, _T("      Sum Collisions: %i\n"), AllocHashCollisions);
  _ftprintf(g_fFile, _T("\n"));
  _ftprintf(g_fFile, _T("      Max used:       %i\n"), AllocHashMaxUsed);
  _ftprintf(g_fFile, _T("      Max Collisions: %i\n"), AllocHashMaxCollisions);

  // Free Hash-Table
  ULONG ulTemp;
  AllocHashEntryType *pHashEntry, *pHashEntryOld;

  // Now, free my own memory
  for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {
    pHashEntry = &AllocHashTable[ulTemp];
    while(pHashEntry != NULL) {
      pHashEntryOld = pHashEntry;
      pHashEntry = pHashEntry->Next;
      if (pHashEntryOld != &AllocHashTable[ulTemp]) {
        // now free the dynamically allocated memory
        free(pHashEntryOld);
      }
    }  // while
  }  // for
  // empty the hash-table
  memset(AllocHashTable, 0, sizeof(AllocHashTable));

  return ulRet;
}  // AllocHashDeinit

// AllocHashFunction
// The has-function (very simple)
static inline ULONG AllocHashFunction(long lRequestID) {
  // I couldn�t find any better and faster
  return lRequestID % ALLOC_HASH_ENTRIES;
}  // AllocHashFunction

// AllocHashInsert
//   lRequestID: Key-Word (RequestID from AllocHook)
//   pContext:   Context-Record, for retrieving Callstack (EIP and EBP is only needed)
//   nDataSize:  How many bytes
static void AllocHashInsert(long lRequestID, CONTEXT &Context, size_t nDataSize) {
  ULONG HashIdx;
  AllocHashEntryType *pHashEntry;

  // change statistical data
  AllocHashEntries++;
  AllocHashCurrentCount++;
  if (AllocHashCurrentCount > AllocHashMaxUsed)
    AllocHashMaxUsed = AllocHashCurrentCount;

  // generate hash-value
  HashIdx = AllocHashFunction(lRequestID);

  pHashEntry = &AllocHashTable[HashIdx];
  if (pHashEntry->lRequestID == 0) {
    // Entry is empty...
  }
  else {
    // Entry is not empy! make a list of entries for this hash value...
    // change statistical data
    // if this happens often, you should increase the hah size or change the heash-function; 
    // to fasten the allocation time
    AllocHashCollisions++;
    AllocHashCurrentCollisions++;
    if (AllocHashCurrentCollisions > AllocHashMaxCollisions)
      AllocHashMaxCollisions = AllocHashCurrentCollisions;

    while(pHashEntry->Next != NULL) {
      pHashEntry = pHashEntry->Next;
    }

    pHashEntry->Next = (AllocHashEntryType*) calloc(sizeof(AllocHashEntryType), 1);
    pHashEntry = pHashEntry->Next;

  }
  pHashEntry->lRequestID = lRequestID;  // Key-Word
  pHashEntry->nDataSize = nDataSize;
  pHashEntry->Next = NULL;
  // Get EIP and save it in the record
  pHashEntry->dwEIPOffset = Context.Eip;
  if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Eip, &(pHashEntry->pcEIPAddr), MAX_EIP_LEN_BUF, &(pHashEntry->dwEIPLen)) == 0) {
    // Could not read memory... remove everything...
    memset(pHashEntry->pcEIPAddr, 0, MAX_EIP_LEN_BUF);
    pHashEntry->dwEIPLen = 0;
    pHashEntry->dwEIPOffset = 0;
  }

  // Get ESP and save it in the record
  pHashEntry->dwESPOffset = Context.Ebp;
  if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Ebp, &(pHashEntry->pcESPAddr), MAX_ESP_LEN_BUF, &(pHashEntry->dwESPLen)) == 0) {
    // Could not read memory... remove everything...
    memset(pHashEntry->pcESPAddr, 0, MAX_ESP_LEN_BUF);
    pHashEntry->dwESPLen = 0;
    pHashEntry->dwESPOffset = 0;

    // Check if I tried to read too much...
    if (GetLastError() == ERROR_PARTIAL_COPY)
    {
      // ask how many I can read:
      MEMORY_BASIC_INFORMATION MemBuffer;
      DWORD dwRet = VirtualQuery((LPCVOID) Context.Ebp, &MemBuffer, sizeof(MemBuffer));
      if (dwRet > 0)
      {
        // calculate the length
        SSIZE_T len = ((DWORD) MemBuffer.BaseAddress + MemBuffer.RegionSize) - Context.Ebp;
        if ( (len > 0) && (len < MAX_ESP_LEN_BUF) )
        {
          // try to read it again (with the shorter length)
          if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Ebp, &(pHashEntry->pcESPAddr), len, &(pHashEntry->dwESPLen)) == 0)
          {
            // ok, now everything goes wrong... remove it...
            memset(pHashEntry->pcESPAddr, 0, MAX_ESP_LEN_BUF);
            pHashEntry->dwESPLen = 0;
            pHashEntry->dwESPOffset = 0;
          }
          else
          {
            pHashEntry->dwESPOffset = Context.Ebp;
          }
        }
      } // VirtualQuery was successfully
    }  // ERROR_PARTIAL_COPY
  }
}

// AllocHashFind
//   If ALLOC_ENTRY_NOT_FOUND is returned, the Key was not found!
//   If the Key was found, a pointer to the entry is returned
static AllocHashEntryType *AllocHashFind(long lRequestID) {
  ULONG HashIdx;
  AllocHashEntryType *pHashEntry;

  // get the Hash-Value
  HashIdx = AllocHashFunction(lRequestID);

  // Just do some simple checks:
  _ASSERTE(HashIdx < ALLOC_HASH_ENTRIES);

  pHashEntry = &AllocHashTable[HashIdx];
  while(pHashEntry != NULL) {
    if (pHashEntry->lRequestID == lRequestID) {
      return pHashEntry;
    }
    pHashEntry = pHashEntry->Next;
  }

  // entry was not found!
  return (AllocHashEntryType*) ALLOC_ENTRY_NOT_FOUND;
}  // AllocHashFind

// AllocHashRemove
//   Return: FALSE (0) : Key was found and removed/marked
//           TRUE (!=0): Key was not found
static BOOL AllocHashRemove(long lRequestID) {
  ULONG HashIdx;
  AllocHashEntryType *pHashEntry, *pHashEntryLast;

  // get the Hash-Value
  HashIdx = AllocHashFunction(lRequestID);

  // Just do some simple checks:
  _ASSERTE(HashIdx < ALLOC_HASH_ENTRIES);

  pHashEntryLast = NULL;
  pHashEntry = &AllocHashTable[HashIdx];
  while(pHashEntry != NULL) {
    if (pHashEntry->lRequestID == lRequestID) {
#ifdef HASH_ENTRY_REMOVE_AT_FREE
      AllocHashFreed++;
      AllocHashCurrentCount--;
      // release my memory
      if (pHashEntryLast == NULL) {
        // It is an entry in the table, so do not release this memory
        if (pHashEntry->Next == NULL) {
          // It was the last entry, so empty the table entry
          memset(&AllocHashTable[HashIdx], 0, sizeof(AllocHashTable[HashIdx]));
        }
        else {
          // There are some more entries, so shorten the list
          *pHashEntry = *(pHashEntry->Next);
          // TODO: Do I need to free the memory here !? I think I should do this...
        }
        return TRUE;
      }
      else {
        // now, I am in an dynamic allocated entry
        // it was a collision, so decrease the current collision count
        AllocHashCurrentCollisions--;
        pHashEntryLast->Next = pHashEntry->Next;
        free(pHashEntry);
        return TRUE;
      }
#else
      // increase the Remove-Count and let the objet stay in memory
      pHashEntry->cRemovedFlag++;
      return TRUE;
#endif
    }
    pHashEntryLast = pHashEntry;
    pHashEntry = pHashEntry->Next;
  }

  // if we are here, we could not find the RequestID
  return FALSE;
}

// ReadProcMemoryFromHash
//   Callback-Funtion for StackWalk for my own CallStack from the Hash-Table-Entries
#if API_VERSION_NUMBER >= 9
static BOOL __stdcall ReadProcMemoryFromHash(HANDLE hRequestID, DWORD lpBaseAddress, PVOID lpBuffer, DWORD nSize, PDWORD lpNumberOfBytesRead) {
#else
static BOOL __stdcall ReadProcMemoryFromHash(HANDLE hRequestID, LPCVOID lpBaseAddress, PVOID lpBuffer, DWORD nSize, PDWORD lpNumberOfBytesRead) {
#endif
  // Try to find the RequestID
  AllocHashEntryType *pHashEntry;
  *lpNumberOfBytesRead = 0;

  pHashEntry = AllocHashFind((LONG) hRequestID);
  if (pHashEntry == (AllocHashEntryType*) ALLOC_ENTRY_NOT_FOUND) {
    // Not found, so I cannot return any memory
    *lpNumberOfBytesRead = 0;
    return FALSE;
  }
  if ( ((DWORD) lpBaseAddress >= pHashEntry->dwESPOffset) && ((DWORD) lpBaseAddress <= (pHashEntry->dwESPOffset+pHashEntry->dwESPLen)) ) {
    // Memory is located in ESP:
    // Calculate the offset
    DWORD dwOffset = (DWORD) lpBaseAddress - pHashEntry->dwESPOffset;
    DWORD dwSize = __min(nSize, MAX_ESP_LEN_BUF-dwOffset);
    memcpy(lpBuffer, &(pHashEntry->pcESPAddr[dwOffset]), dwSize);
    *lpNumberOfBytesRead = dwSize;
    if (dwSize != nSize)
      return FALSE;
  }

  if ( ((DWORD) lpBaseAddress >= pHashEntry->dwEIPOffset) && ((DWORD) lpBaseAddress <= (pHashEntry->dwEIPOffset+pHashEntry->dwEIPLen)) ) {
    // Memory is located in EIP:
    // Calculate the offset
    DWORD dwOffset = (DWORD) lpBaseAddress - pHashEntry->dwEIPOffset;
    DWORD dwSize = __min(nSize, MAX_ESP_LEN_BUF-dwOffset);
    memcpy(lpBuffer, &(pHashEntry->pcEIPAddr[dwOffset]), dwSize);
    *lpNumberOfBytesRead = dwSize;
    if (dwSize != nSize)
      return FALSE;
  }
  
  if (*lpNumberOfBytesRead == 0)  // Memory could not be found
    return FALSE;

  return TRUE;
}

// AllocHashOutLeaks
// Write all Memory (with callstack) which was not freed yet
//   Returns the number of bytes, that are not freed (leaks)
ULONG AllocHashOutLeaks(FILE *fFile) {
  ULONG ulTemp;
  AllocHashEntryType *pHashEntry;
  ULONG ulCount = 0;
  ULONG ulLeaksByte = 0;

  // Move throu every entry
  for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {
    pHashEntry = &AllocHashTable[ulTemp];
    if (pHashEntry->lRequestID != 0) {
      while(pHashEntry != NULL) {
        if ( (pHashEntry->cRemovedFlag <= 0) || (pHashEntry->cRemovedFlag > 1) ) {
          ulCount++;
          _ftprintf(fFile, _T("RequestID: %12i, Removed: %i, Size: %12i\n"), pHashEntry->lRequestID, pHashEntry->cRemovedFlag, pHashEntry->nDataSize);
          CONTEXT c;
          memset( &c, '\0', sizeof c );
          c.Eip = pHashEntry->dwEIPOffset;
          c.Ebp = pHashEntry->dwESPOffset;
          ShowStackRM( NULL, c, fFile, &ReadProcMemoryFromHash, (HANDLE) pHashEntry->lRequestID);
          // Count the number of leaky bytes
          if (pHashEntry->nDataSize > 0)
            ulLeaksByte += pHashEntry->nDataSize;
          else
            ulLeaksByte++;  // If memory was allocated with zero bytes, then just increase the counter 1
        }
        pHashEntry = pHashEntry->Next;
      }
    }
  }
  _ftprintf(fFile, _T("\n**** Number of leaks: %i\n"), ulCount);
  return ulLeaksByte;
}  // AllocHashOutLeaks

// Write all used memory to a file
void AllocHashOut(FILE *fFile) {
  ULONG ulTemp;
  AllocHashEntryType *pHashEntry;

  for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {
    pHashEntry = &AllocHashTable[ulTemp];
    if (pHashEntry->lRequestID != 0) {
      while(pHashEntry != NULL) {
        _ftprintf(fFile, _T("RequestID: %12i, Removed: %i, Size: %12i\n"), pHashEntry->lRequestID, pHashEntry->cRemovedFlag, pHashEntry->nDataSize);
        pHashEntry = pHashEntry->Next;
      }
    }
  }
}  // AllocHashOut

/*******************************************************************************
 * Ende der Hash-Tabelle
 *******************************************************************************/


// The follwoing is copied from dbgint.h:
// <CRT_INTERNALS>
/*
 * For diagnostic purpose, blocks are allocated with extra information and
 * stored in a doubly-linked list.  This makes all blocks registered with
 * how big they are, when they were allocated, and what they are used for.
 */

#define nNoMansLandSize 4

typedef struct _CrtMemBlockHeader
{
        struct _CrtMemBlockHeader * pBlockHeaderNext;
        struct _CrtMemBlockHeader * pBlockHeaderPrev;
        char *                      szFileName;
        int                         nLine;
#ifdef _WIN64
        /* These items are reversed on Win64 to eliminate gaps in the struct
         * and ensure that sizeof(struct)%16 == 0, so 16-byte alignment is
         * maintained in the debug heap.
         */
        int                         nBlockUse;
        size_t                      nDataSize;
#else  /* _WIN64 */
        size_t                      nDataSize;
        int                         nBlockUse;
#endif  /* _WIN64 */
        long                        lRequest;
        unsigned char               gap[nNoMansLandSize];
        /* followed by:
         *  unsigned char           data[nDataSize];
         *  unsigned char           anotherGap[nNoMansLandSize];
         */
} _CrtMemBlockHeader;
#define pbData(pblock) ((unsigned char *)((_CrtMemBlockHeader *)pblock + 1))
#define pHdr(pbData) (((_CrtMemBlockHeader *)pbData)-1)
 
// </CRT_INTERNALS>




// Global data:
static BOOL g_bInitialized = FALSE;
static HINSTANCE g_hImagehlpDll = NULL;

static DWORD g_dwShowCount = 0;  // increase at every ShowStack-Call
static CRITICAL_SECTION g_csFileOpenClose = {0};

// Is used for syncronising call to MyAllocHook (to prevent reentrant calls)
static LONG g_lMallocCalled = 0;


// Deaktivate AllocHook, by increasing the Syncronisation-Counter
static void DeactivateMallocStackwalker(void) {
  InterlockedIncrement(&g_lMallocCalled);
}


// MyAllocHook is Single-Threaded, that means the the calls are serialized in the calling function!
static int MyAllocHook(int nAllocType, void *pvData, 
      size_t nSize, int nBlockUse, long lRequest, 
      const unsigned char * szFileName, int nLine ) {

  static TCHAR *operation[] = { _T(""), _T("ALLOCATIONG"), _T("RE-ALLOCATING"), _T("FREEING") };
  static TCHAR *blockType[] = { _T("Free"), _T("Normal"), _T("CRT"), _T("Ignore"), _T("Client") };

#ifdef IGNORE_CRT_ALLOC
  if (nBlockUse == _CRT_BLOCK)  // Ignore internal C runtime library allocations
    return TRUE;
#endif

  // Prevent from reentrat calls
  if (InterlockedIncrement(&g_lMallocCalled) > 1) { // I was already called
    InterlockedDecrement(&g_lMallocCalled);
    return TRUE;
  }

  if (g_ulShowStackAtAlloc > 0) {
    AllocCheckFileOpen();  // Open logfile
  }

   _ASSERT( ( nAllocType > 0 ) && ( nAllocType < 4 ) );
   _ASSERT( ( nBlockUse >= 0 ) && ( nBlockUse < 5 ) );

  if (nAllocType == 3) { // freeing
    // Try to get the header information
    if (_CrtIsValidHeapPointer(pvData)) {  // it is a valid Heap-Pointer
      // get the ID
      _CrtMemBlockHeader *pHead;
      // get a pointer to memory block header
      pHead = pHdr(pvData);
      nSize = pHead->nDataSize;
      lRequest = pHead->lRequest; // This is the ID!
    }
  }

  if (g_ulShowStackAtAlloc > 0) {
    _ftprintf( g_fFile, _T("##### Memory operation: %s a %d-byte '%s' block (# %ld)"),
      operation[nAllocType], nSize, blockType[nBlockUse], lRequest );
    if ( pvData != NULL )
      _ftprintf( g_fFile, _T(" at 0x%X"), pvData );
    _ftprintf(g_fFile, _T("\n"));
  }

  if (nAllocType == 3) { // freeing:
    if (lRequest != 0) {  // RequestID was found
      BOOL bRet;
      // Try to find the RequestID in the Hash-Table, mark it that it was freed
      bRet = AllocHashRemove(lRequest);
      if(g_ulShowStackAtAlloc > 0) {
        if (bRet == FALSE) {
          // RequestID not found!
          _ftprintf(g_fFile, _T("###### RequestID not found in hash table for FREEING (%i)!\n"), lRequest);
        }
      } // g_ulShowStackAtAlloc > 0
    }
    else {
      if(g_ulShowStackAtAlloc > 0) {
      // No valid RequestID found, display error
      _ftprintf(g_fFile, _T("###### No valid RequestID for FREEING! (0x%X)\n"), pvData);
 
      }
    }
  }  // freeing

  if (nAllocType == 2) { // re-allocating
    // Try to get the header information
    if (_CrtIsValidHeapPointer(pvData)) {  // it is a valid Heap-Pointer
      BOOL bRet;
      LONG lReallocRequest;
      // get the ID
      _CrtMemBlockHeader *pHead;
      // get a pointer to memory block header
      pHead = pHdr(pvData);
      // Try to find the RequestID in the Hash-Table, mark it that it was freed
      lReallocRequest = pHead->lRequest;
      bRet = AllocHashRemove(lReallocRequest);
      if (g_ulShowStackAtAlloc > 0) {
        if (bRet == FALSE) {
          // RequestID not found!
          _ftprintf(g_fFile, _T("###### RequestID not found in hash table for RE-ALLOCATING (%i)!\n"), lReallocRequest);
        }
        else {
          _ftprintf(g_fFile, _T("##### Implicit freeing because of re-allocation (# old: %ld, new: %ld)\n"), lReallocRequest, lRequest);
        }
      }  // g_ulShowStackAtAlloc > 0
    }  // ValidHeapPointer
  }  // re-allocating

  if ( (g_ulShowStackAtAlloc < 3) && (nAllocType == 3) ) {
    InterlockedDecrement(&g_lMallocCalled);
    return TRUE;
  }

  // Get the context DIESES of this Thread
  HANDLE hThread;
  if (DuplicateHandle( GetCurrentProcess(), GetCurrentThread(),
    GetCurrentProcess(), &hThread, 0, false, DUPLICATE_SAME_ACCESS ) == 0) {
      // Something was wrong...
      _ftprintf(g_fFile, _T("###### Could not call 'DuplicateHandle' successfully\n"));
      InterlockedDecrement(&g_lMallocCalled);
      return TRUE;
  }

  CONTEXT c;
  memset( &c, '\0', sizeof c );
  c.ContextFlags = CONTEXT_FULL;

  // init CONTEXT record so we know where to start the stackwalk
  if ( GetThreadContext( hThread, &c )  == 0) {
    if(g_ulShowStackAtAlloc > 1) {
      _ftprintf(g_fFile, _T("###### Could not call 'GetThreadContext' successfully\n"));
    }
    InterlockedDecrement(&g_lMallocCalled);
    return TRUE; // could not get context
  }

  if(g_ulShowStackAtAlloc > 1) {
    if(g_ulShowStackAtAlloc > 2) {
      // output the callstack
      ShowStack( hThread, c, g_fFile);
    }
    else {
      // Output only (re)allocs
      if (nAllocType != 3) {
        ShowStack( hThread, c, g_fFile);
      }
    }
  }  // g_ulShowStackAtAlloc > 1
  CloseHandle( hThread );

  // Only isert in the Hash-Table if it is not a "freeing"
  if (nAllocType != 3) {
    if(lRequest != 0) // Always a valid RequestID should be provided (see comments in the header)
      AllocHashInsert(lRequest, c, nSize);
  }

  InterlockedDecrement(&g_lMallocCalled);
  return TRUE; // allow the memory operation to proceed
}


#define gle (GetLastError())
#define lenof(a) (sizeof(a) / sizeof((a)[0]))
#define MAXNAMELEN 1024 // max name length for found symbols
#define IMGSYMLEN ( sizeof IMAGEHLP_SYMBOL )
#define TTBUFLEN 8096 // for a temp buffer (2^13)



// SymCleanup()
typedef BOOL (__stdcall *tSC)( IN HANDLE hProcess );
tSC pSC = NULL;

// SymFunctionTableAccess()
typedef PVOID (__stdcall *tSFTA)( HANDLE hProcess, DWORD AddrBase );
tSFTA pSFTA = NULL;

// SymGetLineFromAddr()
typedef BOOL (__stdcall *tSGLFA)( IN HANDLE hProcess, IN DWORD dwAddr,
  OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_LINE Line );
tSGLFA pSGLFA = NULL;

// SymGetModuleBase()
typedef DWORD (__stdcall *tSGMB)( IN HANDLE hProcess, IN DWORD dwAddr );
tSGMB pSGMB = NULL;

// SymGetModuleInfo()
typedef BOOL (__stdcall *tSGMI)( IN HANDLE hProcess, IN DWORD dwAddr, OUT PIMAGEHLP_MODULE ModuleInfo );
tSGMI pSGMI = NULL;

// SymGetOptions()
typedef DWORD (__stdcall *tSGO)( VOID );
tSGO pSGO = NULL;

// SymGetSymFromAddr()
typedef BOOL (__stdcall *tSGSFA)( IN HANDLE hProcess, IN DWORD dwAddr,
  OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_SYMBOL Symbol );
tSGSFA pSGSFA = NULL;

// SymInitialize()
typedef BOOL (__stdcall *tSI)( IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess );
tSI pSI = NULL;

// SymLoadModule()
typedef DWORD (__stdcall *tSLM)( IN HANDLE hProcess, IN HANDLE hFile,
  IN PSTR ImageName, IN PSTR ModuleName, IN DWORD BaseOfDll, IN DWORD SizeOfDll );
tSLM pSLM = NULL;

// SymSetOptions()
typedef DWORD (__stdcall *tSSO)( IN DWORD SymOptions );
tSSO pSSO = NULL;

// StackWalk()
typedef BOOL (__stdcall *tSW)( DWORD MachineType, HANDLE hProcess,
  HANDLE hThread, LPSTACKFRAME StackFrame, PVOID ContextRecord,
  PREAD_PROCESS_MEMORY_ROUTINE ReadMemoryRoutine,
  PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine,
  PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine,
  PTRANSLATE_ADDRESS_ROUTINE TranslateAddress );
tSW pSW = NULL;

// UnDecorateSymbolName()
typedef DWORD (__stdcall WINAPI *tUDSN)( PCSTR DecoratedName, PSTR UnDecoratedName,
  DWORD UndecoratedLength, DWORD Flags );
tUDSN pUDSN = NULL;



static void enumAndLoadModuleSymbols( HANDLE hProcess, DWORD pid, FILE *fLogFile );

static int InitStackWalk(void)
{
  if (g_bInitialized != FALSE)
    return 0;  // already initialized

  // we load imagehlp.dll dynamically because the NT4-version does not
  // offer all the functions that are in the NT5 lib
  g_hImagehlpDll = LoadLibrary( _T("imagehlp.dll") );
  if ( g_hImagehlpDll == NULL )
  {
    printf( "LoadLibrary( \"imagehlp.dll\" ): GetLastError = %lu\n", gle );
    g_bInitialized = FALSE;
    return 1;
  }

  pSC = (tSC) GetProcAddress( g_hImagehlpDll, "SymCleanup" );
  pSFTA = (tSFTA) GetProcAddress( g_hImagehlpDll, "SymFunctionTableAccess" );
  pSGLFA = (tSGLFA) GetProcAddress( g_hImagehlpDll, "SymGetLineFromAddr" );
  pSGMB = (tSGMB) GetProcAddress( g_hImagehlpDll, "SymGetModuleBase" );
  pSGMI = (tSGMI) GetProcAddress( g_hImagehlpDll, "SymGetModuleInfo" );
  pSGO = (tSGO) GetProcAddress( g_hImagehlpDll, "SymGetOptions" );
  pSGSFA = (tSGSFA) GetProcAddress( g_hImagehlpDll, "SymGetSymFromAddr" );
  pSI = (tSI) GetProcAddress( g_hImagehlpDll, "SymInitialize" );
  pSSO = (tSSO) GetProcAddress( g_hImagehlpDll, "SymSetOptions" );
  pSW = (tSW) GetProcAddress( g_hImagehlpDll, "StackWalk" );
  pUDSN = (tUDSN) GetProcAddress( g_hImagehlpDll, "UnDecorateSymbolName" );
  pSLM = (tSLM) GetProcAddress( g_hImagehlpDll, "SymLoadModule" );

  if ( pSC == NULL || pSFTA == NULL || pSGMB == NULL || pSGMI == NULL ||
    pSGO == NULL || pSGSFA == NULL || pSI == NULL || pSSO == NULL ||
    pSW == NULL || pUDSN == NULL || pSLM == NULL )
  {
    printf( "GetProcAddress(): some required function not found.\n" );
    FreeLibrary( g_hImagehlpDll );
    g_bInitialized = FALSE;
    return 1;
  }

  g_bInitialized = TRUE;
  InitializeCriticalSection(&g_csFileOpenClose);
  return 0;
}

// This function if NOT multi-threading capable
// It should only be called from the main-Function!
int InitAllocCheckWN(PCTSTR pszFileName, ULONG ulShowStackAtAlloc) {
  if (g_bInitialized) {
    return 2;  // already initialized!
  }
  if (ulShowStackAtAlloc <= 3)
    g_ulShowStackAtAlloc = ulShowStackAtAlloc;
  else
    g_ulShowStackAtAlloc = 3;

  if (pszFileName != NULL) 
    g_pszAllocLogName = _tcsdup(pszFileName);
  else
    g_pszAllocLogName = NULL;

#ifdef _DEBUG
  AllocHashInit();

  _CrtSetAllocHook(MyAllocHook);
#endif

  return InitStackWalk();
}  // InitAllocCheckWN

static TCHAR s_szExceptionLogFileName[_MAX_PATH] = _T("\\exceptions.log");  // default
static BOOL s_bUnhandledExeptionFilterSet = FALSE;
static LONG __stdcall CrashHandlerExceptionFilter(EXCEPTION_POINTERS* pExPtrs)
{
   LONG lRet;
   lRet = StackwalkFilter(pExPtrs, /*EXCEPTION_CONTINUE_SEARCH*/EXCEPTION_EXECUTE_HANDLER, s_szExceptionLogFileName);
   TCHAR lString[500];
   _stprintf(lString,
      _T("*** Unhandled Exception!\n")
      _T("   ExpCode: 0x%8.8X\n")
      _T("   ExpFlags: %d\n")
      _T("   ExpAddress: 0x%8.8X\n")
      _T("   Please report!"),
      pExPtrs->ExceptionRecord->ExceptionCode,
      pExPtrs->ExceptionRecord->ExceptionFlags,
      pExPtrs->ExceptionRecord->ExceptionAddress);
   FatalAppExit(-1,lString);
   return lRet;
}

int InitAllocCheck(BOOL bSetUnhandledExeptionFilter, ULONG ulShowStackAtAlloc)  // will create the filename by itself
{
  TCHAR szModName[_MAX_PATH];
  if (GetModuleFileName(NULL, szModName, sizeof(szModName)/sizeof(TCHAR)) != 0)
  {
    strcpy(s_szExceptionLogFileName, szModName);
    strcat(s_szExceptionLogFileName, _T(".exp.log"));
    strcat(szModName, _T(".mem.log"));
  }
  else
  {
    strcpy(szModName, _T("\\mem-leaks.log"));  // default
  }

  if (bSetUnhandledExeptionFilter != FALSE)
  {
    // set global exception handler (for handling all unhandled exceptions)
    SetUnhandledExceptionFilter(CrashHandlerExceptionFilter);
    s_bUnhandledExeptionFilterSet = TRUE;
  }

  return InitAllocCheckWN(szModName, 0);
}


// This function if NOT multi-threading capable
// It should only be called from the main-Function!
//   Returns the number of bytes that are not freed (leaks)
ULONG DeInitAllocCheck(void) {
  ULONG ulRet = 0;
  if (g_bInitialized) {

#ifdef _DEBUG
    DeactivateMallocStackwalker();  // No deactivate MyAllocHook, because StackWalker will allocate some memory)
    ulRet = AllocHashDeinit();  // output the not freed memory
#endif

    EnterCriticalSection(&g_csFileOpenClose);  // wait until a running stack dump was created
    g_bInitialized = FALSE;

    // de-init symbol handler etc. (SymCleanup())
    if (pSC != NULL)
      pSC( GetCurrentProcess() );
    FreeLibrary( g_hImagehlpDll );

    LeaveCriticalSection(&g_csFileOpenClose);
    if (g_pszAllocLogName != NULL) {
      free(g_pszAllocLogName);
      g_pszAllocLogName = NULL;
    }
    if (g_fFile != NULL) {
       fclose(g_fFile);
       g_fFile = NULL;
    }

  }

  if (s_bUnhandledExeptionFilterSet != TRUE)
  {
    SetUnhandledExceptionFilter(NULL);
    s_bUnhandledExeptionFilterSet = FALSE;
  }
  return ulRet;
}  // DeInitAllocCheck




static TCHAR *GetExpectionCodeText(DWORD dwExceptionCode) {
  switch(dwExceptionCode) {
  case EXCEPTION_ACCESS_VIOLATION: return _T("ACCESS VIOLATION");
  case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return _T("ARRAY BOUNDS EXCEEDED");
  case EXCEPTION_BREAKPOINT: return _T("BREAKPOINT");
  case EXCEPTION_DATATYPE_MISALIGNMENT: return _T("DATATYPE MISALIGNMENT");
  case EXCEPTION_FLT_DENORMAL_OPERAND: return _T("FLT DENORMAL OPERAND");
  case EXCEPTION_FLT_DIVIDE_BY_ZERO: return _T("FLT DIVIDE BY ZERO");
  case EXCEPTION_FLT_INEXACT_RESULT: return _T("FLT INEXACT RESULT");
  case EXCEPTION_FLT_INVALID_OPERATION: return _T("FLT INVALID OPERATION");
  case EXCEPTION_FLT_OVERFLOW: return _T("FLT OVERFLOW");
  case EXCEPTION_FLT_STACK_CHECK: return _T("FLT STACK CHECK");
  case EXCEPTION_FLT_UNDERFLOW: return _T("FLT UNDERFLOW");
  case EXCEPTION_ILLEGAL_INSTRUCTION: return _T("ILLEGAL INSTRUCTION");
  case EXCEPTION_IN_PAGE_ERROR: return _T("IN PAGE ERROR");
  case EXCEPTION_INT_DIVIDE_BY_ZERO: return _T("INT DIVIDE BY ZERO");
  case EXCEPTION_INT_OVERFLOW: return _T("INT OVERFLOW");
  case EXCEPTION_INVALID_DISPOSITION: return _T("INVALID DISPOSITION");
  case EXCEPTION_NONCONTINUABLE_EXCEPTION: return _T("NONCONTINUABLE EXCEPTION");
  case EXCEPTION_PRIV_INSTRUCTION: return _T("PRIV INSTRUCTION");
  case EXCEPTION_SINGLE_STEP: return _T("SINGLE STEP");
  case EXCEPTION_STACK_OVERFLOW: return _T("STACK OVERFLOW");
  case DBG_CONTROL_C : return _T("DBG CONTROL C ");
  default:
    return _T("<unkown exception>");
  }
}  // GetExpectionCodeText

// Function is not multi-threading safe, because of static char!
static TCHAR *GetAdditionalExpectionCodeText(PEXCEPTION_RECORD pExceptionRecord) {
  static TCHAR szTemp[100];

  switch(pExceptionRecord->ExceptionCode) {
  case EXCEPTION_ACCESS_VIOLATION:
    if (pExceptionRecord->NumberParameters == 2) {
      switch(pExceptionRecord->ExceptionInformation[0]) {
      case 0: // read attempt
        _stprintf(szTemp, _T(" read attempt to address 0x%8.8X "), pExceptionRecord->ExceptionInformation[1]);
        return szTemp;
      case 1: // write attempt
        _stprintf(szTemp, _T(" write attempt to address 0x%8.8X "), pExceptionRecord->ExceptionInformation[1]);
        return szTemp;
      default:
        return _T("");
      }
    }  // if (pExceptionRecord->NumberParameters == 2)
    return _T("");
  default:
    return _T("");
  }  // switch(pExceptionRecord->ExceptionCode)
}  // GetAdditionalExpectionCodeText



// if you use C++ exception handling: install a translator function
// with set_se_translator(). In the context of that function (but *not*
// afterwards), you can either do your stack dump, or save the CONTEXT
// record as a local copy. Note that you must do the stack sump at the
// earliest opportunity, to avoid the interesting stackframes being gone
// by the time you do the dump.

// status: 
// - EXCEPTION_CONTINUE_SEARCH: exception wird weitergereicht
// - EXCEPTION_CONTINUE_EXECUTION: 
// - EXCEPTION_EXECUTE_HANDLER:
DWORD StackwalkFilter( EXCEPTION_POINTERS *ep, DWORD status, PCTSTR pszLogFile)
{
  HANDLE hThread;
  FILE *fFile = stdout;  // default to stdout

  if (pszLogFile != NULL) {  // a filename is provided
    // Open the logfile
    fFile = _tfopen(pszLogFile, _T("a"));
    if (fFile != NULL) {  // Is the file too big?
      long size;
      fseek(fFile, 0, SEEK_END);
      size = ftell(fFile);  // Get the size of the file
      if (size >= LOG_FILE_MAX_SIZE) {
        TCHAR *pszTemp = (TCHAR*) malloc(MAX_PATH);
        // It is too big...
        fclose(fFile);
        _tcscpy(pszTemp, pszLogFile);
        _tcscat(pszTemp, _T(".old"));
        _tremove(pszTemp);  // Remove an old file, if exists
        _trename(pszLogFile, pszTemp);  // rename the actual file
        fFile = _tfopen(pszLogFile, _T("w"));  // create a new file
        free(pszTemp);
      }
    }
  }  // if (pszLogFile != NULL) 
  if (fFile == NULL) {
    fFile = stdout;
  }

  // Write infos about the exception
  _ftprintf(fFile, _T("######## EXCEPTION: 0x%8.8X at address: 0x%8.8X"), 
    ep->ExceptionRecord->ExceptionCode,
    ep->ExceptionRecord->ExceptionAddress);
  _ftprintf(fFile, _T(": %s %s\n"), GetExpectionCodeText(ep->ExceptionRecord->ExceptionCode),
    GetAdditionalExpectionCodeText(ep->ExceptionRecord));

  DuplicateHandle( GetCurrentProcess(), GetCurrentThread(),
    GetCurrentProcess(), &hThread, 0, false, DUPLICATE_SAME_ACCESS );
  ShowStack( hThread, *(ep->ContextRecord), fFile);
  CloseHandle( hThread );

  fclose(fFile);

  return status;
}  // StackwalkFilter

void ShowStack( HANDLE hThread, CONTEXT& c, PCTSTR pszLogFile)
{
  FILE *fFile = stdout;  // default to stdout

  if (pszLogFile != NULL) {  // a filename is available
    // Open the logfile
    fFile = _tfopen(pszLogFile, _T("a"));
    if (fFile != NULL) {  // Is the file too big?
      long size;
      fseek(fFile, 0, SEEK_END);
      size = ftell(fFile);  // Get the size of the file
      if (size >= LOG_FILE_MAX_SIZE) {
        TCHAR *pszTemp = (TCHAR*) malloc(MAX_PATH);
        // It is too big...
        fclose(fFile);
        _tcscpy(pszTemp, pszLogFile);
        _tcscat(pszTemp, _T(".old"));
        _tremove(pszTemp);  // Remove an old file, if exists
        _trename(pszLogFile, pszTemp);  // rename the actual file
        fFile = _tfopen(pszLogFile, _T("w"));  // open new file
        free(pszTemp);
      }
    }
  }  // if (pszLogFile != NULL) 
  if (fFile == NULL) {
    fFile = stdout;
  }

  ShowStack( hThread, c, fFile);

  fclose(fFile);
}


static void ShowStack( HANDLE hThread, CONTEXT& c, FILE *fLogFile) {
  ShowStackRM(hThread, c, fLogFile, NULL, GetCurrentProcess());
}

static void ShowStackRM( HANDLE hThread, CONTEXT& c, FILE *fLogFile, PREAD_PROCESS_MEMORY_ROUTINE ReadMemoryFunction, HANDLE hSWProcess) {
  // normally, call ImageNtHeader() and use machine info from PE header
  DWORD imageType = IMAGE_FILE_MACHINE_I386;
  HANDLE hProcess = GetCurrentProcess(); // hProcess normally comes from outside
  int frameNum; // counts walked frames
  DWORD offsetFromSymbol; // tells us how far from the symbol we were
  DWORD symOptions; // symbol handler settings

  static IMAGEHLP_SYMBOL *pSym = NULL;
  char undName[MAXNAMELEN]; // undecorated name
  char undFullName[MAXNAMELEN]; // undecorated name with all shenanigans
  IMAGEHLP_MODULE Module;
  IMAGEHLP_LINE Line;

  std::string symSearchPath;

  static bFirstTime = TRUE;


  // If no logfile is present, outpur to "stdout"
  if (fLogFile == NULL) {
    fLogFile = stdout;
  }

  STACKFRAME s; // in/out stackframe
  memset( &s, '\0', sizeof s );

  if ( (g_bInitialized == FALSE) && (bFirstTime == TRUE) ) {
    InitStackWalk();
  }

  if (g_bInitialized == FALSE)
  {
    // Could not init!!!!
    bFirstTime = FALSE;
    _ftprintf(fLogFile, _T("%lu: Stackwalker not initialized (or was not able to initialize)!\n"), g_dwShowCount);
    return;
  }

// Critical section begin...
  EnterCriticalSection(&g_csFileOpenClose);

  InterlockedIncrement((long*) &g_dwShowCount);  // erh�he counter


  // NOTE: normally, the exe directory and the current directory should be taken
  // from the target process. The current dir would be gotten through injection
  // of a remote thread; the exe fir through either ToolHelp32 or PSAPI.

  if (pSym == NULL) {
    pSym = (IMAGEHLP_SYMBOL *) malloc( IMGSYMLEN + MAXNAMELEN );
    if (!pSym) goto cleanup;  // not enough memory...
  }

  _ftprintf(fLogFile, _T("%lu: "), g_dwShowCount);
  WriteDateTime(fLogFile);
  _ftprintf(fLogFile, _T("\n"));


  if (bFirstTime) {

    CHAR *tt, *p;

    tt = (CHAR*) malloc(sizeof(CHAR) * TTBUFLEN); // Get the temporary buffer
    if (!tt) goto cleanup;  // not enough memory...

    // build symbol search path from:
    symSearchPath = "";
    // current directory
    if ( GetCurrentDirectoryA( TTBUFLEN, tt ) )
      symSearchPath += tt + std::string( ";" );
    // dir with executable
    if ( GetModuleFileNameA( 0, tt, TTBUFLEN ) )
    {
      for ( p = tt + strlen( tt ) - 1; p >= tt; -- p )
      {
        // locate the rightmost path separator
        if ( *p == '\\' || *p == '/' || *p == ':' )
          break;
      }
      // if we found one, p is pointing at it; if not, tt only contains
      // an exe name (no path), and p points before its first byte
      if ( p != tt ) // path sep found?
      {
        if ( *p == ':' ) // we leave colons in place
          ++ p;
        *p = '\0'; // eliminate the exe name and last path sep
        symSearchPath += tt + std::string( ";" );
      }
    }
    // environment variable _NT_SYMBOL_PATH
    if ( GetEnvironmentVariableA( "_NT_SYMBOL_PATH", tt, TTBUFLEN ) )
      symSearchPath += tt + std::string( ";" );
    // environment variable _NT_ALTERNATE_SYMBOL_PATH
    if ( GetEnvironmentVariableA( "_NT_ALTERNATE_SYMBOL_PATH", tt, TTBUFLEN ) )
      symSearchPath += tt + std::string( ";" );
    // environment variable SYSTEMROOT
    if ( GetEnvironmentVariableA( "SYSTEMROOT", tt, TTBUFLEN ) )
      symSearchPath += tt + std::string( ";" );



    if ( symSearchPath.size() > 0 ) // if we added anything, we have a trailing semicolon
      symSearchPath = symSearchPath.substr( 0, symSearchPath.size() - 1 );

    // why oh why does SymInitialize() want a writeable string?
    strncpy( tt, symSearchPath.c_str(), TTBUFLEN );
    tt[TTBUFLEN - 1] = '\0'; // if strncpy() overruns, it doesn't add the null terminator

    // init symbol handler stuff (SymInitialize())
    if ( ! pSI( hProcess, tt, false ) )
    {
      _ftprintf(fLogFile, _T("%lu: SymInitialize(): GetLastError = %lu\n"), g_dwShowCount, gle );
      if (tt) free( tt );
      goto cleanup;
    }

    // SymGetOptions()
    symOptions = pSGO();
    symOptions |= SYMOPT_LOAD_LINES;
    symOptions &= ~SYMOPT_UNDNAME;
    symOptions &= ~SYMOPT_DEFERRED_LOADS;
    pSSO( symOptions ); // SymSetOptions()

    // Enumerate modules and tell imagehlp.dll about them.
    // On NT, this is not necessary, but it won't hurt.
    enumAndLoadModuleSymbols( hProcess, GetCurrentProcessId(), fLogFile );

    if (tt) 
      free( tt );
  }  // bFirstTime = TRUE
  bFirstTime = FALSE;

  // init STACKFRAME for first call
  // Notes: AddrModeFlat is just an assumption. I hate VDM debugging.
  // Notes: will have to be #ifdef-ed for Alphas; MIPSes are dead anyway,
  // and good riddance.
  s.AddrPC.Offset = c.Eip;
  s.AddrPC.Mode = AddrModeFlat;
  s.AddrFrame.Offset = c.Ebp;
  s.AddrFrame.Mode = AddrModeFlat;

  memset( pSym, '\0', IMGSYMLEN + MAXNAMELEN );
  pSym->SizeOfStruct = IMGSYMLEN;
  pSym->MaxNameLength = MAXNAMELEN;

  memset( &Line, '\0', sizeof Line );
  Line.SizeOfStruct = sizeof Line;

  memset( &Module, '\0', sizeof Module );
  Module.SizeOfStruct = sizeof Module;

  offsetFromSymbol = 0;

  for ( frameNum = 0; ; ++ frameNum )
  {
    // get next stack frame (StackWalk(), SymFunctionTableAccess(), SymGetModuleBase())
    // if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can
    // assume that either you are done, or that the stack is so hosed that the next
    // deeper frame could not be found.
    // CONTEXT need not to be suplied if imageTyp is IMAGE_FILE_MACHINE_I386!
    if ( ! pSW( imageType, hSWProcess, hThread, &s, NULL, ReadMemoryFunction, pSFTA, pSGMB, NULL ) )
      break;

    // display its contents
    /*_ftprintf(fLogFile, _T("\n%lu: %3d %c%c %08lx %08lx %08lx %08lx "), g_dwShowCount, 
      frameNum, s.Far? 'F': '.', s.Virtual? 'V': '.',
      s.AddrPC.Offset, s.AddrReturn.Offset,
      s.AddrFrame.Offset, s.AddrStack.Offset );*/
    _ftprintf(fLogFile, _T("\n%lu: %3d"), g_dwShowCount, frameNum);
    if ( s.AddrPC.Offset == 0 )
    {
      _ftprintf(fLogFile, _T("   (-nosymbols- PC == 0)\n"));
    }
    else
    { // we seem to have a valid PC
      // show procedure info (SymGetSymFromAddr())
      if ( ! pSGSFA( hProcess, s.AddrPC.Offset, &offsetFromSymbol, pSym ) )
      {
        if ( gle != 487 )
          _ftprintf(fLogFile, _T("   SymGetSymFromAddr(): GetLastError = %lu\n"), gle );
        else
          _ftprintf(fLogFile, _T("\n"));
      }
      else
      {
        // UnDecorateSymbolName()
        undName[0] = 0;
        pUDSN( pSym->Name, undName, MAXNAMELEN, UNDNAME_NAME_ONLY );
        undFullName[0] = 0;
        pUDSN( pSym->Name, undFullName, MAXNAMELEN, UNDNAME_COMPLETE );
        if (strlen(undName) > 0)
          fprintf(fLogFile, "     %s %+ld bytes\n", undName, (long) offsetFromSymbol );
        else
          fprintf(fLogFile, "     Sig:  %s %+ld bytes\n", pSym->Name, (long) offsetFromSymbol );
        fprintf(fLogFile, "%lu:     Decl: %s\n", g_dwShowCount, undFullName );
      }

      // show line number info, NT5.0-method (SymGetLineFromAddr())
      if ( pSGLFA != NULL )
      { // yes, we have SymGetLineFromAddr()
        if ( ! pSGLFA( hProcess, s.AddrPC.Offset, &offsetFromSymbol, &Line ) )
        {
          if ( gle != 487 )
            _ftprintf(fLogFile, _T("%lu: SymGetLineFromAddr(): GetLastError = %lu\n"), g_dwShowCount, gle );
        }
        else
        {
          fprintf(fLogFile, "%lu:     Line: %s(%lu) %+ld bytes\n", g_dwShowCount,
            Line.FileName, Line.LineNumber, offsetFromSymbol );
        }
      } // yes, we have SymGetLineFromAddr()

      // show module info (SymGetModuleInfo())
      if ( ! pSGMI( hProcess, s.AddrPC.Offset, &Module ) )
      {
        _ftprintf(fLogFile, _T("%lu: SymGetModuleInfo): GetLastError = %lu\n"), g_dwShowCount, gle );
      }
      else
      { // got module info OK
        char ty[80];
        switch ( Module.SymType )
        {
        case SymNone:
          strcpy( ty, "-nosymbols-" );
          break;
        case SymCoff:
          strcpy( ty, "COFF" );
          break;
        case SymCv:
          strcpy( ty, "CV" );
          break;
        case SymPdb:
          strcpy( ty, "PDB" );
          break;
        case SymExport:
          strcpy( ty, "-exported-" );
          break;
        case SymDeferred:
          strcpy( ty, "-deferred-" );
          break;
        case SymSym:
          strcpy( ty, "SYM" );
          break;
        default:
          _snprintf( ty, sizeof ty, "symtype=%ld", (long) Module.SymType );
          break;
        }
        //fprintf(fLogFile, "%lu:     Mod:  %s[%s], base: %08lxh\n", g_dwShowCount,
        //  Module.ModuleName, Module.ImageName, Module.BaseOfImage );
        fprintf(fLogFile, "%lu:     Mod:  %s, base: %08lxh\n", g_dwShowCount,
          Module.ModuleName, Module.BaseOfImage );
        if (Module.SymType == SymNone) { // Gebe nur aus, wenn keine Symbole vorhanden sind!
          _ftprintf(fLogFile, _T("%lu:     Offset: 0x%8.8x\n"), g_dwShowCount, s.AddrPC.Offset);
          fprintf(fLogFile, "%lu:     Sym:  type: %s, file: %s\n", g_dwShowCount,
            ty, Module.LoadedImageName );
        }
      } // got module info OK
    } // we seem to have a valid PC

    // no return address means no deeper stackframe
    if ( s.AddrReturn.Offset == 0 )
    {
      // avoid misunderstandings in the printf() following the loop
      SetLastError( 0 );
      break;
    }

  } // for ( frameNum )

  if ( gle != 0 )
    _ftprintf(fLogFile, _T("\n%lu: StackWalk(): GetLastError = %lu\n"), g_dwShowCount, gle );

cleanup:
  // de-init symbol handler etc. (SymCleanup())
  //pSC( hProcess );

  //if (pSym) free( pSym );
  if (fLogFile) {
    _ftprintf(fLogFile, _T("\n\n"));
    if (g_dwShowCount % 1000)
      fflush(fLogFile);
  }

  LeaveCriticalSection(&g_csFileOpenClose);
// Critical section end...
}  // ShowStackRM



struct ModuleEntry
{
  std::string imageName;
  std::string moduleName;
  DWORD baseAddress;
  DWORD size;
};
typedef std::vector< ModuleEntry > ModuleList;
typedef ModuleList::iterator ModuleListIter;

static bool fillModuleList( ModuleList& modules, DWORD pid, HANDLE hProcess, FILE *fLogFile );
static bool fillModuleListTH32( ModuleList& modules, DWORD pid, FILE *fLogFile );
static bool fillModuleListPSAPI( ModuleList& modules, DWORD pid, HANDLE hProcess, FILE *fLogFile );


static void enumAndLoadModuleSymbols( HANDLE hProcess, DWORD pid, FILE *fLogFile )
{
  static ModuleList modules;
  static ModuleListIter it;
  char *img, *mod;

  // fill in module list
  fillModuleList( modules, pid, hProcess, fLogFile );

  for ( it = modules.begin(); it != modules.end(); ++ it )
  {
    // unfortunately, SymLoadModule() wants writeable strings
    img = (char*) malloc(sizeof(char) * ((*it).imageName.size() + 1));
    strcpy( img, (*it).imageName.c_str() );
    mod = (char*) malloc(sizeof(char) * ((*it).moduleName.size() + 1));
    strcpy( mod, (*it).moduleName.c_str() );

    /*if ( pSLM( hProcess, 0, img, mod, (*it).baseAddress, (*it).size ) == 0 )
      _ftprintf(fLogFile, _T("%lu: Error %lu loading symbols for \"%s\"\n"), g_dwShowCount,
        gle, (*it).moduleName.c_str() );
    else
      _ftprintf(fLogFile, _T("%lu: Symbols loaded: \"%s\"\n"), g_dwShowCount, (*it).moduleName.c_str() );*/
    pSLM( hProcess, 0, img, mod, (*it).baseAddress, (*it).size );

    free(img);
    free(mod);
  }
}  // enumAndLoadModuleSymbols



static bool fillModuleList( ModuleList& modules, DWORD pid, HANDLE hProcess, FILE *fLogFile )
{
  // try toolhelp32 first
  if ( fillModuleListTH32( modules, pid, fLogFile ) )
    return true;
  // nope? try psapi, then
  return fillModuleListPSAPI( modules, pid, hProcess, fLogFile );
}  // fillModuleList



// miscellaneous toolhelp32 declarations; we cannot #include the header
// because not all systems may have it
#define MAX_MODULE_NAME32 255
#define TH32CS_SNAPMODULE   0x00000008
#pragma pack( push, 8 )
typedef struct tagMODULEENTRY32
{
    DWORD   dwSize;
    DWORD   th32ModuleID;       // This module
    DWORD   th32ProcessID;      // owning process
    DWORD   GlblcntUsage;       // Global usage count on the module
    DWORD   ProccntUsage;       // Module usage count in th32ProcessID's context
    BYTE  * modBaseAddr;        // Base address of module in th32ProcessID's context
    DWORD   modBaseSize;        // Size in bytes of module starting at modBaseAddr
    HMODULE hModule;            // The hModule of this module in th32ProcessID's context
    char    szModule[MAX_MODULE_NAME32 + 1];
    char    szExePath[MAX_PATH];
} MODULEENTRY32;
typedef MODULEENTRY32 *  PMODULEENTRY32;
typedef MODULEENTRY32 *  LPMODULEENTRY32;
#pragma pack( pop )



static bool fillModuleListTH32( ModuleList& modules, DWORD pid, FILE *fLogFile )
{
  // CreateToolhelp32Snapshot()
  typedef HANDLE (__stdcall *tCT32S)( DWORD dwFlags, DWORD th32ProcessID );
  // Module32First()
  typedef BOOL (__stdcall *tM32F)( HANDLE hSnapshot, LPMODULEENTRY32 lpme );
  // Module32Next()
  typedef BOOL (__stdcall *tM32N)( HANDLE hSnapshot, LPMODULEENTRY32 lpme );

  // I think the DLL is called tlhelp32.dll on Win9X, so we try both
  const TCHAR *dllname[] = { _T("kernel32.dll"), _T("tlhelp32.dll") };
  HINSTANCE hToolhelp;
  tCT32S pCT32S;
  tM32F pM32F;
  tM32N pM32N;

  HANDLE hSnap;
  MODULEENTRY32 me = { sizeof me };
  bool keepGoing;
  ModuleEntry e;
  int i;

  for ( i = 0; i < lenof( dllname ); ++ i )
  {
    hToolhelp = LoadLibrary( dllname[i] );
    if ( hToolhelp == 0 )
      continue;
    pCT32S = (tCT32S) GetProcAddress( hToolhelp, "CreateToolhelp32Snapshot" );
    pM32F = (tM32F) GetProcAddress( hToolhelp, "Module32First" );
    pM32N = (tM32N) GetProcAddress( hToolhelp, "Module32Next" );
    if ( pCT32S != 0 && pM32F != 0 && pM32N != 0 )
      break; // found the functions!
    FreeLibrary( hToolhelp );
    hToolhelp = 0;
  }

  if ( hToolhelp == 0 ) // nothing found?
    return false;

  hSnap = pCT32S( TH32CS_SNAPMODULE, pid );
  if ( hSnap == (HANDLE) -1 )
    return false;

  keepGoing = !!pM32F( hSnap, &me );
  while ( keepGoing )
  {
    // here, we have a filled-in MODULEENTRY32
    //_ftprintf(fLogFile, _T("%lu: %08lXh %6lu %-15.15s %s\n"), g_dwShowCount, me.modBaseAddr, me.modBaseSize, me.szModule, me.szExePath );
    e.imageName = me.szExePath;
    e.moduleName = me.szModule;
    e.baseAddress = (DWORD) me.modBaseAddr;
    e.size = me.modBaseSize;
    modules.push_back( e );
    keepGoing = !!pM32N( hSnap, &me );
  }

  CloseHandle( hSnap );

  FreeLibrary( hToolhelp );

  return modules.size() != 0;
}  // fillModuleListTH32



// miscellaneous psapi declarations; we cannot #include the header
// because not all systems may have it
typedef struct _MODULEINFO {
    LPVOID lpBaseOfDll;
    DWORD SizeOfImage;
    LPVOID EntryPoint;
} MODULEINFO, *LPMODULEINFO;



static bool fillModuleListPSAPI( ModuleList& modules, DWORD pid, HANDLE hProcess, FILE *fLogFile )
{
  // EnumProcessModules()
  typedef BOOL (__stdcall *tEPM)( HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded );
  // GetModuleFileNameEx()
  typedef DWORD (__stdcall *tGMFNE)( HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize );
  // GetModuleBaseName() -- redundant, as GMFNE() has the same prototype, but who cares?
  typedef DWORD (__stdcall *tGMBN)( HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize );
  // GetModuleInformation()
  typedef BOOL (__stdcall *tGMI)( HANDLE hProcess, HMODULE hModule, LPMODULEINFO pmi, DWORD nSize );

  HINSTANCE hPsapi;
  tEPM pEPM;
  tGMFNE pGMFNE;
  tGMBN pGMBN;
  tGMI pGMI;

  DWORD i;
  ModuleEntry e;
  DWORD cbNeeded;
  MODULEINFO mi;
  HMODULE *hMods = 0;
  char *tt = 0;

  hPsapi = LoadLibrary( _T("psapi.dll") );
  if ( hPsapi == 0 )
    return false;

  modules.clear();

  pEPM = (tEPM) GetProcAddress( hPsapi, "EnumProcessModules" );
  pGMFNE = (tGMFNE) GetProcAddress( hPsapi, "GetModuleFileNameExA" );
  pGMBN = (tGMFNE) GetProcAddress( hPsapi, "GetModuleBaseNameA" );
  pGMI = (tGMI) GetProcAddress( hPsapi, "GetModuleInformation" );
  if ( pEPM == 0 || pGMFNE == 0 || pGMBN == 0 || pGMI == 0 )
  {
    // yuck. Some API is missing.
    FreeLibrary( hPsapi );
    return false;
  }

  hMods = (HMODULE*) malloc(sizeof(HMODULE) * (TTBUFLEN / sizeof HMODULE));
  tt = (char*) malloc(sizeof(char) * TTBUFLEN);
  // not that this is a sample. Which means I can get away with
  // not checking for errors, but you cannot. :)

  if ( ! pEPM( hProcess, hMods, TTBUFLEN, &cbNeeded ) )
  {
    _ftprintf(fLogFile, _T("%lu: EPM failed, GetLastError = %lu\n"), g_dwShowCount, gle );
    goto cleanup;
  }

  if ( cbNeeded > TTBUFLEN )
  {
    _ftprintf(fLogFile, _T("%lu: More than %lu module handles. Huh?\n"), g_dwShowCount, lenof( hMods ) );
    goto cleanup;
  }

  for ( i = 0; i < cbNeeded / sizeof hMods[0]; ++ i )
  {
    // for each module, get:
    // base address, size
    pGMI( hProcess, hMods[i], &mi, sizeof mi );
    e.baseAddress = (DWORD) mi.lpBaseOfDll;
    e.size = mi.SizeOfImage;
    // image file name
    tt[0] = '\0';
    pGMFNE( hProcess, hMods[i], tt, TTBUFLEN );
    e.imageName = tt;
    // module name
    tt[0] = '\0';
    pGMBN( hProcess, hMods[i], tt, TTBUFLEN );
    e.moduleName = tt;
    //_ftprintf(fLogFile, _T("%lu: %08lXh %6lu %-15.15s %s\n"), g_dwShowCount, e.baseAddress,
    //  e.size, e.moduleName.c_str(), e.imageName.c_str() );

    modules.push_back( e );
  }

cleanup:
  if ( hPsapi )
    FreeLibrary( hPsapi );
  free(tt);
  free(hMods);

  return modules.size() != 0;
}  // fillModuleListPSAPI

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.


Written By
France France
Addicted to reverse engineering. At work, I am developing business intelligence software in a team of smart people (independent software vendor).

Need a fast Excel generation component? Try xlsgen.

Comments and Discussions