/*++ Copyright (c) 2000 Microsoft Corporation Module Name: GuardAlloc.h Abstract: Author: Hakki T. Bostanci (hakkib) 06-Apr-2000 Revision History: --*/ #ifndef _GUARDALLOC_H_ #define _GUARDALLOC_H_ #include #include ////////////////////////////////////////////////////////////////////////// // // // #ifdef _WIN32 #undef ALIGNMENT #define ALIGNMENT 8 #endif //WIN32 #ifdef _WIN64 #undef ALIGNMENT #define ALIGNMENT 16 #endif //WIN64 ////////////////////////////////////////////////////////////////////////// // // // inline size_t Align(size_t nDataSize, size_t nBlockSize) { return (nDataSize + (nBlockSize-1)) & ~(nBlockSize-1); } ////////////////////////////////////////////////////////////////////////// // // // #define MAX_STACK_DEPTH 32 #define MAX_SYMBOL_LENGTH 256 template struct CImagehlpSymbol : public IMAGEHLP_SYMBOL { CImagehlpSymbol() { ZeroMemory(this, sizeof(*this)); SizeOfStruct = sizeof(IMAGEHLP_SYMBOL); MaxNameLength = N; } private: CHAR NameData[N-1]; }; struct CImagehlpLine : public IMAGEHLP_LINE { CImagehlpLine() { ZeroMemory(this, sizeof(*this)); SizeOfStruct = sizeof(IMAGEHLP_LINE); } }; struct CImagehlpModule : public IMAGEHLP_MODULE { CImagehlpModule() { ZeroMemory(this, sizeof(*this)); SizeOfStruct = sizeof(IMAGEHLP_MODULE); } }; ////////////////////////////////////////////////////////////////////////// // // // inline PSTR GetFileNameA(PCSTR pPathName) { PCSTR pFileName = pPathName ? strrchr(pPathName, '\\') : 0; return (PSTR) (pFileName ? pFileName + 1 : pPathName); } ////////////////////////////////////////////////////////////////////////// // // // inline void GetExceptionContext(LPEXCEPTION_POINTERS pExceptionPointers, CONTEXT *pContext) { *pContext = *pExceptionPointers->ContextRecord; } ////////////////////////////////////////////////////////////////////////// // // // inline DWORD GetCurrentMachineType() { #if defined(_X86_) return IMAGE_FILE_MACHINE_I386; #elif defined(_MIPS_) return IMAGE_FILE_MACHINE_R4000; #elif defined(_ALPHA_) return IMAGE_FILE_MACHINE_ALPHA; #elif defined(_PPC_) return IMAGE_FILE_MACHINE_POWERPC; #elif defined(_IA64_) return IMAGE_FILE_MACHINE_IA64; #elif defined(_AXP64_) return IMAGE_FILE_MACHINE_AXP64; #else return 0; #endif } ////////////////////////////////////////////////////////////////////////// // // // class CStackFrame : public STACKFRAME { public: CStackFrame() { } CStackFrame(CONTEXT *pContext) { ZeroMemory(this, sizeof(*this)); #if defined(_X86_) AddrPC.Offset = pContext->Eip; AddrFrame.Offset = pContext->Ebp; AddrStack.Offset = pContext->Esp; #elif defined(_MIPS_) AddrPC.Offset = pContext->Fir; AddrFrame.Offset = pContext->IntS6; AddrStack.Offset = pContext->IntSp; #elif defined(_ALPHA_) AddrPC.Offset = pContext->Fir; AddrFrame.Offset = pContext->IntFp; AddrStack.Offset = pContext->IntSp; #elif defined(_PPC_) AddrPC.Offset = pContext->Iar; AddrFrame.Offset = pContext->IntFp; AddrStack.Offset = pContext->Gpr1; #endif AddrPC.Mode = AddrModeFlat; AddrFrame.Mode = AddrModeFlat; AddrStack.Mode = AddrModeFlat; } BOOL Walk( DWORD MachineType = GetCurrentMachineType(), HANDLE hProcess = GetCurrentProcess(), HANDLE hThread = GetCurrentThread(), PVOID ContextRecord = 0, PREAD_PROCESS_MEMORY_ROUTINE ReadMemoryRoutine = 0, PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine = SymFunctionTableAccess, PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine = SymGetModuleBase, PTRANSLATE_ADDRESS_ROUTINE TranslateAddress = 0 ) { return StackWalk( MachineType, hProcess, hThread, this, ContextRecord, ReadMemoryRoutine, FunctionTableAccessRoutine, GetModuleBaseRoutine, TranslateAddress ); } int Dump(HANDLE hProcess, PSTR pBuffer, int nBufferSize) { int nLength = 0; CImagehlpModule Module; SymGetModuleInfo(hProcess, AddrPC.Offset, &Module); ULONG_PTR dwDisplacement = 0; CImagehlpSymbol Symbol; SymGetSymFromAddr(hProcess, AddrPC.Offset, &dwDisplacement, &Symbol); CHAR szUnDSymbol[MAX_SYMBOL_LENGTH] = ""; SymUnDName(&Symbol, szUnDSymbol, sizeof(szUnDSymbol)); DWORD dwLineDisplacement = 0; CImagehlpLine Line; SymGetLineFromAddr(hProcess, AddrPC.Offset, &dwLineDisplacement, &Line); if (IsDebuggerPresent()) { nLength = _snprintf( pBuffer, nBufferSize, "%s(%d) : %s!%s+0x%x (%p, %p, %p, %p)\n", Line.FileName, Line.LineNumber, Module.ModuleName, szUnDSymbol, dwDisplacement, Params[0], Params[1], Params[2], Params[3] ); } else { nLength = _snprintf( pBuffer, nBufferSize, Line.FileName ? "%p %p %p %p %s!%s+0x%x (%s:%d)\n" : "%p %p %p %p %s!%s+0x%x\n", Params[0], Params[1], Params[2], Params[3], Module.ModuleName, szUnDSymbol, dwDisplacement, GetFileNameA(Line.FileName), Line.LineNumber ); } return nLength; } }; ////////////////////////////////////////////////////////////////////////// // // // class CGuardAllocator { public: typedef enum { GUARD_NONE = 0, GUARD_TAIL = 1, GUARD_HEAD = 2, GUARD_FLAGS = GUARD_NONE | GUARD_TAIL | GUARD_HEAD, SAVE_STACK_FRAMES = 4 } GUARD_TYPE; CGuardAllocator(LONG lFlags = GUARD_NONE) { Create(lFlags); } ~CGuardAllocator() { Destroy(); } void Create(LONG lFlags = GUARD_NONE); void Destroy(); void SetGuardType(LONG lFlags); void Walk(FILE *fout); HANDLE TakeSnapShot(); void DeleteSnapShot(HANDLE hSnapShot); void Diff(HANDLE hSnapShot, FILE *fout); void *Alloc(DWORD dwFlags, size_t nSize); BOOL Free(DWORD dwFlags, void *pMem); size_t Size(DWORD dwFlags, const void *pMem); void *Realloc(DWORD dwFlags, void *pMem, size_t nSize); BOOL Validate(DWORD dwFlags, const void *pMem); private: void *(CGuardAllocator::*pfnAlloc)(size_t nSize); BOOL (CGuardAllocator::*pfnFree)(void *pMem); void *AllocGuardNone(size_t nSize); BOOL FreeGuardNone(void *pMem); void *AllocGuardTail(size_t nSize); BOOL FreeGuardTail(void *pMem); void *AllocGuardHead(size_t nSize); BOOL FreeGuardHead(void *pMem); private: struct CAllocation { DWORD m_dwMagic1; BOOL (CGuardAllocator::*pfnFree)(void *pMem); CAllocation *m_pPrev; CAllocation *m_pNext; size_t m_nSize; UINT m_nStackFrames; UINT m_nID; DWORD m_dwMagic2; int Dump(HANDLE hProcess, PSTR pBuffer, int nBufferSize) const; bool IsValid() const { return m_dwMagic1 == '>>>>' && m_dwMagic2 == '<<<<'; } }; private: LONG m_nInitCount; LONG m_lFlags; size_t m_nPageSize; HANDLE m_hProcess; HANDLE m_hProcessHeap; DWORD m_dwOsVersion; CAllocation m_Head; UINT m_nAllocations; UINT m_nNextID; CRITICAL_SECTION m_HeapLock; PCSTR m_pLeaksFileName; }; ////////////////////////////////////////////////////////////////////////// // // // extern CGuardAllocator g_GuardAllocator; ////////////////////////////////////////////////////////////////////////// // // // #ifdef IMPLEMENT_GUARDALLOC ////////////////////////////////////////////////////////////////////////// // // // #include struct CRelativeJmp { CRelativeJmp(PBYTE pFromAddr, PBYTE pToAddr) { jmp = 0xE9; addr = pToAddr - (pFromAddr + 5); } BYTE jmp; INT_PTR addr; }; #include BOOL ReplaceProc(FARPROC pOldProc, FARPROC pNewProc) { CRelativeJmp Code((PBYTE) pOldProc, (PBYTE) pNewProc); DWORD dwNumberOfBytesWritten; return WriteProcessMemory( GetCurrentProcess(), pOldProc, &Code, sizeof(Code), &dwNumberOfBytesWritten ); } inline PVOID FindPtr(PVOID pBase, UINT_PTR pOffset, PIMAGE_SECTION_HEADER psh) { //*** return (PBYTE) pBase + pOffset - psh->VirtualAddress + psh->PointerToRawData; return (PBYTE) pBase + pOffset; } PIMAGE_FILE_HEADER FindImageFileHeader(PVOID pBase) { WORD wMagic = *(WORD *) pBase; if (wMagic == IMAGE_DOS_SIGNATURE) { PIMAGE_DOS_HEADER pdh = (PIMAGE_DOS_HEADER) pBase; if (pdh->e_lfanew) { DWORD dwMagic = *(DWORD *) ((PBYTE) pBase + pdh->e_lfanew); if (dwMagic == IMAGE_NT_SIGNATURE) { return (PIMAGE_FILE_HEADER) ((PBYTE) pBase + pdh->e_lfanew + sizeof(DWORD)); } } } return 0; } PVOID FindImageDirectoryEntry( PVOID pBase, int nDirectory, PIMAGE_SECTION_HEADER &psh ) { PIMAGE_FILE_HEADER pfh = FindImageFileHeader(pBase); if (pfh && pfh->SizeOfOptionalHeader) { PIMAGE_OPTIONAL_HEADER poh = (PIMAGE_OPTIONAL_HEADER)(pfh + 1); if (poh->Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { DWORD VADirectory = poh->DataDirectory[nDirectory].VirtualAddress; if (VADirectory) { psh = (PIMAGE_SECTION_HEADER) ((PBYTE) poh + pfh->SizeOfOptionalHeader); for (int nSection = 0; nSection < pfh->NumberOfSections; ++nSection) { if ( psh->VirtualAddress <= VADirectory && psh->VirtualAddress + psh->SizeOfRawData > VADirectory ) { return FindPtr(pBase, VADirectory, psh); } ++psh; } } } } return 0; } FARPROC *FindImport(PBYTE pBase, PCSTR pDllName, PCSTR pProcName) { PIMAGE_SECTION_HEADER psh; PIMAGE_IMPORT_DESCRIPTOR pImportDir = (PIMAGE_IMPORT_DESCRIPTOR) FindImageDirectoryEntry(pBase, IMAGE_DIRECTORY_ENTRY_IMPORT, psh); if (pImportDir) { while (pImportDir->Name) { PCSTR pName = (PCSTR) FindPtr(pBase, pImportDir->Name, psh); if (stricmp(pName, pDllName) == 0) { PINT_PTR pHintNameArray = (PINT_PTR) FindPtr(pBase, pImportDir->Characteristics, psh); PINT_PTR ppImportByName = pHintNameArray; while (*ppImportByName) { if (*ppImportByName > 0) { PIMAGE_IMPORT_BY_NAME pImportByName = (PIMAGE_IMPORT_BY_NAME) FindPtr(pBase, *ppImportByName, psh); if (strcmp((PCSTR) pImportByName->Name, pProcName) == 0) { FARPROC *pImportAddressTable = (FARPROC *) FindPtr(pBase, pImportDir->FirstThunk, psh); return pImportAddressTable + (ppImportByName - pHintNameArray); } } ++ppImportByName; } } ++pImportDir; } } return 0; } BOOL ReplaceImport(HMODULE hModule, PCSTR pDllName, PCSTR pProcName, FARPROC pNewProc, FARPROC pExpected) { FARPROC *pImport = FindImport((PBYTE) hModule, pDllName, pProcName); if (pImport == 0) { return FALSE; } if (*pImport != pExpected) { //DebugBreak(); } DWORD dwNumberOfBytesWritten; return WriteProcessMemory( GetCurrentProcess(), pImport, &pNewProc, sizeof(pNewProc), &dwNumberOfBytesWritten ); } class CModules { public: CModules() { m_nModules = 0; InitializeCriticalSection(&m_LoadLibraryLock); } ~CModules() { DeleteCriticalSection(&m_LoadLibraryLock); } BOOL Add(PCSTR pName, HMODULE hModule) { EnterCriticalSection(&m_LoadLibraryLock); int nModule = Find(hModule); if (nModule == -1) { PCSTR pModuleName = GetFileNameA(pName); strcpy(m_Modules[m_nModules].szName, pModuleName); if (strchr(pModuleName, '.') == 0) { strcat(m_Modules[m_nModules].szName, ".dll"); } m_Modules[m_nModules].hModule = hModule; m_Modules[m_nModules].nRefs = 1; ++m_nModules; } else { ++m_Modules[nModule].nRefs; } LeaveCriticalSection(&m_LoadLibraryLock); return nModule == -1; } void Free(HMODULE hModule) { EnterCriticalSection(&m_LoadLibraryLock); int nModule = Find(hModule); if (nModule == -1) { OutputDebugStringA("*** Trying to free unloaded dll\n"); DebugBreak(); } else { if (--m_Modules[nModule].nRefs == 0) { --m_nModules; for (int i = nModule; i < m_nModules; ++i) { m_Modules[i] = m_Modules[i + 1]; } } } LeaveCriticalSection(&m_LoadLibraryLock); } private: int Find(PCSTR pName) { for (int i = 0; i < m_nModules; ++i) { if (stricmp(m_Modules[i].szName, pName) == 0) { return i; } } return -1; } int Find(HMODULE hModule) { for (int i = 0; i < m_nModules; ++i) { if (m_Modules[i].hModule == hModule) { return i; } } return -1; } private: struct CModule { CHAR szName[32]; HMODULE hModule; int nRefs; }; private: int m_nModules; CModule m_Modules[1000]; //bugbug CRITICAL_SECTION m_LoadLibraryLock; }; typedef BOOL (*PFNENUMIMAGEMODULESPROC)(PCSTR pName, HMODULE pBase); VOID EnumImageModules(PCSTR pName, HMODULE pBase, PFNENUMIMAGEMODULESPROC pfnCallback) { if (pfnCallback(pName, pBase)) { PIMAGE_SECTION_HEADER psh; PIMAGE_IMPORT_DESCRIPTOR pImportDir = (PIMAGE_IMPORT_DESCRIPTOR) FindImageDirectoryEntry((PBYTE) pBase, IMAGE_DIRECTORY_ENTRY_IMPORT, psh); if (pImportDir) { while (pImportDir->Name) { PSTR pModuleName = (PSTR) FindPtr((PBYTE) pBase, pImportDir->Name, psh); HMODULE hModuleBase = GetModuleHandleA(pModuleName); EnumImageModules(pModuleName, hModuleBase, pfnCallback); ++pImportDir; } } } } ////////////////////////////////////////////////////////////////////////// // // // PVOID (WINAPI *g_pfnHeapAlloc)(HANDLE hHeap, DWORD dwFlags, SIZE_T nSize); PVOID WINAPI SysHeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T nSize) { return g_pfnHeapAlloc(hHeap, dwFlags, nSize); } BOOL (WINAPI *g_pfnHeapFree)(HANDLE hHeap, DWORD dwFlags, PVOID pMem); BOOL WINAPI SysHeapFree(HANDLE hHeap, DWORD dwFlags, PVOID pMem) { return g_pfnHeapFree(hHeap, dwFlags, pMem); } SIZE_T (WINAPI *g_pfnHeapSize)(HANDLE hHeap, DWORD dwFlags, LPCVOID pMem); SIZE_T WINAPI SysHeapSize(HANDLE hHeap, DWORD dwFlags, LPCVOID pMem) { return g_pfnHeapSize(hHeap, dwFlags, pMem); } PVOID (WINAPI *g_pfnHeapReAlloc)(HANDLE hHeap, DWORD dwFlags, LPVOID pMem, SIZE_T nSize); PVOID WINAPI SysHeapReAlloc(HANDLE hHeap, DWORD dwFlags, LPVOID pMem, SIZE_T nSize) { return g_pfnHeapReAlloc(hHeap, dwFlags, pMem, nSize); } BOOL (WINAPI *g_pfnHeapValidate)(HANDLE hHeap, DWORD dwFlags, LPCVOID pMem); BOOL WINAPI SysHeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID pMem) { return g_pfnHeapValidate(hHeap, dwFlags, pMem); } ////////////////////////////////////////////////////////////////////////// // // // __declspec(thread) static g_bDisable = FALSE; LPVOID WINAPI MyHeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T nSize) { return g_GuardAllocator.Alloc(dwFlags, nSize); } BOOL WINAPI MyHeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID pMem) { return g_GuardAllocator.Free(dwFlags, pMem); } SIZE_T WINAPI MyHeapSize(HANDLE hHeap, DWORD dwFlags, LPCVOID pMem) { return g_GuardAllocator.Size(dwFlags, pMem); } LPVOID WINAPI MyHeapReAlloc(HANDLE hHeap, DWORD dwFlags, LPVOID pMem, SIZE_T nSize) { return g_GuardAllocator.Realloc(dwFlags, pMem, nSize); } BOOL WINAPI MyHeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID pMem) { return g_GuardAllocator.Validate(dwFlags, pMem); } ////////////////////////////////////////////////////////////////////////// // // // CModules *g_pModules = 0; BOOL EnumImageModulesProc(PCSTR pName, HMODULE pBase); HMODULE (WINAPI *g_pfnLoadLibraryA)(LPCSTR lpLibFileName); HMODULE WINAPI MyLoadLibraryA(LPCSTR lpLibFileNameA) { HMODULE hModule = g_pfnLoadLibraryA(lpLibFileNameA); EnumImageModules(lpLibFileNameA, hModule, EnumImageModulesProc); return hModule; } HMODULE (WINAPI *g_pfnLoadLibraryW)(LPCWSTR lpLibFileName); HMODULE WINAPI MyLoadLibraryW(LPCWSTR lpLibFileNameW) { HMODULE hModule = g_pfnLoadLibraryW(lpLibFileNameW); int nSize = WideCharToMultiByte(CP_ACP, 0, lpLibFileNameW, -1, 0, 0, 0, 0); PSTR lpLibFileNameA = (PSTR) _alloca(nSize); WideCharToMultiByte(CP_ACP, 0, lpLibFileNameW, -1, lpLibFileNameA, nSize, 0, 0); EnumImageModules(lpLibFileNameA, hModule, EnumImageModulesProc); return hModule; } HMODULE (WINAPI *g_pfnLoadLibraryExA)(LPCSTR lpLibFileNameA, HANDLE hFile, DWORD dwFlags); HMODULE WINAPI MyLoadLibraryExA(LPCSTR lpLibFileNameA, HANDLE hFile, DWORD dwFlags) { HMODULE hModule = g_pfnLoadLibraryExA(lpLibFileNameA, hFile, dwFlags); EnumImageModules(lpLibFileNameA, hModule, EnumImageModulesProc); return hModule; } HMODULE (WINAPI *g_pfnLoadLibraryExW)(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags); HMODULE WINAPI MyLoadLibraryExW(LPCWSTR lpLibFileNameW, HANDLE hFile, DWORD dwFlags) { HMODULE hModule = g_pfnLoadLibraryExW(lpLibFileNameW, hFile, dwFlags); int nSize = WideCharToMultiByte(CP_ACP, 0, lpLibFileNameW, -1, 0, 0, 0, 0); PSTR lpLibFileNameA = (PSTR) _alloca(nSize); WideCharToMultiByte(CP_ACP, 0, lpLibFileNameW, -1, lpLibFileNameA, nSize, 0, 0); EnumImageModules(lpLibFileNameA, hModule, EnumImageModulesProc); return hModule; } BOOL (WINAPI *g_pfnFreeLibrary)(HMODULE hLibModule); BOOL WINAPI MyFreeLibrary(HMODULE hLibModule) { g_pModules->Free(hLibModule); return g_pfnFreeLibrary(hLibModule); } BOOL EnumImageModulesProc(PCSTR pName, HMODULE pBase) { if (!pBase || !g_pModules->Add(pName, pBase)) { return FALSE; } //if (stricmp(pName, "user32.dll") == 0 || stricmp(pName, "user32") == 0)//*** //{ // return TRUE; //} OutputDebugStringA("Patching "); //*** OutputDebugStringA(pName); //*** OutputDebugStringA("\n"); //*** ReplaceImport(pBase, "kernel32.dll", "LoadLibraryA", (FARPROC) MyLoadLibraryA, (FARPROC) g_pfnLoadLibraryA); ReplaceImport(pBase, "kernel32.dll", "LoadLibraryW", (FARPROC) MyLoadLibraryW, (FARPROC) g_pfnLoadLibraryW); ReplaceImport(pBase, "kernel32.dll", "LoadLibraryExA", (FARPROC) MyLoadLibraryExA, (FARPROC) g_pfnLoadLibraryExA); ReplaceImport(pBase, "kernel32.dll", "LoadLibraryExW", (FARPROC) MyLoadLibraryExW, (FARPROC) g_pfnLoadLibraryExW); ReplaceImport(pBase, "kernel32.dll", "FreeLibrary", (FARPROC) MyFreeLibrary, (FARPROC) g_pfnFreeLibrary); ReplaceImport(pBase, "kernel32.dll", "HeapAlloc", (FARPROC) MyHeapAlloc, (FARPROC) g_pfnHeapAlloc); ReplaceImport(pBase, "kernel32.dll", "HeapReAlloc", (FARPROC) MyHeapReAlloc, (FARPROC) g_pfnHeapReAlloc); ReplaceImport(pBase, "kernel32.dll", "HeapFree", (FARPROC) MyHeapFree, (FARPROC) g_pfnHeapFree); ReplaceImport(pBase, "kernel32.dll", "HeapSize", (FARPROC) MyHeapSize, (FARPROC) g_pfnHeapSize); ReplaceImport(pBase, "kernel32.dll", "HeapValidate", (FARPROC) MyHeapValidate, (FARPROC) g_pfnHeapValidate); return TRUE; } void CGuardAllocator::Create(LONG lFlags) { SetGuardType(lFlags); if (m_nInitCount++ == 0) { OutputDebugStringA("Initializing debug heap\n"); SYSTEM_INFO si; GetSystemInfo(&si); m_nPageSize = si.dwPageSize; m_Head.m_pNext = &m_Head; m_Head.m_pPrev = &m_Head; m_nAllocations = 0; m_nNextID = 0; InitializeCriticalSection(&m_HeapLock); m_hProcess = GetCurrentProcess(); m_hProcessHeap = GetProcessHeap(); m_dwOsVersion = GetVersion(); HMODULE hKernel32 = GetModuleHandle(TEXT("kernel32.dll")); *(FARPROC*)& g_pfnLoadLibraryA = GetProcAddress(hKernel32, "LoadLibraryA"); *(FARPROC*)& g_pfnLoadLibraryW = GetProcAddress(hKernel32, "LoadLibraryW"); *(FARPROC*)& g_pfnLoadLibraryExA = GetProcAddress(hKernel32, "LoadLibraryExA"); *(FARPROC*)& g_pfnLoadLibraryExW = GetProcAddress(hKernel32, "LoadLibraryExW"); *(FARPROC*)& g_pfnFreeLibrary = GetProcAddress(hKernel32, "FreeLibrary"); *(FARPROC*)& g_pfnHeapAlloc = GetProcAddress(hKernel32, "HeapAlloc"); *(FARPROC*)& g_pfnHeapReAlloc = GetProcAddress(hKernel32, "HeapReAlloc"); *(FARPROC*)& g_pfnHeapFree = GetProcAddress(hKernel32, "HeapFree"); *(FARPROC*)& g_pfnHeapSize = GetProcAddress(hKernel32, "HeapSize"); *(FARPROC*)& g_pfnHeapValidate = GetProcAddress(hKernel32, "HeapValidate"); g_pModules = new CModules; if (m_dwOsVersion & 0x80000000 == 0) { CHAR szModuleName[MAX_PATH]; GetModuleFileNameA(0, szModuleName, MAX_PATH); EnumImageModules( szModuleName, GetModuleHandle(0), EnumImageModulesProc ); /*ReplaceProc((FARPROC) g_pfnHeapAlloc, (FARPROC) MyHeapAlloc); ReplaceProc((FARPROC) g_pfnHeapReAlloc, (FARPROC) MyHeapReAlloc); ReplaceProc((FARPROC) g_pfnHeapSize, (FARPROC) MyHeapSize); ReplaceProc((FARPROC) g_pfnHeapFree, (FARPROC) MyHeapFree); ReplaceProc((FARPROC) g_pfnHeapFree, (FARPROC) MyHeapValidate);*/ } m_pLeaksFileName = 0; } } ////////////////////////////////////////////////////////////////////////// // // // void CGuardAllocator::Destroy() { if (m_pLeaksFileName) { FILE *fout = fopen(m_pLeaksFileName, "wt"); Walk(fout); fclose(fout); } if (--m_nInitCount == 0) { DeleteCriticalSection(&m_HeapLock); delete g_pModules; } } ////////////////////////////////////////////////////////////////////////// // // // void CGuardAllocator::SetGuardType(LONG lFlags) { //bugbug: this is not MT safe m_lFlags = lFlags; switch (m_lFlags & GUARD_FLAGS) { case GUARD_NONE: pfnAlloc = AllocGuardNone; pfnFree = FreeGuardNone; break; case GUARD_TAIL: pfnAlloc = AllocGuardTail; pfnFree = FreeGuardTail; break; case GUARD_HEAD: pfnAlloc = AllocGuardHead; pfnFree = FreeGuardHead; break; } } ////////////////////////////////////////////////////////////////////////// // // // void CGuardAllocator::Walk(FILE *fout) { g_bDisable = TRUE; SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES); SymInitialize(m_hProcess, 0, TRUE); const int nBufferSize = 16*1024; CHAR Buffer[nBufferSize]; int nLeakedAllocs = 0; size_t nLeakedBytes = 0; EnterCriticalSection(&m_HeapLock); for ( CAllocation *pAllocation = m_Head.m_pNext; pAllocation != &m_Head; pAllocation = pAllocation->m_pNext ) { int nLength = pAllocation->Dump(m_hProcess, Buffer, nBufferSize); fwrite(Buffer, 1, nLength, fout); //OutputDebugStringA(Buffer); nLeakedAllocs += 1; nLeakedBytes += pAllocation->m_nSize; } if (nLeakedAllocs != 0) { int nLength = _snprintf( Buffer, nBufferSize, "\nLeaked %d bytes in %d allocations (%d total)\n", nLeakedBytes, nLeakedAllocs, m_nAllocations ); fwrite(Buffer, 1, nLength, fout); //OutputDebugStringA(Buffer); } LeaveCriticalSection(&m_HeapLock); SymCleanup(m_hProcess); g_bDisable = FALSE; } ////////////////////////////////////////////////////////////////////////// // // // int CGuardAllocator::CAllocation::Dump(HANDLE hProcess, PSTR pBuffer, int nBufferSize) const { int nLength = _snprintf( pBuffer, nBufferSize, "\nAllocation @%p, %d bytes\n", this, m_nSize ); size_t nStackFramesSize = Align(m_nStackFrames * sizeof(CStackFrame), ALIGNMENT); CStackFrame *pStackFrame = (CStackFrame *) ((PBYTE) this - nStackFramesSize); for (UINT i = 0; i < m_nStackFrames; ++i, ++pStackFrame) { nLength += pStackFrame->Dump( hProcess, pBuffer + nLength, nBufferSize - nLength ); } return nLength; } ////////////////////////////////////////////////////////////////////////// // // // HANDLE CGuardAllocator::TakeSnapShot() { g_bDisable = TRUE; UINT *pSnapShot = (UINT *) Alloc(0, m_nAllocations * sizeof(UINT)); int nAllocation = 0; for ( CAllocation *pAllocation = m_Head.m_pNext; pAllocation != &m_Head; pAllocation = pAllocation->m_pNext ) { pSnapShot[nAllocation++] = pAllocation->m_nID; } g_bDisable = FALSE; return (HANDLE) pSnapShot; } ////////////////////////////////////////////////////////////////////////// // // // void CGuardAllocator::DeleteSnapShot(HANDLE hSnapShot) { g_bDisable = TRUE; Free(0, hSnapShot); g_bDisable = FALSE; } ////////////////////////////////////////////////////////////////////////// // // // void CGuardAllocator::Diff(HANDLE hSnapShot, FILE *fout) { g_bDisable = TRUE; SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES); SymInitialize(m_hProcess, 0, TRUE); const int nBufferSize = 16*1024; CHAR Buffer[nBufferSize]; UINT *pSnapShot = (UINT *) hSnapShot; int nAllocation = 0; for ( CAllocation *pAllocation = m_Head.m_pNext; pAllocation != &m_Head; pAllocation = pAllocation->m_pNext ) { while (pAllocation->m_nID < pSnapShot[nAllocation]) { ++nAllocation; } if (pAllocation->m_nID != pSnapShot[nAllocation]) { int nLength = pAllocation->Dump(m_hProcess, Buffer, nBufferSize); fwrite(Buffer, 1, nLength, fout); //OutputDebugStringA(Buffer); } } SymCleanup(m_hProcess); g_bDisable = FALSE; } ////////////////////////////////////////////////////////////////////////// // // // template struct static_assert { enum { is_non_zero }; }; template <> struct static_assert<0> { enum { is_zero }; }; void *CGuardAllocator::Alloc(DWORD dwFlags, size_t nSize) { if (m_nInitCount == 0) { OutputDebugStringA("Heap call before initialization\n"); DebugBreak(); } if (g_bDisable) { return SysHeapAlloc(m_hProcessHeap, dwFlags, nSize); } g_bDisable = TRUE; const int nMaxStackDepth = 20; CStackFrame StackFrames[nMaxStackDepth + 1]; int nStackFrames = 0; if (m_lFlags & SAVE_STACK_FRAMES) { // get the context of the current thread CONTEXT Context; if (m_dwOsVersion & 0x80000000) { __try { RaiseException(0, 0, 0, 0); } __except(GetExceptionContext(GetExceptionInformation(), &Context)) { } } else { Context.ContextFlags = CONTEXT_CONTROL; GetThreadContext(GetCurrentThread(), &Context); } // bugbug: putting the above block in a function like // GetCurrentThreadContext(&Context); doesn't seem to work // capture the stack frames CStackFrame StackFrame(&Context); while (nStackFrames < nMaxStackDepth && StackFrame.Walk()) { StackFrames[nStackFrames++] = StackFrame; } } //static_assert::is_zero; size_t nStackFramesSize = Align(nStackFrames * sizeof(CStackFrame), ALIGNMENT); // allocate memory (large enough for the stack frames + allocation data size + requested size) // bugbug: putting CAllocation at front doesn't make sense for GUARD_HEAD void *pMem = (this->*pfnAlloc)(nStackFramesSize + sizeof(CAllocation) + nSize); g_bDisable = FALSE; if (pMem == 0) { OutputDebugStringA("*** Out of memory in alloc()\n"); return 0; } // copy the stack frames first CopyMemory(pMem, StackFrames, nStackFramesSize); // next, fill in the allocation data CAllocation *pAllocation = (CAllocation *) ((PBYTE)pMem + nStackFramesSize); pAllocation->m_dwMagic1 = '>>>>'; pAllocation->m_nSize = nSize; pAllocation->m_nStackFrames = nStackFrames; pAllocation->pfnFree = pfnFree; pAllocation->m_dwMagic2 = '<<<<'; EnterCriticalSection(&m_HeapLock); pAllocation->m_pPrev = &m_Head; pAllocation->m_pNext = m_Head.m_pNext; pAllocation->m_pPrev->m_pNext = pAllocation; pAllocation->m_pNext->m_pPrev = pAllocation; pAllocation->m_nID = m_nNextID++; ++m_nAllocations; LeaveCriticalSection(&m_HeapLock); // return the end of allocation data struct as the allocated memory return pAllocation + 1; } ////////////////////////////////////////////////////////////////////////// // // // BOOL CGuardAllocator::Free(DWORD dwFlags, void *pMem) { if (m_nInitCount == 0) { OutputDebugStringA("Heap call before initialization\n"); DebugBreak(); } if (g_bDisable) { return SysHeapFree(m_hProcessHeap, dwFlags, pMem); } if (pMem == 0) { return TRUE; } // get to the allocation data, it comes just before the pointer CAllocation *pAllocation = (CAllocation *) pMem - 1; if (!pAllocation->IsValid()) { OutputDebugStringA("*** Invalid pointer passed to free()\n"); //***DebugBreak(); return FALSE; } // unlink this block EnterCriticalSection(&m_HeapLock); pAllocation->m_pPrev->m_pNext = pAllocation->m_pNext; pAllocation->m_pNext->m_pPrev = pAllocation->m_pPrev; --m_nAllocations; LeaveCriticalSection(&m_HeapLock); // get to head of the real allocated block and call the appropriate deallocator size_t nStackFramesSize = Align(pAllocation->m_nStackFrames * sizeof(CStackFrame), ALIGNMENT); return (this->*pAllocation->pfnFree)((PBYTE)pAllocation - nStackFramesSize); } ////////////////////////////////////////////////////////////////////////// // // // size_t CGuardAllocator::Size(DWORD dwFlags, const void *pMem) { if (m_nInitCount == 0) { OutputDebugStringA("Heap call before initialization\n"); DebugBreak(); } if (g_bDisable) { return SysHeapSize(m_hProcessHeap, dwFlags, pMem); } CAllocation *pAllocation = (CAllocation *) pMem - 1; return pAllocation->m_nSize; } ////////////////////////////////////////////////////////////////////////// // // // void *CGuardAllocator::Realloc(DWORD dwFlags, void *pMem, size_t nSize) { if (m_nInitCount == 0) { OutputDebugStringA("Heap call before initialization\n"); DebugBreak(); } if (g_bDisable) { return SysHeapReAlloc(m_hProcessHeap, dwFlags, pMem, nSize); } CAllocation *pAllocation = (CAllocation *) pMem - 1; if (!pAllocation->IsValid()) { OutputDebugStringA("*** Invalid pointer passed to realloc()\n"); DebugBreak(); return 0; } void *pNewMem = Alloc(0, nSize); CopyMemory(pNewMem, pMem, Size(0, pMem)); Free(0, pMem); return pNewMem; } ////////////////////////////////////////////////////////////////////////// // // // BOOL CGuardAllocator::Validate(DWORD dwFlags, const void *pMem) { if (m_nInitCount == 0) { OutputDebugStringA("Heap call before initialization\n"); DebugBreak(); } if (g_bDisable) { return SysHeapValidate(m_hProcessHeap, dwFlags, pMem); } BOOL bValid = TRUE; EnterCriticalSection(&m_HeapLock); __try { if (pMem) { CAllocation *pAllocation = (CAllocation *) pMem - 1; if (!pAllocation->IsValid()) { bValid = FALSE; } } else { for ( CAllocation *pAllocation = m_Head.m_pNext; pAllocation != &m_Head && bValid; pAllocation = pAllocation->m_pNext ) { if (!pAllocation->IsValid()) { bValid = FALSE; } } } } __except(EXCEPTION_EXECUTE_HANDLER) { bValid = FALSE; } LeaveCriticalSection(&m_HeapLock); return bValid; } ////////////////////////////////////////////////////////////////////////// // // // void *CGuardAllocator::AllocGuardNone(size_t nSize) { return SysHeapAlloc(m_hProcessHeap, HEAP_ZERO_MEMORY, nSize); } ////////////////////////////////////////////////////////////////////////// // // // BOOL CGuardAllocator::FreeGuardNone(void *pMem) { return SysHeapFree(m_hProcessHeap, 0, pMem); } ////////////////////////////////////////////////////////////////////////// // // // void *CGuardAllocator::AllocGuardTail(size_t nSize) { const size_t nAllocSize = Align(nSize, ALIGNMENT); const size_t nTotalSize = Align(nAllocSize + m_nPageSize, m_nPageSize); // reserve/allocate the memory void *pBase = VirtualAlloc( 0, nTotalSize, MEM_RESERVE, PAGE_NOACCESS ); if (!pBase) { return 0; } // commit the r/w memory void *pAlloc = VirtualAlloc( pBase, nTotalSize - m_nPageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); if (!pAlloc) { VirtualFree(pBase, 0, MEM_RELEASE); return 0; } // commit the guard page void *pGuard = VirtualAlloc( (PBYTE) pBase + nTotalSize - m_nPageSize, m_nPageSize, MEM_COMMIT, PAGE_NOACCESS ); if (!pGuard) { VirtualFree(pAlloc, 0, MEM_DECOMMIT); VirtualFree(pBase, 0, MEM_RELEASE); return 0; } return (PBYTE) pBase + nTotalSize - m_nPageSize - nAllocSize; } ////////////////////////////////////////////////////////////////////////// // // // BOOL CGuardAllocator::FreeGuardTail(void *pMem) { PVOID pBase = (PVOID) ((UINT_PTR) pMem & ~(m_nPageSize-1)); return VirtualFree(pBase, 0, MEM_RELEASE); } ////////////////////////////////////////////////////////////////////////// // // // void *CGuardAllocator::AllocGuardHead(size_t nSize) { const size_t nAllocSize = Align(nSize, ALIGNMENT); const size_t nTotalSize = Align(nAllocSize + m_nPageSize, m_nPageSize); // reserve/allocate the memory void *pBase = VirtualAlloc( 0, nTotalSize, MEM_RESERVE, PAGE_NOACCESS ); if (!pBase) { return 0; } // commit the r/w memory void *pAlloc = VirtualAlloc( (PBYTE) pBase + m_nPageSize, nTotalSize - m_nPageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); if (!pAlloc) { VirtualFree(pBase, 0, MEM_RELEASE); return 0; } // commit the guard page void *pGuard = VirtualAlloc( pBase, m_nPageSize, MEM_COMMIT, PAGE_NOACCESS ); if (!pGuard) { VirtualFree(pAlloc, 0, MEM_DECOMMIT); VirtualFree(pBase, 0, MEM_RELEASE); return 0; } return (PBYTE) pBase + m_nPageSize; } ////////////////////////////////////////////////////////////////////////// // // // BOOL CGuardAllocator::FreeGuardHead(void *pMem) { PVOID pBase = (PVOID) ((UINT_PTR) pMem - m_nPageSize); return VirtualFree(pBase, 0, MEM_RELEASE); } ////////////////////////////////////////////////////////////////////////// // // // #ifdef _DLL #pragma message("Need to link with static CRT libs to be able to replace malloc/free") #else //_DLL ////////////////////////////////////////////////////////////////////////// // // // void * __cdecl ::operator new(size_t nSize) { return g_GuardAllocator.Alloc(0, nSize); } #ifdef _DEBUG void* __cdecl operator new(size_t nSize, int, LPCSTR, int) { return g_GuardAllocator.Alloc(0, nSize); } #endif //_DEBUG void __cdecl ::operator delete(void *pMem) { g_GuardAllocator.Free(0, pMem); } ////////////////////////////////////////////////////////////////////////// // // // extern "C" { #undef _malloc_dbg #undef _free_dbg #undef _msize_dbg #undef _realloc_dbg #undef _expand_dbg void * __cdecl malloc(size_t nSize) { return g_GuardAllocator.Alloc(0, nSize); } void * __cdecl _malloc_dbg(size_t nSize, int, const char *, int) { return g_GuardAllocator.Alloc(0, nSize); } void __cdecl free(void *pMem) { g_GuardAllocator.Free(0, pMem); } void __cdecl _free_dbg(void *pMem, int) { g_GuardAllocator.Free(0, pMem); } size_t __cdecl _msize(void *pMem) { return g_GuardAllocator.Size(0, pMem); } size_t __cdecl _msize_dbg(void *pMem, int) { return g_GuardAllocator.Size(0, pMem); } void * __cdecl realloc(void *pMem, size_t nSize) { return g_GuardAllocator.Realloc(0, pMem, nSize); } void * __cdecl _realloc_dbg(void *pMem, size_t nSize, int, const char *, int) { return g_GuardAllocator.Realloc(0, pMem, nSize); } void * __cdecl _expand(void *, size_t) { return 0; } void * __cdecl _expand_dbg(void *, size_t, int, const char *, int) { return 0; } ////////////////////////////////////////////////////////////////////////// // // // void __cdecl wWinMainCRTStartup(); void __cdecl WinMainCRTStartup(); void __cdecl wmainCRTStartup(); void __cdecl mainCRTStartup(); void __cdecl ModuleEntry() { g_GuardAllocator.Create(); #ifdef _CONSOLE #ifdef UNICODE wmainCRTStartup(); #else //UNICODE mainCRTStartup(); #endif #else //_CONSOLE #ifdef UNICODE wWinMainCRTStartup(); #else //UNICODE WinMainCRTStartup(); #endif #endif //_CONSOLE g_GuardAllocator.Destroy(); } } ////////////////////////////////////////////////////////////////////////// // // // #pragma comment(linker, "/force:multiple") #pragma comment(linker, "/entry:ModuleEntry") #endif //_DLL #pragma comment(lib, "dbghelp") #endif IMPLEMENT_GUARDALLOC #endif //_GUARDALLOC_H_