IT技术博客大学习 共学习 共进步
全部 移动开发 后端 数据库 AI 算法 安全 DevOps 前端 设计 开发者

内存越界的概念和调试方法

大树底下 2010-05-31 23:51:37 累计浏览 7,275 次
本机暂存

调试了两天,搞定了项目里一个内存越界的bug,于是记录一下,备查。

所谓内存越界(Heap Corruption),就是指当内存输入超出了预分配的空间大小,就会覆盖该空间之后的一段存储区域,导致系统异常。越界访问是非常常见的一种黑客手段。

内存越界通常有如下的表现:

1 程序出现异常 异常的现象有很多,如:代码突然跑到不相干的地方去执行,访问异常,正常的变量操作也报错…
     原因:代码段紊乱,this指针被改变,指针指向的数据被改变
2 程序按逻辑执行,但数据错误  如:变量内容突然消失
     原因:变量所在空间被其它操作误删除等
3 其它莫名其妙的错误

内存越界的常见原因:

1 写越界:      向10个字节的数组写入了20个字节;内存操作越界,如char szText[10];memset(szText,0,30);
2 错误的函数调用:   sprintf等fmt中的预定义和实际输入的变量数不一致,如sprintf(szData,"Name:%d title:%s",1)

3 错误的调用方式:  用stdcall 的函数指针 调用pascall的函数

此外,在VS2005之后的版本中,当Debug编译版本程序出现内存越界时,VS会捕获并提示,具体说来:

在VC里面,用release模式编译运行程序的时候,堆分配(Heap allocation)的时候调用的是malloc,如果你要分配10byte的空间,那么就会只分配10byte空间,而用debug模式的时候,堆分配调用的是_malloc_dbg,如果你只要分配10byte的空间,那么它会分配出除了你要的10byte之外,还要多出约36byte空间,用于存储一些薄记信息,debug堆分配出来之后就会按顺序连成一个链。

分配出的10byte空间的前面会有一个32byte的附加信息,存储的是一个_CrtMemBlockHeader结构,可以在DBGINT.H中找到该结构的定义:

typedef struct _CrtMemBlockHeader
{
// Pointer to the block allocated just before this one:
   struct _CrtMemBlockHeader *pBlockHeaderNext;
// Pointer to the block allocated just after this one:
   struct _CrtMemBlockHeader *pBlockHeaderPrev;
   char *szFileName;    // File name
   int nLine;                  // Line number
   size_t nDataSize;      // Size of user block
   int nBlockUse;         // Type of block
   long lRequest;          // Allocation number
// Buffer just before (lower than) the user's memory:
   unsigned char gap[nNoMansLandSize];
} _CrtMemBlockHeader;

/* In an actual memory block in the debug heap,
 * this structure is followed by:
 *   unsigned char data[nDataSize];
 *   unsigned char anotherGap[nNoMansLandSize];
 */

szFileName是存储的发起分配操作的那行代码所在的文件的路径和名称,而nLine则是行号。nDataSize是请求分配的大小,我们的例子里当然就是10了,nBlockUse是类型,而lRequest是请求号。最后一项gap,又称NoMansLand,是 4byte(nNoMansLandSize=4)大小的一段区域,注意看最后几行注释就明白了,在这个结构后面跟的是用户真正需要的10byte数据区域,而其后还跟了一个4byte的Gap,那么也就是说用户申请分配的区域是被一个头结构,和一个4byte的gap包起来的。在释放这10byte空间的时候,会检查这些信息。Gap被分配之后会被以0xFD填充。检查中如果gap中的值变化了,就会以Assert fail的方式报错。不过vc6中提示的比较难懂,DAMAGE :after Normal block(#dd) at 0xhhhhhhhh,而vs2005里面会提示Heap Corruption Detected!而如果你是release版本,那么这个错误就会潜伏直到它的破坏力发生作用。

排查内存越界错误的方法包括:

1.使用BoundChecker进行调试,BoundChecker在代码编译时加入了大量的附加处理,其中包括内存堆栈检测等,其实ms的debug模式也做了许多的类似操作,但debug模式下的一些代码行为,如初始化变量,和Release下的代码执行不同,所以用debug调试不是完美的方式

2.类内部出现莫名其妙的错误时,查看 this 指针是否变化,必要的时候进行手手工检查

3.在调试的 "查看" 窗口 输入:@err,hr 看全局错误变量的内容

4.注释掉部分代码,看是否错误还出现,注释的最佳方式是:二分法

5.查看程序异常处的反汇编代码,分析原因

6.最后的办法:代码复查

我使用的是VS2010,所以没法安装BoundChecker等工具,最后只能通过代码复查,找到了错误所在

this->distr = (float *)REALLOC(this->distr,newcount*sizeof(float));

其中,斜体部分是缺失的代码。由于申请的内存比计划的少了75%,访问时自然就越界了。

以上是我的总结,大家多多指正。

同分类推荐文章

  1. 绿盟科技《APT组织研究年鉴》(2026 版)正式发布 (2026-06-16 20:21:10)
  2. 【已复现】Linux内核Fragnesia权限提升漏洞(CVE-2026-46300) (2026-06-15 10:53:58)
  3. 企业文档安全最佳实践(二):给文档上“身份证”——手动标密与智能自动标密 (2026-06-12 17:18:33)

查看更多 安全 文章 →

建议继续学习

  1. Fix Bug的五个阶段 (累计阅读 42,972)
  2. 调试工具之GDB (累计阅读 14,828)
  3. gdb的基本工作原理是什么? (累计阅读 11,679)
  4. 深入理解Nginx之调试优化技巧 (累计阅读 8,223)
  5. webapp网页调试工具Chrome Devtools (累计阅读 6,982)
  6. 十五个只有程序员会乐的事情 (累计阅读 6,805)
  7. 程序员的样子 (累计阅读 6,103)
  8. libcurl中使用curl_easy_getinfo 产生段错误分析 (累计阅读 5,780)
  9. 让邮件飞一会儿 (累计阅读 5,709)
  10. 移动终端开发必备知识 (累计阅读 4,993)