hurtdeep
驱动牛犊
驱动牛犊
  • 注册日期2003-07-17
  • 最后登录2009-04-01
  • 粉丝0
  • 关注0
  • 积分11分
  • 威望35点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
阅读:3229回复:0

内存检测

楼主#
更多 发布于:2009-04-01 09:49
长期以来,对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
}


在以后有时间的情况下,我会继续探讨下去,这里希望一些高手能指点一下。                                                                  
                                                                                          仝少飞

游客

返回顶部