/*++ Copyright (c) 2000 Microsoft Corporation Module Name: EmulateHeap.cpp Abstract: This SHIM is for the layer and it emulates the Win9x heap manager. In fact, much of the code is adapted from the Win9x sources .\heap.c and .\lmem.c. This SHIM hooks all the heap allocation/deallocation functions including the local/global functions. Notes: This is a general purpose shim. History: 11/16/2000 prashkud & linstev Created --*/ #include "precomp.h" IMPLEMENT_SHIM_BEGIN(EmulateHeap) #include "ShimHookMacro.h" APIHOOK_ENUM_BEGIN APIHOOK_ENUM_ENTRY(HeapCreate) APIHOOK_ENUM_ENTRY(HeapDestroy) APIHOOK_ENUM_ENTRY(HeapValidate) APIHOOK_ENUM_ENTRY(HeapCompact) APIHOOK_ENUM_ENTRY(HeapWalk) APIHOOK_ENUM_ENTRY(HeapLock) APIHOOK_ENUM_ENTRY(HeapUnlock) APIHOOK_ENUM_ENTRY(GetProcessHeap) APIHOOK_ENUM_ENTRY(LocalAlloc) APIHOOK_ENUM_ENTRY(LocalFree) APIHOOK_ENUM_ENTRY(LocalReAlloc) APIHOOK_ENUM_ENTRY(LocalLock) APIHOOK_ENUM_ENTRY(LocalUnlock) APIHOOK_ENUM_ENTRY(LocalHandle) APIHOOK_ENUM_ENTRY(LocalSize) APIHOOK_ENUM_ENTRY(LocalFlags) APIHOOK_ENUM_ENTRY(GlobalAlloc) APIHOOK_ENUM_ENTRY(GlobalFree) APIHOOK_ENUM_ENTRY(GlobalReAlloc) APIHOOK_ENUM_ENTRY(GlobalLock) APIHOOK_ENUM_ENTRY(GlobalUnlock) APIHOOK_ENUM_ENTRY(GlobalHandle) APIHOOK_ENUM_ENTRY(GlobalSize) APIHOOK_ENUM_ENTRY(GlobalFlags) APIHOOK_ENUM_ENTRY(RtlAllocateHeap) APIHOOK_ENUM_ENTRY(RtlReAllocateHeap) APIHOOK_ENUM_ENTRY(RtlFreeHeap) APIHOOK_ENUM_ENTRY(RtlSizeHeap) APIHOOK_ENUM_END extern "C" { BOOL _HeapInit(); HANDLE _HeapCreate(DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize); BOOL _HeapDestroy(HANDLE hHeap); LPVOID _HeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes); LPVOID _HeapReAlloc(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem, SIZE_T dwBytes); BOOL _HeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem); DWORD _HeapSize(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem); HLOCAL APIENTRY _LocalAlloc(UINT dwFlags, UINT dwBytes); HLOCAL APIENTRY _LocalFree(HLOCAL hMem); LPVOID _LocalReAlloc(LPVOID lpMem, SIZE_T dwBytes, UINT uFlags); LPVOID _LocalLock(HLOCAL hMem); BOOL _LocalUnlock(HLOCAL hMem); HANDLE _LocalHandle(LPCVOID hMem); UINT _LocalSize(HLOCAL hMem); UINT _LocalFlags(HLOCAL hMem); HANDLE _GetProcessHeap(void); BOOL _IsOurHeap(HANDLE hHeap); BOOL _IsOurLocalHeap(HANDLE hMem); BOOL _IsOnOurHeap(LPCVOID lpMem); } /*++ Helper functions so as not to bloat the stubs. --*/ BOOL UseOurHeap(HANDLE hHeap, LPCVOID lpMem) { return (_IsOurHeap(hHeap) || ((hHeap == RtlProcessHeap()) && _IsOnOurHeap(lpMem))); } BOOL ValidateNTHeap(HANDLE hHeap, LPCVOID lpMem) { BOOL bRet = FALSE; __try { bRet = ORIGINAL_API(HeapValidate)(hHeap, 0, lpMem); } __except(EXCEPTION_EXECUTE_HANDLER) { LOGN( eDbgLevelError, "[ValidateHeap] %08lx:%08lx is invalid", hHeap, lpMem); } return bRet; } /*++ Stub APIs. --*/ LPVOID APIHOOK(RtlAllocateHeap)( HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes ) { if (_IsOurHeap(hHeap)) { return _HeapAlloc(hHeap, dwFlags, dwBytes); } else { DPFN( eDbgLevelInfo, "NTHEAP: RtlAllocateHeap"); return ORIGINAL_API(RtlAllocateHeap)(hHeap, dwFlags, dwBytes); } } HANDLE APIHOOK(HeapCreate)( DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize ) { return _HeapCreate(flOptions, dwInitialSize, dwMaximumSize); } LPVOID APIHOOK(RtlReAllocateHeap)( HANDLE hHeap, DWORD dwFlags, LPVOID lpMem, SIZE_T dwBytes ) { LPVOID uRet = FALSE; if (UseOurHeap(hHeap, lpMem)) { uRet = _HeapReAlloc(hHeap, dwFlags, lpMem, dwBytes); } else { DPFN( eDbgLevelInfo, "NTHEAP: RtlReAllocateHeap"); if (ValidateNTHeap(hHeap, lpMem)) { uRet = ORIGINAL_API(RtlReAllocateHeap)(hHeap, dwFlags, lpMem, dwBytes); } } return uRet; } BOOL APIHOOK(RtlFreeHeap)( HANDLE hHeap, DWORD dwFlags, LPVOID lpMem ) { BOOL bRet = FALSE; if (UseOurHeap(hHeap, lpMem)) { bRet = _HeapFree(hHeap, dwFlags, lpMem); } else { DPFN( eDbgLevelInfo, "NTHEAP: RtlFreeHeap"); if (ValidateNTHeap(hHeap, lpMem)) { bRet = ORIGINAL_API(RtlFreeHeap)(hHeap, dwFlags, lpMem); } } return bRet; } HANDLE APIHOOK(GetProcessHeap)(VOID) { return _GetProcessHeap(); } BOOL APIHOOK(HeapDestroy)(HANDLE hHeap) { if (_IsOurHeap(hHeap)) { return _HeapDestroy(hHeap); } else { DPFN( eDbgLevelInfo, "NTHEAP: HeapDestroy"); return ORIGINAL_API(HeapDestroy)(hHeap); } } DWORD APIHOOK(RtlSizeHeap)( HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem ) { BOOL bRet = FALSE; if (UseOurHeap(hHeap, lpMem)) { bRet = _HeapSize(hHeap, dwFlags, lpMem); } else { DPFN( eDbgLevelInfo, "NTHEAP: RtlSizeHeap"); if (ValidateNTHeap(hHeap, lpMem)) { bRet = ORIGINAL_API(RtlSizeHeap)(hHeap, dwFlags, lpMem); } } return bRet; } BOOL APIHOOK(HeapValidate)( HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem ) { BOOL bRet = FALSE; if (UseOurHeap(hHeap, lpMem)) { // Win9x return values SetLastError(ERROR_CALL_NOT_IMPLEMENTED); bRet = -1; } else { DPFN( eDbgLevelInfo, "NTHEAP: HeapValidate"); __try { bRet = ORIGINAL_API(HeapValidate)(hHeap, dwFlags, lpMem); } __except(EXCEPTION_EXECUTE_HANDLER) { LOGN( eDbgLevelError, "[HeapValidate] %08lx:%08lx is invalid", hHeap, lpMem); } } return bRet; } HLOCAL APIHOOK(LocalAlloc)( UINT uFlags, SIZE_T uBytes ) { return _LocalAlloc(uFlags, uBytes); } HLOCAL APIHOOK(LocalFree)( HLOCAL hMem ) { HLOCAL hRet = NULL; if (_IsOurLocalHeap(hMem)) { hRet = _LocalFree(hMem); } else { DPFN( eDbgLevelInfo, "NTHEAP: LocalFree %08lx", hMem); __try { hRet = ORIGINAL_API(LocalFree)(hMem); } __except(EXCEPTION_EXECUTE_HANDLER) { LOGN( eDbgLevelError, "[LocalFree] Exception: Invalid Pointer %08lx", hMem); } } return hRet; } HLOCAL APIHOOK(LocalReAlloc)( HLOCAL hMem, SIZE_T uBytes, UINT uFlags ) { HLOCAL hRet = NULL; if (_IsOurLocalHeap(hMem)) { hRet = _LocalReAlloc(hMem, uBytes, uFlags); } else { DPFN( eDbgLevelInfo, "NTHEAP: LocalReAlloc %08lx", hMem); __try { hRet = ORIGINAL_API(LocalReAlloc)(hMem, uBytes, uFlags); } __except(EXCEPTION_EXECUTE_HANDLER) { LOGN( eDbgLevelError, "[LocalReAlloc] Exception: Invalid Pointer %08lx", hMem); } } return hRet; } LPVOID APIHOOK(LocalLock)( HLOCAL hMem ) { LPVOID pRet = NULL; if (_IsOurLocalHeap(hMem)) { pRet = _LocalLock(hMem); } else { DPFN( eDbgLevelInfo, "NTHEAP: LocalLock %08lx", hMem); __try { pRet = ORIGINAL_API(LocalLock)(hMem); } __except(EXCEPTION_EXECUTE_HANDLER) { LOGN( eDbgLevelError, "[LocalLock] Exception: Invalid Pointer %08lx", hMem); } } return pRet; } BOOL APIHOOK(LocalUnlock)( HLOCAL hMem ) { BOOL bRet = FALSE; if (_IsOurLocalHeap(hMem)) { bRet = _LocalUnlock(hMem); } else { DPFN( eDbgLevelInfo, "NTHEAP: LocalUnlock %08lx", hMem); __try { bRet = ORIGINAL_API(LocalUnlock)(hMem); } __except(EXCEPTION_EXECUTE_HANDLER) { LOGN( eDbgLevelError, "[LocalUnLock] Exception: Invalid Pointer %08lx", hMem); } } return bRet; } HANDLE APIHOOK(LocalHandle)( LPCVOID hMem ) { HANDLE hRet = NULL; if (_IsOurLocalHeap((HANDLE)hMem)) { hRet = _LocalHandle(hMem); } else { DPFN( eDbgLevelInfo, "NTHEAP: LocalHandle %08lx", hMem); __try { hRet = ORIGINAL_API(LocalHandle)(hMem); } __except(EXCEPTION_EXECUTE_HANDLER) { LOGN( eDbgLevelError, "[LocalHandle] Exception: Invalid Pointer %08lx", hMem); } } return hRet; } UINT APIHOOK(LocalSize)( HLOCAL hMem ) { UINT uRet = 0; if (_IsOurLocalHeap(hMem)) { uRet = _LocalSize(hMem); } else { DPFN( eDbgLevelInfo, "NTHEAP: LocalSize %08lx", hMem); __try { uRet = ORIGINAL_API(LocalSize)(hMem); } __except(EXCEPTION_EXECUTE_HANDLER) { LOGN( eDbgLevelError, "[LocalSize] Exception: Invalid Pointer %08lx", hMem); } } return uRet; } UINT APIHOOK(LocalFlags)( HLOCAL hMem ) { UINT uRet = 0; if (_IsOurLocalHeap(hMem)) { uRet = _LocalFlags(hMem); } else { DPFN( eDbgLevelInfo, "NTHEAP: LocalFlags %08lx", hMem); __try { uRet = ORIGINAL_API(LocalFlags)(hMem); } __except(EXCEPTION_EXECUTE_HANDLER) { LOGN( eDbgLevelError, "[LocalFlags] Exception: Invalid Pointer %08lx", hMem); } } return uRet; } HGLOBAL APIHOOK(GlobalAlloc)( UINT uFlags, SIZE_T uBytes ) { uFlags = (((uFlags & GMEM_ZEROINIT) ? LMEM_ZEROINIT : 0 ) | ((uFlags & GMEM_MOVEABLE) ? LMEM_MOVEABLE : 0 ) | ((uFlags & GMEM_FIXED) ? LMEM_FIXED : 0 )); return _LocalAlloc(uFlags, uBytes); } HGLOBAL APIHOOK(GlobalFree)( HGLOBAL hMem ) { HGLOBAL hRet = NULL; if (_IsOurLocalHeap(hMem)) { hRet = _LocalFree(hMem); } else { DPFN( eDbgLevelInfo, "NTHEAP: GlobalFree %08lx", hMem); __try { hRet = ORIGINAL_API(GlobalFree)(hMem); } __except(EXCEPTION_EXECUTE_HANDLER) { LOGN( eDbgLevelError, "[GlobalFree] Exception: Invalid Pointer %08lx", hMem); } } return hRet; } HGLOBAL APIHOOK(GlobalReAlloc)( HGLOBAL hMem, SIZE_T uBytes, UINT uFlags ) { UINT uLocalFlags = (((uFlags & GMEM_ZEROINIT) ? LMEM_ZEROINIT : 0 ) | ((uFlags & GMEM_MOVEABLE) ? LMEM_MOVEABLE : 0 ) | ((uFlags & GMEM_FIXED) ? LMEM_FIXED : 0 )); HLOCAL hRet = NULL; if (_IsOurLocalHeap(hMem)) { hRet = _LocalReAlloc(hMem, uBytes, uLocalFlags); } else { DPFN( eDbgLevelInfo, "NTHEAP: GlobalReAlloc %08lx", hMem); __try { hRet = ORIGINAL_API(GlobalReAlloc)(hMem, uBytes, uFlags); } __except(EXCEPTION_EXECUTE_HANDLER) { LOGN( eDbgLevelError, "[GlobalReAlloc] Exception: Invalid Pointer %08lx", hMem); } } return hRet; } LPVOID APIHOOK(GlobalLock)( HGLOBAL hMem ) { LPVOID pRet = NULL; if (_IsOurLocalHeap(hMem)) { pRet = _LocalLock(hMem); } else { DPFN( eDbgLevelInfo, "NTHEAP: GlobalLock %08lx", hMem); __try { pRet = ORIGINAL_API(GlobalLock)(hMem); } __except(EXCEPTION_EXECUTE_HANDLER) { LOGN( eDbgLevelError, "[GlobalLock] Exception: Invalid Pointer %08lx", hMem); } } return pRet; } BOOL APIHOOK(GlobalUnlock)( HGLOBAL hMem ) { BOOL bRet = FALSE; if (_IsOurLocalHeap(hMem)) { bRet = _LocalUnlock(hMem); } else { DPFN( eDbgLevelInfo, "NTHEAP: GlobalUnlock %08lx", hMem); __try { bRet = ORIGINAL_API(GlobalUnlock)(hMem); } __except(EXCEPTION_EXECUTE_HANDLER) { LOGN( eDbgLevelError, "[GlobalUnLock] Exception: Invalid Pointer %08lx", hMem); } } return bRet; } HANDLE APIHOOK(GlobalHandle)( LPCVOID hMem ) { HANDLE hRet = NULL; if (_IsOurLocalHeap((HANDLE)hMem)) { hRet = _LocalHandle(hMem); } else { DPFN( eDbgLevelInfo, "NTHEAP: GlobalHandle %08lx", hMem); __try { hRet = ORIGINAL_API(GlobalHandle)(hMem); } __except(EXCEPTION_EXECUTE_HANDLER) { LOGN( eDbgLevelError, "[GlobalHandle] Exception: Invalid Pointer %08lx for Heap", hMem); } } return hRet; } UINT APIHOOK(GlobalSize)( HGLOBAL hMem ) { UINT uRet = 0; if (_IsOurLocalHeap(hMem)) { uRet = _LocalSize(hMem); } else { DPFN( eDbgLevelInfo, "NTHEAP: GlobalSize %08lx", hMem); __try { uRet = ORIGINAL_API(GlobalSize)(hMem); } __except(EXCEPTION_EXECUTE_HANDLER) { LOGN( eDbgLevelError, "[GlobalSize] Exception: Invalid Pointer %08lx for Heap", hMem); } } return uRet; } UINT APIHOOK(GlobalFlags)( HGLOBAL hMem ) { UINT uRet = 0; if (_IsOurLocalHeap(hMem)) { uRet = _LocalFlags(hMem); // Convert the flags UINT uNewRet = uRet; uRet = 0; if (uNewRet & LMEM_DISCARDABLE) { uRet |= GMEM_DISCARDABLE; } if (uNewRet & LMEM_DISCARDED) { uRet |= GMEM_DISCARDED; } } else { DPFN( eDbgLevelInfo, "NTHEAP: GlobalFlags %08lx", hMem); __try { uRet = ORIGINAL_API(GlobalFlags)(hMem); } __except(EXCEPTION_EXECUTE_HANDLER) { LOGN( eDbgLevelError, "[GlobalFlags] Exception: Invalid Pointer %08lx for Heap", hMem); } } return uRet; } UINT APIHOOK(HeapCompact)( HANDLE hHeap, DWORD dwFlags ) { if (_IsOurHeap(hHeap)) { // Win9x return values SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return 0; } else { DPFN( eDbgLevelInfo, "NTHEAP: HeapCompact"); return ORIGINAL_API(HeapCompact)(hHeap, dwFlags); } } BOOL APIHOOK(HeapWalk)( HANDLE hHeap, LPPROCESS_HEAP_ENTRY pEntry ) { if (_IsOurHeap(hHeap)) { // Win9x return values SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return 0; } else { DPFN( eDbgLevelInfo, "NTHEAP: HeapWalk"); return ORIGINAL_API(HeapWalk)(hHeap, pEntry); } } BOOL APIHOOK(HeapLock)( HANDLE hHeap ) { if (_IsOurHeap(hHeap)) { // Win9x return values SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return 0; } else { DPFN( eDbgLevelInfo, "NTHEAP: HeapLock"); return ORIGINAL_API(HeapLock)(hHeap); } } BOOL APIHOOK(HeapUnlock)( HANDLE hHeap ) { if (_IsOurHeap(hHeap)) { // Win9x return values SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return 0; } else { DPFN( eDbgLevelInfo, "NTHEAP: HeapUnlock"); return ORIGINAL_API(HeapUnlock)(hHeap); } } /*++ Register hooked functions --*/ BOOL NOTIFY_FUNCTION( DWORD fdwReason ) { BOOL bRet = TRUE; if (fdwReason == DLL_PROCESS_ATTACH) { bRet = _HeapInit(); if (bRet) { LOGN(eDbgLevelInfo, "[NotifyFn] Win9x heap manager initialized"); } else { LOGN(eDbgLevelError, "[NotifyFn] Win9x heap manager initialization failed!"); } } return bRet; } HOOK_BEGIN CALL_NOTIFY_FUNCTION APIHOOK_ENTRY(KERNEL32.DLL, HeapCreate) APIHOOK_ENTRY(KERNEL32.DLL, HeapDestroy) APIHOOK_ENTRY(KERNEL32.DLL, HeapValidate) APIHOOK_ENTRY(KERNEL32.DLL, HeapCompact) APIHOOK_ENTRY(KERNEL32.DLL, HeapWalk) APIHOOK_ENTRY(KERNEL32.DLL, HeapLock) APIHOOK_ENTRY(KERNEL32.DLL, HeapUnlock) APIHOOK_ENTRY(KERNEL32.DLL, GetProcessHeap) APIHOOK_ENTRY(KERNEL32.DLL, LocalAlloc) APIHOOK_ENTRY(KERNEL32.DLL, LocalFree) APIHOOK_ENTRY(KERNEL32.DLL, LocalReAlloc) APIHOOK_ENTRY(KERNEL32.DLL, LocalLock) APIHOOK_ENTRY(KERNEL32.DLL, LocalUnlock) APIHOOK_ENTRY(KERNEL32.DLL, LocalHandle) APIHOOK_ENTRY(KERNEL32.DLL, LocalSize) APIHOOK_ENTRY(KERNEL32.DLL, LocalFlags) APIHOOK_ENTRY(KERNEL32.DLL, GlobalAlloc) APIHOOK_ENTRY(KERNEL32.DLL, GlobalFree) APIHOOK_ENTRY(KERNEL32.DLL, GlobalReAlloc) APIHOOK_ENTRY(KERNEL32.DLL, GlobalLock) APIHOOK_ENTRY(KERNEL32.DLL, GlobalUnlock) APIHOOK_ENTRY(KERNEL32.DLL, GlobalHandle) APIHOOK_ENTRY(KERNEL32.DLL, GlobalSize) APIHOOK_ENTRY(KERNEL32.DLL, GlobalFlags) APIHOOK_ENTRY(NTDLL.DLL, RtlAllocateHeap) APIHOOK_ENTRY(NTDLL.DLL, RtlReAllocateHeap) APIHOOK_ENTRY(NTDLL.DLL, RtlFreeHeap) APIHOOK_ENTRY(NTDLL.DLL, RtlSizeHeap) HOOK_END IMPLEMENT_SHIM_END