在Linux中运行的C程序出现内存泄漏现象,怎么解决?

在Linux中运行的C程序出现内存泄漏现象,怎么解决?,第1张

内存泄漏指由于疏忽或错误山余造成程序未能释放已经不再使用的内存的情况。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存早瞎后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪费。

可以使用相应的软件测试工具对软件进行检测。

1. ccmalloc-Linux和Solaris下对C和C++程序的简单的使用内存泄漏和malloc调试库。

2. Dmalloc-Debug Malloc Library.

3. Electric

Fence-Linux分发版中由Bruce Perens编写的malloc()调试库。

4. Leaky-Linux下检测内存泄漏的程序。

5. LeakTracer-Linux、Solaris和HP-UX下跟踪和分析C++程序中的内存泄漏。

6. MEMWATCH-由Johan

Lindh编写,是一个开放源代码C语言内存错误检测工具,主要是通过gcc的precessor来进行。

7. Valgrind-Debugging and profiling Linux programs, aiming at

programs written in C and C++.

8. KCachegrind-A visualization tool for the profiling data

generated by Cachegrind and Calltree.

9. Leak

Monitor-一个Firefox扩展,能找出跟Firefox相关的泄漏类型。

10. IE Leak Detector

(Drip/IE Sieve)-Drip和IE Sieve leak

detectors帮助网页开发员提升动态网页性能通过报告可避免的因为IE局限的内存泄漏。

11. Windows Leaks

Detector-探测任何Win32应用程序中的任何资源泄漏(内存,句柄等),基于Win API调用钩子。

12. SAP Memory

Analyzer-是一款开源的JAVA内存分析软件,可用于陆唯空辅助查找JAVA程序的内存泄漏,能容易找到大块内存并验证谁在一直占用它,它是基于Eclipse

RCP(Rich Client Platform),可以下载RCP的独立版本或者Eclipse的插件。

13. DTrace-即动态跟踪Dynamic

Tracing,是一款开源软件,能在Unix类似平台运行,用户能够动态检测 *** 作系统内核和用户进程,以更精确地掌握系统的资源使用状况,提高系统性能,减少支持成本,并进行有效的调节。

14. IBM Rational PurifyPlus-帮助开发人员查明C/C++、托管.NET、Java和VB6代码中的性能和可靠性错误。PurifyPlus

将内存错误和泄漏检测、应用程序性能描述、代码覆盖分析等功能组合在一个单一、完整的工具包中。

15. Parasoft Insure++-针对C/C++应用的运行时错误自动检测工具,它能够自动监测C/C++程序,发现其中存在着的内存破坏、内存泄漏、指针错误和I/O等错误。并通过使用一系列独特的技术(SCI技术和变异测试等),彻底的检查和测试我们的代码,精确定位错误的准确位置并给出详细的诊断信息。能作为Microsoft

Visual C++的一个插件运行。

16. Compuware DevPartner for Visual C++ BoundsChecker

Suite-为C++开发者设计的运行错误检测和调试工具软件。作为Microsoft Visual Studio和C++ 6.0的一个插件运行。

17. Electric Software GlowCode-包括内存泄漏检查,code

profiler,函数调用跟踪等功能。给C++和.Net开发者提供完整的错误诊断,和运行时性能分析工具包。

18. Compuware DevPartner Java

Edition-包含Java内存检测,代码覆盖率测试,代码性能测试,线程死锁,分布式应用等几大功能模块。

19. Quest JProbe-分析Java的内存泄漏。

20. ej-technologies JProfiler-一个全功能的Java剖析工具,专用于分析J2SE和J2EE应用程序。它把CPU、执行绪和内存的剖析组合在一个强大的应用中。JProfiler可提供许多IDE整合和应用服务器整合用途。JProfiler直觉式的GUI让你可以找到效能瓶颈、抓出内存泄漏、并解决执行绪的问题。4.3.2注册码:A-G666#76114F-1olm9mv1i5uuly#0126

21. BEA JRockit-用来诊断Java内存泄漏并指出根本原因,专门针对Intel平台并得到优化,能在Intel硬件上获得最高的性能。

22. SciTech Software AB .NET Memory

Profiler-找到内存泄漏并优化内存使用针对C#,VB.Net,或其它.Net程序。

23. YourKit .NET &Java Profiler-业界领先的Java和.NET程序性能分析工具。

24. AutomatedQA AQTime-AutomatedQA的获奖产品performance profiling和memory

debugging工具集的下一代替换产品,支持Microsoft, Borland, Intel, Compaq 和

GNU编译器。可以为.NET和Windows程序生成全面细致的报告,从而帮助您轻松隔离并排除代码中含有的性能问题和内存/资源泄露问题。支持.Net

1.0,1.1,2.0,3.0和Windows 32/64位应用程序。

25. JavaScript Memory Leak Detector-微软全球产品开发欧洲团队(Global Product

Development- Europe team, GPDE)

发布的一款调试工具,用来探测JavaScript代码中的内存泄漏,运行为IE系列的一个插件。

#include<stdio.h>

int main()

{

int a

scanf("%d",&a)

switch(a)

{

case 1:a=a+1

case 2:a=a+1

default: a=a+2

}

printf("%d\n",a)

return 0

}

例如这个,你输入1按照实际要求应该是输出2的,但是没团败此写break后,a从case 1里没跳出switch,使得塌迅a=2进入case 2,a=3,然后又进入default,a=5,最后就输出枯枣5了(如果写了break,应该是2的)

缓冲区溢出漏洞入门介绍

文/hokersome

一、引言

不管你是否相信,几十年来,缓冲区溢出一直引起许多严重的安全性问题。甚至毫不夸张的说,当前网络种种安全问题至少有50%源自缓冲区溢出的问题。远的不说,一个冲击波病毒已经令人谈溢出色变了。而作为一名黑客,了解缓冲区溢出漏洞则是一门必修课。网上关于溢出的漏洞的文章有很多,但是大多太深或者集中在一个主题,不适合初学者做一般性了解。为此,我写了这篇文章,主要是针对初学者,对缓冲区溢出漏洞进行一般性的介绍。

缓冲区溢出漏洞是之所以这么多,是在于它的产生是如此的简单。只要C/C++程序员稍微放松警惕,他的代码里面可能就出现了一个缓冲区溢出漏洞,甚至即使经过仔细检查的代码,也会存在缓冲区溢出漏洞。

二、溢出

听我说了这些废话,你一定很想知道究竟什么缓冲区溢出漏洞,溢出究竟是怎么发生的。好,现在我们来先弄清楚什么是溢出。以下的我将假设你对C语言编程有一点了解,一点点就够了,当然,越多越好。

尽管缓冲区溢出也会发生在非C/C++语言上,但考虑到各种语言的运用程度,我们可以在某种程度上说,缓冲区溢出是C/C++的专利。相信我,如果你在一个用VB写的程序里面找溢出漏洞,你将会很出名。回到说C/C++,在这两种使用非常广泛的语言里面,并没有边界来检查数组和指针的引用,这样做的目的是为了提高效率,而不幸的是,这也留下了严重的安全问题。先看下面一段简单的代码:

#include<stdio.h>

void main()

{

char buf[8]

gets(buf)

}

程序运行的时候,如果你输入“Hello”,或者“Kitty”,那么一切正常,但是如果输入“Today is a good day”,那么我得通知你,程序发生溢出了。很显然,buf这个数组只申请到8个字节的内存空间,而输入的字符却超过了这个数目,于是,多余的字符将会占领程序中不属于自己的内存。因为C/C++语言并不检查边界,于是,程序将看似正常继续运行。如果被溢出部分占领的内存并不重要,或者是一块没有使用的内存,那么,程序将会继续看似正常的运行到结束。但是,如果溢出部分占领的正好的是存放了程序重要数据的内存,那么一切将会不堪设想。

实际上,缓冲区溢出通常有两种,堆溢出和堆栈溢出。尽管两者实质都是一样,但由于利用的方式不同,我将在下面分开介绍。不过在介绍之前,还是来做一些必要的知识预备。

三、知识预备

要理解大多数缓冲区溢出的本质,首先需要理解当程序运行时机器中的内存是如何分配的。在许多系统上,每个进程都有其自己的虚拟地址空间,它们以某种方式映射到羡知实际内存。我们不必关心描述用来将虚拟地址空间映射成基本体系结构的确切机制,而只关心理论上允许寻址大块连续内存的进程。

程序运行时,其内存里面一般都包含这些部分:1)程序参数和程序环境;2)程序堆栈,它通常在程序执行时增长,一般情况下,它向下朝堆增长。3)堆,它也在程序执行时增长,相反,它向上朝堆栈增长;4)BSS 段,它铅派姿包含未初始化的全局可用的数据(例如,全局变量); 5)数据段,它包含初始化的全局可用的数据(通常是全局变量);6)文本段,它包含只读程序代码。BSS、数据和文本段组成静态内存:在程序运行之前这些段的大小已经固定。程序运行时虽然可以更改个别变量,但不能将数据分配到这些段中。下面以一个简单的例子来说明以上的看起来让人头晕的东西:

#include<stdio.h>

char buf[3]="abc"

int i

void main()

{

i=1

return

}

其中,i属于BBS段,而buf属于数据段。两者都属于静态内存,因为他们在程序中虽然可以改变值,但是其分配的内存大小是固定的,如buf的数据大于三个字符,将会覆盖其他数据。

与静态内存形成对比,堆和堆栈是动态的,可以在程序运行的时候改变大小。堆的程序员接口因语言而异。在C语言中,堆是经由 malloc() 和其它相关函数来访问的,而C++中的new运算符则是堆的程序槐绝员接口。堆栈则比较特殊,主要是在调用函数时来保存现场,以便函数返回之后能继续运行。

四、堆溢出

堆溢出的思路很简单,覆盖重要的变量以达到自己的目的。而在实际 *** 作的时候,这显得比较困难,尤其是源代码不可见的时候。第一,你必须确定哪个变量是重要的变量;第二,你必须找到一个内存地址比目标变量低的溢出点;第三,在特定目的下,你还必须让在为了覆盖目标变量而在中途覆盖了其他变量之后,程序依然能运行下去。下面以一个源代码看见的程序来举例演示一次简单的堆溢出是如何发生的:

#include "malloc.h"

#include "string.h"

#include "stdio.h"

void main()

{

char *large_str = (char *)malloc(sizeof(char)*1024)

char *important = (char *)malloc(sizeof(char)*6)

char *str = (char *)malloc(sizeof(char)*4)

strcpy(important,"abcdef")//给important赋初值

//下面两行代码是为了看str和important的地址

printf("%d/n",str)

printf("%d/n",important)

gets(large_str)//输入一个字符串

strcpy(str, large_str)//代码本意是将输入的字符串拷贝到str

printf("%s/n",important)

}

在实际应用中,这样的代码当然是不存在的,这只是一个最简单的实验程序。现在我们的目标是important这个字符串变成"hacker"。str和important的地址在不同的环境中并不是一定的,我这里是7868032和7868080。很好,important的地址比str大,这就为溢出创造了可能。计算一下可以知道,两者中间隔了48个字节,因此在输入溢出字符串时候,可以先输入48个任意字符,然后再输入hakcer回车,哈哈,出来了,important成了"hacker"。

五、堆栈溢出

堆溢出的一个关键问题是很难找到所谓的重要变量,而堆栈溢出则不存在这个问题,因为它将覆盖一个非常重要的东西----函数的返回地址。在进行函数调用的时候,断点或者说返回地址将保存到堆栈里面,以便函数结束之后继续运行。而堆栈溢出的思路就是在函数里面找到一个溢出点,把堆栈里面的返回地址覆盖,替换成一个自己指定的地方,而在那个地方,我们将把一些精心设计了的攻击代码。由于攻击代码的编写需要一些汇编知识,这里我将不打算涉及。我们这里的目标是写出一个通过覆盖堆栈返回地址而让程序执行到另一个函数的堆栈溢出演示程序。

因为堆栈是往下增加的,因此,先进入堆栈的地址反而要大,这为在函数中找到溢出点提供了可能。试想,而堆栈是往上增加的,我们将永远无法在函数里面找到一个溢出点去覆盖返回地址。还是先从一个最简单的例子开始:

void test(int i)

{

char buf[12]

}

void main()

{

test(1)

}

test 函数具有一个局部参数和一个静态分配的缓冲区。为了查看这两个变量所在的内存地址(彼此相对的地址),我们将对代码略作修改:

void test(int i)

{

char buf[12]

printf("&i = %d/n", &i)

printf("&buf[0] = %d/n", buf)

}

void main()

{

test(1)

}

需要说明的是,由于个人习惯的原因,我把地址结果输出成10进制形式,但愿这并不影响文章的叙述。在我这里,产生下列输出:&i = 6684072 &buf[0] = 6684052。这里我补充一下,当调用一个函数的时候,首先是参数入栈,然后是返回地址。并且,这些数据都是倒着表示的,因为返回地址是4个字节,所以可以知道,返回地址应该是保存在从6684068到6684071。因为数据是倒着表示的,所以实际上返回地址就是:buf[19]*256*256*256+buf[18]*256*256+buf[17]*256+buf[16]。

我们的目标还没有达到,下面我们继续。在上面程序的基础,修改成:

#include <stdio.h>

void main()

{

void test(int i)

test(1)

}

void test(int i)

{

void come()

char buf[12]//用于发生溢出的数组

int addr[4]

int k=(int)&i-(int)buf//计算参数到溢出数组之间的距离

int go=(int)&come

//由于EIP地址是倒着表示的,所以首先把come()函数的地址分离成字节

addr[0]=(go <<24)>>24

addr[1]=(go <<16)>>24

addr[2]=(go <<8)>>24

addr[3]=go>>24

//用come()函数的地址覆盖EIP

for(int j=0j<4j++)

{

buf[k-j-1]=addr[3-j]

}

}

void come()

{

printf("Success!")

}

一切搞定!运行之后,"Success!"成功打印出来!不过,由于这个程序破坏了堆栈,所以系统会提示程序遇到问题需要关闭。但这并不要紧,因为至少我们已经迈出了万里长征的第一步。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/12221245.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-21
下一篇 2023-05-21

发表评论

登录后才能评论

评论列表(0条)

保存