阅读:3283回复:0
内存检测
长期以来,对windows下从c++的开发始终存在着一个头痛的问题,那就是内存泄漏的问题。
记得早期我对内存泄漏的检测也是慢慢改进的 初始阶段主要是靠经验,后来主要是自己写内存管理类来分配,释放等,效果还是不错的, 此和VC中提供了CRT 内存管理机制的思路是差不多的。 但是出于无奈,我们一些软件公司在开发一些项目时,没有重视内存分配,直接使用new 等分配内存, 从而使得最后有些内存泄漏无法彻底清除,当然也有很多内存检测工具供你使用,但是有时效果总是 不佳。 于是,我觉得自己可以尝试一下写个简单实用的工具,主要是面向检测windows下的VC或VC.Net程序。 下面是我近来几个星期的一些尝试和经验。由于水平有限和自己工作比较忙,基本都是花很少时间尝试而 没有深入研究,所以仅供参考。 1:首先我想到的是既然用new 来分配内存,我只要截获new调用的API,并hook,然后加入自己的处理函数 所以第一我就想到了过滤驱动,遗憾的是windows内存管理并没有驱动,这是显而易见的,所以排除掉。 我马上想到了内存hook,所以经过跟踪发现 new是调用 HeapAlloc(kernel32.dll) 于是Hook HeapAlloc,Hook成功后却发现两个棘手的问题。 A:多线程问题 ,B 重入问题 由于 HeapAlloc是经常被调用的API而且是在不同的线程,而Hook函数中要将HeapAlloc的源码恢复(会API hook的应该很清楚), 显然对源码恢复的代码一定要串行化,而串行化不能使用windows提供的一些同步对象,因为这些对象中也 使用了New,所以会导致重入而递归调用。 因此我用汇编写了一个lock对象,主要是用到i386汇编中的lock cmpxchg(锁住总线和比较与交换) 去掉Hook函数中所有不必要的函数以免重入。如下 Hook(…) { Lock, 调用原HeapAlloc //Send new memory address to another process(避免重入,让其他进程处理) Unlock Return } 以上对一些小的程序还可以,但是对一些大的工程总是运行到一定程度出现异常, 为什么会出现异常,一直困扰着我,至今不知原因,我只是凭经验猜想有可能是Hook函数和HeapAlloc 是在不同的Module所导致的。 由于没有时间深究和水平有限所以放弃。 2:凭经验猜想有可能是Hook函数和HeapAlloc是在不同的Module所导致的。所以我想到直接修改HeapAlloc所在的DLL。 经过对kernel32.dll的分析,发现HeapAlloc只是简单的forward call RtlAllocateHeap(ntdll.dll) 由于ntdll.dll非常紧凑,所以要在ntdll.dll中新加一个export函数比较困难,于是我选择了一个我们工程没有 Import的一个不使用的函数下手,改成的函数大概如下 LPVOID pRet = RtlAllocateHeap(hHeap,dwFlags,dwBytes); if( (DWORD)pRet >= 0xXXXXXXXX&& (DWORD)pRet <= 0xXXXXXXXX) { _asm { int 3; } } return pRet; 当然通过对汇编的不断调试和改进,顺利的修改了此函数为新的RtlAllocateHeap,改原RtlAllocateHeap为RtlAllocateHeab 将新的ntdll.dll copy 到我们运行的工程目录下,却发现我们的工程文件始终调用system32下的ntdll.dll,我于是修改了 工程文件的import接口,让他强制调用新的ntdll.dll。 然而修改后的工程文件总会出现异常,不知什么原因,想直接修改system32下的ntdll.dll,但害怕系统崩溃,这可需大量时间, 然后放弃。 3:经过思考,我想既然 .net 中的debug使用CRT进行了内存管理,就先看看.net的CRT 经过对CRT的源码跟踪有个惊喜发现。 extern "C" _CRTIMP void * __cdecl _malloc_dbg ( size_t nSize, int nBlockUse, const char * szFileName, int nLine ) { void *res = _nh_malloc_dbg(nSize, _newmode, nBlockUse, szFileName, nLine); RTCCALLBACK(_RTC_Allocate_hook, (res, nSize, 0)); return res; } RTCCALLBACK,显然CRT内存管理提供自己的hook函数,提供连接。 Here are the links to MSDN articles about CRT debugging techniques and hooking: http://msdn.microsoft.com/en-us/library/zh712wwf.aspx http://msdn.microsoft.com/en-us/library/z2zscsc2.aspx Sample of CRT hooks usage: http://msdn.microsoft.com/en-us/library/b31tft51.aspx 程序如下, _CrtSetAllocHook( MyAllocHook ) ; ________________________________________ int __cdecl MyAllocHook( int nAllocType, void * pvData, size_t nSize, int nBlockUse, long lRequest, const unsigned char * szFileName, int nLine ) { char *operation[] = { "", "allocating", "re-allocating", "freeing" }; char *blockType[] = { "Free", "Normal", "CRT", "Ignore", "Client" }; if ( nBlockUse == _CRT_BLOCK ) // Ignore internal C runtime library allocations return( TRUE ); _ASSERT( ( nAllocType > 0 ) && ( nAllocType < 4 ) ); _ASSERT( ( nBlockUse >= 0 ) && ( nBlockUse < 5 ) ); fprintf( logFile, "Memory operation in %s, line %d: %s a %d-byte '%s' block (#%ld)\n", szFileName, nLine, operation[nAllocType], nSize, blockType[nBlockUse], lRequest ); if ( pvData != NULL ) fprintf( logFile, " at %p", pvData ); return( TRUE ); // Allow the memory operation to proceed } 在以后有时间的情况下,我会继续探讨下去,这里希望一些高手能指点一下。 仝少飞 |
|