#include #include #include "prtypes.h" #include "prlock.h" #include "nscore.h" #include "nsAutoLock.h" #include "nsStackFrameWin.h" #include "nsDebugHelpWin32.h" #include "nsTraceMallocCallbacks.h" // XXX These are *very* quick hacks and need improvement! static PRBool GetSymbolFromAddress(uint32 addr, char* outBuf) { PRBool ok; ok = EnsureSymInitialized(); if(!ok) return ok; char buf[sizeof(IMAGEHLP_SYMBOL) + 512]; PIMAGEHLP_SYMBOL symbol = (PIMAGEHLP_SYMBOL) buf; symbol->SizeOfStruct = sizeof(buf); symbol->MaxNameLength = 512; DWORD displacement; ok = SymGetSymFromAddr(::GetCurrentProcess(), addr, &displacement, symbol); if(ok) { char buf2[512]; sprintf(buf2, "%s+0x%08X", symbol->Name, displacement); strcat(outBuf, buf2); } else strcat(outBuf, "dunno"); return ok; } static PRBool GetModuleFromAddress(uint32 addr, char* outBuf) { PRBool ok; ok = EnsureSymInitialized(); if(!ok) return ok; IMAGEHLP_MODULE module; module.SizeOfStruct = sizeof(IMAGEHLP_MODULE); ok = SymGetModuleInfo(::GetCurrentProcess(), addr, &module); if(ok) strcat(outBuf, module.ModuleName); else strcat(outBuf, "dunno"); return ok; } /***************************************************************************/ #ifdef VERBOSE #define SHOW(x_, buf_) \ printf(#x_" = %#x... %s\n", x_, \ (buf_[0] = 0, \ GetModuleFromAddress((uint32)x_, buf_), \ strcat(buf," :: "), \ GetSymbolFromAddress((uint32)x_, buf_), \ buf_)); #else #define SHOW(x_, buf_) #endif //VERBOSE /***************************************************************************/ #ifdef VERBOSE // XXX This is a quick hack to show that x86 Win32 stack walking can be done // with this sort of loop following the bp. void dumpStack() { uint32* bp_; uint32* bpdown; uint32 pc; char buf[512]; _asm { mov bp_ , ebp } /* Stack walking code adapted from Kipp's "leaky". */ while (1) { bpdown = (uint32*) *bp_++; pc = *bp_; // These addresses are iffy... if (pc < 0x00400000 || pc > 0x7fffffff || bpdown < bp_) break; SHOW(pc, buf); bp_ = bpdown; } printf("\n"); } #endif char* _stdcall call2(void* v) { // dumpStack(); // return 0; return (char *)malloc(123); } int call1(char c, int i, double d, ... ) { free(call2(0)); return 0; } /***************************************************************************/ // shows how to use the dhw stuff to hook imported functions static BOOL g_lockOut = FALSE; //stop reentrancy DHW_DECLARE_FUN_TYPE_AND_PROTO(dhw_malloc, void*, __cdecl, MALLOC_, (size_t)); DHWImportHooker &getMallocHooker() { static DHWImportHooker gMallocHooker("MSVCRTD.dll", "malloc", (PROC) dhw_malloc); return gMallocHooker; } void * __cdecl dhw_malloc( size_t size ) { PRUint32 start = PR_IntervalNow(); void* result = DHW_ORIGINAL(MALLOC_, getMallocHooker())(size); PRUint32 end = PR_IntervalNow(); if (g_lockOut) return result; g_lockOut = TRUE; #ifdef VERBOSE printf("* malloc called to get %d bytes. returned %#x\n", size, result); #endif MallocCallback(result, size, start, end); // dumpStack(); // printf("\n"); g_lockOut = FALSE; return result; } DHW_DECLARE_FUN_TYPE_AND_PROTO(dhw_calloc, void*, __cdecl, CALLOC_, (size_t,size_t)); DHWImportHooker &getCallocHooker() { static DHWImportHooker gCallocHooker("MSVCRTD.dll", "calloc", (PROC) dhw_calloc); return gCallocHooker; } void * __cdecl dhw_calloc( size_t count, size_t size ) { PRUint32 start = PR_IntervalNow(); void* result = DHW_ORIGINAL(CALLOC_, getCallocHooker())(count,size); PRUint32 end = PR_IntervalNow(); if (g_lockOut) return result; g_lockOut = TRUE; #ifdef VERBOSE printf("* calloc called to get %d many of %d bytes. returned %#x\n", count, size, result); #endif CallocCallback(result, count, size, start, end); // dumpStack(); // printf("\n"); g_lockOut = FALSE; return result; } DHW_DECLARE_FUN_TYPE_AND_PROTO(dhw_free, void, __cdecl, FREE_, (void*)); DHWImportHooker &getFreeHooker() { static DHWImportHooker gFreeHooker("MSVCRTD.dll", "free", (PROC) dhw_free); return gFreeHooker; } void __cdecl dhw_free( void* p ) { PRUint32 start = PR_IntervalNow(); DHW_ORIGINAL(FREE_, getFreeHooker())(p); PRUint32 end = PR_IntervalNow(); if (g_lockOut) return; g_lockOut = TRUE; #ifdef VERBOSE printf("* free called for %#x\n", p); #endif FreeCallback(p, start, end); // dumpStack(); // printf("\n"); g_lockOut = FALSE; } DHW_DECLARE_FUN_TYPE_AND_PROTO(dhw_realloc, void*, __cdecl, REALLOC_, (void*, size_t)); DHWImportHooker &getReallocHooker() { static DHWImportHooker gReallocHooker("MSVCRTD.dll", "realloc", (PROC) dhw_realloc); return gReallocHooker; } void * __cdecl dhw_realloc(void * pin, size_t size) { PRUint32 start = PR_IntervalNow(); void* pout = DHW_ORIGINAL(REALLOC_, getReallocHooker())(pin, size); PRUint32 end = PR_IntervalNow(); if (g_lockOut) return pout; g_lockOut = TRUE; #ifdef VERBOSE printf("* realloc called to resize to %d. old ptr: %#x. new ptr: %#x\n", size, pin, pout); #endif ReallocCallback(pin, pout, size, start, end); // dumpStack(); // printf("\n"); g_lockOut = FALSE; return pout; } // Note the mangled name! DHW_DECLARE_FUN_TYPE_AND_PROTO(dhw_new, void*, __cdecl, NEW_, (size_t)); DHWImportHooker &getNewHooker() { static DHWImportHooker gNewHooker("MSVCRTD.dll", "??2@YAPAXI@Z", (PROC) dhw_new); return gNewHooker; } void * __cdecl dhw_new(size_t size) { PRUint32 start = PR_IntervalNow(); void* result = DHW_ORIGINAL(NEW_, getNewHooker())(size); PRUint32 end = PR_IntervalNow(); if (g_lockOut) return result; g_lockOut = TRUE; #ifdef VERBOSE printf("* new called to get %d bytes. returned %#x\n", size, result); dumpStack(); #endif MallocCallback(result, size, start, end);//do we need a different one for new? // printf("\n"); g_lockOut = FALSE; return result; } // Note the mangled name! DHW_DECLARE_FUN_TYPE_AND_PROTO(dhw_delete, void, __cdecl, DELETE_, (void*)); DHWImportHooker &getDeleteHooker() { static DHWImportHooker gDeleteHooker("MSVCRTD.dll", "??3@YAXPAX@Z", (PROC) dhw_delete); return gDeleteHooker; } void __cdecl dhw_delete(void* p) { PRUint32 start = PR_IntervalNow(); DHW_ORIGINAL(DELETE_, getDeleteHooker())(p); PRUint32 end = PR_IntervalNow(); if (g_lockOut) return; g_lockOut = TRUE; #ifdef VERBOSE printf("* delete called for %#x\n", p); dumpStack(); #endif FreeCallback(p, start, end); // printf("\n"); g_lockOut = FALSE; } /***************************************************************************/ // A demonstration of using the _CrtSetAllocHook based hooking. // This system sucks because you don't get to see the allocated pointer. #if 0 class myAllocationSizePrinter : public DHWAllocationSizeDebugHook { public: PRBool AllocHook(size_t size) { alloc_calls++ ; total_mem += size; if(verbosity) { printf("alloc called to get %d bytes.\n", size); dumpStack(); } return PR_TRUE; } PRBool ReallocHook(size_t size, size_t sizeOld) { realloc_calls++ ; total_mem += size; total_mem -= sizeOld; if(verbosity) { printf("realloc called to size to %d bytes. Old size: %d.\n", size, sizeOld); dumpStack(); } return PR_TRUE; } PRBool FreeHook(size_t size) { free_calls++ ; total_mem -= size; if(verbosity) { printf("free called to release %d bytes.\n", size); dumpStack(); } return PR_TRUE; } myAllocationSizePrinter(int v) : verbosity(v), alloc_calls(0), realloc_calls(0), free_calls(0), total_mem(0) {} virtual ~myAllocationSizePrinter(){} void report() { printf("%d allocs, %d reallocs, %d frees, %d bytes leaked\n", alloc_calls, realloc_calls, free_calls, total_mem); } private: void dumpStack() { if(verbosity == 2) ::dumpStack(); } int verbosity; int alloc_calls; int realloc_calls; int free_calls; size_t total_mem; }; #endif /*C Callbacks*/ PR_IMPLEMENT(void) StartupHooker() { if (!EnsureSymInitialized()) return; //run through get all hookers DHWImportHooker &loadlibraryW = DHWImportHooker::getLoadLibraryWHooker(); DHWImportHooker &loadlibraryExW = DHWImportHooker::getLoadLibraryExWHooker(); DHWImportHooker &loadlibraryA = DHWImportHooker::getLoadLibraryAHooker(); DHWImportHooker &loadlibraryExA = DHWImportHooker::getLoadLibraryExAHooker(); DHWImportHooker &mallochooker = getMallocHooker(); DHWImportHooker &reallochooker = getReallocHooker(); DHWImportHooker &callochooker = getCallocHooker(); DHWImportHooker &freehooker = getFreeHooker(); DHWImportHooker &newhooker = getNewHooker(); DHWImportHooker &deletehooker = getDeleteHooker(); printf("Startup Hooker\n"); } PR_IMPLEMENT(void) ShutdownHooker() { } #if 0 int main() { // A demonstration of using the (sucky) _CrtSetAllocHook based hooking. myAllocationSizePrinter ap(0); dhwSetAllocationSizeDebugHook(&ap); // show that the ImportHooker is hooking calls from loaded dll DHW_DECLARE_FUN_TYPE(void, __stdcall, SOMECALL_, (void)); HMODULE module = ::LoadLibrary("Other.dll"); if(module) { SOMECALL_ _SomeCall = (SOMECALL_) GetProcAddress(module, "SomeCall"); if(_SomeCall) _SomeCall(); } // show that the ImportHooker is hooking sneaky calls made from this dll. HMODULE module2 = ::LoadLibrary("MSVCRTD.dll"); if(module2) { MALLOC_ _sneakyMalloc = (MALLOC_) GetProcAddress(module2, "malloc"); if(_sneakyMalloc) { void* p = _sneakyMalloc(987); free(p); } } call1('x', 1, 1.0, "hi there", 2); char* p = new char[10]; delete p; void* v = malloc(10); v = realloc(v, 15); v = realloc(v, 5); free(v);` ap.report(); dhwClearAllocationSizeDebugHook(); return 0; } #endif //0